Stop connecting to a video room if the widget messaging disappears (#8660)

* Stop connecting to a video room if the widget messaging disappears

* Clean up more listeners

* Clean up even more listeners
This commit is contained in:
Robin 2022-05-24 07:43:27 -04:00 committed by GitHub
parent 0343548dbe
commit 7edc4b1965
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 12 deletions

View file

@ -136,14 +136,40 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
} }
} }
// Now that we got the messaging, we need a way to ensure that it doesn't get stopped
const dontStopMessaging = new Promise<void>((resolve, reject) => {
const listener = (uid: string) => {
if (uid === jitsiUid) {
cleanup();
reject(new Error("Messaging stopped"));
}
};
const done = () => {
cleanup();
resolve();
};
const cleanup = () => {
messagingStore.off(WidgetMessagingStoreEvent.StopMessaging, listener);
this.off(VideoChannelEvent.Connect, done);
this.off(VideoChannelEvent.Disconnect, done);
};
messagingStore.on(WidgetMessagingStoreEvent.StopMessaging, listener);
this.on(VideoChannelEvent.Connect, done);
this.on(VideoChannelEvent.Disconnect, done);
});
if (!messagingStore.isWidgetReady(jitsiUid)) { if (!messagingStore.isWidgetReady(jitsiUid)) {
// Wait for the widget to be ready to receive our join event // Wait for the widget to be ready to receive our join event
try { try {
await waitForEvent( await Promise.race([
waitForEvent(
messagingStore, messagingStore,
WidgetMessagingStoreEvent.WidgetReady, WidgetMessagingStoreEvent.WidgetReady,
(uid: string) => uid === jitsiUid, (uid: string) => uid === jitsiUid,
); ),
dontStopMessaging,
]);
} catch (e) { } catch (e) {
throw new Error(`Video channel in room ${roomId} never became ready: ${e}`); throw new Error(`Video channel in room ${roomId} never became ready: ${e}`);
} }
@ -178,11 +204,12 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
videoDevice: videoDevice?.label, videoDevice: videoDevice?.label,
}); });
try { try {
await waitForJoin; await Promise.race([waitForJoin, dontStopMessaging]);
} catch (e) { } catch (e) {
// If it timed out, clean up our advance preparations // If it timed out, clean up our advance preparations
this.activeChannel = null; this.activeChannel = null;
this.roomId = null; this.roomId = null;
messaging.off(`action:${ElementWidgetActions.CallParticipants}`, this.onParticipants); messaging.off(`action:${ElementWidgetActions.CallParticipants}`, this.onParticipants);
messaging.off(`action:${ElementWidgetActions.MuteAudio}`, this.onMuteAudio); messaging.off(`action:${ElementWidgetActions.MuteAudio}`, this.onMuteAudio);
messaging.off(`action:${ElementWidgetActions.UnmuteAudio}`, this.onUnmuteAudio); messaging.off(`action:${ElementWidgetActions.UnmuteAudio}`, this.onUnmuteAudio);

View file

@ -25,6 +25,7 @@ import WidgetUtils from "../../utils/WidgetUtils";
export enum WidgetMessagingStoreEvent { export enum WidgetMessagingStoreEvent {
StoreMessaging = "store_messaging", StoreMessaging = "store_messaging",
StopMessaging = "stop_messaging",
WidgetReady = "widget_ready", WidgetReady = "widget_ready",
} }
@ -71,9 +72,7 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<unknown> {
} }
public stopMessaging(widget: Widget, roomId: string) { public stopMessaging(widget: Widget, roomId: string) {
const uid = WidgetUtils.calcWidgetUid(widget.id, roomId); this.stopMessagingByUid(WidgetUtils.calcWidgetUid(widget.id, roomId));
this.widgetMap.remove(uid)?.stop();
this.readyWidgets.delete(uid);
} }
public getMessaging(widget: Widget, roomId: string): ClientWidgetApi { public getMessaging(widget: Widget, roomId: string): ClientWidgetApi {
@ -86,6 +85,8 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<unknown> {
*/ */
public stopMessagingByUid(widgetUid: string) { public stopMessagingByUid(widgetUid: string) {
this.widgetMap.remove(widgetUid)?.stop(); this.widgetMap.remove(widgetUid)?.stop();
this.readyWidgets.delete(widgetUid);
this.emit(WidgetMessagingStoreEvent.StopMessaging, widgetUid);
} }
/** /**

View file

@ -114,23 +114,26 @@ describe("VideoChannelStore", () => {
expect(store.roomId).toBeFalsy(); expect(store.roomId).toBeFalsy();
expect(store.connected).toEqual(false); expect(store.connected).toEqual(false);
store.connect("!1:example.org", null, null); const connectPromise = store.connect("!1:example.org", null, null);
await confirmConnect(); await confirmConnect();
await expect(connectPromise).resolves.toBeUndefined();
expect(store.roomId).toEqual("!1:example.org"); expect(store.roomId).toEqual("!1:example.org");
expect(store.connected).toEqual(true); expect(store.connected).toEqual(true);
store.disconnect(); const disconnectPromise = store.disconnect();
await confirmDisconnect(); await confirmDisconnect();
await expect(disconnectPromise).resolves.toBeUndefined();
expect(store.roomId).toBeFalsy(); expect(store.roomId).toBeFalsy();
expect(store.connected).toEqual(false); expect(store.connected).toEqual(false);
WidgetMessagingStore.instance.stopMessaging(widget, "!1:example.org"); WidgetMessagingStore.instance.stopMessaging(widget, "!1:example.org");
}); });
it("waits for messaging when connecting", async () => { it("waits for messaging when connecting", async () => {
store.connect("!1:example.org", null, null); const connectPromise = store.connect("!1:example.org", null, null);
WidgetMessagingStore.instance.storeMessaging(widget, "!1:example.org", messaging); WidgetMessagingStore.instance.storeMessaging(widget, "!1:example.org", messaging);
widgetReady(); widgetReady();
await confirmConnect(); await confirmConnect();
await expect(connectPromise).resolves.toBeUndefined();
expect(store.roomId).toEqual("!1:example.org"); expect(store.roomId).toEqual("!1:example.org");
expect(store.connected).toEqual(true); expect(store.connected).toEqual(true);
@ -138,4 +141,19 @@ describe("VideoChannelStore", () => {
await confirmDisconnect(); await confirmDisconnect();
WidgetMessagingStore.instance.stopMessaging(widget, "!1:example.org"); WidgetMessagingStore.instance.stopMessaging(widget, "!1:example.org");
}); });
it("rejects if the widget's messaging gets stopped mid-connect", async () => {
WidgetMessagingStore.instance.storeMessaging(widget, "!1:example.org", messaging);
widgetReady();
expect(store.roomId).toBeFalsy();
expect(store.connected).toEqual(false);
const connectPromise = store.connect("!1:example.org", null, null);
// Wait for the store to contact the widget API, then stop the messaging
await messageSent;
WidgetMessagingStore.instance.stopMessaging(widget, "!1:example.org");
await expect(connectPromise).rejects.toBeDefined();
expect(store.roomId).toBeFalsy();
expect(store.connected).toEqual(false);
});
}); });