Preserve voice session across tab navigation
Add watchdog mechanism to ensure STT continues when switching tabs: - Monitor STT state every 500ms and restart if session active but STT stopped - Handle AppState changes to restart STT when app returns to foreground - Prevents session interruption due to native audio session changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5efd696ef2
commit
bdb4ceb8d2
@ -1,6 +1,6 @@
|
||||
import { Tabs } from 'expo-router';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { Platform, View } from 'react-native';
|
||||
import { Platform, View, AppState, AppStateStatus } from 'react-native';
|
||||
import { Feather } from '@expo/vector-icons';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
@ -143,6 +143,55 @@ export default function TabLayout() {
|
||||
}
|
||||
}, [status, sttIsListening, startListening]);
|
||||
|
||||
// ============================================================================
|
||||
// TAB NAVIGATION PERSISTENCE
|
||||
// Ensure voice session continues when user switches between tabs.
|
||||
// The session state is in VoiceContext (root level), but STT may stop due to:
|
||||
// 1. Native audio session changes
|
||||
// 2. Tab unmount/remount (though tabs layout doesn't unmount)
|
||||
// 3. AppState changes (background/foreground)
|
||||
// ============================================================================
|
||||
|
||||
// Monitor and recover STT state during tab navigation
|
||||
// If session is active but STT stopped unexpectedly, restart it
|
||||
useEffect(() => {
|
||||
// Check every 500ms if STT needs to be restarted
|
||||
const intervalId = setInterval(() => {
|
||||
// Only act if session should be active (isListening from VoiceContext)
|
||||
// but STT is not actually listening, and we're not in speaking/processing mode
|
||||
if (
|
||||
sessionActiveRef.current &&
|
||||
!sttIsListening &&
|
||||
status !== 'speaking' &&
|
||||
status !== 'processing'
|
||||
) {
|
||||
console.log('[TabLayout] STT watchdog: restarting STT (session active but STT stopped)');
|
||||
startListening();
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [sttIsListening, status, startListening]);
|
||||
|
||||
// Handle app state changes (background/foreground)
|
||||
// When app comes back to foreground, restart STT if session was active
|
||||
useEffect(() => {
|
||||
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||||
if (nextAppState === 'active' && sessionActiveRef.current) {
|
||||
// App came to foreground, give it a moment then check STT
|
||||
setTimeout(() => {
|
||||
if (sessionActiveRef.current && !sttIsListening && status !== 'speaking' && status !== 'processing') {
|
||||
console.log('[TabLayout] App foregrounded - restarting STT');
|
||||
startListening();
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
return () => subscription.remove();
|
||||
}, [sttIsListening, status, startListening]);
|
||||
|
||||
// Handle voice FAB press - toggle listening mode
|
||||
const handleVoiceFABPress = useCallback(() => {
|
||||
if (isListening) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user