@@ -140,6 +140,8 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
140140 private encryptionKeys = new Map < string , Array < { key : Uint8Array ; timestamp : number } > > ( ) ;
141141 private lastEncryptionKeyUpdateRequest ?: number ;
142142
143+ private disconnectDelayId : string | undefined ;
144+
143145 // We use this to store the last membership fingerprints we saw, so we can proactively re-send encryption keys
144146 // if it looks like a membership has been updated.
145147 private lastMembershipFingerprints : Set < string > | undefined ;
@@ -1011,19 +1013,24 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10111013 newContent = this . makeNewMembership ( localDeviceId ) ;
10121014 }
10131015
1014- const stateKey = legacy ? localUserId : this . makeMembershipStateKey ( localUserId , localDeviceId ) ;
10151016 try {
1016- await this . client . sendStateEvent ( this . room . roomId , EventType . GroupCallMemberPrefix , newContent , stateKey ) ;
1017- logger . info ( `Sent updated call member event.` ) ;
1018-
1019- // check periodically to see if we need to refresh our member event
1020- if ( this . isJoined ( ) ) {
1021- if ( legacy ) {
1017+ if ( legacy ) {
1018+ await this . client . sendStateEvent (
1019+ this . room . roomId ,
1020+ EventType . GroupCallMemberPrefix ,
1021+ newContent ,
1022+ localUserId ,
1023+ ) ;
1024+ if ( this . isJoined ( ) ) {
1025+ // check periodically to see if we need to refresh our member event
10221026 this . memberEventTimeout = setTimeout (
10231027 this . triggerCallMembershipEventUpdate ,
10241028 MEMBER_EVENT_CHECK_PERIOD ,
10251029 ) ;
1026- } else {
1030+ }
1031+ } else if ( this . isJoined ( ) ) {
1032+ const stateKey = this . makeMembershipStateKey ( localUserId , localDeviceId ) ;
1033+ const prepareDelayedDisconnection = async ( ) : Promise < void > => {
10271034 try {
10281035 // TODO: If delayed event times out, re-join!
10291036 const res = await this . client . _unstable_sendDelayedStateEvent (
@@ -1035,12 +1042,63 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10351042 { } , // leave event
10361043 stateKey ,
10371044 ) ;
1038- this . scheduleDelayDisconnection ( res . delay_id ) ;
1045+ this . disconnectDelayId = res . delay_id ;
1046+ } catch ( e ) {
1047+ // TODO: Retry if rate-limited
1048+ logger . error ( "Failed to prepare delayed disconnection event:" , e ) ;
1049+ }
1050+ } ;
1051+ await prepareDelayedDisconnection ( ) ;
1052+ // Send join event _after_ preparing the delayed disconnection event
1053+ await this . client . sendStateEvent (
1054+ this . room . roomId ,
1055+ EventType . GroupCallMemberPrefix ,
1056+ newContent ,
1057+ stateKey ,
1058+ ) ;
1059+ // If sending state cancels your own delayed state, prepare another delayed state
1060+ // TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state
1061+ if ( this . disconnectDelayId !== undefined ) {
1062+ try {
1063+ await this . client . _unstable_updateDelayedEvent (
1064+ this . disconnectDelayId ,
1065+ UpdateDelayedEventAction . Restart ,
1066+ ) ;
1067+ } catch ( e ) {
1068+ // TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited)
1069+ logger . warn ( "Failed to update delayed disconnection event, prepare it again:" , e ) ;
1070+ this . disconnectDelayId = undefined ;
1071+ await prepareDelayedDisconnection ( ) ;
1072+ }
1073+ }
1074+ if ( this . disconnectDelayId !== undefined ) {
1075+ this . scheduleDelayDisconnection ( ) ;
1076+ }
1077+ } else {
1078+ let sentDelayedDisconnect = false ;
1079+ if ( this . disconnectDelayId !== undefined ) {
1080+ try {
1081+ await this . client . _unstable_updateDelayedEvent (
1082+ this . disconnectDelayId ,
1083+ UpdateDelayedEventAction . Send ,
1084+ ) ;
1085+ sentDelayedDisconnect = true ;
10391086 } catch ( e ) {
1040- logger . error ( "Failed to send delayed event:" , e ) ;
1087+ // TODO: Retry if rate-limited
1088+ logger . error ( "Failed to send our delayed disconnection event:" , e ) ;
10411089 }
1090+ this . disconnectDelayId = undefined ;
1091+ }
1092+ if ( ! sentDelayedDisconnect ) {
1093+ await this . client . sendStateEvent (
1094+ this . room . roomId ,
1095+ EventType . GroupCallMemberPrefix ,
1096+ { } ,
1097+ this . makeMembershipStateKey ( localUserId , localDeviceId ) ,
1098+ ) ;
10421099 }
10431100 }
1101+ logger . info ( "Sent updated call member event." ) ;
10441102 } catch ( e ) {
10451103 const resendDelay = CALL_MEMBER_EVENT_RETRY_DELAY_MIN + Math . random ( ) * 2000 ;
10461104 logger . warn ( `Failed to send call member event (retrying in ${ resendDelay } ): ${ e } ` ) ;
@@ -1049,18 +1107,19 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10491107 }
10501108 }
10511109
1052- private scheduleDelayDisconnection ( delayId : string ) : void {
1053- this . memberEventTimeout = setTimeout ( ( ) => this . delayDisconnection ( delayId ) , 5000 ) ;
1110+ private scheduleDelayDisconnection ( ) : void {
1111+ this . memberEventTimeout = setTimeout ( this . delayDisconnection , 5000 ) ;
10541112 }
10551113
1056- private async delayDisconnection ( delayId : string ) : Promise < void > {
1114+ private readonly delayDisconnection = async ( ) : Promise < void > = > {
10571115 try {
1058- await this . client . _unstable_updateDelayedEvent ( delayId , UpdateDelayedEventAction . Restart ) ;
1059- this . scheduleDelayDisconnection ( delayId ) ;
1116+ await this . client . _unstable_updateDelayedEvent ( this . disconnectDelayId ! , UpdateDelayedEventAction . Restart ) ;
1117+ this . scheduleDelayDisconnection ( ) ;
10601118 } catch ( e ) {
1061- logger . error ( "Failed to delay our disconnection event" , e ) ;
1119+ // TODO: Retry if rate-limited
1120+ logger . error ( "Failed to delay our disconnection event:" , e ) ;
10621121 }
1063- }
1122+ } ;
10641123
10651124 private stateEventsContainOngoingLegacySession ( callMemberEvents : Map < string , MatrixEvent > | undefined ) : boolean {
10661125 if ( ! callMemberEvents ?. size ) {
0 commit comments