From f107437f94095ed50e3de4f2b81025c52c2f9ddd Mon Sep 17 00:00:00 2001 From: Greg Trihus Date: Fri, 29 May 2026 12:07:08 -0500 Subject: [PATCH 1/3] TT-7290,7216,6739,7244,7081 Enhance MediaRecord and CommentEditor components with audio draft management and scrollbar handling - TT-7290 Added `hideSegmentControls` prop to `MediaRecord` for better UI control. - TT-7081 Implemented scrollbar width measurement and handling in `usePaneWidth` and `DiscussionPanel` to improve layout responsiveness. - Introduced `onAudioDraftChange` callback in `CommentEditor` to manage audio draft state. - Updated `DiscussionCard` to track audio draft status and integrate with `CommentEditor`. - Refactored `DiscussionList` to utilize new state management for changes and scrollbar adjustments. --- .../components/Discussions/CommentEditor.tsx | 238 +++++++++--------- .../components/Discussions/DiscussionCard.tsx | 17 +- .../components/Discussions/DiscussionList.tsx | 11 +- .../Discussions/DiscussionPanel.tsx | 37 ++- src/renderer/src/components/MediaRecord.tsx | 3 + src/renderer/src/components/usePaneWidth.ts | 18 +- src/renderer/src/utils/getScrollbarWidth.ts | 26 ++ src/renderer/src/utils/index.ts | 1 + 8 files changed, 218 insertions(+), 133 deletions(-) create mode 100644 src/renderer/src/utils/getScrollbarWidth.ts diff --git a/src/renderer/src/components/Discussions/CommentEditor.tsx b/src/renderer/src/components/Discussions/CommentEditor.tsx index 6eee00df..a9f8206a 100644 --- a/src/renderer/src/components/Discussions/CommentEditor.tsx +++ b/src/renderer/src/components/Discussions/CommentEditor.tsx @@ -1,4 +1,5 @@ import { + Box, Button, TextField, Tooltip, @@ -23,11 +24,15 @@ const RowDiv = styled('div')(() => ({ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', + flexWrap: 'wrap', + minWidth: 0, })); const ColumnDiv = styled('div')(() => ({ display: 'flex', flexDirection: 'column', + minWidth: 0, + width: '100%', })); const StatusMessage = styled(Typography)(({ theme }) => ({ @@ -49,6 +54,7 @@ interface IProps extends IStateProps { onCancel?: () => void; setCanSaveRecording: (canSave: boolean) => void; onTextChange: (txt: string) => void; + onAudioDraftChange?: (hasDraft: boolean) => void; } export const CommentEditor = (props: IProps) => { const { @@ -63,6 +69,7 @@ export const CommentEditor = (props: IProps) => { onCancel, setCanSaveRecording, onTextChange, + onAudioDraftChange, } = props; const { playing, @@ -85,6 +92,7 @@ export const CommentEditor = (props: IProps) => { const doRecordRef = useRef(false); const [recording, setRecording] = useState(false); const [myChanged, setMyChanged] = useState(false); + const [showRecorder, setShowRecorder] = useState(false); const { commentId } = useArtifactType(); const { @@ -97,6 +105,16 @@ export const CommentEditor = (props: IProps) => { isChanged, } = useContext(UnsavedContext).state; + const setAudioDraft = (hasDraft: boolean) => { + onAudioDraftChange?.(hasDraft); + }; + + const clearUnsavedIfEmpty = () => { + if (!curText.length && !canSaveRef.current) { + toolChanged(toolId, false); + } + }; + useEffect(() => { return () => { if (doRecordRef.current) setCommentRecording(false); @@ -123,11 +141,13 @@ export const CommentEditor = (props: IProps) => { 100 ).then(() => { doRecordRef.current = true; + setShowRecorder(true); setStartRecord(false); }); } catch { //do it anyway... doRecordRef.current = true; + setShowRecorder(true); setStartRecord(false); } }, [startRecord, playing, itemPlaying, commentPlaying]); @@ -137,11 +157,19 @@ export const CommentEditor = (props: IProps) => { canSaveRef.current = valid; setCanSave(valid); setCanSaveRecording(valid); + setAudioDraft(valid); if (valid) toolChanged(toolId, true); + else clearUnsavedIfEmpty(); } }; const onRecording = (r: boolean) => { setRecording(r); + if (r) { + toolChanged(toolId, true); + } else if (doRecordRef.current) { + // Paused/stopped: enable parent Add before blob-ready/canSave propagates (TT-7216). + setAudioDraft(true); + } }; const handleTextChange = (e: any) => { setCurText(e.target.value); @@ -163,6 +191,7 @@ export const CommentEditor = (props: IProps) => { }; const handleRecord = () => { + toolChanged(toolId, true); setStartRecord(true); setCommentRecording(true); }; @@ -172,7 +201,13 @@ export const CommentEditor = (props: IProps) => { setStatusText(''); setCurText(''); doRecordRef.current = false; + setShowRecorder(false); + canSaveRef.current = false; + setCanSave(false); + setCanSaveRecording(false); + setAudioDraft(false); clearCompleted(toolId); + clearUnsavedIfEmpty(); }; useEffect(() => { @@ -182,6 +217,81 @@ export const CommentEditor = (props: IProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [refresh]); + const recorderNode = ( + + + + ); + + const actionButtons = + onOk && + (!cancelOnlyIfChanged || doRecordRef.current || myChanged) && ( + + + + + + + + + + + + + ); + return ( { label={t.comment} focused /> - {doRecordRef.current ? ( - <> + {showRecorder ? ( + + {recorderNode} - -
-
- {onOk && - (!cancelOnlyIfChanged || - doRecordRef.current || - myChanged) && ( - - - - - - )} - {onOk && ( - - - - - - )} -
- {statusText} -
+ {actionButtons} + {statusText}
- +
) : ( @@ -303,37 +333,7 @@ export const CommentEditor = (props: IProps) => {
{statusText} - {onOk && - (!cancelOnlyIfChanged || doRecordRef.current || myChanged) && ( - - - - - - )} - {onOk && ( - - - - - - )} + {actionButtons}
)} diff --git a/src/renderer/src/components/Discussions/DiscussionCard.tsx b/src/renderer/src/components/Discussions/DiscussionCard.tsx index a6b196d4..7e2679e3 100644 --- a/src/renderer/src/components/Discussions/DiscussionCard.tsx +++ b/src/renderer/src/components/Discussions/DiscussionCard.tsx @@ -277,6 +277,7 @@ export const DiscussionCard = (props: IProps) => { const [comment, setComment] = useState(''); const commentMediaId = useRef(undefined); const [canSaveRecording, setCanSaveRecording] = useState(false); + const [hasAudioDraft, setHasAudioDraft] = useState(false); const { userIsAdmin } = useRole(); const CommentAuthor = (comment: CommentD) => @@ -357,6 +358,7 @@ export const DiscussionCard = (props: IProps) => { setEditCategory(''); setComment(''); commentText.current = ''; + setHasAudioDraft(false); setMoveTo(undefined); }; @@ -801,6 +803,7 @@ export const DiscussionCard = (props: IProps) => { cardSavingRef.current = true; commentText.current = ''; commentMediaId.current = ''; + setHasAudioDraft(false); onAddComplete && onAddComplete(''); setEditing(false); setChanged(false); @@ -968,6 +971,7 @@ export const DiscussionCard = (props: IProps) => { comment={commentText.current} refresh={refresh} setCanSaveRecording={setCanSaveRecording} + onAudioDraftChange={setHasAudioDraft} fileName={fileName(editSubject, '')} afterUploadCb={afterUploadCb} passageId={passageId} @@ -985,10 +989,19 @@ export const DiscussionCard = (props: IProps) => { segSavingRef.current || !mediafileId || editSubject === '' || - !(canSaveRecording || myComments.length > 0 || comment) + !( + canSaveRecording || + hasAudioDraft || + myComments.length > 0 || + comment + ) } > - {discussion.id ? ts.save : t.addComment} + {onAddComplete + ? t.addComment + : discussion.id + ? ts.save + : t.addComment}