From c0090852ad577582ea529d4bc97a783085c0652a Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 1 Dec 2022 10:45:34 -0500 Subject: [PATCH] Make GroupCall work better with widgets If the client uses a widget to join group calls, like Element Web does, then the local device could be joined to the call without GroupCall knowing. This adds a field to GroupCall that allows the client to tell GroupCall when it's using another session to join the call. --- spec/unit/webrtc/groupCall.spec.ts | 25 +++++++++++++------------ src/webrtc/groupCall.ts | 24 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/spec/unit/webrtc/groupCall.spec.ts b/spec/unit/webrtc/groupCall.spec.ts index 047474f8e..7085f50c1 100644 --- a/spec/unit/webrtc/groupCall.spec.ts +++ b/spec/unit/webrtc/groupCall.spec.ts @@ -180,13 +180,13 @@ describe('Group Call', function() { room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_1); groupCall = new GroupCall(mockClient, room, GroupCallType.Video, false, GroupCallIntent.Prompt); + room.currentState.members[FAKE_USER_ID_1] = { + userId: FAKE_USER_ID_1, + membership: "join", + } as unknown as RoomMember; }); it("does not initialize local call feed, if it already is", async () => { - room.currentState.members[FAKE_USER_ID_1] = { - userId: FAKE_USER_ID_1, - } as unknown as RoomMember; - await groupCall.initLocalCallFeed(); jest.spyOn(groupCall, "initLocalCallFeed"); await groupCall.enter(); @@ -216,10 +216,6 @@ describe('Group Call', function() { }); it("sends member state event to room on enter", async () => { - room.currentState.members[FAKE_USER_ID_1] = { - userId: FAKE_USER_ID_1, - } as unknown as RoomMember; - await groupCall.create(); try { @@ -249,10 +245,6 @@ describe('Group Call', function() { }); it("sends member state event to room on leave", async () => { - room.currentState.members[FAKE_USER_ID_1] = { - userId: FAKE_USER_ID_1, - } as unknown as RoomMember; - await groupCall.create(); await groupCall.enter(); mockSendState.mockClear(); @@ -267,6 +259,15 @@ describe('Group Call', function() { ); }); + it("includes local device in participants when entered via another session", async () => { + groupCall.enteredViaAnotherSession = true; + + const hasLocalParticipant = groupCall.participants.get( + room.getMember(mockClient.getUserId()!)!, + )?.has(mockClient.getDeviceId()!); + expect(hasLocalParticipant).toBe(true); + }); + it("starts with mic unmuted in regular calls", async () => { try { await groupCall.create(); diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index 7713d6de9..fde46182b 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -285,6 +285,21 @@ export class GroupCall extends TypedEventEmitter< this._creationTs = value; } + private _enteredViaAnotherSession = false; + + /** + * Whether the local device has entered this call via another session, such + * as a widget. + */ + public get enteredViaAnotherSession(): boolean { + return this._enteredViaAnotherSession; + } + + public set enteredViaAnotherSession(value: boolean) { + this._enteredViaAnotherSession = value; + this.updateParticipants(); + } + /** * Executes the given callback on all calls in this group call. * @param f The callback. @@ -1170,7 +1185,7 @@ export class GroupCall extends TypedEventEmitter< const participants = new Map>(); const now = Date.now(); - const entered = this.state === GroupCallState.Entered; + const entered = this.state === GroupCallState.Entered || this.enteredViaAnotherSession; let nextExpiration = Infinity; for (const e of this.getMemberStateEvents()) { @@ -1344,8 +1359,11 @@ export class GroupCall extends TypedEventEmitter< await this.updateDevices(devices => { const newDevices = devices.filter(d => { const device = deviceMap.get(d.device_id); - return device?.last_seen_ts !== undefined - && !(d.device_id === this.client.getDeviceId()! && this.state !== GroupCallState.Entered); + return device?.last_seen_ts !== undefined && !( + d.device_id === this.client.getDeviceId()! + && this.state !== GroupCallState.Entered + && !this.enteredViaAnotherSession + ); }); // Skip the update if the devices are unchanged