1+ import { AudioSession } from '@livekit/react-native' ;
12import { RTCAudioSession } from '@livekit/react-native-webrtc' ;
23import notifee , { AndroidForegroundServiceType , AndroidImportance } from '@notifee/react-native' ;
34import { getRecordingPermissionsAsync , requestRecordingPermissionsAsync } from 'expo-audio' ;
@@ -381,18 +382,58 @@ export const useLiveKitStore = create<LiveKitState>((set, get) => ({
381382 message : 'Cannot connect to room - permissions not granted' ,
382383 context : { roomName : roomInfo . Name } ,
383384 } ) ;
385+ Alert . alert ( 'Voice Connection Error' , 'Microphone permission is required to join a voice channel. Please grant the permission in your device settings.' , [
386+ { text : 'Cancel' , style : 'cancel' } ,
387+ { text : 'Open Settings' , onPress : ( ) => Linking . openSettings ( ) } ,
388+ ] ) ;
384389 return ;
385390 }
386391
387392 const { currentRoom, voipServerWebsocketSslAddress } = get ( ) ;
388393
389- // Disconnect from current room if connected
394+ // Validate connection parameters before attempting to connect
395+ if ( ! voipServerWebsocketSslAddress ) {
396+ logger . error ( {
397+ message : 'Cannot connect to room - no VoIP server address available' ,
398+ context : { roomName : roomInfo . Name } ,
399+ } ) ;
400+ Alert . alert ( 'Voice Connection Error' , 'Voice server address is not available. Please try again later.' ) ;
401+ return ;
402+ }
403+
404+ if ( ! token ) {
405+ logger . error ( {
406+ message : 'Cannot connect to room - no token provided' ,
407+ context : { roomName : roomInfo . Name } ,
408+ } ) ;
409+ Alert . alert ( 'Voice Connection Error' , 'Voice channel token is missing. Please try refreshing the voice channels.' ) ;
410+ return ;
411+ }
412+
413+ // Disconnect from current room if connected (use full cleanup flow)
390414 if ( currentRoom ) {
391- currentRoom . disconnect ( ) ;
415+ await get ( ) . disconnectFromRoom ( ) ;
392416 }
393417
394418 set ( { isConnecting : true } ) ;
395419
420+ // Start the native audio session before connecting (required for production builds)
421+ // In dev builds, the audio session may persist across hot reloads, but in production
422+ // cold starts it must be explicitly started for WebRTC to function correctly
423+ if ( Platform . OS !== 'web' ) {
424+ try {
425+ await AudioSession . startAudioSession ( ) ;
426+ logger . info ( {
427+ message : 'Audio session started successfully' ,
428+ } ) ;
429+ } catch ( audioSessionError ) {
430+ logger . warn ( {
431+ message : 'Failed to start audio session - continuing with connection attempt' ,
432+ context : { error : audioSessionError } ,
433+ } ) ;
434+ }
435+ }
436+
396437 // Create a new room
397438 const room = new Room ( ) ;
398439
@@ -576,10 +617,30 @@ export const useLiveKitStore = create<LiveKitState>((set, get) => ({
576617 } catch ( error ) {
577618 logger . error ( {
578619 message : 'Failed to connect to room' ,
579- context : { error } ,
620+ context : { error, roomName : roomInfo ?. Name } ,
580621 } ) ;
581622
623+ // Stop audio session on failure since we started it above
624+ if ( Platform . OS !== 'web' ) {
625+ try {
626+ await AudioSession . stopAudioSession ( ) ;
627+ } catch ( stopError ) {
628+ logger . warn ( {
629+ message : 'Failed to stop audio session after connection error' ,
630+ context : { error : stopError } ,
631+ } ) ;
632+ }
633+ }
634+
582635 set ( { isConnecting : false } ) ;
636+
637+ // Show user-visible error so the failure is not silent in production builds
638+ const errorMessage = error instanceof Error ? error . message : 'An unknown error occurred' ;
639+ Alert . alert (
640+ 'Voice Connection Failed' ,
641+ `Unable to connect to voice channel "${ roomInfo ?. Name || 'Unknown' } ". ${ errorMessage } ` ,
642+ [ { text : 'OK' } ]
643+ ) ;
583644 }
584645 } ,
585646
@@ -589,6 +650,21 @@ export const useLiveKitStore = create<LiveKitState>((set, get) => ({
589650 await currentRoom . disconnect ( ) ;
590651 await audioService . playDisconnectedFromAudioRoomSound ( ) ;
591652
653+ // Stop the native audio session that was started during connectToRoom
654+ if ( Platform . OS !== 'web' ) {
655+ try {
656+ await AudioSession . stopAudioSession ( ) ;
657+ logger . debug ( {
658+ message : 'Audio session stopped' ,
659+ } ) ;
660+ } catch ( audioSessionError ) {
661+ logger . warn ( {
662+ message : 'Failed to stop audio session' ,
663+ context : { error : audioSessionError } ,
664+ } ) ;
665+ }
666+ }
667+
592668 // End CallKeep call (works on all platforms - web has no-op implementation)
593669 try {
594670 await callKeepService . endCall ( ) ;
0 commit comments