You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-07 23:02:56 +03:00
Prepare delayed call leave events more reliably (#4447)
* Prepare delayed call leave events more reliably - Try sending call join after preparing delayed leave - On leave, send delayed leave instead of a new event * Don't rely on errcodes for retry logic because they are unavailable in widget mode * Make arrow method readonly SonarCloud rule typescript:S2933 * Test coverage for restarting delayed call leave * Remove unneeded unstable_features mock It's unneeded because all affected methods are mocked
This commit is contained in:
committed by
GitHub
parent
66c80949e8
commit
13a967ae8f
@@ -46,9 +46,6 @@ describe("MatrixRTCSession", () => {
|
||||
client = new MatrixClient({ baseUrl: "base_url" });
|
||||
client.getUserId = jest.fn().mockReturnValue("@alice:example.org");
|
||||
client.getDeviceId = jest.fn().mockReturnValue("AAAAAAA");
|
||||
client.doesServerSupportUnstableFeature = jest.fn((feature) =>
|
||||
Promise.resolve(feature === "org.matrix.msc4140"),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -414,6 +411,8 @@ describe("MatrixRTCSession", () => {
|
||||
client._unstable_sendDelayedStateEvent = sendDelayedStateMock;
|
||||
client.sendEvent = sendEventMock;
|
||||
|
||||
client._unstable_updateDelayedEvent = jest.fn();
|
||||
|
||||
mockRoom = makeMockRoom([]);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
});
|
||||
@@ -490,6 +489,13 @@ describe("MatrixRTCSession", () => {
|
||||
);
|
||||
await Promise.race([sentDelayedState, new Promise((resolve) => realSetTimeout(resolve, 500))]);
|
||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1);
|
||||
|
||||
// should have tried updating the delayed leave to test that it wasn't replaced by own state
|
||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);
|
||||
// should update delayed disconnect
|
||||
jest.advanceTimersByTime(5000);
|
||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(2);
|
||||
|
||||
jest.useRealTimers();
|
||||
}
|
||||
|
||||
|
@@ -140,6 +140,8 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
|
||||
private encryptionKeys = new Map<string, Array<{ key: Uint8Array; timestamp: number }>>();
|
||||
private lastEncryptionKeyUpdateRequest?: number;
|
||||
|
||||
private disconnectDelayId: string | undefined;
|
||||
|
||||
// We use this to store the last membership fingerprints we saw, so we can proactively re-send encryption keys
|
||||
// if it looks like a membership has been updated.
|
||||
private lastMembershipFingerprints: Set<string> | undefined;
|
||||
@@ -1011,19 +1013,24 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
|
||||
newContent = this.makeNewMembership(localDeviceId);
|
||||
}
|
||||
|
||||
const stateKey = legacy ? localUserId : this.makeMembershipStateKey(localUserId, localDeviceId);
|
||||
try {
|
||||
await this.client.sendStateEvent(this.room.roomId, EventType.GroupCallMemberPrefix, newContent, stateKey);
|
||||
logger.info(`Sent updated call member event.`);
|
||||
|
||||
// check periodically to see if we need to refresh our member event
|
||||
if (this.isJoined()) {
|
||||
if (legacy) {
|
||||
if (legacy) {
|
||||
await this.client.sendStateEvent(
|
||||
this.room.roomId,
|
||||
EventType.GroupCallMemberPrefix,
|
||||
newContent,
|
||||
localUserId,
|
||||
);
|
||||
if (this.isJoined()) {
|
||||
// check periodically to see if we need to refresh our member event
|
||||
this.memberEventTimeout = setTimeout(
|
||||
this.triggerCallMembershipEventUpdate,
|
||||
MEMBER_EVENT_CHECK_PERIOD,
|
||||
);
|
||||
} else {
|
||||
}
|
||||
} else if (this.isJoined()) {
|
||||
const stateKey = this.makeMembershipStateKey(localUserId, localDeviceId);
|
||||
const prepareDelayedDisconnection = async (): Promise<void> => {
|
||||
try {
|
||||
// TODO: If delayed event times out, re-join!
|
||||
const res = await this.client._unstable_sendDelayedStateEvent(
|
||||
@@ -1035,12 +1042,63 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
|
||||
{}, // leave event
|
||||
stateKey,
|
||||
);
|
||||
this.scheduleDelayDisconnection(res.delay_id);
|
||||
this.disconnectDelayId = res.delay_id;
|
||||
} catch (e) {
|
||||
logger.error("Failed to send delayed event:", e);
|
||||
// TODO: Retry if rate-limited
|
||||
logger.error("Failed to prepare delayed disconnection event:", e);
|
||||
}
|
||||
};
|
||||
await prepareDelayedDisconnection();
|
||||
// Send join event _after_ preparing the delayed disconnection event
|
||||
await this.client.sendStateEvent(
|
||||
this.room.roomId,
|
||||
EventType.GroupCallMemberPrefix,
|
||||
newContent,
|
||||
stateKey,
|
||||
);
|
||||
// If sending state cancels your own delayed state, prepare another delayed state
|
||||
// TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state
|
||||
if (this.disconnectDelayId !== undefined) {
|
||||
try {
|
||||
await this.client._unstable_updateDelayedEvent(
|
||||
this.disconnectDelayId,
|
||||
UpdateDelayedEventAction.Restart,
|
||||
);
|
||||
} catch (e) {
|
||||
// TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited)
|
||||
logger.warn("Failed to update delayed disconnection event, prepare it again:", e);
|
||||
this.disconnectDelayId = undefined;
|
||||
await prepareDelayedDisconnection();
|
||||
}
|
||||
}
|
||||
if (this.disconnectDelayId !== undefined) {
|
||||
this.scheduleDelayDisconnection();
|
||||
}
|
||||
} else {
|
||||
let sentDelayedDisconnect = false;
|
||||
if (this.disconnectDelayId !== undefined) {
|
||||
try {
|
||||
await this.client._unstable_updateDelayedEvent(
|
||||
this.disconnectDelayId,
|
||||
UpdateDelayedEventAction.Send,
|
||||
);
|
||||
sentDelayedDisconnect = true;
|
||||
} catch (e) {
|
||||
// TODO: Retry if rate-limited
|
||||
logger.error("Failed to send our delayed disconnection event:", e);
|
||||
}
|
||||
this.disconnectDelayId = undefined;
|
||||
}
|
||||
if (!sentDelayedDisconnect) {
|
||||
await this.client.sendStateEvent(
|
||||
this.room.roomId,
|
||||
EventType.GroupCallMemberPrefix,
|
||||
{},
|
||||
this.makeMembershipStateKey(localUserId, localDeviceId),
|
||||
);
|
||||
}
|
||||
}
|
||||
logger.info("Sent updated call member event.");
|
||||
} catch (e) {
|
||||
const resendDelay = CALL_MEMBER_EVENT_RETRY_DELAY_MIN + Math.random() * 2000;
|
||||
logger.warn(`Failed to send call member event (retrying in ${resendDelay}): ${e}`);
|
||||
@@ -1049,18 +1107,19 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
|
||||
}
|
||||
}
|
||||
|
||||
private scheduleDelayDisconnection(delayId: string): void {
|
||||
this.memberEventTimeout = setTimeout(() => this.delayDisconnection(delayId), 5000);
|
||||
private scheduleDelayDisconnection(): void {
|
||||
this.memberEventTimeout = setTimeout(this.delayDisconnection, 5000);
|
||||
}
|
||||
|
||||
private async delayDisconnection(delayId: string): Promise<void> {
|
||||
private readonly delayDisconnection = async (): Promise<void> => {
|
||||
try {
|
||||
await this.client._unstable_updateDelayedEvent(delayId, UpdateDelayedEventAction.Restart);
|
||||
this.scheduleDelayDisconnection(delayId);
|
||||
await this.client._unstable_updateDelayedEvent(this.disconnectDelayId!, UpdateDelayedEventAction.Restart);
|
||||
this.scheduleDelayDisconnection();
|
||||
} catch (e) {
|
||||
logger.error("Failed to delay our disconnection event", e);
|
||||
// TODO: Retry if rate-limited
|
||||
logger.error("Failed to delay our disconnection event:", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private stateEventsContainOngoingLegacySession(callMemberEvents: Map<string, MatrixEvent> | undefined): boolean {
|
||||
if (!callMemberEvents?.size) {
|
||||
|
Reference in New Issue
Block a user