From cd669cbf3ea7caf8581e05a5788965509db62bf5 Mon Sep 17 00:00:00 2001 From: klopez4212 Date: Sun, 5 Jul 2026 07:22:42 +0100 Subject: [PATCH] fix(dictation): stop recording on channel/thread switch When the composer's draftKey changes (channel or thread switch), stop any active dictation session so transcript events from a stale WebRTC connection don't leak into the wrong draft. Signed-off-by: klopez4212 --- .../dictation/hooks/useComposerDictation.ts | 16 ++++++++++++++-- .../src/features/messages/ui/MessageComposer.tsx | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/desktop/src/features/dictation/hooks/useComposerDictation.ts b/desktop/src/features/dictation/hooks/useComposerDictation.ts index 808787059..b945e68a3 100644 --- a/desktop/src/features/dictation/hooks/useComposerDictation.ts +++ b/desktop/src/features/dictation/hooks/useComposerDictation.ts @@ -1,5 +1,5 @@ import type * as React from "react"; -import { useRef } from "react"; +import { useEffect, useRef } from "react"; import { useDictation } from "./useDictation"; interface UseComposerDictationOptions { @@ -13,6 +13,8 @@ interface UseComposerDictationOptions { /** Ref to a function that updates the Tiptap editor document. */ setEditorContentRef: React.MutableRefObject<(text: string) => void>; submitMessageRef: React.MutableRefObject<() => void>; + /** When this key changes (channel/thread switch), active dictation is stopped. */ + draftKey?: string | null; } /** @@ -27,12 +29,13 @@ export function useComposerDictation({ setComposerContent, setEditorContentRef, submitMessageRef, + draftKey, }: UseComposerDictationOptions) { const isSendBlockedRef = useRef(false); isSendBlockedRef.current = disabledRef.current || isSendingRef.current || isUploadingRef.current; - return useDictation({ + const dictation = useDictation({ getText: () => syncContentRef.current(), setText: (text) => { setComposerContent(text); @@ -48,4 +51,13 @@ export function useComposerDictation({ }, isSendBlockedRef, }); + + // Stop dictation when the channel/thread changes so that transcript events + // from a stale WebRTC session don't leak into the wrong draft. + // biome-ignore lint/correctness/useExhaustiveDependencies: draftKey is the sole trigger + useEffect(() => { + dictation.stopRecording(); + }, [draftKey]); + + return dictation; } diff --git a/desktop/src/features/messages/ui/MessageComposer.tsx b/desktop/src/features/messages/ui/MessageComposer.tsx index 84b84ce11..62c645b0f 100644 --- a/desktop/src/features/messages/ui/MessageComposer.tsx +++ b/desktop/src/features/messages/ui/MessageComposer.tsx @@ -275,6 +275,7 @@ function MessageComposerImpl({ setComposerContent, setEditorContentRef, submitMessageRef, + draftKey: effectiveDraftKey, }); const composerScrollRef = React.useRef(null); // Set after `useLinkEditor` exists below; the editor's link-click handler @@ -1047,5 +1048,4 @@ function MessageComposerImpl({ ); } - export const MessageComposer = React.memo(MessageComposerImpl);