@@ -239,6 +239,22 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
239
239
messageHistoryLimit,
240
240
} ) ;
241
241
242
+ const checkIsDisconnected = useCallback ( ( ) => {
243
+ return (
244
+ resourceStatusRef . current . mic === 'disconnected' ||
245
+ resourceStatusRef . current . audioPlayer === 'disconnected' ||
246
+ resourceStatusRef . current . socket === 'disconnected'
247
+ ) ;
248
+ } , [ ] ) ;
249
+
250
+ const checkIsDisconnecting = useCallback ( ( ) => {
251
+ return (
252
+ resourceStatusRef . current . mic === 'disconnecting' ||
253
+ resourceStatusRef . current . audioPlayer === 'disconnecting' ||
254
+ resourceStatusRef . current . socket === 'disconnecting'
255
+ ) ;
256
+ } , [ ] ) ;
257
+
242
258
const updateError = useCallback ( ( err : VoiceError | null ) => {
243
259
setError ( err ) ;
244
260
if ( err !== null ) {
@@ -284,10 +300,7 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
284
300
285
301
const client = useVoiceClient ( {
286
302
onAudioMessage : ( message : AudioOutputMessage ) => {
287
- if (
288
- resourceStatusRef . current . audioPlayer === 'disconnecting' ||
289
- resourceStatusRef . current . audioPlayer === 'disconnected'
290
- ) {
303
+ if ( checkIsDisconnecting ( ) || checkIsDisconnected ( ) ) {
291
304
// disconnection in progress, and resources are being cleaned up.
292
305
// ignore the message
293
306
return ;
@@ -297,6 +310,12 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
297
310
} ,
298
311
onMessage : useCallback (
299
312
( message : JSONMessage ) => {
313
+ if ( checkIsDisconnecting ( ) || checkIsDisconnected ( ) ) {
314
+ // disconnection in progress, and resources are being cleaned up.
315
+ // ignore the message
316
+ return ;
317
+ }
318
+
300
319
// store message
301
320
messageStore . onMessage ( message ) ;
302
321
@@ -327,7 +346,13 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
327
346
onError . current ?.( error ) ;
328
347
}
329
348
} ,
330
- [ messageStore , player , toolStatus ] ,
349
+ [
350
+ checkIsDisconnected ,
351
+ checkIsDisconnecting ,
352
+ messageStore ,
353
+ player ,
354
+ toolStatus ,
355
+ ] ,
331
356
) ,
332
357
onClientError,
333
358
onToolCallError : useCallback (
@@ -364,34 +389,32 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
364
389
toolStatus . clearStore ( ) ;
365
390
setIsPaused ( false ) ;
366
391
392
+ const resourceShutdownFns = [ ] ;
367
393
if ( resourceStatusRef . current . audioPlayer === 'connected' ) {
368
- void player . stopAll ( ) . then ( ( ) => {
369
- resourceStatusRef . current . audioPlayer = 'disconnected' ;
370
- } ) ;
394
+ resourceShutdownFns . push ( player . stopAll ( ) ) ;
371
395
}
372
-
373
396
if ( resourceStatusRef . current . mic === 'connected' ) {
374
- stopStream ( ) ;
375
- void micStopFnRef . current ?.( ) . then ( ( ) => {
376
- resourceStatusRef . current . mic = 'disconnected' ;
377
- } ) ;
397
+ resourceShutdownFns . push ( micStopFnRef . current ?.( ) ) ;
378
398
}
379
399
380
- if ( ! error ) {
381
- // if there's an error, keep the error status. otherwise, set status to disconnected
382
- setStatus ( { value : 'disconnected' } ) ;
400
+ if ( resourceShutdownFns . length > 0 ) {
401
+ void Promise . all ( resourceShutdownFns ) . then ( ( ) => {
402
+ resourceStatusRef . current . audioPlayer = 'disconnected' ;
403
+ resourceStatusRef . current . mic = 'disconnected' ;
404
+ // if audio player and mic were connected at the time the socket
405
+ // shut down, we can assume that the connection was closed by
406
+ // the server, and not the user. Therefore, set the status
407
+ // to 'disconnected'
408
+ setStatus ( { value : 'disconnected' } ) ;
409
+ onClose . current ?.( event ) ;
410
+ } ) ;
411
+ } else {
412
+ // if audio player and mic were not connected at the time the socket,
413
+ // no need to setStatus because the user initiated the disconnect.
414
+ onClose . current ?.( event ) ;
383
415
}
384
- onClose . current ?.( event ) ;
385
416
} ,
386
- [
387
- clearMessagesOnDisconnect ,
388
- error ,
389
- messageStore ,
390
- player ,
391
- stopStream ,
392
- stopTimer ,
393
- toolStatus ,
394
- ] ,
417
+ [ clearMessagesOnDisconnect , messageStore , player , stopTimer , toolStatus ] ,
395
418
) ,
396
419
onToolCall : props . onToolCall ,
397
420
} ) ;
@@ -488,6 +511,13 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
488
511
return ;
489
512
}
490
513
514
+ if ( checkIsDisconnecting ( ) ) {
515
+ console . warn (
516
+ 'Currently disconnecting from a chat. Cannot connect until the previous call is disconnected.' ,
517
+ ) ;
518
+ return ;
519
+ }
520
+
491
521
updateError ( null ) ;
492
522
setStatus ( { value : 'connecting' } ) ;
493
523
resourceStatusRef . current . socket = 'connecting' ;
@@ -612,8 +642,8 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
612
642
613
643
stopTimer ( ) ;
614
644
615
- // MICROPHONE - shut this down before shutting down the websocket
616
- // call stopStream separately because the user could stop the
645
+ // MICROPHONE - shut this down before shutting down the websocket.
646
+ // Call stopStream separately because the user could stop the
617
647
// the connection before the microphone is initialized
618
648
stopStream ( ) ;
619
649
await mic . stop ( ) ;
@@ -639,10 +669,10 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
639
669
setIsPaused ( false ) ;
640
670
} , [
641
671
stopTimer ,
642
- client ,
643
- player ,
644
672
stopStream ,
645
673
mic ,
674
+ client ,
675
+ player ,
646
676
clearMessagesOnDisconnect ,
647
677
toolStatus ,
648
678
messageStore ,
@@ -663,12 +693,9 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
663
693
) ;
664
694
665
695
useEffect ( ( ) => {
666
- if (
667
- error !== null &&
668
- status . value !== 'error' &&
669
- status . value !== 'disconnected'
670
- ) {
671
- // If the status is ever set to `error`, disconnect the voice.
696
+ if ( error !== null && status . value !== 'error' ) {
697
+ // If the status is ever set to `error`, disconnect the call
698
+ // and clean up resources.
672
699
setStatus ( { value : 'error' , reason : error . message } ) ;
673
700
void disconnectAndCleanUpResources ( ) ;
674
701
}
@@ -678,6 +705,13 @@ export const VoiceProvider: FC<VoiceProviderProps> = ({
678
705
// disconnect from socket when the voice provider component unmounts
679
706
return ( ) => {
680
707
void disconnectAndCleanUpResources ( ) ;
708
+ setStatus ( { value : 'disconnected' } ) ;
709
+ isConnectingRef . current = false ;
710
+ resourceStatusRef . current = {
711
+ mic : 'disconnected' ,
712
+ audioPlayer : 'disconnected' ,
713
+ socket : 'disconnected' ,
714
+ } ;
681
715
} ;
682
716
// eslint-disable-next-line react-hooks/exhaustive-deps
683
717
} , [ ] ) ;
0 commit comments