Proactively fix stuck devices in video rooms (#8587)
* Proactively fix stuck devices in video rooms * Fix tests * Explain why we're disabling the lint rule * Apply code review suggestions * Back VideoChannelStore's flags by SettingsStore instead of localStorage
This commit is contained in:
parent
6f851108be
commit
ceda77d7dc
9 changed files with 149 additions and 49 deletions
|
@ -18,15 +18,13 @@ import EventEmitter from "events";
|
|||
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { ClientWidgetApi, IWidgetApiRequest } from "matrix-widget-api";
|
||||
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
import { ElementWidgetActions } from "./widgets/ElementWidgetActions";
|
||||
import { WidgetMessagingStore, WidgetMessagingStoreEvent } from "./widgets/WidgetMessagingStore";
|
||||
import {
|
||||
VIDEO_CHANNEL_MEMBER,
|
||||
IVideoChannelMemberContent,
|
||||
getVideoChannel,
|
||||
} from "../utils/VideoChannelUtils";
|
||||
import { getVideoChannel, addOurDevice, removeOurDevice } from "../utils/VideoChannelUtils";
|
||||
import { timeout } from "../utils/promise";
|
||||
import WidgetUtils from "../utils/WidgetUtils";
|
||||
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||
|
@ -83,9 +81,13 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
|
|||
|
||||
private activeChannel: ClientWidgetApi;
|
||||
|
||||
private _roomId: string;
|
||||
public get roomId(): string { return this._roomId; }
|
||||
private set roomId(value: string) { this._roomId = value; }
|
||||
// This is persisted to settings so we can detect unclean disconnects
|
||||
public get roomId(): string | null { return SettingsStore.getValue("videoChannelRoomId"); }
|
||||
private set roomId(value: string | null) {
|
||||
SettingsStore.setValue("videoChannelRoomId", null, SettingLevel.DEVICE, value);
|
||||
}
|
||||
|
||||
private get room(): Room { return this.matrixClient.getRoom(this.roomId); }
|
||||
|
||||
private _connected = false;
|
||||
public get connected(): boolean { return this._connected; }
|
||||
|
@ -95,18 +97,14 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
|
|||
public get participants(): IJitsiParticipant[] { return this._participants; }
|
||||
private set participants(value: IJitsiParticipant[]) { this._participants = value; }
|
||||
|
||||
private _audioMuted = localStorage.getItem("mx_audioMuted") === "true";
|
||||
public get audioMuted(): boolean { return this._audioMuted; }
|
||||
public get audioMuted(): boolean { return SettingsStore.getValue("audioInputMuted"); }
|
||||
public set audioMuted(value: boolean) {
|
||||
this._audioMuted = value;
|
||||
localStorage.setItem("mx_audioMuted", value.toString());
|
||||
SettingsStore.setValue("audioInputMuted", null, SettingLevel.DEVICE, value);
|
||||
}
|
||||
|
||||
private _videoMuted = localStorage.getItem("mx_videoMuted") === "true";
|
||||
public get videoMuted(): boolean { return this._videoMuted; }
|
||||
public get videoMuted(): boolean { return SettingsStore.getValue("videoInputMuted"); }
|
||||
public set videoMuted(value: boolean) {
|
||||
this._videoMuted = value;
|
||||
localStorage.setItem("mx_videoMuted", value.toString());
|
||||
SettingsStore.setValue("videoInputMuted", null, SettingLevel.DEVICE, value);
|
||||
}
|
||||
|
||||
public connect = async (roomId: string, audioDevice: MediaDeviceInfo, videoDevice: MediaDeviceInfo) => {
|
||||
|
@ -198,13 +196,13 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
|
|||
}
|
||||
|
||||
this.connected = true;
|
||||
this.matrixClient.getRoom(roomId).on(RoomEvent.MyMembership, this.onMyMembership);
|
||||
this.room.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||
window.addEventListener("beforeunload", this.setDisconnected);
|
||||
|
||||
this.emit(VideoChannelEvent.Connect, roomId);
|
||||
|
||||
// Tell others that we're connected, by adding our device to room state
|
||||
this.updateDevices(roomId, devices => Array.from(new Set(devices).add(this.matrixClient.getDeviceId())));
|
||||
await addOurDevice(this.room);
|
||||
};
|
||||
|
||||
public disconnect = async () => {
|
||||
|
@ -221,10 +219,11 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
|
|||
|
||||
public setDisconnected = async () => {
|
||||
const roomId = this.roomId;
|
||||
const room = this.room;
|
||||
|
||||
this.activeChannel.off(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
|
||||
this.activeChannel.off(`action:${ElementWidgetActions.CallParticipants}`, this.onParticipants);
|
||||
this.matrixClient.getRoom(roomId).off(RoomEvent.MyMembership, this.onMyMembership);
|
||||
room.off(RoomEvent.MyMembership, this.onMyMembership);
|
||||
window.removeEventListener("beforeunload", this.setDisconnected);
|
||||
|
||||
this.activeChannel = null;
|
||||
|
@ -235,11 +234,7 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
|
|||
this.emit(VideoChannelEvent.Disconnect, roomId);
|
||||
|
||||
// Tell others that we're disconnected, by removing our device from room state
|
||||
await this.updateDevices(roomId, devices => {
|
||||
const devicesSet = new Set(devices);
|
||||
devicesSet.delete(this.matrixClient.getDeviceId());
|
||||
return Array.from(devicesSet);
|
||||
});
|
||||
await removeOurDevice(room);
|
||||
};
|
||||
|
||||
private ack = (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
|
@ -248,18 +243,6 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
|
|||
this.activeChannel.transport.reply(ev.detail, {});
|
||||
};
|
||||
|
||||
private updateDevices = async (roomId: string, fn: (devices: string[]) => string[]) => {
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
if (room.getMyMembership() !== "join") return;
|
||||
|
||||
const devicesState = room.currentState.getStateEvents(VIDEO_CHANNEL_MEMBER, this.matrixClient.getUserId());
|
||||
const devices = devicesState?.getContent<IVideoChannelMemberContent>()?.devices ?? [];
|
||||
|
||||
await this.matrixClient.sendStateEvent(
|
||||
roomId, VIDEO_CHANNEL_MEMBER, { devices: fn(devices) }, this.matrixClient.getUserId(),
|
||||
);
|
||||
};
|
||||
|
||||
private onHangup = async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
this.ack(ev);
|
||||
// In case this hangup is caused by Jitsi Meet crashing at startup,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue