* 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.
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
/*
|
|
* Copyright 2024 New Vector Ltd.
|
|
* Copyright 2020 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 { type ClientWidgetApi, type Widget } from "matrix-widget-api";
|
|
import { type EmptyObject } from "matrix-js-sdk/src/matrix";
|
|
|
|
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
|
import { type ActionPayload } from "../../dispatcher/payloads";
|
|
import { EnhancedMap } from "../../utils/maps";
|
|
import WidgetUtils from "../../utils/WidgetUtils";
|
|
|
|
export enum WidgetMessagingStoreEvent {
|
|
StoreMessaging = "store_messaging",
|
|
StopMessaging = "stop_messaging",
|
|
}
|
|
|
|
/**
|
|
* Temporary holding store for widget messaging instances. This is eventually
|
|
* going to be merged with a more complete WidgetStore, but for now it's
|
|
* easiest to split this into a single place.
|
|
*/
|
|
export class WidgetMessagingStore extends AsyncStoreWithClient<EmptyObject> {
|
|
private static readonly internalInstance = (() => {
|
|
const instance = new WidgetMessagingStore();
|
|
instance.start();
|
|
return instance;
|
|
})();
|
|
|
|
private widgetMap = new EnhancedMap<string, ClientWidgetApi>(); // <widget UID, ClientWidgetAPi>
|
|
|
|
public constructor() {
|
|
super(defaultDispatcher);
|
|
}
|
|
|
|
public static get instance(): WidgetMessagingStore {
|
|
return WidgetMessagingStore.internalInstance;
|
|
}
|
|
|
|
protected async onAction(payload: ActionPayload): Promise<void> {
|
|
// nothing to do
|
|
}
|
|
|
|
protected async onReady(): Promise<any> {
|
|
// just in case
|
|
this.widgetMap.clear();
|
|
}
|
|
|
|
public storeMessaging(widget: Widget, roomId: string | undefined, widgetApi: ClientWidgetApi): void {
|
|
this.stopMessaging(widget, roomId);
|
|
const uid = WidgetUtils.calcWidgetUid(widget.id, roomId);
|
|
this.widgetMap.set(uid, widgetApi);
|
|
|
|
this.emit(WidgetMessagingStoreEvent.StoreMessaging, uid, widgetApi);
|
|
}
|
|
|
|
public stopMessaging(widget: Widget, roomId: string | undefined): void {
|
|
this.stopMessagingByUid(WidgetUtils.calcWidgetUid(widget.id, roomId));
|
|
}
|
|
|
|
public getMessaging(widget: Widget, roomId: string | undefined): ClientWidgetApi | undefined {
|
|
return this.widgetMap.get(WidgetUtils.calcWidgetUid(widget.id, roomId));
|
|
}
|
|
|
|
/**
|
|
* Stops the widget messaging instance for a given widget UID.
|
|
* @param {string} widgetUid The widget UID.
|
|
*/
|
|
public stopMessagingByUid(widgetUid: string): void {
|
|
const messaging = this.widgetMap.remove(widgetUid);
|
|
if (messaging !== undefined) {
|
|
messaging.stop();
|
|
this.emit(WidgetMessagingStoreEvent.StopMessaging, widgetUid);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the widget messaging class for a given widget UID.
|
|
* @param {string} widgetUid The widget UID.
|
|
* @returns {ClientWidgetApi} The widget API, or a falsy value if not found.
|
|
*/
|
|
public getMessagingForUid(widgetUid: string): ClientWidgetApi | undefined {
|
|
return this.widgetMap.get(widgetUid);
|
|
}
|
|
}
|