@@ -28,7 +28,6 @@ import type EventEmitter from "events";
2828import type { IApp } from "../stores/WidgetStore" ;
2929import SdkConfig , { DEFAULTS } from "../SdkConfig" ;
3030import SettingsStore from "../settings/SettingsStore" ;
31- import MediaDeviceHandler , { MediaDeviceKindEnum } from "../MediaDeviceHandler" ;
3231import { timeout } from "../utils/promise" ;
3332import WidgetUtils from "../utils/WidgetUtils" ;
3433import { WidgetType } from "../widgets/WidgetType" ;
@@ -188,47 +187,17 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
188187 */
189188 public abstract clean ( ) : Promise < void > ;
190189
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-
203190 /**
204191 * Contacts the widget to disconnect from the call.
205192 */
206193 protected abstract performDisconnection ( ) : Promise < void > ;
207194
208195 /**
209196 * 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.
215198 * Only call this if the call state is: ConnectionState.Disconnected.
216199 */
217200 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-
232201 const messagingStore = WidgetMessagingStore . instance ;
233202 this . messaging = messagingStore . getMessagingForUid ( this . widgetUid ) ?? null ;
234203 if ( ! this . messaging ) {
@@ -249,13 +218,23 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
249218 throw new Error ( `Failed to bind call widget in room ${ this . roomId } : ${ e } ` ) ;
250219 }
251220 }
252- await this . performConnection ( audioInput , videoInput ) ;
221+ }
253222
223+ protected setConnected ( ) : void {
254224 this . room . on ( RoomEvent . MyMembership , this . onMyMembership ) ;
255225 window . addEventListener ( "beforeunload" , this . beforeUnload ) ;
256226 this . connectionState = ConnectionState . Connected ;
257227 }
258228
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+
259238 /**
260239 * Disconnects the user from the call.
261240 */
@@ -268,15 +247,6 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
268247 this . close ( ) ;
269248 }
270249
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-
280250 /**
281251 * Stops further communication with the widget and tells the UI to close.
282252 */
@@ -462,66 +432,10 @@ export class JitsiCall extends Call {
462432 } ) ;
463433 }
464434
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 ) ;
495438 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-
525439 ActiveWidgetStore . instance . on ( ActiveWidgetStoreEvent . Dock , this . onDock ) ;
526440 ActiveWidgetStore . instance . on ( ActiveWidgetStoreEvent . Undock , this . onUndock ) ;
527441 }
@@ -544,18 +458,17 @@ export class JitsiCall extends Call {
544458 }
545459 }
546460
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 ) ;
550464 ActiveWidgetStore . instance . off ( ActiveWidgetStoreEvent . Dock , this . onDock ) ;
551465 ActiveWidgetStore . instance . off ( ActiveWidgetStoreEvent . Undock , this . onUndock ) ;
552-
553- super . setDisconnected ( ) ;
466+ super . close ( ) ;
554467 }
555468
556469 public destroy ( ) : void {
557470 this . room . off ( RoomStateEvent . Update , this . onRoomState ) ;
558- this . on ( CallEvent . ConnectionState , this . onConnectionState ) ;
471+ this . off ( CallEvent . ConnectionState , this . onConnectionState ) ;
559472 if ( this . participantsExpirationTimer !== null ) {
560473 clearTimeout ( this . participantsExpirationTimer ) ;
561474 this . participantsExpirationTimer = null ;
@@ -607,27 +520,21 @@ export class JitsiCall extends Call {
607520 await this . messaging ! . transport . send ( ElementWidgetActions . SpotlightLayout , { } ) ;
608521 } ;
609522
523+ private readonly onJoin = ( ev : CustomEvent < IWidgetApiRequest > ) : void => {
524+ ev . preventDefault ( ) ;
525+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
526+ this . setConnected ( ) ;
527+ } ;
528+
610529 private readonly onHangup = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
611530 // If we're already in the middle of a client-initiated disconnection,
612531 // ignore the event
613532 if ( this . connectionState === ConnectionState . Disconnecting ) return ;
614533
615534 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-
623535 this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
624536 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 ( ) ;
631538 } ;
632539}
633540
@@ -823,55 +730,38 @@ export class ElementCall extends Call {
823730 ElementCall . createOrGetCallWidget ( room . roomId , room . client , skipLobby , isVideoRoom ( room ) ) ;
824731 }
825732
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 ) ;
830736 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 ) ;
832738 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- }
855739 }
856740
857741 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 , { } ) ;
858752 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 ] ) ;
866754 } catch ( e ) {
867755 throw new Error ( `Failed to hangup call in room ${ this . roomId } : ${ e } ` ) ;
868756 }
869757 }
870758
871- public setDisconnected ( ) : void {
759+ public close ( ) : void {
760+ this . messaging ! . off ( `action:${ ElementWidgetActions . JoinCall } ` , this . onJoin ) ;
872761 this . messaging ! . off ( `action:${ ElementWidgetActions . HangupCall } ` , this . onHangup ) ;
762+ this . messaging ! . off ( `action:${ ElementWidgetActions . Close } ` , this . onClose ) ;
873763 this . messaging ! . off ( `action:${ ElementWidgetActions . DeviceMute } ` , this . onDeviceMute ) ;
874- super . setDisconnected ( ) ;
764+ super . close ( ) ;
875765 }
876766
877767 public destroy ( ) : void {
@@ -918,15 +808,20 @@ export class ElementCall extends Call {
918808 this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
919809 } ;
920810
811+ private readonly onJoin = ( ev : CustomEvent < IWidgetApiRequest > ) : void => {
812+ ev . preventDefault ( ) ;
813+ this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
814+ this . setConnected ( ) ;
815+ } ;
816+
921817 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+
922822 ev . preventDefault ( ) ;
923823 this . messaging ! . transport . reply ( ev . detail , { } ) ; // ack
924824 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- }
930825 } ;
931826
932827 private readonly onClose = async ( ev : CustomEvent < IWidgetApiRequest > ) : Promise < void > => {
0 commit comments