@@ -31,7 +31,6 @@ import {
31
31
import type EventEmitter from "events" ;
32
32
import type { IApp } from "../stores/WidgetStore" ;
33
33
import SettingsStore from "../settings/SettingsStore" ;
34
- import MediaDeviceHandler , { MediaDeviceKindEnum } from "../MediaDeviceHandler" ;
35
34
import { timeout } from "../utils/promise" ;
36
35
import WidgetUtils from "../utils/WidgetUtils" ;
37
36
import { WidgetType } from "../widgets/WidgetType" ;
@@ -192,47 +191,17 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
192
191
*/
193
192
public abstract clean ( ) : Promise < void > ;
194
193
195
- /**
196
- * Contacts the widget to connect to the call or prompt the user to connect to the call.
197
- * @param {MediaDeviceInfo | null } audioInput The audio input to use, or
198
- * null to start muted.
199
- * @param {MediaDeviceInfo | null } audioInput The video input to use, or
200
- * null to start muted.
201
- */
202
- protected abstract performConnection (
203
- audioInput : MediaDeviceInfo | null ,
204
- videoInput : MediaDeviceInfo | null ,
205
- ) : Promise < void > ;
206
-
207
194
/**
208
195
* Contacts the widget to disconnect from the call.
209
196
*/
210
197
protected abstract performDisconnection ( ) : Promise < void > ;
211
198
212
199
/**
213
200
* Starts the communication between the widget and the call.
214
- * The call then waits for the necessary requirements to actually perform the connection
215
- * or connects right away depending on the call type. (Jitsi, Legacy, ElementCall...)
216
- * It uses the media devices set in MediaDeviceHandler.
217
- * The widget associated with the call must be active
218
- * for this to succeed.
201
+ * The widget associated with the call must be active for this to succeed.
219
202
* Only call this if the call state is: ConnectionState.Disconnected.
220
203
*/
221
204
public async start ( ) : Promise < void > {
222
- const { [ MediaDeviceKindEnum . AudioInput ] : audioInputs , [ MediaDeviceKindEnum . VideoInput ] : videoInputs } =
223
- ( await MediaDeviceHandler . getDevices ( ) ) ! ;
224
-
225
- let audioInput : MediaDeviceInfo | null = null ;
226
- if ( ! MediaDeviceHandler . startWithAudioMuted ) {
227
- const deviceId = MediaDeviceHandler . getAudioInput ( ) ;
228
- audioInput = audioInputs . find ( ( d ) => d . deviceId === deviceId ) ?? audioInputs [ 0 ] ?? null ;
229
- }
230
- let videoInput : MediaDeviceInfo | null = null ;
231
- if ( ! MediaDeviceHandler . startWithVideoMuted ) {
232
- const deviceId = MediaDeviceHandler . getVideoInput ( ) ;
233
- videoInput = videoInputs . find ( ( d ) => d . deviceId === deviceId ) ?? videoInputs [ 0 ] ?? null ;
234
- }
235
-
236
205
const messagingStore = WidgetMessagingStore . instance ;
237
206
this . messaging = messagingStore . getMessagingForUid ( this . widgetUid ) ?? null ;
238
207
if ( ! this . messaging ) {
@@ -253,13 +222,23 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
253
222
throw new Error ( `Failed to bind call widget in room ${ this . roomId } : ${ e } ` ) ;
254
223
}
255
224
}
256
- await this . performConnection ( audioInput , videoInput ) ;
225
+ }
257
226
227
+ protected setConnected ( ) : void {
258
228
this . room . on ( RoomEvent . MyMembership , this . onMyMembership ) ;
259
229
window . addEventListener ( "beforeunload" , this . beforeUnload ) ;
260
230
this . connectionState = ConnectionState . Connected ;
261
231
}
262
232
233
+ /**
234
+ * Manually marks the call as disconnected.
235
+ */
236
+ protected setDisconnected ( ) : void {
237
+ this . room . off ( RoomEvent . MyMembership , this . onMyMembership ) ;
238
+ window . removeEventListener ( "beforeunload" , this . beforeUnload ) ;
239
+ this . connectionState = ConnectionState . Disconnected ;
240
+ }
241
+
263
242
/**
264
243
* Disconnects the user from the call.
265
244
*/
@@ -272,15 +251,6 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
272
251
this . close ( ) ;
273
252
}
274
253
275
- /**
276
- * Manually marks the call as disconnected.
277
- */
278
- public setDisconnected ( ) : void {
279
- this . room . off ( RoomEvent . MyMembership , this . onMyMembership ) ;
280
- window . removeEventListener ( "beforeunload" , this . beforeUnload ) ;
281
- this . connectionState = ConnectionState . Disconnected ;
282
- }
283
-
284
254
/**
285
255
* Stops further communication with the widget and tells the UI to close.
286
256
*/
@@ -466,66 +436,10 @@ export class JitsiCall extends Call {
466
436
} ) ;
467
437
}
468
438
469
- protected async performConnection (
470
- audioInput : MediaDeviceInfo | null ,
471
- videoInput : MediaDeviceInfo | null ,
472
- ) : Promise < void > {
473
- // Ensure that the messaging doesn't get stopped while we're waiting for responses
474
- const dontStopMessaging = new Promise < void > ( ( resolve , reject ) => {
475
- const messagingStore = WidgetMessagingStore . instance ;
476
-
477
- const listener = ( uid : string ) : void => {
478
- if ( uid === this . widgetUid ) {
479
- cleanup ( ) ;
480
- reject ( new Error ( "Messaging stopped" ) ) ;
481
- }
482
- } ;
483
- const done = ( ) : void => {
484
- cleanup ( ) ;
485
- resolve ( ) ;
486
- } ;
487
- const cleanup = ( ) : void => {
488
- messagingStore . off ( WidgetMessagingStoreEvent . StopMessaging , listener ) ;
489
- this . off ( CallEvent . ConnectionState , done ) ;
490
- } ;
491
-
492
- messagingStore . on ( WidgetMessagingStoreEvent . StopMessaging , listener ) ;
493
- this . on ( CallEvent . ConnectionState , done ) ;
494
- } ) ;
495
-
496
- // Empirically, it's possible for Jitsi Meet to crash instantly at startup,
497
- // sending a hangup event that races with the rest of this method, so we need
498
- // to add the hangup listener now rather than later
439
+ public async start ( ) : Promise < void > {
440
+ await super . start ( ) ;
441
+ this . messaging ! . on ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
499
442
this . messaging ! . on ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
500
-
501
- // Actually perform the join
502
- const response = waitForEvent (
503
- this . messaging ! ,
504
- `action:${ ElementWidgetActions . JoinCall } ` ,
505
- ( ev : CustomEvent < IWidgetApiRequest > ) => {
506
- ev . preventDefault ( ) ;
507
- this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
508
- return true ;
509
- } ,
510
- ) ;
511
- const request = this . messaging ! . transport . send ( ElementWidgetActions . JoinCall , {
512
- audioInput : audioInput ?. label ?? null ,
513
- videoInput : videoInput ?. label ?? null ,
514
- } ) ;
515
- try {
516
- await Promise . race ( [ Promise . all ( [ request , response ] ) , dontStopMessaging ] ) ;
517
- } catch ( e ) {
518
- // If it timed out, clean up our advance preparations
519
- this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
520
-
521
- if ( this . messaging ! . transport . ready ) {
522
- // The messaging still exists, which means Jitsi might still be going in the background
523
- this . messaging ! . transport . send ( ElementWidgetActions . HangupCall , { force : true } ) ;
524
- }
525
-
526
- throw new Error ( `Failed to join call in room ${ this . roomId } : ${ e } ` ) ;
527
- }
528
-
529
443
ActiveWidgetStore . instance . on ( ActiveWidgetStoreEvent . Dock , this . onDock ) ;
530
444
ActiveWidgetStore . instance . on ( ActiveWidgetStoreEvent . Undock , this . onUndock ) ;
531
445
}
@@ -548,18 +462,17 @@ export class JitsiCall extends Call {
548
462
}
549
463
}
550
464
551
- public setDisconnected ( ) : void {
552
- // During tests this.messaging can be undefined
553
- this . messaging ? .off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
465
+ public close ( ) : void {
466
+ this . messaging ! . off ( `action: ${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
467
+ this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
554
468
ActiveWidgetStore . instance . off ( ActiveWidgetStoreEvent . Dock , this . onDock ) ;
555
469
ActiveWidgetStore . instance . off ( ActiveWidgetStoreEvent . Undock , this . onUndock ) ;
556
-
557
- super . setDisconnected ( ) ;
470
+ super . close ( ) ;
558
471
}
559
472
560
473
public destroy ( ) : void {
561
474
this . room . off ( RoomStateEvent . Update , this . onRoomState ) ;
562
- this . on ( CallEvent . ConnectionState , this . onConnectionState ) ;
475
+ this . off ( CallEvent . ConnectionState , this . onConnectionState ) ;
563
476
if ( this . participantsExpirationTimer !== null ) {
564
477
clearTimeout ( this . participantsExpirationTimer ) ;
565
478
this . participantsExpirationTimer = null ;
@@ -611,27 +524,21 @@ export class JitsiCall extends Call {
611
524
await this . messaging ! . transport . send ( ElementWidgetActions . SpotlightLayout , { } ) ;
612
525
} ;
613
526
527
+ private readonly onJoin = ( ev : CustomEvent < IWidgetApiRequest > ) : void => {
528
+ ev . preventDefault ( ) ;
529
+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
530
+ this . setConnected ( ) ;
531
+ } ;
532
+
614
533
private readonly onHangup = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
615
534
// If we're already in the middle of a client-initiated disconnection,
616
535
// ignore the event
617
536
if ( this . connectionState === ConnectionState . Disconnecting ) return ;
618
537
619
538
ev . preventDefault ( ) ;
620
-
621
- // In case this hangup is caused by Jitsi Meet crashing at startup,
622
- // wait for the connection event in order to avoid racing
623
- if ( this . connectionState === ConnectionState . Disconnected ) {
624
- await waitForEvent ( this , CallEvent . ConnectionState ) ;
625
- }
626
-
627
539
this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
628
540
this . setDisconnected ( ) ;
629
- this . close ( ) ;
630
- // In video rooms we immediately want to restart the call after hangup
631
- // The lobby will be shown again and it connects to all signals from Jitsi.
632
- if ( isVideoRoom ( this . room ) ) {
633
- this . start ( ) ;
634
- }
541
+ if ( ! isVideoRoom ( this . room ) ) this . close ( ) ;
635
542
} ;
636
543
}
637
544
@@ -855,55 +762,38 @@ export class ElementCall extends Call {
855
762
ElementCall . createOrGetCallWidget ( room . roomId , room . client , skipLobby , isVideoRoom ( room ) ) ;
856
763
}
857
764
858
- protected async performConnection (
859
- audioInput : MediaDeviceInfo | null ,
860
- videoInput : MediaDeviceInfo | null ,
861
- ) : Promise < void > {
765
+ public async start ( ) : Promise < void > {
766
+ await super . start ( ) ;
767
+ this . messaging ! . on ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
862
768
this . messaging ! . on ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
863
- this . messaging ! . once ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
769
+ this . messaging ! . on ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
864
770
this . messaging ! . on ( `action:${ ElementWidgetActions . DeviceMute } ` , this . onDeviceMute ) ;
865
-
866
- // TODO: if the widget informs us when the join button is clicked (widget action), so we can
867
- // - set state to connecting
868
- // - send call notify
869
- const session = this . client . matrixRTC . getActiveRoomSession ( this . room ) ;
870
- if ( session ) {
871
- await waitForEvent (
872
- session ,
873
- MatrixRTCSessionEvent . MembershipsChanged ,
874
- ( _ , newMemberships : CallMembership [ ] ) =>
875
- newMemberships . some ( ( m ) => m . sender === this . client . getUserId ( ) ) ,
876
- false , // allow user to wait as long as they want (no timeout)
877
- ) ;
878
- } else {
879
- await waitForEvent (
880
- this . client . matrixRTC ,
881
- MatrixRTCSessionManagerEvents . SessionStarted ,
882
- ( roomId : string , session : MatrixRTCSession ) =>
883
- this . session . callId === session . callId && roomId === this . roomId ,
884
- false , // allow user to wait as long as they want (no timeout)
885
- ) ;
886
- }
887
771
}
888
772
889
773
protected async performDisconnection ( ) : Promise < void > {
774
+ const response = waitForEvent (
775
+ this . messaging ! ,
776
+ `action:${ ElementWidgetActions . HangupCall } ` ,
777
+ ( ev : CustomEvent < IWidgetApiRequest > ) => {
778
+ ev . preventDefault ( ) ;
779
+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
780
+ return true ;
781
+ } ,
782
+ ) ;
783
+ const request = this . messaging ! . transport . send ( ElementWidgetActions . HangupCall , { } ) ;
890
784
try {
891
- await this . messaging ! . transport . send ( ElementWidgetActions . HangupCall , { } ) ;
892
- await waitForEvent (
893
- this . session ,
894
- MatrixRTCSessionEvent . MembershipsChanged ,
895
- ( _ , newMemberships : CallMembership [ ] ) =>
896
- ! newMemberships . some ( ( m ) => m . sender === this . client . getUserId ( ) ) ,
897
- ) ;
785
+ await Promise . all ( [ request , response ] ) ;
898
786
} catch ( e ) {
899
787
throw new Error ( `Failed to hangup call in room ${ this . roomId } : ${ e } ` ) ;
900
788
}
901
789
}
902
790
903
- public setDisconnected ( ) : void {
791
+ public close ( ) : void {
792
+ this . messaging ! . off ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
904
793
this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
794
+ this . messaging ! . off ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
905
795
this . messaging ! . off ( `action:${ ElementWidgetActions . DeviceMute } ` , this . onDeviceMute ) ;
906
- super . setDisconnected ( ) ;
796
+ super . close ( ) ;
907
797
}
908
798
909
799
public destroy ( ) : void {
@@ -950,15 +840,20 @@ export class ElementCall extends Call {
950
840
this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
951
841
} ;
952
842
843
+ private readonly onJoin = ( ev : CustomEvent < IWidgetApiRequest > ) : void => {
844
+ ev . preventDefault ( ) ;
845
+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
846
+ this . setConnected ( ) ;
847
+ } ;
848
+
953
849
private readonly onHangup = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
850
+ // If we're already in the middle of a client-initiated disconnection,
851
+ // ignore the event
852
+ if ( this . connectionState === ConnectionState . Disconnecting ) return ;
853
+
954
854
ev . preventDefault ( ) ;
955
855
this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
956
856
this . setDisconnected ( ) ;
957
- // In video rooms we immediately want to reconnect after hangup
958
- // This starts the lobby again and connects to all signals from EC.
959
- if ( isVideoRoom ( this . room ) ) {
960
- this . start ( ) ;
961
- }
962
857
} ;
963
858
964
859
private readonly onClose = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
0 commit comments