From 25339523ecfa5ed4ebad930b8716b042a9ec94d1 Mon Sep 17 00:00:00 2001 From: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co> Date: Tue, 7 Jul 2026 12:26:48 +0100 Subject: [PATCH] fix(dictation): abort in-flight start on cleanup/unmount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cleanup() tore down resources but never set startAbortedRef, so a composer unmounting while startRecording() awaited start_dictation / listen / getUserMedia / addModule would resume the async start against a torn-down instance — opening the mic/worklet and leaving the native session and event listeners running after the component was gone. Set the abort flag first in cleanup(); a fresh startRecording clears it before its first await, so subsequent starts are unaffected. --- .../src/features/dictation/hooks/useLocalDictation.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/desktop/src/features/dictation/hooks/useLocalDictation.ts b/desktop/src/features/dictation/hooks/useLocalDictation.ts index 949515164..f4e8efca7 100644 --- a/desktop/src/features/dictation/hooks/useLocalDictation.ts +++ b/desktop/src/features/dictation/hooks/useLocalDictation.ts @@ -197,6 +197,15 @@ export function useLocalDictation({ }, []); const cleanup = useCallback(() => { + // Abort any in-flight startRecording so it bails after its next await + // instead of resuming and opening the mic/worklet or leaving the native + // session/listeners running after teardown. `cleanup` is the unmount + // handler (and the catch-path teardown); without this, unmounting while + // startRecording awaits `start_dictation`/`listen`/`getUserMedia`/ + // `addModule` would let the async start finish against a torn-down + // instance. A fresh startRecording clears this flag before its first + // await, so it never wrongly aborts a subsequent start. + startAbortedRef.current = true; // Flush any remaining audio before teardown, then stop the native engine. // Scope the stop to THIS hook instance's session. `cleanup` runs as every // instance's unmount handler, so an unscoped stop here would let a