@@ -34,6 +34,7 @@ import {
3434    type  IMembershipManager , 
3535    type  MembershipManagerEventHandlerMap , 
3636}  from  "./IMembershipManager.ts" ; 
37+ import  {  type  EmptyObject  }  from  "src/matrix.ts" ; 
3738
3839/* MembershipActionTypes: 
3940                            
@@ -77,6 +78,8 @@ On Leave: ─────────  STOP ALL ABOVE
7778(s) Successful restart/resend 
7879*/ 
7980
81+ const  STICK_DURATION_MS  =  60  *  60  *  1000 ;  // 60 minutes 
82+ 
8083/** 
8184 * The different types of actions the MembershipManager can take. 
8285 * @internal  
@@ -295,9 +298,9 @@ export class MembershipManager
295298            MatrixClient , 
296299            |  "getUserId" 
297300            |  "getDeviceId" 
298-             |  "sendStateEvent" 
299-             |  "_unstable_sendDelayedStateEvent" 
300301            |  "_unstable_updateDelayedEvent" 
302+             |  "_unstable_sendStickyEvent" 
303+             |  "_unstable_sendStickyDelayedEvent" 
301304        > , 
302305        private  getOldestMembership : ( )  =>  CallMembership  |  undefined , 
303306        public  readonly  sessionDescription : SessionDescription , 
@@ -371,7 +374,11 @@ export class MembershipManager
371374        return  this . joinConfig ?. membershipEventExpiryHeadroomMs  ??  5_000 ; 
372375    } 
373376    private  computeNextExpiryActionTs ( iteration : number ) : number  { 
374-         return  this . state . startTime  +  this . membershipEventExpiryMs  *  iteration  -  this . membershipEventExpiryHeadroomMs ; 
377+         return  ( 
378+             this . state . startTime  + 
379+             Math . min ( this . membershipEventExpiryMs ,  STICK_DURATION_MS )  *  iteration  - 
380+             this . membershipEventExpiryHeadroomMs 
381+         ) ; 
375382    } 
376383    private  get  delayedLeaveEventDelayMs ( ) : number  { 
377384        return  this . delayedLeaveEventDelayMsOverride  ??  this . joinConfig ?. delayedLeaveEventDelayMs  ??  8_000 ; 
@@ -450,14 +457,15 @@ export class MembershipManager
450457        // (Another client could have canceled it, the homeserver might have removed/lost it due to a restart, ...) 
451458        // In the `then` and `catch` block we treat both cases differently. "if (this.state.hasMemberStateEvent) {} else {}" 
452459        return  await  this . client 
453-             . _unstable_sendDelayedStateEvent ( 
460+             . _unstable_sendStickyDelayedEvent ( 
454461                this . room . roomId , 
462+                 STICK_DURATION_MS , 
455463                { 
456464                    delay : this . delayedLeaveEventDelayMs , 
457465                } , 
466+                 null , 
458467                EventType . GroupCallMemberPrefix , 
459-                 { } ,  // leave event 
460-                 this . stateKey , 
468+                 {  sticky_key : this . stateKey  }  as  EmptyObject  &  {  sticky_key : string  } ,  // leave event 
461469            ) 
462470            . then ( ( response )  =>  { 
463471                this . state . expectedServerDelayLeaveTs  =  Date . now ( )  +  this . delayedLeaveEventDelayMs ; 
@@ -482,7 +490,7 @@ export class MembershipManager
482490                if  ( this . manageMaxDelayExceededSituation ( e ) )  { 
483491                    return  createInsertActionUpdate ( repeatActionType ) ; 
484492                } 
485-                 const  update  =  this . actionUpdateFromErrors ( e ,  repeatActionType ,  "sendDelayedStateEvent " ) ; 
493+                 const  update  =  this . actionUpdateFromErrors ( e ,  repeatActionType ,  "_unstable_sendStickyDelayedEvent " ) ; 
486494                if  ( update )  return  update ; 
487495
488496                if  ( this . state . hasMemberStateEvent )  { 
@@ -640,12 +648,10 @@ export class MembershipManager
640648
641649    private  async  sendJoinEvent ( ) : Promise < ActionUpdate >  { 
642650        return  await  this . client 
643-             . sendStateEvent ( 
644-                 this . room . roomId , 
645-                 EventType . GroupCallMemberPrefix , 
646-                 this . makeMyMembership ( this . membershipEventExpiryMs ) , 
647-                 this . stateKey , 
648-             ) 
651+             . _unstable_sendStickyEvent ( this . room . roomId ,  STICK_DURATION_MS ,  null ,  EventType . GroupCallMemberPrefix ,  { 
652+                 ...this . makeMyMembership ( this . membershipEventExpiryMs ) , 
653+                 sticky_key : this . stateKey , 
654+             } ) 
649655            . then ( ( )  =>  { 
650656                this . setAndEmitProbablyLeft ( false ) ; 
651657                this . state . startTime  =  Date . now ( ) ; 
@@ -677,7 +683,11 @@ export class MembershipManager
677683                } ; 
678684            } ) 
679685            . catch ( ( e )  =>  { 
680-                 const  update  =  this . actionUpdateFromErrors ( e ,  MembershipActionType . SendJoinEvent ,  "sendStateEvent" ) ; 
686+                 const  update  =  this . actionUpdateFromErrors ( 
687+                     e , 
688+                     MembershipActionType . SendJoinEvent , 
689+                     "_unstable_sendStickyEvent" , 
690+                 ) ; 
681691                if  ( update )  return  update ; 
682692                throw  e ; 
683693            } ) ; 
@@ -686,12 +696,10 @@ export class MembershipManager
686696    private  async  updateExpiryOnJoinedEvent ( ) : Promise < ActionUpdate >  { 
687697        const  nextExpireUpdateIteration  =  this . state . expireUpdateIterations  +  1 ; 
688698        return  await  this . client 
689-             . sendStateEvent ( 
690-                 this . room . roomId , 
691-                 EventType . GroupCallMemberPrefix , 
692-                 this . makeMyMembership ( this . membershipEventExpiryMs  *  nextExpireUpdateIteration ) , 
693-                 this . stateKey , 
694-             ) 
699+             . _unstable_sendStickyEvent ( this . room . roomId ,  STICK_DURATION_MS ,  null ,  EventType . GroupCallMemberPrefix ,  { 
700+                 ...this . makeMyMembership ( this . membershipEventExpiryMs ) , 
701+                 sticky_key : this . stateKey , 
702+             } ) 
695703            . then ( ( )  =>  { 
696704                // Success, we reset retries and schedule update. 
697705                this . resetRateLimitCounter ( MembershipActionType . UpdateExpiry ) ; 
@@ -706,22 +714,36 @@ export class MembershipManager
706714                } ; 
707715            } ) 
708716            . catch ( ( e )  =>  { 
709-                 const  update  =  this . actionUpdateFromErrors ( e ,  MembershipActionType . UpdateExpiry ,  "sendStateEvent" ) ; 
717+                 const  update  =  this . actionUpdateFromErrors ( 
718+                     e , 
719+                     MembershipActionType . UpdateExpiry , 
720+                     "_unstable_sendStickyEvent" , 
721+                 ) ; 
710722                if  ( update )  return  update ; 
711723
712724                throw  e ; 
713725            } ) ; 
714726    } 
715727    private  async  sendFallbackLeaveEvent ( ) : Promise < ActionUpdate >  { 
716728        return  await  this . client 
717-             . sendStateEvent ( this . room . roomId ,  EventType . GroupCallMemberPrefix ,  { } ,  this . stateKey ) 
729+             . _unstable_sendStickyEvent ( 
730+                 this . room . roomId , 
731+                 STICK_DURATION_MS , 
732+                 null , 
733+                 EventType . GroupCallMemberPrefix , 
734+                 {  sticky_key : this . stateKey  }  as  EmptyObject  &  {  sticky_key : string  } ,  // leave event 
735+             ) 
718736            . then ( ( )  =>  { 
719737                this . resetRateLimitCounter ( MembershipActionType . SendLeaveEvent ) ; 
720738                this . state . hasMemberStateEvent  =  false ; 
721739                return  {  replace : [ ]  } ; 
722740            } ) 
723741            . catch ( ( e )  =>  { 
724-                 const  update  =  this . actionUpdateFromErrors ( e ,  MembershipActionType . SendLeaveEvent ,  "sendStateEvent" ) ; 
742+                 const  update  =  this . actionUpdateFromErrors ( 
743+                     e , 
744+                     MembershipActionType . SendLeaveEvent , 
745+                     "_unstable_sendStickyEvent" , 
746+                 ) ; 
725747                if  ( update )  return  update ; 
726748                throw  e ; 
727749            } ) ; 
0 commit comments