You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
@@ -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<void>;
|
||||
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<EncryptionKeysEventContent>((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<EncryptionKeysEventContent>((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 });
|
||||
|
||||
@@ -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<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "findEventById" | "client">,
|
||||
client: Pick<MatrixClient, "fetchRoomEvent">,
|
||||
): Promise<CallMembership[]> {
|
||||
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<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "findEventById" | "client">,
|
||||
client: Pick<MatrixClient, "fetchRoomEvent">,
|
||||
sessionDescription: SlotDescription,
|
||||
): Promise<CallMembership[]> {
|
||||
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<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "findEventById" | "client">,
|
||||
client: Pick<MatrixClient, "fetchRoomEvent">,
|
||||
slotDescription: SlotDescription,
|
||||
existingMemberships?: CallMembership[],
|
||||
): Promise<CallMembership[]> {
|
||||
@@ -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<MatrixEvent | undefined> => {
|
||||
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<MatrixRTCSession> {
|
||||
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<MatrixRTCSession> {
|
||||
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<void> => {
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
|
||||
this.logger = rootLogger.getChild("[MatrixRTCSessionManager]");
|
||||
}
|
||||
|
||||
public start(): void {
|
||||
public async start(): Promise<void> {
|
||||
// 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<MatrixRTCSessionM
|
||||
* Gets the main MatrixRTC session for a room, returning an empty session
|
||||
* if no members are currently participating
|
||||
*/
|
||||
public getRoomSession(room: Room): MatrixRTCSession {
|
||||
public async getRoomSession(room: Room): Promise<MatrixRTCSession> {
|
||||
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<MatrixRTCSessionM
|
||||
}
|
||||
|
||||
private onRoom = (room: Room): void => {
|
||||
this.refreshRoom(room);
|
||||
void this.refreshRoom(room);
|
||||
};
|
||||
|
||||
private onRoomState = (event: MatrixEvent, _state: RoomState): void => {
|
||||
@@ -121,13 +121,13 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
|
||||
}
|
||||
|
||||
if (event.getType() == EventType.GroupCallMemberPrefix) {
|
||||
this.refreshRoom(room);
|
||||
void this.refreshRoom(room);
|
||||
}
|
||||
};
|
||||
|
||||
private refreshRoom(room: Room): void {
|
||||
private async refreshRoom(room: Room): Promise<void> {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user