From 4bbb2401421c459fd7de430565cd29a27c3ac9d1 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 6 Oct 2025 15:06:03 +0200 Subject: [PATCH] fix lints Signed-off-by: Timo K --- spec/unit/matrixrtc/MatrixRTCSession.spec.ts | 125 ++++++++++--------- src/matrixrtc/MatrixRTCSession.ts | 123 ++++++++++-------- src/matrixrtc/MatrixRTCSessionManager.ts | 16 +-- 3 files changed, 143 insertions(+), 121 deletions(-) diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index 390a201fe..f030f4235 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -38,6 +38,8 @@ describe("MatrixRTCSession", () => { client.getDeviceId = jest.fn().mockReturnValue("AAAAAAA"); client.sendEvent = jest.fn().mockResolvedValue({ event_id: "success" }); client.decryptEventIfNeeded = jest.fn(); + client.fetchRoomEvent = jest.fn().mockResolvedValue(undefined); + client._unstable_sendDelayedStateEvent = jest.fn().mockResolvedValue({ event_id: "success" }); }); afterEach(async () => { @@ -48,10 +50,10 @@ describe("MatrixRTCSession", () => { }); describe("roomSessionForRoom", () => { - it("creates a room-scoped session from room state", () => { + it("creates a room-scoped session from room state", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships.length).toEqual(1); expect(sess?.memberships[0].slotDescription.id).toEqual(""); expect(sess?.memberships[0].scope).toEqual("m.room"); @@ -61,26 +63,26 @@ describe("MatrixRTCSession", () => { expect(sess?.slotDescription.id).toEqual(""); }); - it("ignores memberships where application is not m.call", () => { + it("ignores memberships where application is not m.call", async () => { const testMembership = Object.assign({}, membershipTemplate, { application: "not-m.call", }); const mockRoom = makeMockRoom([testMembership]); - const sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + const sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships).toHaveLength(0); }); - it("ignores memberships where callId is not empty", () => { + it("ignores memberships where callId is not empty", async () => { const testMembership = Object.assign({}, membershipTemplate, { call_id: "not-empty", scope: "m.room", }); const mockRoom = makeMockRoom([testMembership]); - const sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + const sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships).toHaveLength(0); }); - it("ignores expired memberships events", () => { + it("ignores expired memberships events", async () => { jest.useFakeTimers(); const expiredMembership = Object.assign({}, membershipTemplate); expiredMembership.expires = 1000; @@ -88,38 +90,38 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([membershipTemplate, expiredMembership]); jest.advanceTimersByTime(2000); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships.length).toEqual(1); expect(sess?.memberships[0].deviceId).toEqual("AAAAAAA"); jest.useRealTimers(); }); - it("ignores memberships events of members not in the room", () => { + it("ignores memberships events of members not in the room", async () => { const mockRoom = makeMockRoom([membershipTemplate]); mockRoom.hasMembershipState = (state) => state === KnownMembership.Join; - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships.length).toEqual(0); }); - it("honours created_ts", () => { + it("honours created_ts", async () => { jest.useFakeTimers(); jest.setSystemTime(500); const expiredMembership = Object.assign({}, membershipTemplate); expiredMembership.created_ts = 500; expiredMembership.expires = 1000; const mockRoom = makeMockRoom([expiredMembership]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships[0].getAbsoluteExpiry()).toEqual(1500); jest.useRealTimers(); }); - it("returns empty session if no membership events are present", () => { + it("returns empty session if no membership events are present", async () => { const mockRoom = makeMockRoom([]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess?.memberships).toHaveLength(0); }); - it("safely ignores events with no memberships section", () => { + it("safely ignores events with no memberships section", async () => { const roomId = secureRandomString(8); const event = { getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), @@ -150,11 +152,11 @@ describe("MatrixRTCSession", () => { }), }), }; - sess = MatrixRTCSession.sessionForRoom(client, mockRoom as unknown as Room, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom as unknown as Room, callSession); expect(sess.memberships).toHaveLength(0); }); - it("safely ignores events with junk memberships section", () => { + it("safely ignores events with junk memberships section", async () => { const roomId = secureRandomString(8); const event = { getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), @@ -185,29 +187,29 @@ describe("MatrixRTCSession", () => { }), }), }; - sess = MatrixRTCSession.sessionForRoom(client, mockRoom as unknown as Room, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom as unknown as Room, callSession); expect(sess.memberships).toHaveLength(0); }); - it("ignores memberships with no device_id", () => { + it("ignores memberships with no device_id", async () => { const testMembership = Object.assign({}, membershipTemplate); (testMembership.device_id as string | undefined) = undefined; const mockRoom = makeMockRoom([testMembership]); - const sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + const sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess.memberships).toHaveLength(0); }); - it("ignores memberships with no call_id", () => { + it("ignores memberships with no call_id", async () => { const testMembership = Object.assign({}, membershipTemplate); (testMembership.call_id as string | undefined) = undefined; const mockRoom = makeMockRoom([testMembership]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess.memberships).toHaveLength(0); }); }); describe("getOldestMembership", () => { - it("returns the oldest membership event", () => { + it("returns the oldest membership event", async () => { jest.useFakeTimers(); jest.setSystemTime(4000); const mockRoom = makeMockRoom([ @@ -216,7 +218,7 @@ describe("MatrixRTCSession", () => { Object.assign({}, membershipTemplate, { device_id: "bar", created_ts: 2000 }), ]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess.getOldestMembership()!.deviceId).toEqual("old"); jest.useRealTimers(); }); @@ -229,7 +231,7 @@ describe("MatrixRTCSession", () => { [undefined, "audio", "audio"], ["audio", "audio", "audio"], ["audio", "video", undefined], - ])("gets correct consensus for %s + %s = %s", (intentA, intentB, result) => { + ])("gets correct consensus for %s + %s = %s", async (intentA, intentB, result) => { jest.useFakeTimers(); jest.setSystemTime(4000); const mockRoom = makeMockRoom([ @@ -237,7 +239,7 @@ describe("MatrixRTCSession", () => { Object.assign({}, membershipTemplate, { "m.call.intent": intentB }), ]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess.getConsensusCallIntent()).toEqual(result); jest.useRealTimers(); }); @@ -249,7 +251,7 @@ describe("MatrixRTCSession", () => { livekit_service_url: "https://active.url", livekit_alias: "!active:active.url", }; - it("gets the correct active focus with oldest_membership", () => { + it("gets the correct active focus with oldest_membership", async () => { jest.useFakeTimers(); jest.setSystemTime(3000); const mockRoom = makeMockRoom([ @@ -262,7 +264,7 @@ describe("MatrixRTCSession", () => { Object.assign({}, membershipTemplate, { device_id: "bar", created_ts: 2000 }), ]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess.joinRoomSession([{ type: "livekit", livekit_service_url: "htts://test.org" }], { type: "livekit", @@ -273,7 +275,7 @@ describe("MatrixRTCSession", () => { ); jest.useRealTimers(); }); - it("does not provide focus if the selection method is unknown", () => { + it("does not provide focus if the selection method is unknown", async () => { const mockRoom = makeMockRoom([ Object.assign({}, membershipTemplate, { device_id: "foo", @@ -284,7 +286,7 @@ describe("MatrixRTCSession", () => { Object.assign({}, membershipTemplate, { device_id: "bar", created_ts: 2000 }), ]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess.joinRoomSession([{ type: "livekit", livekit_service_url: "htts://test.org" }], { type: "livekit", @@ -300,7 +302,7 @@ describe("MatrixRTCSession", () => { let sendStateEventMock: jest.Mock; let sentStateEvent: Promise; - beforeEach(() => { + beforeEach(async () => { sentStateEvent = new Promise((resolve) => { sendStateEventMock = jest.fn(resolve); }); @@ -311,7 +313,7 @@ describe("MatrixRTCSession", () => { client._unstable_updateDelayedEvent = jest.fn(); mockRoom = makeMockRoom([]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); }); afterEach(async () => { @@ -347,8 +349,14 @@ describe("MatrixRTCSession", () => { sess!.joinRoomSession([mockFocus], mockFocus, { notificationType: "ring" }); await Promise.race([sentStateEvent, new Promise((resolve) => setTimeout(resolve, 5000))]); + const { resolve: r, promise: p } = Promise.withResolvers(); + sess?.once(MatrixRTCSessionEvent.JoinStateChanged, r); + await p; mockRoomState(mockRoom, [{ ...membershipTemplate, user_id: client.getUserId()! }]); sess!.onRTCSessionMemberUpdate(); + const { resolve, promise } = Promise.withResolvers(); + sess?.once(MatrixRTCSessionEvent.MembershipsChanged, resolve); + await promise; const ownMembershipId = sess?.memberships[0].eventId; expect(client.sendEvent).toHaveBeenCalledWith(mockRoom!.roomId, EventType.RTCNotification, { @@ -361,7 +369,6 @@ describe("MatrixRTCSession", () => { "lifetime": 30000, "sender_ts": expect.any(Number), }); - // Check if deprecated notify event is also sent. expect(client.sendEvent).toHaveBeenCalledWith(mockRoom!.roomId, EventType.CallNotify, { "application": "m.call", @@ -498,9 +505,9 @@ describe("MatrixRTCSession", () => { }); describe("onMembershipsChanged", () => { - it("does not emit if no membership changes", () => { + it("does not emit if no membership changes", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); const onMembershipsChanged = jest.fn(); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); @@ -509,9 +516,9 @@ describe("MatrixRTCSession", () => { expect(onMembershipsChanged).not.toHaveBeenCalled(); }); - it("emits on membership changes", () => { + it("emits on membership changes", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); const onMembershipsChanged = jest.fn(); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); @@ -555,7 +562,7 @@ describe("MatrixRTCSession", () => { let sendEventMock: jest.Mock; let sendToDeviceMock: jest.Mock; - beforeEach(() => { + beforeEach(async () => { sendStateEventMock = jest.fn().mockResolvedValue({ event_id: "id" }); sendDelayedStateMock = jest.fn().mockResolvedValue({ event_id: "id" }); sendEventMock = jest.fn().mockResolvedValue({ event_id: "id" }); @@ -566,7 +573,7 @@ describe("MatrixRTCSession", () => { client.encryptAndSendToDevice = sendToDeviceMock; mockRoom = makeMockRoom([]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); }); afterEach(async () => { @@ -685,7 +692,7 @@ describe("MatrixRTCSession", () => { device_id: "BBBBBBB", }); const mockRoom = makeMockRoom([membershipTemplate, member2]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); // joining will trigger an initial key send const keysSentPromise1 = new Promise((resolve) => { @@ -734,7 +741,7 @@ describe("MatrixRTCSession", () => { jest.useFakeTimers(); try { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); const keysSentPromise1 = new Promise((resolve) => { sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload)); @@ -785,7 +792,7 @@ describe("MatrixRTCSession", () => { const mockRoom = makeMockRoom([member1, member2]); mockRoomState(mockRoom, [member1, member2]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); await keysSentPromise1; @@ -830,7 +837,7 @@ describe("MatrixRTCSession", () => { }; const mockRoom = makeMockRoom([member1, member2]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); await keysSentPromise1; @@ -894,7 +901,7 @@ describe("MatrixRTCSession", () => { device_id: "BBBBBBB", }); const mockRoom = makeMockRoom([membershipTemplate, member2]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); const onMyEncryptionKeyChanged = jest.fn(); sess.on( @@ -984,7 +991,7 @@ describe("MatrixRTCSession", () => { if (i === 0) { // if first time around then set up the session - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); } else { // otherwise update the state reducing the membership each time in order to trigger key rotation @@ -1010,7 +1017,7 @@ describe("MatrixRTCSession", () => { jest.useFakeTimers(); try { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); const keysSentPromise1 = new Promise((resolve) => { sendEventMock.mockImplementation(resolve); @@ -1051,7 +1058,7 @@ describe("MatrixRTCSession", () => { }); const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true, @@ -1074,7 +1081,7 @@ describe("MatrixRTCSession", () => { describe("receiving", () => { it("collects keys from encryption events", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); mockRoom.emitTimelineEvent( makeMockEvent("io.element.call.encryption_keys", "@bob:example.org", "1234roomId", { @@ -1099,7 +1106,7 @@ describe("MatrixRTCSession", () => { it("collects keys at non-zero indices", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); mockRoom.emitTimelineEvent( makeMockEvent("io.element.call.encryption_keys", "@bob:example.org", "1234roomId", { @@ -1125,7 +1132,7 @@ describe("MatrixRTCSession", () => { it("collects keys by merging", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); mockRoom.emitTimelineEvent( makeMockEvent("io.element.call.encryption_keys", "@bob:example.org", "1234roomId", { @@ -1176,7 +1183,7 @@ describe("MatrixRTCSession", () => { it("ignores older keys at same index", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); mockRoom.emitTimelineEvent( makeMockEvent( @@ -1235,7 +1242,7 @@ describe("MatrixRTCSession", () => { it("key timestamps are treated as monotonic", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); mockRoom.emitTimelineEvent( makeMockEvent( @@ -1277,9 +1284,9 @@ describe("MatrixRTCSession", () => { ); }); - it("ignores keys event for the local participant", () => { + it("ignores keys event for the local participant", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); mockRoom.emitTimelineEvent( @@ -1302,7 +1309,7 @@ describe("MatrixRTCSession", () => { jest.useFakeTimers(); try { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); // defaults to getTs() jest.setSystemTime(1000); @@ -1356,9 +1363,9 @@ describe("MatrixRTCSession", () => { }); }); describe("read status", () => { - it("returns the correct probablyLeft status", () => { + it("returns the correct probablyLeft status", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess!.probablyLeft).toBe(undefined); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); @@ -1372,9 +1379,9 @@ describe("MatrixRTCSession", () => { expect(sess!.probablyLeft).toBe(true); }); - it("returns membershipStatus once joinRoomSession got called", () => { + it("returns membershipStatus once joinRoomSession got called", async () => { const mockRoom = makeMockRoom([membershipTemplate]); - sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); expect(sess!.membershipStatus).toBe(undefined); sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); diff --git a/src/matrixrtc/MatrixRTCSession.ts b/src/matrixrtc/MatrixRTCSession.ts index 6706a93f4..fe483c3b2 100644 --- a/src/matrixrtc/MatrixRTCSession.ts +++ b/src/matrixrtc/MatrixRTCSession.ts @@ -17,6 +17,7 @@ limitations under the License. import { type Logger, logger as rootLogger } from "../logger.ts"; import { TypedEventEmitter } from "../models/typed-event-emitter.ts"; import { EventTimeline } from "../models/event-timeline.ts"; +import { MatrixEvent } from "../models/event.ts"; import { type Room } from "../models/room.ts"; import { type MatrixClient } from "../client.ts"; import { EventType, RelationType } from "../@types/event.ts"; @@ -50,7 +51,6 @@ import { } from "./RoomAndToDeviceKeyTransport.ts"; import { TypedReEmitter } from "../ReEmitter.ts"; import { ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts"; -import { MatrixEvent } from "../models/event.ts"; /** * Events emitted by MatrixRTCSession @@ -311,8 +311,9 @@ export class MatrixRTCSession extends TypedEventEmitter< */ public static async callMembershipsForRoom( room: Pick, + client: Pick, ): Promise { - return await MatrixRTCSession.sessionMembershipsForSlot(room, { + return await MatrixRTCSession.sessionMembershipsForSlot(room, client, { id: "", application: "m.call", }); @@ -323,9 +324,10 @@ export class MatrixRTCSession extends TypedEventEmitter< */ public static async sessionMembershipsForRoom( room: Pick, + client: Pick, sessionDescription: SlotDescription, ): Promise { - return await this.sessionMembershipsForSlot(room, sessionDescription); + return await this.sessionMembershipsForSlot(room, client, sessionDescription); } /** @@ -334,6 +336,7 @@ export class MatrixRTCSession extends TypedEventEmitter< */ public static async sessionMembershipsForSlot( room: Pick, + client: Pick, slotDescription: SlotDescription, existingMemberships?: CallMembership[], ): Promise { @@ -350,9 +353,18 @@ export class MatrixRTCSession extends TypedEventEmitter< let membership = existingMemberships?.find((m) => m.eventId === memberEvent.getId()); if (!membership) { const relatedEventId = memberEvent.relationEventId; + const getRelatedMatrixEvent = async (): Promise => { + const eventData = await client + .fetchRoomEvent(room.roomId, relatedEventId!) + .catch((e) => + logger.error(`Could not get related event ${relatedEventId} for call membership`, e), + ); + + return eventData ? new MatrixEvent(eventData) : undefined; + }; const relatedEvent = relatedEventId ? room.findEventById(relatedEventId) - : new MatrixEvent(await room.client.fetchRoomEvent(room.roomId, relatedEventId!)); + : await getRelatedMatrixEvent(); try { membership = new CallMembership(memberEvent, relatedEvent); @@ -403,7 +415,7 @@ export class MatrixRTCSession extends TypedEventEmitter< * @deprecated Use `MatrixRTCSession.sessionForSlot` with sessionDescription `{ id: "", application: "m.call" }` instead. */ public static async roomSessionForRoom(client: MatrixClient, room: Room): Promise { - const callMemberships = await MatrixRTCSession.sessionMembershipsForSlot(room, { + const callMemberships = await MatrixRTCSession.sessionMembershipsForSlot(room, client, { id: "", application: "m.call", }); @@ -413,7 +425,7 @@ export class MatrixRTCSession extends TypedEventEmitter< /** * @deprecated Use `MatrixRTCSession.sessionForSlot` instead. */ - public static async sessionForRoom( + public static sessionForRoom( client: MatrixClient, room: Room, slotDescription: SlotDescription, @@ -431,7 +443,7 @@ export class MatrixRTCSession extends TypedEventEmitter< room: Room, slotDescription: SlotDescription, ): Promise { - const callMemberships = await MatrixRTCSession.sessionMembershipsForSlot(room, slotDescription); + const callMemberships = await MatrixRTCSession.sessionMembershipsForSlot(room, client, slotDescription); return new MatrixRTCSession(client, room, callMemberships, slotDescription); } @@ -472,6 +484,7 @@ export class MatrixRTCSession extends TypedEventEmitter< | "off" | "on" | "decryptEventIfNeeded" + | "fetchRoomEvent" >, private roomSubset: Pick< Room, @@ -784,14 +797,14 @@ export class MatrixRTCSession extends TypedEventEmitter< * Call this when the Matrix room members have changed. */ public onRoomMemberUpdate = (): void => { - this.recalculateSessionMembers(); + void this.recalculateSessionMembers(); }; /** * Call this when something changed that may impacts the current MatrixRTC members in this session. */ public onRTCSessionMemberUpdate = (): void => { - this.recalculateSessionMembers(); + void this.recalculateSessionMembers(); }; /** @@ -801,52 +814,54 @@ export class MatrixRTCSession extends TypedEventEmitter< * * This function should be called when the room members or call memberships might have changed. */ - private recalculateSessionMembers = (): void => { + private recalculateSessionMembers = async (): Promise => { const oldMemberships = this.memberships; - void MatrixRTCSession.sessionMembershipsForSlot(this.room, this.slotDescription, oldMemberships).then( - (newMemberships) => { - this.memberships = newMemberships; - this._slotId = this._slotId ?? this.memberships[0]?.slotId; - - const changed = - oldMemberships.length != this.memberships.length || - // If they have the same length, this is enough to check "changed" - oldMemberships.some((m, i) => !CallMembership.equal(m, this.memberships[i])); - - if (changed) { - this.logger.info( - `Memberships for call in room ${this.roomSubset.roomId} have changed: emitting (${this.memberships.length} members)`, - ); - logDurationSync(this.logger, "emit MatrixRTCSessionEvent.MembershipsChanged", () => { - this.emit(MatrixRTCSessionEvent.MembershipsChanged, oldMemberships, this.memberships); - }); - - void this.membershipManager?.onRTCSessionMemberUpdate(this.memberships); - // The `ownMembership` will be set when calling `onRTCSessionMemberUpdate`. - const ownMembership = this.membershipManager?.ownMembership; - if (this.pendingNotificationToSend && ownMembership && oldMemberships.length === 0) { - // If we're the first member in the call, we're responsible for - // sending the notification event - if (ownMembership.eventId && this.joinConfig?.notificationType) { - this.sendCallNotify( - ownMembership.eventId, - this.joinConfig.notificationType, - ownMembership.callIntent, - ); - } else { - this.logger.warn("Own membership eventId is undefined, cannot send call notification"); - } - } - // If anyone else joins the session it is no longer our responsibility to send the notification. - // (If we were the joiner we already did sent the notification in the block above.) - if (this.memberships.length > 0) this.pendingNotificationToSend = undefined; - } - // This also needs to be done if `changed` = false - // A member might have updated their fingerprint (created_ts) - void this.encryptionManager?.onMembershipsUpdate(oldMemberships); - - this.setExpiryTimer(); - }, + const newMemberships = await MatrixRTCSession.sessionMembershipsForSlot( + this.room, + this.client, + this.slotDescription, + oldMemberships, ); + this.memberships = newMemberships; + this._slotId = this._slotId ?? this.memberships[0]?.slotId; + + const changed = + oldMemberships.length != this.memberships.length || + // If they have the same length, this is enough to check "changed" + oldMemberships.some((m, i) => !CallMembership.equal(m, this.memberships[i])); + + if (changed) { + this.logger.info( + `Memberships for call in room ${this.roomSubset.roomId} have changed: emitting (${this.memberships.length} members)`, + ); + logDurationSync(this.logger, "emit MatrixRTCSessionEvent.MembershipsChanged", () => { + this.emit(MatrixRTCSessionEvent.MembershipsChanged, oldMemberships, this.memberships); + }); + + void this.membershipManager?.onRTCSessionMemberUpdate(this.memberships); + // The `ownMembership` will be set when calling `onRTCSessionMemberUpdate`. + const ownMembership = this.membershipManager?.ownMembership; + if (this.pendingNotificationToSend && ownMembership && oldMemberships.length === 0) { + // If we're the first member in the call, we're responsible for + // sending the notification event + if (ownMembership.eventId && this.joinConfig?.notificationType) { + this.sendCallNotify( + ownMembership.eventId, + this.joinConfig.notificationType, + ownMembership.callIntent, + ); + } else { + this.logger.warn("Own membership eventId is undefined, cannot send call notification"); + } + } + // If anyone else joins the session it is no longer our responsibility to send the notification. + // (If we were the joiner we already did sent the notification in the block above.) + if (this.memberships.length > 0) this.pendingNotificationToSend = undefined; + } + // This also needs to be done if `changed` = false + // A member might have updated their fingerprint (created_ts) + void this.encryptionManager?.onMembershipsUpdate(oldMemberships); + + this.setExpiryTimer(); }; } diff --git a/src/matrixrtc/MatrixRTCSessionManager.ts b/src/matrixrtc/MatrixRTCSessionManager.ts index f2f49cc91..6f60b6ed1 100644 --- a/src/matrixrtc/MatrixRTCSessionManager.ts +++ b/src/matrixrtc/MatrixRTCSessionManager.ts @@ -62,11 +62,11 @@ export class MatrixRTCSessionManager extends TypedEventEmitter { // We shouldn't need to null-check here, but matrix-client.spec.ts mocks getRooms // returning nothing, and breaks tests if you change it to return an empty array :'( for (const room of this.client.getRooms() ?? []) { - const session = MatrixRTCSession.sessionForRoom(this.client, room, this.sessionDescription); + const session = await MatrixRTCSession.sessionForRoom(this.client, room, this.sessionDescription); if (session.memberships.length > 0) { this.roomSessions.set(room.roomId, session); } @@ -98,11 +98,11 @@ export class MatrixRTCSessionManager extends TypedEventEmitter { if (!this.roomSessions.has(room.roomId)) { this.roomSessions.set( room.roomId, - MatrixRTCSession.sessionForRoom(this.client, room, this.sessionDescription), + await MatrixRTCSession.sessionForRoom(this.client, room, this.sessionDescription), ); } @@ -110,7 +110,7 @@ export class MatrixRTCSessionManager extends TypedEventEmitter { - this.refreshRoom(room); + void this.refreshRoom(room); }; private onRoomState = (event: MatrixEvent, _state: RoomState): void => { @@ -121,13 +121,13 @@ export class MatrixRTCSessionManager extends TypedEventEmitter { const isNewSession = !this.roomSessions.has(room.roomId); - const session = this.getRoomSession(room); + const session = await this.getRoomSession(room); const wasActiveAndKnown = session.memberships.length > 0 && !isNewSession; // This needs to be here and the event listener cannot be setup in the MatrixRTCSession,