* Make useEventEmitterState more efficient By not invoking the initializing function on every render * Make useWidgets more efficient By not calling WidgetStore on every render * Add new group call experience Labs flag * Add viewingCall field to RoomViewStore state Currently has no effect, but in the future this will signal to RoomView to show the call or call lobby. * Add element_call.use_exclusively config flag As documented in element-web, this will tell the app to use Element Call exclusively for calls, disabling Jitsi and legacy 1:1 calls. * Make placeCall return a promise So that the UI can know when placeCall completes * Update start call buttons to new group call designs Since RoomView doesn't do anything with viewingCall yet, these buttons won't have any effect when starting native group calls, but the logic is at least all there and ready to be hooked up. * Allow calls to be detected if the new group call experience is enabled * Test the RoomHeader changes * Iterate code
119 lines
4.5 KiB
TypeScript
119 lines
4.5 KiB
TypeScript
/*
|
|
Copyright 2018-2021 The Matrix.org Foundation C.I.C.
|
|
|
|
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 EventEmitter from 'events';
|
|
import { IWidget } from 'matrix-widget-api';
|
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
|
|
|
import { WidgetType } from "../widgets/WidgetType";
|
|
|
|
/**
|
|
* Acts as a place to get & set widget state, storing local echo state and
|
|
* proxying through state from the js-sdk.
|
|
*/
|
|
class WidgetEchoStore extends EventEmitter {
|
|
private roomWidgetEcho: {
|
|
[roomId: string]: {
|
|
[widgetId: string]: IWidget;
|
|
};
|
|
};
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
this.roomWidgetEcho = {
|
|
// Map as below. Object is the content of the widget state event,
|
|
// so for widgets that have been deleted locally, the object is empty.
|
|
// roomId: {
|
|
// widgetId: IWidget
|
|
// }
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gets the widgets for a room, subtracting those that are pending deletion.
|
|
* Widgets that are pending addition are not included, since widgets are
|
|
* represented as MatrixEvents, so to do this we'd have to create fake MatrixEvents,
|
|
* and we don't really need the actual widget events anyway since we just want to
|
|
* show a spinner / prevent widgets being added twice.
|
|
*
|
|
* @param {string} roomId The ID of the room to get widgets for
|
|
* @param {MatrixEvent[]} currentRoomWidgets Current widgets for the room
|
|
* @returns {MatrixEvent[]} List of widgets in the room, minus any pending removal
|
|
*/
|
|
getEchoedRoomWidgets(roomId: string, currentRoomWidgets: MatrixEvent[]): MatrixEvent[] {
|
|
const echoedWidgets = [];
|
|
|
|
const roomEchoState = Object.assign({}, this.roomWidgetEcho[roomId]);
|
|
|
|
for (const w of currentRoomWidgets) {
|
|
const widgetId = w.getStateKey();
|
|
// If there's no echo, or the echo still has a widget present, show the *old* widget
|
|
// we don't include widgets that have changed for the same reason we don't include new ones,
|
|
// ie. we'd need to fake matrix events to do so and there's currently no need.
|
|
if (!roomEchoState[widgetId] || Object.keys(roomEchoState[widgetId]).length !== 0) {
|
|
echoedWidgets.push(w);
|
|
}
|
|
delete roomEchoState[widgetId];
|
|
}
|
|
|
|
return echoedWidgets;
|
|
}
|
|
|
|
roomHasPendingWidgetsOfType(roomId: string, currentRoomWidgets: MatrixEvent[], type?: WidgetType): boolean {
|
|
const roomEchoState = Object.assign({}, this.roomWidgetEcho[roomId]);
|
|
|
|
// any widget IDs that are already in the room are not pending, so
|
|
// echoes for them don't count as pending.
|
|
for (const w of currentRoomWidgets) {
|
|
const widgetId = w.getStateKey();
|
|
delete roomEchoState[widgetId];
|
|
}
|
|
|
|
// if there's anything left then there are pending widgets.
|
|
if (type === undefined) {
|
|
return Object.keys(roomEchoState).length > 0;
|
|
} else {
|
|
return Object.values(roomEchoState).some((widget) => {
|
|
return type.matches(widget.type);
|
|
});
|
|
}
|
|
}
|
|
|
|
roomHasPendingWidgets(roomId: string, currentRoomWidgets: MatrixEvent[]): boolean {
|
|
return this.roomHasPendingWidgetsOfType(roomId, currentRoomWidgets);
|
|
}
|
|
|
|
setRoomWidgetEcho(roomId: string, widgetId: string, state: IWidget) {
|
|
if (this.roomWidgetEcho[roomId] === undefined) this.roomWidgetEcho[roomId] = {};
|
|
|
|
this.roomWidgetEcho[roomId][widgetId] = state;
|
|
this.emit('update', roomId, widgetId);
|
|
}
|
|
|
|
removeRoomWidgetEcho(roomId: string, widgetId: string) {
|
|
delete this.roomWidgetEcho[roomId][widgetId];
|
|
if (Object.keys(this.roomWidgetEcho[roomId]).length === 0) delete this.roomWidgetEcho[roomId];
|
|
this.emit('update', roomId, widgetId);
|
|
}
|
|
}
|
|
|
|
let singletonWidgetEchoStore: WidgetEchoStore | null = null;
|
|
if (!singletonWidgetEchoStore) {
|
|
singletonWidgetEchoStore = new WidgetEchoStore();
|
|
}
|
|
export default singletonWidgetEchoStore;
|