You've already forked element-web
mirror of
https://github.com/element-hq/element-web.git
synced 2025-08-05 05:21:16 +03:00
* Avoid destroying calls until they are hidden from the UI We often want calls to exist even when no more participants are left in the MatrixRTC session. So, we should avoid destroying calls as long as they're being presented in the UI; this means that the user has an intent to either join the call or continue looking at an error screen, and we shouldn't interrupt that interaction. The RoomViewStore is now what takes care of creating and destroying calls, rather than the CallView. In general it seems kinda impossible to safely create and destroy model objects from React lifecycle hooks, so moving this responsibility to a store seemed appropriate and resolves existing issues with calls in React strict mode. * Wait for a close action before closing a call This creates a distinction between the user hanging up and the widget being ready to close, which is useful for allowing Element Call to show error screens when disconnected from the call, for example. * Don't expect a 'close' action in video rooms These use the returnToLobby option and are expected to remain visible when the user leaves the call.
114 lines
3.8 KiB
TypeScript
114 lines
3.8 KiB
TypeScript
/*
|
|
Copyright 2024 New Vector Ltd.
|
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { MatrixWidgetType } from "matrix-widget-api";
|
|
|
|
import type { GroupCall, Room, RoomMember, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
|
import { mkEvent } from "./test-utils";
|
|
import { Call, type ConnectionState, ElementCall, JitsiCall } from "../../src/models/Call";
|
|
import { CallStore } from "../../src/stores/CallStore";
|
|
|
|
export class MockedCall extends Call {
|
|
public static readonly EVENT_TYPE = "org.example.mocked_call";
|
|
public readonly STUCK_DEVICE_TIMEOUT_MS = 1000 * 60 * 60; // 1 hour
|
|
|
|
private constructor(
|
|
room: Room,
|
|
public readonly event: MatrixEvent,
|
|
) {
|
|
super(
|
|
{
|
|
id: event.getStateKey()!,
|
|
eventId: "$1:example.org",
|
|
roomId: room.roomId,
|
|
type: MatrixWidgetType.Custom,
|
|
url: "https://example.org",
|
|
name: "Group call",
|
|
creatorUserId: "@alice:example.org",
|
|
// waitForIframeLoad = false, makes the widget API wait for the 'contentLoaded' event.
|
|
waitForIframeLoad: false,
|
|
},
|
|
room.client,
|
|
);
|
|
this.groupCall = { creationTs: this.event.getTs() } as unknown as GroupCall;
|
|
}
|
|
|
|
public static get(room: Room): MockedCall | null {
|
|
const [event] = room.currentState.getStateEvents(this.EVENT_TYPE);
|
|
return event === undefined || "m.terminated" in event.getContent() ? null : new MockedCall(room, event);
|
|
}
|
|
|
|
public static create(room: Room, id: string) {
|
|
room.addLiveEvents(
|
|
[
|
|
mkEvent({
|
|
event: true,
|
|
type: this.EVENT_TYPE,
|
|
room: room.roomId,
|
|
user: "@alice:example.org",
|
|
content: { "m.type": "m.video", "m.intent": "m.prompt" },
|
|
skey: id,
|
|
ts: Date.now(),
|
|
}),
|
|
],
|
|
{ addToState: true },
|
|
);
|
|
// @ts-ignore deliberately calling a private method
|
|
// Let CallStore know that a call might now exist
|
|
CallStore.instance.updateRoom(room);
|
|
}
|
|
|
|
public readonly groupCall: GroupCall;
|
|
|
|
public get participants(): Map<RoomMember, Set<string>> {
|
|
return super.participants;
|
|
}
|
|
public set participants(value: Map<RoomMember, Set<string>>) {
|
|
super.participants = value;
|
|
}
|
|
|
|
public setConnectionState(value: ConnectionState): void {
|
|
super.connectionState = value;
|
|
}
|
|
|
|
// No action needed for any of the following methods since this is just a mock
|
|
public async clean(): Promise<void> {}
|
|
// Public to allow spying
|
|
public async performConnection(): Promise<void> {}
|
|
public async performDisconnection(): Promise<void> {}
|
|
|
|
public destroy() {
|
|
// Terminate the call for good measure
|
|
this.room.addLiveEvents(
|
|
[
|
|
mkEvent({
|
|
event: true,
|
|
type: MockedCall.EVENT_TYPE,
|
|
room: this.room.roomId,
|
|
user: "@alice:example.org",
|
|
content: { ...this.event.getContent(), "m.terminated": "Call ended" },
|
|
skey: this.widget.id,
|
|
ts: Date.now(),
|
|
}),
|
|
],
|
|
{ addToState: true },
|
|
);
|
|
|
|
super.destroy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets up the call store to use mocked calls.
|
|
*/
|
|
export const useMockedCalls = () => {
|
|
Call.get = (room) => MockedCall.get(room);
|
|
JitsiCall.create = async (room) => MockedCall.create(room, "1");
|
|
ElementCall.create = (room) => MockedCall.create(room, "1");
|
|
};
|