summaryrefslogtreecommitdiff
path: root/includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-04-24 14:03:36 +0200
committerRaindropsSys <contact@minteck.org>2023-04-24 14:03:36 +0200
commit633c92eae865e957121e08de634aeee11a8b3992 (patch)
tree09d881bee1dae0b6eee49db1dfaf0f500240606c /includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts
parentc4657e4509733699c0f26a3c900bab47e915d5a0 (diff)
downloadpluralconnect-633c92eae865e957121e08de634aeee11a8b3992.tar.gz
pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.tar.bz2
pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.zip
Updated 18 files, added 1692 files and deleted includes/system/compare.inc (automated)
Diffstat (limited to 'includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts')
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts232
1 files changed, 232 insertions, 0 deletions
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts
new file mode 100644
index 0000000..08487bd
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/webrtc/groupCallEventHandler.ts
@@ -0,0 +1,232 @@
+/*
+Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { MatrixEvent } from "../models/event";
+import { MatrixClient, ClientEvent } from "../client";
+import { GroupCall, GroupCallIntent, GroupCallType, IGroupCallDataChannelOptions } from "./groupCall";
+import { Room } from "../models/room";
+import { RoomState, RoomStateEvent } from "../models/room-state";
+import { RoomMember } from "../models/room-member";
+import { logger } from "../logger";
+import { EventType } from "../@types/event";
+import { SyncState } from "../sync";
+
+export enum GroupCallEventHandlerEvent {
+ Incoming = "GroupCall.incoming",
+ Outgoing = "GroupCall.outgoing",
+ Ended = "GroupCall.ended",
+ Participants = "GroupCall.participants",
+}
+
+export type GroupCallEventHandlerEventHandlerMap = {
+ [GroupCallEventHandlerEvent.Incoming]: (call: GroupCall) => void;
+ [GroupCallEventHandlerEvent.Outgoing]: (call: GroupCall) => void;
+ [GroupCallEventHandlerEvent.Ended]: (call: GroupCall) => void;
+ [GroupCallEventHandlerEvent.Participants]: (participants: RoomMember[], call: GroupCall) => void;
+};
+
+interface RoomDeferred {
+ prom: Promise<void>;
+ resolve?: () => void;
+}
+
+export class GroupCallEventHandler {
+ public groupCalls = new Map<string, GroupCall>(); // roomId -> GroupCall
+
+ // All rooms we know about and whether we've seen a 'Room' event
+ // for them. The promise will be fulfilled once we've processed that
+ // event which means we're "up to date" on what calls are in a room
+ // and get
+ private roomDeferreds = new Map<string, RoomDeferred>();
+
+ public constructor(private client: MatrixClient) {}
+
+ public async start(): Promise<void> {
+ // We wait until the client has started syncing for real.
+ // This is because we only support one call at a time, and want
+ // the latest. We therefore want the latest state of the room before
+ // we create a group call for the room so we can be fairly sure that
+ // the group call we create is really the latest one.
+ if (this.client.getSyncState() !== SyncState.Syncing) {
+ logger.debug("GroupCallEventHandler start() waiting for client to start syncing");
+ await new Promise<void>((resolve) => {
+ const onSync = (): void => {
+ if (this.client.getSyncState() === SyncState.Syncing) {
+ this.client.off(ClientEvent.Sync, onSync);
+ return resolve();
+ }
+ };
+ this.client.on(ClientEvent.Sync, onSync);
+ });
+ }
+
+ const rooms = this.client.getRooms();
+
+ for (const room of rooms) {
+ this.createGroupCallForRoom(room);
+ }
+
+ this.client.on(ClientEvent.Room, this.onRoomsChanged);
+ this.client.on(RoomStateEvent.Events, this.onRoomStateChanged);
+ }
+
+ public stop(): void {
+ this.client.removeListener(RoomStateEvent.Events, this.onRoomStateChanged);
+ }
+
+ private getRoomDeferred(roomId: string): RoomDeferred {
+ let deferred = this.roomDeferreds.get(roomId);
+ if (deferred === undefined) {
+ let resolveFunc: () => void;
+ deferred = {
+ prom: new Promise<void>((resolve) => {
+ resolveFunc = resolve;
+ }),
+ };
+ deferred.resolve = resolveFunc!;
+ this.roomDeferreds.set(roomId, deferred);
+ }
+
+ return deferred;
+ }
+
+ public waitUntilRoomReadyForGroupCalls(roomId: string): Promise<void> {
+ return this.getRoomDeferred(roomId).prom;
+ }
+
+ public getGroupCallById(groupCallId: string): GroupCall | undefined {
+ return [...this.groupCalls.values()].find((groupCall) => groupCall.groupCallId === groupCallId);
+ }
+
+ private createGroupCallForRoom(room: Room): void {
+ const callEvents = room.currentState.getStateEvents(EventType.GroupCallPrefix);
+ const sortedCallEvents = callEvents.sort((a, b) => b.getTs() - a.getTs());
+
+ for (const callEvent of sortedCallEvents) {
+ const content = callEvent.getContent();
+
+ if (content["m.terminated"] || callEvent.isRedacted()) {
+ continue;
+ }
+
+ logger.debug(
+ `GroupCallEventHandler createGroupCallForRoom() choosing group call from possible calls (stateKey=${callEvent.getStateKey()}, ts=${callEvent.getTs()}, roomId=${
+ room.roomId
+ }, numOfPossibleCalls=${callEvents.length})`,
+ );
+
+ this.createGroupCallFromRoomStateEvent(callEvent);
+ break;
+ }
+
+ logger.info(`GroupCallEventHandler createGroupCallForRoom() processed room (roomId=${room.roomId})`);
+ this.getRoomDeferred(room.roomId).resolve!();
+ }
+
+ private createGroupCallFromRoomStateEvent(event: MatrixEvent): GroupCall | undefined {
+ const roomId = event.getRoomId();
+ const content = event.getContent();
+
+ const room = this.client.getRoom(roomId);
+
+ if (!room) {
+ logger.warn(
+ `GroupCallEventHandler createGroupCallFromRoomStateEvent() couldn't find room for call (roomId=${roomId})`,
+ );
+ return;
+ }
+
+ const groupCallId = event.getStateKey();
+
+ const callType = content["m.type"];
+
+ if (!Object.values(GroupCallType).includes(callType)) {
+ logger.warn(
+ `GroupCallEventHandler createGroupCallFromRoomStateEvent() received invalid call type (type=${callType}, roomId=${roomId})`,
+ );
+ return;
+ }
+
+ const callIntent = content["m.intent"];
+
+ if (!Object.values(GroupCallIntent).includes(callIntent)) {
+ logger.warn(`Received invalid group call intent (type=${callType}, roomId=${roomId})`);
+ return;
+ }
+
+ const isPtt = Boolean(content["io.element.ptt"]);
+
+ let dataChannelOptions: IGroupCallDataChannelOptions | undefined;
+
+ if (content?.dataChannelsEnabled && content?.dataChannelOptions) {
+ // Pull out just the dataChannelOptions we want to support.
+ const { ordered, maxPacketLifeTime, maxRetransmits, protocol } = content.dataChannelOptions;
+ dataChannelOptions = { ordered, maxPacketLifeTime, maxRetransmits, protocol };
+ }
+
+ const groupCall = new GroupCall(
+ this.client,
+ room,
+ callType,
+ isPtt,
+ callIntent,
+ groupCallId,
+ // Because without Media section a WebRTC connection is not possible, so need a RTCDataChannel to set up a
+ // no media WebRTC connection anyway.
+ content?.dataChannelsEnabled || this.client.isVoipWithNoMediaAllowed,
+ dataChannelOptions,
+ this.client.isVoipWithNoMediaAllowed,
+ );
+
+ this.groupCalls.set(room.roomId, groupCall);
+ this.client.emit(GroupCallEventHandlerEvent.Incoming, groupCall);
+
+ return groupCall;
+ }
+
+ private onRoomsChanged = (room: Room): void => {
+ this.createGroupCallForRoom(room);
+ };
+
+ private onRoomStateChanged = (event: MatrixEvent, state: RoomState): void => {
+ const eventType = event.getType();
+
+ if (eventType === EventType.GroupCallPrefix) {
+ const groupCallId = event.getStateKey();
+ const content = event.getContent();
+
+ const currentGroupCall = this.groupCalls.get(state.roomId);
+
+ if (!currentGroupCall && !content["m.terminated"] && !event.isRedacted()) {
+ this.createGroupCallFromRoomStateEvent(event);
+ } else if (currentGroupCall && currentGroupCall.groupCallId === groupCallId) {
+ if (content["m.terminated"] || event.isRedacted()) {
+ currentGroupCall.terminate(false);
+ } else if (content["m.type"] !== currentGroupCall.type) {
+ // TODO: Handle the callType changing when the room state changes
+ logger.warn(
+ `GroupCallEventHandler onRoomStateChanged() currently does not support changing type (roomId=${state.roomId})`,
+ );
+ }
+ } else if (currentGroupCall && currentGroupCall.groupCallId !== groupCallId) {
+ // TODO: Handle new group calls and multiple group calls
+ logger.warn(
+ `GroupCallEventHandler onRoomStateChanged() currently does not support multiple calls (roomId=${state.roomId})`,
+ );
+ }
+ }
+ };
+}