Skip to content

Commit 2308a10

Browse files
toger5Half-Shot
authored andcommitted
make sticky events a non breaking change (default to state events. use joinConfig to use sticky events)
Signed-off-by: Timo K <[email protected]>
1 parent 7fedcaf commit 2308a10

File tree

2 files changed

+85
-37
lines changed

2 files changed

+85
-37
lines changed

src/matrixrtc/MatrixRTCSession.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ export interface MembershipConfig {
186186
* but only applies to calls to the `_unstable_updateDelayedEvent` endpoint with a body of `{action:"restart"}`.)
187187
*/
188188
delayedLeaveEventRestartLocalTimeoutMs?: number;
189+
190+
/**
191+
* If the membership manager should publish its own membership via sticky events or via the room state.
192+
* @default false (room state)
193+
*/
194+
useStickyEvents?: boolean;
189195
}
190196

191197
export interface EncryptionConfig {
@@ -307,21 +313,31 @@ export class MatrixRTCSession extends TypedEventEmitter<
307313
public static sessionMembershipsForRoom(
308314
room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "getStickyEventsMap">,
309315
sessionDescription: SessionDescription,
316+
// default both true this implied we combine sticky and state events for the final call state
317+
// (prefer sticky events in case of a duplicate)
310318
useStickyEvents: boolean = true,
319+
useStateEvents: boolean = true,
311320
): CallMembership[] {
312321
const logger = rootLogger.getChild(`[MatrixRTCSession ${room.roomId}]`);
313-
let callMemberEvents;
322+
let callMemberEvents = [] as MatrixEvent[];
314323
if (useStickyEvents) {
324+
// prefill with sticky events
315325
callMemberEvents = Array.from(room.getStickyEventsMap().values()).filter(
316326
(e) => e.getType() === EventType.GroupCallMemberPrefix,
317327
);
318-
} else {
328+
}
329+
if (useStateEvents) {
319330
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
320331
if (!roomState) {
321332
logger.warn("Couldn't get state for room " + room.roomId);
322333
throw new Error("Could't get state for room " + room.roomId);
323334
}
324-
callMemberEvents = roomState.getStateEvents(EventType.GroupCallMemberPrefix);
335+
const callMemberStateEvents = roomState.getStateEvents(EventType.GroupCallMemberPrefix);
336+
// only care about state events which have keys which we have not yet seen in the sticky events.
337+
callMemberStateEvents.filter((e) =>
338+
callMemberEvents.some((stickyEvent) => stickyEvent.getContent().state_key === e.getStateKey()),
339+
);
340+
callMemberEvents.concat(callMemberStateEvents);
325341
}
326342

327343
const callMemberships: CallMembership[] = [];
@@ -438,6 +454,8 @@ export class MatrixRTCSession extends TypedEventEmitter<
438454
| "getUserId"
439455
| "getDeviceId"
440456
| "sendEvent"
457+
| "sendStateEvent"
458+
| "_unstable_sendDelayedStateEvent"
441459
| "_unstable_updateDelayedEvent"
442460
| "_unstable_sendStickyEvent"
443461
| "_unstable_sendStickyDelayedEvent"

src/matrixrtc/MembershipManager.ts

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ limitations under the License.
1616
import { AbortError } from "p-retry";
1717

1818
import { EventType } from "../@types/event.ts";
19-
import { UpdateDelayedEventAction } from "../@types/requests.ts";
19+
import {
20+
type ISendEventResponse,
21+
type SendDelayedEventResponse,
22+
UpdateDelayedEventAction,
23+
} from "../@types/requests.ts";
2024
import { type MatrixClient } from "../client.ts";
2125
import { UnsupportedDelayedEventsEndpointError } from "../errors.ts";
2226
import { ConnectionError, HTTPError, MatrixError } from "../http-api/errors.ts";
@@ -314,6 +318,8 @@ export class MembershipManager
314318
MatrixClient,
315319
| "getUserId"
316320
| "getDeviceId"
321+
| "sendStateEvent"
322+
| "_unstable_sendDelayedStateEvent"
317323
| "_unstable_updateDelayedEvent"
318324
| "_unstable_sendStickyEvent"
319325
| "_unstable_sendStickyDelayedEvent"
@@ -413,6 +419,11 @@ export class MembershipManager
413419
private get delayedLeaveEventRestartLocalTimeoutMs(): number {
414420
return this.joinConfig?.delayedLeaveEventRestartLocalTimeoutMs ?? 2000;
415421
}
422+
423+
private get useStickyEvents(): boolean {
424+
return this.joinConfig?.useStickyEvents ?? false;
425+
}
426+
416427
// LOOP HANDLER:
417428
private async membershipLoopHandler(type: MembershipActionType): Promise<ActionUpdate> {
418429
switch (type) {
@@ -467,23 +478,36 @@ export class MembershipManager
467478
}
468479
}
469480

481+
// an abstraction to switch between sending state or a sticky event
482+
private clientSendDelayedEvent: (myMembership: EmptyObject) => Promise<SendDelayedEventResponse> = (
483+
myMembership,
484+
) =>
485+
this.useStickyEvents
486+
? this.client._unstable_sendStickyDelayedEvent(
487+
this.room.roomId,
488+
STICK_DURATION_MS,
489+
{ delay: this.delayedLeaveEventDelayMs },
490+
null,
491+
EventType.GroupCallMemberPrefix,
492+
Object.assign(myMembership, { sticky_key: this.stateKey }),
493+
)
494+
: this.client._unstable_sendDelayedStateEvent(
495+
this.room.roomId,
496+
{ delay: this.delayedLeaveEventDelayMs },
497+
EventType.GroupCallMemberPrefix,
498+
myMembership,
499+
this.stateKey,
500+
);
501+
private sendDelayedEventMethodName: () => string = () =>
502+
this.useStickyEvents ? "_unstable_sendStickyDelayedEvent" : "_unstable_sendDelayedStateEvent";
503+
470504
// HANDLERS (used in the membershipLoopHandler)
471505
private async sendOrResendDelayedLeaveEvent(): Promise<ActionUpdate> {
472506
// We can reach this at the start of a call (where we do not yet have a membership: state.hasMemberStateEvent=false)
473507
// or during a call if the state event canceled our delayed event or caused by an unexpected error that removed our delayed event.
474508
// (Another client could have canceled it, the homeserver might have removed/lost it due to a restart, ...)
475509
// In the `then` and `catch` block we treat both cases differently. "if (this.state.hasMemberStateEvent) {} else {}"
476-
return await this.client
477-
._unstable_sendStickyDelayedEvent(
478-
this.room.roomId,
479-
STICK_DURATION_MS,
480-
{
481-
delay: this.delayedLeaveEventDelayMs,
482-
},
483-
null,
484-
EventType.GroupCallMemberPrefix,
485-
{ sticky_key: this.stateKey } as EmptyObject & { sticky_key: string }, // leave event
486-
)
510+
return await this.clientSendDelayedEvent({})
487511
.then((response) => {
488512
this.state.expectedServerDelayLeaveTs = Date.now() + this.delayedLeaveEventDelayMs;
489513
this.setAndEmitProbablyLeft(false);
@@ -507,7 +531,7 @@ export class MembershipManager
507531
if (this.manageMaxDelayExceededSituation(e)) {
508532
return createInsertActionUpdate(repeatActionType);
509533
}
510-
const update = this.actionUpdateFromErrors(e, repeatActionType, "_unstable_sendStickyDelayedEvent");
534+
const update = this.actionUpdateFromErrors(e, repeatActionType, this.sendDelayedEventMethodName());
511535
if (update) return update;
512536

513537
if (this.state.hasMemberStateEvent) {
@@ -663,12 +687,27 @@ export class MembershipManager
663687
});
664688
}
665689

690+
private clientSendMembership: (myMembership: SessionMembershipData | EmptyObject) => Promise<ISendEventResponse> = (
691+
myMembership,
692+
) =>
693+
this.useStickyEvents
694+
? this.client._unstable_sendStickyEvent(
695+
this.room.roomId,
696+
STICK_DURATION_MS,
697+
null,
698+
EventType.GroupCallMemberPrefix,
699+
Object.assign(myMembership, { sticky_key: this.stateKey }),
700+
)
701+
: this.client.sendStateEvent(
702+
this.room.roomId,
703+
EventType.GroupCallMemberPrefix,
704+
myMembership,
705+
this.stateKey,
706+
);
707+
private sendMembershipMethodName: () => string = () =>
708+
this.useStickyEvents ? "_unstable_sendStickyEvent" : "sendStateEvent";
666709
private async sendJoinEvent(): Promise<ActionUpdate> {
667-
return await this.client
668-
._unstable_sendStickyEvent(this.room.roomId, STICK_DURATION_MS, null, EventType.GroupCallMemberPrefix, {
669-
...this.makeMyMembership(this.membershipEventExpiryMs),
670-
sticky_key: this.stateKey,
671-
})
710+
return await this.clientSendMembership(this.makeMyMembership(this.membershipEventExpiryMs))
672711
.then(() => {
673712
this.setAndEmitProbablyLeft(false);
674713
this.state.startTime = Date.now();
@@ -703,7 +742,7 @@ export class MembershipManager
703742
const update = this.actionUpdateFromErrors(
704743
e,
705744
MembershipActionType.SendJoinEvent,
706-
"_unstable_sendStickyEvent",
745+
this.sendMembershipMethodName(),
707746
);
708747
if (update) return update;
709748
throw e;
@@ -712,11 +751,9 @@ export class MembershipManager
712751

713752
private async updateExpiryOnJoinedEvent(): Promise<ActionUpdate> {
714753
const nextExpireUpdateIteration = this.state.expireUpdateIterations + 1;
715-
return await this.client
716-
._unstable_sendStickyEvent(this.room.roomId, STICK_DURATION_MS, null, EventType.GroupCallMemberPrefix, {
717-
...this.makeMyMembership(this.membershipEventExpiryMs),
718-
sticky_key: this.stateKey,
719-
})
754+
return await this.clientSendMembership(
755+
this.makeMyMembership(this.membershipEventExpiryMs * nextExpireUpdateIteration),
756+
)
720757
.then(() => {
721758
// Success, we reset retries and schedule update.
722759
this.resetRateLimitCounter(MembershipActionType.UpdateExpiry);
@@ -734,22 +771,15 @@ export class MembershipManager
734771
const update = this.actionUpdateFromErrors(
735772
e,
736773
MembershipActionType.UpdateExpiry,
737-
"_unstable_sendStickyEvent",
774+
this.sendMembershipMethodName(),
738775
);
739776
if (update) return update;
740777

741778
throw e;
742779
});
743780
}
744781
private async sendFallbackLeaveEvent(): Promise<ActionUpdate> {
745-
return await this.client
746-
._unstable_sendStickyEvent(
747-
this.room.roomId,
748-
STICK_DURATION_MS,
749-
null,
750-
EventType.GroupCallMemberPrefix,
751-
{ sticky_key: this.stateKey } as EmptyObject & { sticky_key: string }, // leave event
752-
)
782+
return await this.clientSendMembership({})
753783
.then(() => {
754784
this.resetRateLimitCounter(MembershipActionType.SendLeaveEvent);
755785
this.state.hasMemberStateEvent = false;
@@ -759,7 +789,7 @@ export class MembershipManager
759789
const update = this.actionUpdateFromErrors(
760790
e,
761791
MembershipActionType.SendLeaveEvent,
762-
"_unstable_sendStickyEvent",
792+
this.sendMembershipMethodName(),
763793
);
764794
if (update) return update;
765795
throw e;

0 commit comments

Comments
 (0)