fix(recorder): Resolve infinite loop stale closure & reset status text on discard

This commit is contained in:
michael.borak
2026-01-24 01:47:47 +01:00
parent e24b448c6c
commit 897f2ec0c2

View File

@@ -187,9 +187,10 @@ const Recorder: React.FC<RecorderProps> = ({
} }
}; };
// Refs for interval access to avoid dependency cycles // Refs for interval access to avoid dependency cycles and stale closures
const lastSpeechTimeRef = useRef<number>(Date.now()); const lastSpeechTimeRef = useRef<number>(Date.now());
const isStoppingRef = useRef(false); const isStoppingRef = useRef(false);
const autoStartEnabledRef = useRef(autoStartEnabled);
// Update refs when state changes // Update refs when state changes
useEffect(() => { useEffect(() => {
@@ -200,6 +201,10 @@ const Recorder: React.FC<RecorderProps> = ({
isStoppingRef.current = isStopping; isStoppingRef.current = isStopping;
}, [isStopping]); }, [isStopping]);
useEffect(() => {
autoStartEnabledRef.current = autoStartEnabled;
}, [autoStartEnabled]);
// 1. Event Listeners Effect (Run ONCE when recording starts) // 1. Event Listeners Effect (Run ONCE when recording starts)
useEffect(() => { useEffect(() => {
let unlistenVAD: () => void; let unlistenVAD: () => void;
@@ -348,17 +353,14 @@ const Recorder: React.FC<RecorderProps> = ({
// NEW: Check if speech was actually detected during the session // 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 we recorded 20s of silence (Auto-Stop), we shouldn't transcribe. // IMPORTANT: Check applies to BOTH 'voice' and 'meeting' modes to prevent "Batch Null" errors on false triggers.
if (!hasSpeechDetected && recordingMode === 'voice') { if (!hasSpeechDetected) {
// 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.
console.log("No speech detected during recording. Skipping transcription."); console.log("No speech detected during recording. Skipping transcription.");
addToast("Recording discarded (No speech/audio detected)", 'info'); addToast("Recording discarded (No speech/audio detected)", 'info');
setStatus('Ready to record'); // Reset status text
// If auto-start is on, we just loop back. // If auto-start is on, we just loop back (in finally block).
// skip the rest. // But we skip the expensive/failing API call.
} else { } else {
// Wait a moment for file flush (safety) // Wait a moment for file flush (safety)
@@ -489,11 +491,15 @@ const Recorder: React.FC<RecorderProps> = ({
setIsStopping(false); setIsStopping(false);
// AUTO-RESTART LOGIC // 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..."); console.log("Auto-Start enabled: Restarting listener loop...");
// Short delay to ensure backend cleanup // Short delay to ensure backend cleanup
setTimeout(() => { setTimeout(() => {
startRecording(); // Double check ref before restarting
if (autoStartEnabledRef.current) {
startRecording();
}
}, 1000); }, 1000);
} }
} }