-
Notifications
You must be signed in to change notification settings - Fork 4
RU-T47 Fixing issue with voice channel #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Mock for @livekit/react-native | ||
| export const AudioSession = { | ||
| startAudioSession: jest.fn().mockResolvedValue(undefined), | ||
| stopAudioSession: jest.fn().mockResolvedValue(undefined), | ||
| configureAudio: jest.fn().mockResolvedValue(undefined), | ||
| getAudioOutputs: jest.fn().mockResolvedValue([]), | ||
| selectAudioOutput: jest.fn().mockResolvedValue(undefined), | ||
| showAudioRoutePicker: jest.fn().mockResolvedValue(undefined), | ||
| setAppleAudioConfiguration: jest.fn().mockResolvedValue(undefined), | ||
| }; | ||
|
|
||
| export const registerGlobals = jest.fn(); | ||
|
|
||
| export default { | ||
| AudioSession, | ||
| registerGlobals, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import { AudioSession } from '@livekit/react-native'; | ||
| import { RTCAudioSession } from '@livekit/react-native-webrtc'; | ||
| import notifee, { AndroidForegroundServiceType, AndroidImportance } from '@notifee/react-native'; | ||
| import { getRecordingPermissionsAsync, requestRecordingPermissionsAsync } from 'expo-audio'; | ||
|
|
@@ -381,18 +382,58 @@ export const useLiveKitStore = create<LiveKitState>((set, get) => ({ | |
| message: 'Cannot connect to room - permissions not granted', | ||
| context: { roomName: roomInfo.Name }, | ||
| }); | ||
| Alert.alert('Voice Connection Error', 'Microphone permission is required to join a voice channel. Please grant the permission in your device settings.', [ | ||
| { text: 'Cancel', style: 'cancel' }, | ||
| { text: 'Open Settings', onPress: () => Linking.openSettings() }, | ||
| ]); | ||
| return; | ||
| } | ||
|
|
||
| const { currentRoom, voipServerWebsocketSslAddress } = get(); | ||
|
|
||
| // Disconnect from current room if connected | ||
| // Validate connection parameters before attempting to connect | ||
| if (!voipServerWebsocketSslAddress) { | ||
| logger.error({ | ||
| message: 'Cannot connect to room - no VoIP server address available', | ||
| context: { roomName: roomInfo.Name }, | ||
| }); | ||
| Alert.alert('Voice Connection Error', 'Voice server address is not available. Please try again later.'); | ||
| return; | ||
| } | ||
|
|
||
| if (!token) { | ||
| logger.error({ | ||
| message: 'Cannot connect to room - no token provided', | ||
| context: { roomName: roomInfo.Name }, | ||
| }); | ||
| Alert.alert('Voice Connection Error', 'Voice channel token is missing. Please try refreshing the voice channels.'); | ||
| return; | ||
| } | ||
|
|
||
| // Disconnect from current room if connected (use full cleanup flow) | ||
| if (currentRoom) { | ||
| currentRoom.disconnect(); | ||
| await get().disconnectFromRoom(); | ||
| } | ||
|
|
||
| set({ isConnecting: true }); | ||
|
|
||
| // Start the native audio session before connecting (required for production builds) | ||
| // In dev builds, the audio session may persist across hot reloads, but in production | ||
| // cold starts it must be explicitly started for WebRTC to function correctly | ||
| if (Platform.OS !== 'web') { | ||
| try { | ||
| await AudioSession.startAudioSession(); | ||
| logger.info({ | ||
| message: 'Audio session started successfully', | ||
| }); | ||
| } catch (audioSessionError) { | ||
| logger.warn({ | ||
| message: 'Failed to start audio session - continuing with connection attempt', | ||
| context: { error: audioSessionError }, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // Create a new room | ||
| const room = new Room(); | ||
|
|
||
|
|
@@ -576,10 +617,30 @@ export const useLiveKitStore = create<LiveKitState>((set, get) => ({ | |
| } catch (error) { | ||
| logger.error({ | ||
| message: 'Failed to connect to room', | ||
| context: { error }, | ||
| context: { error, roomName: roomInfo?.Name }, | ||
| }); | ||
|
|
||
| // Stop audio session on failure since we started it above | ||
| if (Platform.OS !== 'web') { | ||
| try { | ||
| await AudioSession.stopAudioSession(); | ||
| } catch (stopError) { | ||
| logger.warn({ | ||
| message: 'Failed to stop audio session after connection error', | ||
| context: { error: stopError }, | ||
| }); | ||
| } | ||
| } | ||
|
Comment on lines
618
to
+633
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's see the structure of livekit-store.ts and find the connectToRoom method
fd -a "livekit-store.ts" src/Repository: Resgrid/Unit Length of output: 106 🏁 Script executed: # Get the full connectToRoom method to understand the complete flow
wc -l src/stores/app/livekit-store.tsRepository: Resgrid/Unit Length of output: 91 🏁 Script executed: # Search for the connectToRoom method and its context
rg -n "connectToRoom|async.*connectToRoom" src/stores/app/livekit-store.ts -A 150 | head -200Repository: Resgrid/Unit Length of output: 8313 🏁 Script executed: # Check for any connected flag or state tracking
rg -n "connected|isConnected|connectionStatus" src/stores/app/livekit-store.ts -B 2 -A 2Repository: Resgrid/Unit Length of output: 3428 🏁 Script executed: # Look at the audio service for error handling as indicated in the original request
fd -a "audio.service" src/Repository: Resgrid/Unit Length of output: 380 🏁 Script executed: # Get the complete connectToRoom method from line 372 onwards
sed -n '372,640p' src/stores/app/livekit-store.ts | cat -nRepository: Resgrid/Unit Length of output: 12776 🏁 Script executed: # Check the audio.service.ts for error handling
cat -n src/services/audio.service.ts | head -100Repository: Resgrid/Unit Length of output: 4057 🏁 Script executed: # Get the complete catch block to see all error handling
sed -n '617,680p' src/stores/app/livekit-store.ts | cat -nRepository: Resgrid/Unit Length of output: 2477 🏁 Script executed: # Find and inspect setupAudioRouting function
rg -n "setupAudioRouting" src/stores/app/livekit-store.ts -B 2 -A 20Repository: Resgrid/Unit Length of output: 2265 🏁 Script executed: # Check audio.service.ts for error handling in playConnectToAudioRoomSound
rg -n "playConnectToAudioRoomSound" src/services/audio.service.ts -B 2 -A 15Repository: Resgrid/Unit Length of output: 774 🏁 Script executed: # Find playSound implementation in audio.service.ts
rg -n "playSound" src/services/audio.service.ts -B 2 -A 20 | head -100Repository: Resgrid/Unit Length of output: 2295 Consider guarding post-connect side effects from room connection status. The outer try/catch will treat any later error (audio routing, sound playback) as a connection failure and show a misleading "Connection Failed" alert, even though the room is already connected and set to
🤖 Prompt for AI Agents |
||
|
|
||
| set({ isConnecting: false }); | ||
|
|
||
| // Show user-visible error so the failure is not silent in production builds | ||
| const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'; | ||
| Alert.alert( | ||
| 'Voice Connection Failed', | ||
| `Unable to connect to voice channel "${roomInfo?.Name || 'Unknown'}". ${errorMessage}`, | ||
| [{ text: 'OK' }] | ||
| ); | ||
| } | ||
| }, | ||
|
|
||
|
|
@@ -589,6 +650,21 @@ export const useLiveKitStore = create<LiveKitState>((set, get) => ({ | |
| await currentRoom.disconnect(); | ||
| await audioService.playDisconnectedFromAudioRoomSound(); | ||
|
|
||
| // Stop the native audio session that was started during connectToRoom | ||
| if (Platform.OS !== 'web') { | ||
| try { | ||
| await AudioSession.stopAudioSession(); | ||
| logger.debug({ | ||
| message: 'Audio session stopped', | ||
| }); | ||
| } catch (audioSessionError) { | ||
| logger.warn({ | ||
| message: 'Failed to stop audio session', | ||
| context: { error: audioSessionError }, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // End CallKeep call (works on all platforms - web has no-op implementation) | ||
| try { | ||
| await callKeepService.endCall(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Localize new user-facing Alert strings.
These new alerts introduce hardcoded English strings; route them through
t()with translation keys so they’re localized.💡 Example adjustment
As per coding guidelines, "Ensure all text is wrapped in t() from react-i18next for translations with dictionary files stored in src/translations".
Also applies to: 638-642
🤖 Prompt for AI Agents