From 897f2ec0c219f2897c1f0207439455aa7deb953e Mon Sep 17 00:00:00 2001 From: "michael.borak" Date: Sat, 24 Jan 2026 01:47:47 +0100 Subject: [PATCH] fix(recorder): Resolve infinite loop stale closure & reset status text on discard --- src/components/Recorder.tsx | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/Recorder.tsx b/src/components/Recorder.tsx index d322e23..ff1046b 100644 --- a/src/components/Recorder.tsx +++ b/src/components/Recorder.tsx @@ -187,9 +187,10 @@ const Recorder: React.FC = ({ } }; - // Refs for interval access to avoid dependency cycles + // Refs for interval access to avoid dependency cycles and stale closures const lastSpeechTimeRef = useRef(Date.now()); const isStoppingRef = useRef(false); + const autoStartEnabledRef = useRef(autoStartEnabled); // Update refs when state changes useEffect(() => { @@ -200,6 +201,10 @@ const Recorder: React.FC = ({ isStoppingRef.current = isStopping; }, [isStopping]); + useEffect(() => { + autoStartEnabledRef.current = autoStartEnabled; + }, [autoStartEnabled]); + // 1. Event Listeners Effect (Run ONCE when recording starts) useEffect(() => { let unlistenVAD: () => void; @@ -348,17 +353,14 @@ const Recorder: React.FC = ({ // NEW: Check if speech was actually detected during the session // If we recorded 20s of silence (Auto-Stop), we shouldn't transcribe. - // If we recorded 20s of silence (Auto-Stop), we shouldn't transcribe. - if (!hasSpeechDetected && recordingMode === 'voice') { - // Note: For 'meeting' mode, system audio might have happened without VAD triggering? - // But our updated backend VAD logic includes System Audio in 'is_speech' event. - // So we can trust hasSpeechDetected for both modes now. - + // IMPORTANT: Check applies to BOTH 'voice' and 'meeting' modes to prevent "Batch Null" errors on false triggers. + if (!hasSpeechDetected) { console.log("No speech detected during recording. Skipping transcription."); addToast("Recording discarded (No speech/audio detected)", 'info'); + setStatus('Ready to record'); // Reset status text - // If auto-start is on, we just loop back. - // skip the rest. + // If auto-start is on, we just loop back (in finally block). + // But we skip the expensive/failing API call. } else { // Wait a moment for file flush (safety) @@ -489,11 +491,15 @@ const Recorder: React.FC = ({ setIsStopping(false); // AUTO-RESTART LOGIC - if (autoStartEnabled) { + // Use REF to get the latest state (fix for "starts again even if I uncheck") + if (autoStartEnabledRef.current) { console.log("Auto-Start enabled: Restarting listener loop..."); // Short delay to ensure backend cleanup setTimeout(() => { - startRecording(); + // Double check ref before restarting + if (autoStartEnabledRef.current) { + startRecording(); + } }, 1000); } }