From 18a7289b44ab9f66bb7046800d98fde9709a0c84 Mon Sep 17 00:00:00 2001 From: Sara Hentzel Date: Mon, 15 Jun 2026 12:44:10 -0500 Subject: [PATCH 1/4] TT-7414 Cleanup title editing - TitleEdit doesn't keep copy. Value is only sent down if not editing. --- .../src/components/Sheet/ScriptureTable.tsx | 98 ++++++++++--------- .../src/components/Sheet/TitleEdit.tsx | 12 +-- src/renderer/src/control/MediaTitle.tsx | 38 +++++-- 3 files changed, 87 insertions(+), 61 deletions(-) diff --git a/src/renderer/src/components/Sheet/ScriptureTable.tsx b/src/renderer/src/components/Sheet/ScriptureTable.tsx index df6da6b3..aad98bfb 100644 --- a/src/renderer/src/components/Sheet/ScriptureTable.tsx +++ b/src/renderer/src/components/Sheet/ScriptureTable.tsx @@ -1039,54 +1039,60 @@ export function ScriptureTable(props: IProps) { interface MyWorkflow extends ISheet { [key: string]: any; } - const updateData = (changes: ICellChange[]) => { - waitForIt( - 'finish save or update', - () => !savingRef.current && !updateRef.current, - () => false, - 50 - ).then(() => { - setUpdate(true); - const newsht = [...sheetRef.current]; - changes.forEach((c) => { - const { ws, i } = getByIndex(newsht, c.row); - const myWf = ws as MyWorkflow | undefined; - const name = colNames[c.col]; - const isNumberCol = c.col === secNumCol || c.col === passNumCol; - - if (isNumberCol && !isValidNumber(c.value || '')) { - showMessage(s.nonNumber); - } else if (myWf && myWf[name as keyof MyWorkflow] !== c.value) { - const isSection = c.col < 2; - const sectionUpdated = isSection - ? currentDateTime() - : ws?.sectionUpdated; - const passageUpdated = isSection - ? ws?.passageUpdated - : currentDateTime(); - const value = name === 'book' ? findBook(c.value as string) : c.value; - const passageType = - name === 'reference' - ? passageTypeFromRef(c.value as string, flat) - : ws?.passageType; - - newsht[i] = { - ...ws, - [name as keyof MyWorkflow]: isNumberCol - ? parseInt(value ?? '') - : value, - sectionUpdated, - passageUpdated, - passageType, - } as ISheet; - } - }); - if (changes.length > 0) { - setSheet(newsht); - setChanged(true); + const applyCellChanges = (changes: ICellChange[]) => { + setUpdate(true); + const newsht = [...sheetRef.current]; + changes.forEach((c) => { + const { ws, i } = getByIndex(newsht, c.row); + const myWf = ws as MyWorkflow | undefined; + const name = colNames[c.col]; + const isNumberCol = c.col === secNumCol || c.col === passNumCol; + + if (isNumberCol && !isValidNumber(c.value || '')) { + showMessage(s.nonNumber); + } else if (myWf && myWf[name as keyof MyWorkflow] !== c.value) { + const isSection = c.col < 2; + const sectionUpdated = isSection + ? currentDateTime() + : ws?.sectionUpdated; + const passageUpdated = isSection + ? ws?.passageUpdated + : currentDateTime(); + const value = name === 'book' ? findBook(c.value as string) : c.value; + const passageType = + name === 'reference' + ? passageTypeFromRef(c.value as string, flat) + : ws?.passageType; + + newsht[i] = { + ...ws, + [name as keyof MyWorkflow]: isNumberCol + ? parseInt(value ?? '') + : value, + sectionUpdated, + passageUpdated, + passageType, + } as ISheet; } - setUpdate(false); }); + if (changes.length > 0) { + setSheet(newsht); + setChanged(true); + } + setUpdate(false); + }; + + const updateData = (changes: ICellChange[]) => { + if (savingRef.current || updateRef.current) { + waitForIt( + 'finish save or update', + () => !savingRef.current && !updateRef.current, + () => false, + 50 + ).then(() => applyCellChanges(changes)); + return; + } + applyCellChanges(changes); }; const updateTitleMedia = async (index: number, mediaId: string) => { diff --git a/src/renderer/src/components/Sheet/TitleEdit.tsx b/src/renderer/src/components/Sheet/TitleEdit.tsx index d775a589..4a2002c2 100644 --- a/src/renderer/src/components/Sheet/TitleEdit.tsx +++ b/src/renderer/src/components/Sheet/TitleEdit.tsx @@ -29,12 +29,7 @@ export function TitleEdit({ }: IProps) { const [planId] = useGlobal('plan'); //will be constant here const [memory] = useGlobal('memory'); - const [titlex, setTitle] = useState(title || ''); - const [titleMediafile, setTitleMediafile] = useState(''); - - useEffect(() => { - setTitle(title); - }, [title]); + const [titleMediafile, setTitleMediafile] = useState(mediaId || ''); useEffect(() => { setTitleMediafile(mediaId); @@ -42,7 +37,6 @@ export function TitleEdit({ const handleChangeTitle = (value: string) => { onTextChange(value); - setTitle(value); return ''; }; @@ -53,13 +47,13 @@ export function TitleEdit({ return ( <> - {readonly && !showpublish && ((<>{titlex}) as ReactElement)} + {readonly && !showpublish && ((<>{title}) as ReactElement)} {(!readonly || showpublish) && ( toolId + 'rec', [toolId]); useEffect(() => { - setCurText(title ?? ''); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [title]); + if (prevTitleKeyRef.current !== titlekey) { + prevTitleKeyRef.current = titlekey; + isFocusedRef.current = false; + setCurText(title ?? ''); + return; + } + if (!isFocusedRef.current) { + setCurText(title ?? ''); + } + }, [title, titlekey]); useEffect(() => { langRef.current = language; @@ -240,13 +251,26 @@ export default function MediaTitle(props: IProps) { showMessage(t.recording); return; } - setCurText(e.target.value); - if (onTextChange && e.target.value !== title) { - const err = onTextChange(e.target.value); + const value = e.target.value; + curTextRef.current = value; + setCurText(value); + if (onTextChange) { + const err = onTextChange(value); setHelperText(err); } }; + const handleFocus = () => { + isFocusedRef.current = true; + }; + + const handleBlur = () => { + isFocusedRef.current = false; + if (onTextChange) { + onTextChange(curTextRef.current); + } + }; + const handlePlay = (e: any) => { e.stopPropagation(); //on first play...tell the media player to download the media @@ -375,6 +399,8 @@ export default function MediaTitle(props: IProps) { value={curText} onClick={language ? handleLangPick : undefined} onChange={handleTextChange} + onFocus={handleFocus} + onBlur={handleBlur} onKeyDown={handleKeyDown} helperText={helperText} size="small" From ef5ca0a8d49db39965ff37183ff50c4585697cbf Mon Sep 17 00:00:00 2001 From: Sara Hentzel Date: Tue, 16 Jun 2026 14:55:47 -0500 Subject: [PATCH 2/4] check value before sending ontextchange --- src/renderer/src/control/MediaTitle.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/control/MediaTitle.tsx b/src/renderer/src/control/MediaTitle.tsx index 39da89e3..fe2966fc 100644 --- a/src/renderer/src/control/MediaTitle.tsx +++ b/src/renderer/src/control/MediaTitle.tsx @@ -168,9 +168,8 @@ export default function MediaTitle(props: IProps) { } = props; const [canSaveRecording, setCanSaveRecording] = useState(false); const canSaveRef = useRef(false); - const [curText, setCurText] = useState(title ?? ''); + const [curText, setCurTextx] = useState(title ?? ''); const curTextRef = useRef(curText); - curTextRef.current = curText; const isFocusedRef = useRef(false); const prevTitleKeyRef = useRef(titlekey); const [startRecord, setStartRecord] = useState(false); @@ -200,7 +199,10 @@ export default function MediaTitle(props: IProps) { // Track playing media globally to coordinate playback (only one plays at a time) const [playingMediaId, setPlayingMediaId] = useGlobal('playingMediaId'); - + const setCurText = (text: string) => { + setCurTextx(text); + curTextRef.current = text; + }; useEffect(() => setHelperText(helper ?? ''), [helper]); useEffect(() => { @@ -252,7 +254,6 @@ export default function MediaTitle(props: IProps) { return; } const value = e.target.value; - curTextRef.current = value; setCurText(value); if (onTextChange) { const err = onTextChange(value); @@ -266,8 +267,10 @@ export default function MediaTitle(props: IProps) { const handleBlur = () => { isFocusedRef.current = false; - if (onTextChange) { - onTextChange(curTextRef.current); + const value = curTextRef.current; + if (onTextChange && value !== (title ?? '')) { + const err = onTextChange(value); + setHelperText(err); } }; @@ -427,6 +430,7 @@ export default function MediaTitle(props: IProps) { // eslint-disable-next-line react-hooks/exhaustive-deps [ curText, + title, recording, mediaId, canSaveRecording, From 60b7fdf1d1dae3f6ca3beeba76fe19017c935321 Mon Sep 17 00:00:00 2001 From: Sara Hentzel Date: Tue, 16 Jun 2026 15:19:23 -0500 Subject: [PATCH 3/4] add titlekey to TitleText dependency list --- src/renderer/src/control/MediaTitle.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/control/MediaTitle.tsx b/src/renderer/src/control/MediaTitle.tsx index fe2966fc..c2dd7812 100644 --- a/src/renderer/src/control/MediaTitle.tsx +++ b/src/renderer/src/control/MediaTitle.tsx @@ -431,6 +431,7 @@ export default function MediaTitle(props: IProps) { [ curText, title, + titlekey, recording, mediaId, canSaveRecording, From 9283e011a063d3ce770b6f659f652f3debacfbed Mon Sep 17 00:00:00 2001 From: Sara Hentzel Date: Tue, 16 Jun 2026 15:45:17 -0500 Subject: [PATCH 4/4] trim title --- src/renderer/src/control/MediaTitle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/control/MediaTitle.tsx b/src/renderer/src/control/MediaTitle.tsx index c2dd7812..a36b8ca8 100644 --- a/src/renderer/src/control/MediaTitle.tsx +++ b/src/renderer/src/control/MediaTitle.tsx @@ -253,7 +253,7 @@ export default function MediaTitle(props: IProps) { showMessage(t.recording); return; } - const value = e.target.value; + const value = e.target.value.trim(); setCurText(value); if (onTextChange) { const err = onTextChange(value);