Skip to content

Commit

Permalink
Refactor to fix constant redraw of word aligner. Still much cleanup t…
Browse files Browse the repository at this point in the history
…o do.
  • Loading branch information
PhotoNomad0 committed May 9, 2024
1 parent 475e728 commit 26ea6e6
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 58 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@
"autoprefixer": "^10.1.0",
"bible-reference-rcl": "1.4.0-beta",
"core-js": "^3.8.3",
"deep-diff": "^1.0.2",
"deep-equal": "^2.0.5",
"gitea-react-toolkit": "2.4.0",
"localforage": "^1.9.0",
"lodash.clonedeep": "^4.5.0",
"markdown-translatable": "2.0.3",
"next": "12",
"postcss": "^8.2.1",
Expand All @@ -58,7 +60,7 @@
"translation-helps-rcl": "3.6.0-beta",
"use-deep-compare-effect": "^1.3.1",
"word-aligner": "^1.0.0",
"word-aligner-rcl": "1.0.4"
"word-aligner-rcl": "file:.yalc/word-aligner-rcl"
},
"devDependencies": {
"@cypress/code-coverage": "^3.9.11",
Expand Down
100 changes: 100 additions & 0 deletions src/components/WordAlignerArea.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, {useEffect, useState} from 'react'
import PropTypes from 'prop-types'
import DialogTitle from '@mui/material/DialogTitle'
import { RxLink2, RxLinkBreak2 } from 'react-icons/rx'
import { AlignmentHelpers, WordAligner } from 'word-aligner-rcl'

// popup dialog for user to align verse
export default function WordAlignerArea({
aligned,
alignmentIconStyle,
contextId,
lexiconCache,
loadLexiconEntry,
onChange,
showPopover,
sourceLanguage,
sourceLanguageFont,
sourceFontSizePercent,
targetLanguage,
targetLanguageFont,
targetFontSizePercent,
title,
translate,
verseAlignments,
targetWords,
style,
}) {
const [aligned_, setAligned] = useState(aligned)
const [alignmentChange, setAlignmentChange] = useState(null)

useEffect(() => {
console.log('WordAlignerArea: initialized')
}, [])

useEffect(() => {
if (aligned !== aligned_) {
console.log('WordAlignerArea: set alignment to', aligned)
setAligned(aligned)
}
}, [aligned])


function onAlignmentChange(results) {
// const onAlignmentsChange = alignerStatus?.actions?.onAlignmentsChange
// const alignmentComplete = onAlignmentsChange?.(results)
const alignmentComplete = AlignmentHelpers.areAlgnmentsComplete(results.targetWords, results.verseAlignments);
setAlignmentChange(results) // save the most recent change
setAligned(alignmentComplete) // update alignment complete status
}

return (
<>
<DialogTitle style={{ cursor: 'move' }} id="draggable-aligner-dialog-title" >
<span>
{`Aligning: ${title}`}
{aligned_? (
<RxLink2 style={alignmentIconStyle} id='valid_icon' color='#BBB' />
) : (
<RxLinkBreak2 style={alignmentIconStyle} id='invalid_icon' color='#000' />
)}
</span>
</DialogTitle>
<div style={{ width : `95%`, margin: '10px' }} >
<WordAligner
style={style}
verseAlignments={verseAlignments}
targetWords={targetWords}
translate={translate}
contextId={contextId}
targetLanguage={targetLanguage}
targetLanguageFont={targetLanguageFont}
sourceLanguage={sourceLanguage}
showPopover={showPopover}
lexiconCache={lexiconCache}
loadLexiconEntry={loadLexiconEntry}
onChange={onAlignmentChange}
/>
</div>
</>
)
}

WordAlignerArea.propTypes = {
aligned: PropTypes.bool,
contextId: PropTypes.object.isRequired,
lexiconCache: PropTypes.object,
loadLexiconEntry: PropTypes.func.isRequired,
onChange: PropTypes.func,
showPopover: PropTypes.func.isRequired,
sourceLanguage: PropTypes.string.isRequired,
sourceLanguageFont: PropTypes.string,
sourceFontSizePercent: PropTypes.number,
targetLanguageFont: PropTypes.string,
targetFontSizePercent: PropTypes.number,
translate: PropTypes.func.isRequired,
title: PropTypes.string,
verseAlignments: PropTypes.array.isRequired,
targetWords: PropTypes.array.isRequired,
};

137 changes: 85 additions & 52 deletions src/components/WordAlignerDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import React, {
useState,
} from 'react'
import PropTypes from 'prop-types'
import cloneDeep from 'lodash.clonedeep'
import * as isEqual from 'deep-equal'
import Dialog from '@mui/material/Dialog'
import DialogTitle from '@mui/material/DialogTitle'
import { RxLink2, RxLinkBreak2 } from 'react-icons/rx'
Expand All @@ -21,6 +23,7 @@ import {
import { useBoundsUpdater } from 'translation-helps-rcl'
import PopoverComponent from './PopoverComponent'
import { StoreContext } from '@context/StoreContext'
import WordAlignerArea from './WordAlignerArea';

const alignmentIconStyle = { marginLeft:'50px' }

Expand All @@ -31,10 +34,11 @@ export default function WordAlignerDialog({
translate,
getLexiconData,
}) {
const [alignerData, setAlignerData] = useState(null)
const [currentAlignerData, setCurrentAlignerData] = useState(null)
const [alignmentChange, setAlignmentChange] = useState(null)
const [aligned, setAligned] = useState(false)
const [lexiconData, setLexiconData] = useState(null)
const [dialogState, setDialogState] = useState({})
const [showResetWarning, setShowResetWarning] = useState(false)
const dialogRef = useRef(null) // for keeping track of aligner dialog position

Expand All @@ -52,9 +56,9 @@ export default function WordAlignerDialog({
} = useBoundsUpdater({ // keeps track of drag bounds
workspaceRef: mainScreenRef,
cardRef: dialogRef,
open: !!alignerData,
open: !!currentAlignerData,
displayState: {
alignerData
alignerData: currentAlignerData
},
})

Expand All @@ -74,27 +78,60 @@ export default function WordAlignerDialog({
}

useEffect(() => {
setAlignerData(alignerData_)
// see if alignment data has changed
const alignments = alignerData_?.alignments || null;
const wordBank = alignerData_?.wordBank || null;
const show = alignerData_?.showDialog;
const verseAlignments_ = dialogState?.verseAlignments || null;
const targetWords_ = dialogState?.targetWords || null;
const changedTW = !isEqual(wordBank, targetWords_);
if (changedTW) {
// const differences = diff(wordBank, targetWords_);
console.log("targetWords differences")
}
const changedVA = !isEqual(alignments, verseAlignments_);
if (changedVA) {
// const differences = diff(verseAlignments, verseAlignments_);
console.log("verseAlignments differences")
}
const showDialog = !!(alignments && wordBank);
const changedShow = (!show !== !showDialog)

if (changedTW || changedVA || changedShow) {
const verseAlignments = cloneDeep(alignments);
const targetWords = cloneDeep(wordBank);
setDialogState({
verseAlignments,
targetWords,
showDialog,
})
}

setCurrentAlignerData(cloneDeep(alignerData_))
}, [alignerData_])

useEffect(() => {
console.log('WordAlignerDialog: initialized')
}, [])

useEffect(() => { // monitor changes in alignment dialog position and open state
if (alignerData &&
if (currentAlignerData &&
dialogRef?.current?.clientWidth &&
dialogRef?.current?.clientHeight) {
doUpdateBounds()
}
}, [dialogRef?.current, alignerData])
}, [dialogRef?.current, currentAlignerData])

/**
* called on every alignment change. We save this new alignment state so that it can be applied if user clicks accept.
* We also update the aligned status so that the UI can be updated dynamically
* @param {object} results
*/
function onAlignmentChange(results) {
const onAlignmentsChange = alignerStatus?.actions?.onAlignmentsChange
const alignmentComplete = onAlignmentsChange?.(results)
setAlignmentChange(results) // save the most recent change
setAligned(alignmentComplete) // update alignment complete status
// const onAlignmentsChange = alignerStatus?.actions?.onAlignmentsChange
// const alignmentComplete = onAlignmentsChange?.(results)
// setAlignmentChange(results) // save the most recent change
setAligned(false) // update alignment complete status
}

function showPopover(PopoverTitle, wordDetails, positionCoord, rawData) {
Expand All @@ -108,13 +145,13 @@ export default function WordAlignerDialog({
})
}

const errorMessage = alignerData?.errorMessage
const errorMessage = currentAlignerData?.errorMessage

useEffect(() => { // set initial aligned state
if (alignerData) {
if (currentAlignerData) {
setAligned(!!alignerStatus?.state?.aligned)
}
}, [alignerData, alignerStatus])
}, [currentAlignerData, alignerStatus])

const {
projectId,
Expand All @@ -135,17 +172,26 @@ export default function WordAlignerDialog({
setAlignmentChange(null)
}

function setShowDialog(show) {
const _dialogState = {
...dialogState,
showDialog: !!show,
}
setDialogState(_dialogState)
}

/**
* reset all the alignments
*/
function doReset() {
console.log('WordAlignerDialog() - reset Alignments Clicked')
setShowResetWarning(false)
const alignmentData_ = AlignmentHelpers.resetAlignments(alignerData?.alignments, alignerData?.wordBank)
setShowDialog(false) // momentarily hide the dialog
const alignmentData_ = AlignmentHelpers.resetAlignments(dialogState?.verseAlignments, dialogState?.targetWords)

setAlignerData({ // this causes word aligner to redraw with empty alignments
alignments: alignmentData_.verseAlignments,
wordBank: alignmentData_.targetWords,
setDialogState({ // this causes word aligner to redraw with empty alignments
verseAlignments: alignmentData_.verseAlignments,
targetWords: alignmentData_.targetWords,
showDialog: true,
})

const latestChange = alignmentChange || {}
Expand All @@ -157,51 +203,38 @@ export default function WordAlignerDialog({
setAlignmentChange(alignmentChange_) // clear the last alignment changes in case user next does save
}

const enableResetWarning = useMemo( () => (showResetWarning && !!alignerData), [showResetWarning && !!alignerData])
const showDialog = !!dialogState?.showDialog;
const haveAlignerData = !!(dialogState?.verseAlignments && dialogState?.targetWords)
const enableResetWarning = useMemo( () => (showDialog && haveAlignerData), [showDialog, haveAlignerData])

return (
<>
<Dialog
fullWidth={true}
maxWidth={'lg'}
onClose={() => {}}
open={!!alignerData}
open={!!showDialog}
PaperComponent={PaperComponent}
bounds={bounds}
aria-labelledby="draggable-aligner-dialog-title"
>
<DialogTitle style={{ cursor: 'move' }} id="draggable-aligner-dialog-title" >
<span>
{`Aligning: ${title}`}
{aligned? (
<RxLink2 style={alignmentIconStyle} id='valid_icon' color='#BBB' />
) : (
<RxLinkBreak2 style={alignmentIconStyle} id='invalid_icon' color='#000' />
)}
</span>
</DialogTitle>
<div style={{ width : `95%`, margin: '10px' }} >
{(!errorMessage && alignerData?.alignments) ?
<WordAligner
style={{ maxHeight: `${height}px`, overflowY: 'auto' }}
verseAlignments={alignerData?.alignments || null}
targetWords={alignerData?.wordBank || null}
translate={translate}
contextId={{ reference: alignerStatus?.state?.reference || {} }}
targetLanguage={alignerStatus?.state?.targetLanguage}
targetLanguageFont={''}
sourceLanguage={alignerStatus?.state?.sourceLanguage}
showPopover={showPopover}
lexicons={{}}
loadLexiconEntry={getLexiconData}
onChange={onAlignmentChange}
/>
: // if error, then show message
<div style={{ textAlign: 'center', fontSize: '1.5em', fontStyle: 'italic', fontWeight: 'bold' }}>
{ errorMessage || '' }
</div>
}
</div>
<WordAlignerArea
aligned={!!alignerStatus?.state?.aligned}
alignmentIconStyle={alignmentIconStyle}
title={title}
style={{ maxHeight: `${height}px`, overflowY: 'auto' }}
verseAlignments={dialogState?.verseAlignments}
targetWords={dialogState?.targetWords}
translate={translate}
contextId={{ reference: alignerStatus?.state?.reference || {} }}
targetLanguage={alignerStatus?.state?.targetLanguage}
targetLanguageFont={''}
sourceLanguage={alignerStatus?.state?.sourceLanguage}
showPopover={showPopover}
lexiconCache={{}}
loadLexiconEntry={getLexiconData}
/>

<span style={{ width : `95%`, height: '60px', display: 'flex', flexDirection: 'row', justifyContent: 'center' }}>
<Button variant="outlined" style={{ margin: '10px 100px' }} onClick={cancelAlignment}>
Cancel
Expand Down
15 changes: 10 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3796,6 +3796,11 @@ dedent@^0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==

deep-diff@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26"
integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==

deep-equal@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
Expand Down Expand Up @@ -11100,14 +11105,14 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"

word-aligner-rcl@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/word-aligner-rcl/-/word-aligner-rcl-1.0.4.tgz#254bc6b980680827df8e679f26bec4bc05972be0"
integrity sha512-uq8mZLLOo18rNqo4MsM4mD9iSSyRH2BwG1+2dqGlNu5A89B6YPii5e6XoN6CMazSA5OQKa3zqtcBftkowe1L/Q==
"word-aligner-rcl@file:.yalc/word-aligner-rcl":
version "1.1.0-beta.10"
dependencies:
bible-reference-range "^1.1.0"
deep-diff "^1.0.2"
deep-equal "^2.0.5"
file-loader "^6.2.0"
lodash "^4.17.21"
lodash.clonedeep "^4.5.0"
string-punctuation-tokenizer "2.2.0"
usfm-js "3.4.2"
word-aligner "1.0.2"
Expand Down

0 comments on commit 26ea6e6

Please sign in to comment.