1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2026-01-03 23:22:30 +03:00

Refactor GroupCall participant management

This refactoring brings a number of improvements to GroupCall, which I've unfortunately had to combine into a single commit due to coupling:

- Moves the expiration timestamp field on call membership state to be per-device
- Makes the participants of a group call visible without having to enter the call yourself
- Enables users to join group calls from multiple devices
- Identifies active speakers by their call feed, rather than just their user ID
- Plays nicely with clients that can be in multiple calls in a room at once
- Fixes a memory leak caused by the call retry loop never stopping
- Changes GroupCall to update its state synchronously, and write back to room state asynchronously
  - This was already sort of halfway being done, but now we'd be committing to it
  - Generally improves the robustness of the state machine
  - It means that group call joins will appear instant, in a sense

For many reasons, this is a breaking change.
This commit is contained in:
Robin Townsend
2022-11-21 11:58:27 -05:00
parent dd98d7eb2c
commit f46ecf970c
10 changed files with 735 additions and 668 deletions

View File

@@ -16,26 +16,21 @@ limitations under the License.
import { mocked } from "jest-mock";
import { ClientEvent } from "../../../src/client";
import { RoomMember } from "../../../src/models/room-member";
import { SyncState } from "../../../src/sync";
import {
ClientEvent,
GroupCall,
GroupCallIntent,
GroupCallState,
GroupCallType,
IContent,
MatrixEvent,
Room,
RoomState,
} from "../../../src";
import { SyncState } from "../../../src/sync";
import { GroupCallTerminationReason } from "../../../src/webrtc/groupCall";
GroupCallTerminationReason,
} from "../../../src/webrtc/groupCall";
import { IContent, MatrixEvent } from "../../../src/models/event";
import { Room } from "../../../src/models/room";
import { RoomState } from "../../../src/models/room-state";
import { GroupCallEventHandler, GroupCallEventHandlerEvent } from "../../../src/webrtc/groupCallEventHandler";
import { flushPromises } from "../../test-utils/flushPromises";
import {
makeMockGroupCallMemberStateEvent,
makeMockGroupCallStateEvent,
MockCallMatrixClient,
} from "../../test-utils/webrtc";
import { makeMockGroupCallStateEvent, MockCallMatrixClient } from "../../test-utils/webrtc";
const FAKE_USER_ID = "@alice:test.dummy";
const FAKE_DEVICE_ID = "AAAAAAA";
@@ -47,6 +42,7 @@ describe('Group Call Event Handler', function() {
let groupCallEventHandler: GroupCallEventHandler;
let mockClient: MockCallMatrixClient;
let mockRoom: Room;
let mockMember: RoomMember;
beforeEach(() => {
mockClient = new MockCallMatrixClient(
@@ -54,13 +50,21 @@ describe('Group Call Event Handler', function() {
);
groupCallEventHandler = new GroupCallEventHandler(mockClient.typed());
mockMember = {
userId: FAKE_USER_ID,
membership: "join",
} as unknown as RoomMember;
mockRoom = {
on: () => {},
off: () => {},
roomId: FAKE_ROOM_ID,
currentState: {
getStateEvents: jest.fn().mockReturnValue([makeMockGroupCallStateEvent(
FAKE_ROOM_ID, FAKE_GROUP_CALL_ID,
)]),
},
getMember: (userId: string) => userId === FAKE_USER_ID ? mockMember : null,
} as unknown as Room;
(mockClient as any).getRoom = jest.fn().mockReturnValue(mockRoom);
@@ -211,27 +215,6 @@ describe('Group Call Event Handler', function() {
);
});
it("sends member events to group calls", async () => {
await groupCallEventHandler.start();
const mockGroupCall = {
onMemberStateChanged: jest.fn(),
};
groupCallEventHandler.groupCalls.set(FAKE_ROOM_ID, mockGroupCall as unknown as GroupCall);
const mockStateEvent = makeMockGroupCallMemberStateEvent(FAKE_ROOM_ID, FAKE_GROUP_CALL_ID);
mockClient.emitRoomState(
mockStateEvent,
{
roomId: FAKE_ROOM_ID,
} as unknown as RoomState,
);
expect(mockGroupCall.onMemberStateChanged).toHaveBeenCalledWith(mockStateEvent);
});
describe("ignoring invalid group call state events", () => {
let mockClientEmit: jest.Func;