diff --git a/package.json b/package.json index 0c7c027..42981fc 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/src/components/WordAlignerArea.js b/src/components/WordAlignerArea.js new file mode 100644 index 0000000..9e04ea4 --- /dev/null +++ b/src/components/WordAlignerArea.js @@ -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 ( + <> + + + {`Aligning: ${title}`} + {aligned_? ( + + ) : ( + + )} + + +
+ +
+ + ) +} + +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, +}; + diff --git a/src/components/WordAlignerDialog.js b/src/components/WordAlignerDialog.js index 7111b8a..dc36869 100644 --- a/src/components/WordAlignerDialog.js +++ b/src/components/WordAlignerDialog.js @@ -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' @@ -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' } @@ -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 @@ -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 }, }) @@ -74,16 +78,49 @@ 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. @@ -91,10 +128,10 @@ export default function WordAlignerDialog({ * @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) { @@ -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, @@ -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 || {} @@ -157,7 +203,9 @@ 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 ( <> @@ -165,43 +213,28 @@ export default function WordAlignerDialog({ fullWidth={true} maxWidth={'lg'} onClose={() => {}} - open={!!alignerData} + open={!!showDialog} PaperComponent={PaperComponent} bounds={bounds} aria-labelledby="draggable-aligner-dialog-title" > - - - {`Aligning: ${title}`} - {aligned? ( - - ) : ( - - )} - - -
- {(!errorMessage && alignerData?.alignments) ? - - : // if error, then show message -
- { errorMessage || '' } -
- } -
+ +