@@ -28,7 +28,6 @@ import type EventEmitter from "events";
28
28
import type { IApp } from "../stores/WidgetStore" ;
29
29
import SdkConfig , { DEFAULTS } from "../SdkConfig" ;
30
30
import SettingsStore from "../settings/SettingsStore" ;
31
- import MediaDeviceHandler , { MediaDeviceKindEnum } from "../MediaDeviceHandler" ;
32
31
import { timeout } from "../utils/promise" ;
33
32
import WidgetUtils from "../utils/WidgetUtils" ;
34
33
import { WidgetType } from "../widgets/WidgetType" ;
@@ -188,47 +187,17 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
188
187
*/
189
188
public abstract clean ( ) : Promise < void > ;
190
189
191
- /**
192
- * Contacts the widget to connect to the call or prompt the user to connect to the call.
193
- * @param {MediaDeviceInfo | null } audioInput The audio input to use, or
194
- * null to start muted.
195
- * @param {MediaDeviceInfo | null } audioInput The video input to use, or
196
- * null to start muted.
197
- */
198
- protected abstract performConnection (
199
- audioInput : MediaDeviceInfo | null ,
200
- videoInput : MediaDeviceInfo | null ,
201
- ) : Promise < void > ;
202
-
203
190
/**
204
191
* Contacts the widget to disconnect from the call.
205
192
*/
206
193
protected abstract performDisconnection ( ) : Promise < void > ;
207
194
208
195
/**
209
196
* Starts the communication between the widget and the call.
210
- * The call then waits for the necessary requirements to actually perform the connection
211
- * or connects right away depending on the call type. (Jitsi, Legacy, ElementCall...)
212
- * It uses the media devices set in MediaDeviceHandler.
213
- * The widget associated with the call must be active
214
- * for this to succeed.
197
+ * The widget associated with the call must be active for this to succeed.
215
198
* Only call this if the call state is: ConnectionState.Disconnected.
216
199
*/
217
200
public async start ( ) : Promise < void > {
218
- const { [ MediaDeviceKindEnum . AudioInput ] : audioInputs , [ MediaDeviceKindEnum . VideoInput ] : videoInputs } =
219
- ( await MediaDeviceHandler . getDevices ( ) ) ! ;
220
-
221
- let audioInput : MediaDeviceInfo | null = null ;
222
- if ( ! MediaDeviceHandler . startWithAudioMuted ) {
223
- const deviceId = MediaDeviceHandler . getAudioInput ( ) ;
224
- audioInput = audioInputs . find ( ( d ) => d . deviceId === deviceId ) ?? audioInputs [ 0 ] ?? null ;
225
- }
226
- let videoInput : MediaDeviceInfo | null = null ;
227
- if ( ! MediaDeviceHandler . startWithVideoMuted ) {
228
- const deviceId = MediaDeviceHandler . getVideoInput ( ) ;
229
- videoInput = videoInputs . find ( ( d ) => d . deviceId === deviceId ) ?? videoInputs [ 0 ] ?? null ;
230
- }
231
-
232
201
const messagingStore = WidgetMessagingStore . instance ;
233
202
this . messaging = messagingStore . getMessagingForUid ( this . widgetUid ) ?? null ;
234
203
if ( ! this . messaging ) {
@@ -249,13 +218,23 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
249
218
throw new Error ( `Failed to bind call widget in room ${ this . roomId } : ${ e } ` ) ;
250
219
}
251
220
}
252
- await this . performConnection ( audioInput , videoInput ) ;
221
+ }
253
222
223
+ protected setConnected ( ) : void {
254
224
this . room . on ( RoomEvent . MyMembership , this . onMyMembership ) ;
255
225
window . addEventListener ( "beforeunload" , this . beforeUnload ) ;
256
226
this . connectionState = ConnectionState . Connected ;
257
227
}
258
228
229
+ /**
230
+ * Manually marks the call as disconnected.
231
+ */
232
+ protected setDisconnected ( ) : void {
233
+ this . room . off ( RoomEvent . MyMembership , this . onMyMembership ) ;
234
+ window . removeEventListener ( "beforeunload" , this . beforeUnload ) ;
235
+ this . connectionState = ConnectionState . Disconnected ;
236
+ }
237
+
259
238
/**
260
239
* Disconnects the user from the call.
261
240
*/
@@ -268,15 +247,6 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
268
247
this . close ( ) ;
269
248
}
270
249
271
- /**
272
- * Manually marks the call as disconnected.
273
- */
274
- public setDisconnected ( ) : void {
275
- this . room . off ( RoomEvent . MyMembership , this . onMyMembership ) ;
276
- window . removeEventListener ( "beforeunload" , this . beforeUnload ) ;
277
- this . connectionState = ConnectionState . Disconnected ;
278
- }
279
-
280
250
/**
281
251
* Stops further communication with the widget and tells the UI to close.
282
252
*/
@@ -462,66 +432,10 @@ export class JitsiCall extends Call {
462
432
} ) ;
463
433
}
464
434
465
- protected async performConnection (
466
- audioInput : MediaDeviceInfo | null ,
467
- videoInput : MediaDeviceInfo | null ,
468
- ) : Promise < void > {
469
- // Ensure that the messaging doesn't get stopped while we're waiting for responses
470
- const dontStopMessaging = new Promise < void > ( ( resolve , reject ) => {
471
- const messagingStore = WidgetMessagingStore . instance ;
472
-
473
- const listener = ( uid : string ) : void => {
474
- if ( uid === this . widgetUid ) {
475
- cleanup ( ) ;
476
- reject ( new Error ( "Messaging stopped" ) ) ;
477
- }
478
- } ;
479
- const done = ( ) : void => {
480
- cleanup ( ) ;
481
- resolve ( ) ;
482
- } ;
483
- const cleanup = ( ) : void => {
484
- messagingStore . off ( WidgetMessagingStoreEvent . StopMessaging , listener ) ;
485
- this . off ( CallEvent . ConnectionState , done ) ;
486
- } ;
487
-
488
- messagingStore . on ( WidgetMessagingStoreEvent . StopMessaging , listener ) ;
489
- this . on ( CallEvent . ConnectionState , done ) ;
490
- } ) ;
491
-
492
- // Empirically, it's possible for Jitsi Meet to crash instantly at startup,
493
- // sending a hangup event that races with the rest of this method, so we need
494
- // to add the hangup listener now rather than later
435
+ public async start ( ) : Promise < void > {
436
+ await super . start ( ) ;
437
+ this . messaging ! . on ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
495
438
this . messaging ! . on ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
496
-
497
- // Actually perform the join
498
- const response = waitForEvent (
499
- this . messaging ! ,
500
- `action:${ ElementWidgetActions . JoinCall } ` ,
501
- ( ev : CustomEvent < IWidgetApiRequest > ) => {
502
- ev . preventDefault ( ) ;
503
- this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
504
- return true ;
505
- } ,
506
- ) ;
507
- const request = this . messaging ! . transport . send ( ElementWidgetActions . JoinCall , {
508
- audioInput : audioInput ?. label ?? null ,
509
- videoInput : videoInput ?. label ?? null ,
510
- } ) ;
511
- try {
512
- await Promise . race ( [ Promise . all ( [ request , response ] ) , dontStopMessaging ] ) ;
513
- } catch ( e ) {
514
- // If it timed out, clean up our advance preparations
515
- this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
516
-
517
- if ( this . messaging ! . transport . ready ) {
518
- // The messaging still exists, which means Jitsi might still be going in the background
519
- this . messaging ! . transport . send ( ElementWidgetActions . HangupCall , { force : true } ) ;
520
- }
521
-
522
- throw new Error ( `Failed to join call in room ${ this . roomId } : ${ e } ` ) ;
523
- }
524
-
525
439
ActiveWidgetStore . instance . on ( ActiveWidgetStoreEvent . Dock , this . onDock ) ;
526
440
ActiveWidgetStore . instance . on ( ActiveWidgetStoreEvent . Undock , this . onUndock ) ;
527
441
}
@@ -544,18 +458,17 @@ export class JitsiCall extends Call {
544
458
}
545
459
}
546
460
547
- public setDisconnected ( ) : void {
548
- // During tests this.messaging can be undefined
549
- this . messaging ? .off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
461
+ public close ( ) : void {
462
+ this . messaging ! . off ( `action: ${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
463
+ this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
550
464
ActiveWidgetStore . instance . off ( ActiveWidgetStoreEvent . Dock , this . onDock ) ;
551
465
ActiveWidgetStore . instance . off ( ActiveWidgetStoreEvent . Undock , this . onUndock ) ;
552
-
553
- super . setDisconnected ( ) ;
466
+ super . close ( ) ;
554
467
}
555
468
556
469
public destroy ( ) : void {
557
470
this . room . off ( RoomStateEvent . Update , this . onRoomState ) ;
558
- this . on ( CallEvent . ConnectionState , this . onConnectionState ) ;
471
+ this . off ( CallEvent . ConnectionState , this . onConnectionState ) ;
559
472
if ( this . participantsExpirationTimer !== null ) {
560
473
clearTimeout ( this . participantsExpirationTimer ) ;
561
474
this . participantsExpirationTimer = null ;
@@ -607,27 +520,21 @@ export class JitsiCall extends Call {
607
520
await this . messaging ! . transport . send ( ElementWidgetActions . SpotlightLayout , { } ) ;
608
521
} ;
609
522
523
+ private readonly onJoin = ( ev : CustomEvent < IWidgetApiRequest > ) : void => {
524
+ ev . preventDefault ( ) ;
525
+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
526
+ this . setConnected ( ) ;
527
+ } ;
528
+
610
529
private readonly onHangup = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
611
530
// If we're already in the middle of a client-initiated disconnection,
612
531
// ignore the event
613
532
if ( this . connectionState === ConnectionState . Disconnecting ) return ;
614
533
615
534
ev . preventDefault ( ) ;
616
-
617
- // In case this hangup is caused by Jitsi Meet crashing at startup,
618
- // wait for the connection event in order to avoid racing
619
- if ( this . connectionState === ConnectionState . Disconnected ) {
620
- await waitForEvent ( this , CallEvent . ConnectionState ) ;
621
- }
622
-
623
535
this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
624
536
this . setDisconnected ( ) ;
625
- this . close ( ) ;
626
- // In video rooms we immediately want to restart the call after hangup
627
- // The lobby will be shown again and it connects to all signals from Jitsi.
628
- if ( isVideoRoom ( this . room ) ) {
629
- this . start ( ) ;
630
- }
537
+ if ( ! isVideoRoom ( this . room ) ) this . close ( ) ;
631
538
} ;
632
539
}
633
540
@@ -823,55 +730,38 @@ export class ElementCall extends Call {
823
730
ElementCall . createOrGetCallWidget ( room . roomId , room . client , skipLobby , isVideoRoom ( room ) ) ;
824
731
}
825
732
826
- protected async performConnection (
827
- audioInput : MediaDeviceInfo | null ,
828
- videoInput : MediaDeviceInfo | null ,
829
- ) : Promise < void > {
733
+ public async start ( ) : Promise < void > {
734
+ await super . start ( ) ;
735
+ this . messaging ! . on ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
830
736
this . messaging ! . on ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
831
- this . messaging ! . once ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
737
+ this . messaging ! . on ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
832
738
this . messaging ! . on ( `action:${ ElementWidgetActions . DeviceMute } ` , this . onDeviceMute ) ;
833
-
834
- // TODO: if the widget informs us when the join button is clicked (widget action), so we can
835
- // - set state to connecting
836
- // - send call notify
837
- const session = this . client . matrixRTC . getActiveRoomSession ( this . room ) ;
838
- if ( session ) {
839
- await waitForEvent (
840
- session ,
841
- MatrixRTCSessionEvent . MembershipsChanged ,
842
- ( _ , newMemberships : CallMembership [ ] ) =>
843
- newMemberships . some ( ( m ) => m . sender === this . client . getUserId ( ) ) ,
844
- false , // allow user to wait as long as they want (no timeout)
845
- ) ;
846
- } else {
847
- await waitForEvent (
848
- this . client . matrixRTC ,
849
- MatrixRTCSessionManagerEvents . SessionStarted ,
850
- ( roomId : string , session : MatrixRTCSession ) =>
851
- this . session . callId === session . callId && roomId === this . roomId ,
852
- false , // allow user to wait as long as they want (no timeout)
853
- ) ;
854
- }
855
739
}
856
740
857
741
protected async performDisconnection ( ) : Promise < void > {
742
+ const response = waitForEvent (
743
+ this . messaging ! ,
744
+ `action:${ ElementWidgetActions . HangupCall } ` ,
745
+ ( ev : CustomEvent < IWidgetApiRequest > ) => {
746
+ ev . preventDefault ( ) ;
747
+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
748
+ return true ;
749
+ } ,
750
+ ) ;
751
+ const request = this . messaging ! . transport . send ( ElementWidgetActions . HangupCall , { } ) ;
858
752
try {
859
- await this . messaging ! . transport . send ( ElementWidgetActions . HangupCall , { } ) ;
860
- await waitForEvent (
861
- this . session ,
862
- MatrixRTCSessionEvent . MembershipsChanged ,
863
- ( _ , newMemberships : CallMembership [ ] ) =>
864
- ! newMemberships . some ( ( m ) => m . sender === this . client . getUserId ( ) ) ,
865
- ) ;
753
+ await Promise . all ( [ request , response ] ) ;
866
754
} catch ( e ) {
867
755
throw new Error ( `Failed to hangup call in room ${ this . roomId } : ${ e } ` ) ;
868
756
}
869
757
}
870
758
871
- public setDisconnected ( ) : void {
759
+ public close ( ) : void {
760
+ this . messaging ! . off ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
872
761
this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
762
+ this . messaging ! . off ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
873
763
this . messaging ! . off ( `action:${ ElementWidgetActions . DeviceMute } ` , this . onDeviceMute ) ;
874
- super . setDisconnected ( ) ;
764
+ super . close ( ) ;
875
765
}
876
766
877
767
public destroy ( ) : void {
@@ -918,15 +808,20 @@ export class ElementCall extends Call {
918
808
this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
919
809
} ;
920
810
811
+ private readonly onJoin = ( ev : CustomEvent < IWidgetApiRequest > ) : void => {
812
+ ev . preventDefault ( ) ;
813
+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
814
+ this . setConnected ( ) ;
815
+ } ;
816
+
921
817
private readonly onHangup = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
818
+ // If we're already in the middle of a client-initiated disconnection,
819
+ // ignore the event
820
+ if ( this . connectionState === ConnectionState . Disconnecting ) return ;
821
+
922
822
ev . preventDefault ( ) ;
923
823
this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
924
824
this . setDisconnected ( ) ;
925
- // In video rooms we immediately want to reconnect after hangup
926
- // This starts the lobby again and connects to all signals from EC.
927
- if ( isVideoRoom ( this . room ) ) {
928
- this . start ( ) ;
929
- }
930
825
} ;
931
826
932
827
private readonly onClose = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
0 commit comments