Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

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

Merged
merged 3 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions src/stores/VideoChannelStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
// Wait for the widget to be ready to receive our join event
try {
await waitForEvent(
messagingStore,
WidgetMessagingStoreEvent.WidgetReady,
(uid: string) => uid === jitsiUid,
);
await Promise.race([
waitForEvent(
messagingStore,
WidgetMessagingStoreEvent.WidgetReady,
(uid: string) => uid === jitsiUid,
),
dontStopMessaging,
]);
} catch (e) {
throw new Error(`Video channel in room ${roomId} never became ready: ${e}`);
}
Expand Down Expand Up @@ -178,11 +204,12 @@ export default class VideoChannelStore extends AsyncStoreWithClient<null> {
videoDevice: videoDevice?.label,
});
try {
await waitForJoin;
await Promise.race([waitForJoin, dontStopMessaging]);
} catch (e) {
// If it timed out, clean up our advance preparations
this.activeChannel = null;
this.roomId = null;

messaging.off(`action:${ElementWidgetActions.CallParticipants}`, this.onParticipants);
messaging.off(`action:${ElementWidgetActions.MuteAudio}`, this.onMuteAudio);
messaging.off(`action:${ElementWidgetActions.UnmuteAudio}`, this.onUnmuteAudio);
Expand Down
7 changes: 4 additions & 3 deletions src/stores/widgets/WidgetMessagingStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import WidgetUtils from "../../utils/WidgetUtils";

export enum WidgetMessagingStoreEvent {
StoreMessaging = "store_messaging",
StopMessaging = "stop_messaging",
WidgetReady = "widget_ready",
}

Expand Down Expand Up @@ -71,9 +72,7 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<unknown> {
}

public stopMessaging(widget: Widget, roomId: string) {
const uid = WidgetUtils.calcWidgetUid(widget.id, roomId);
this.widgetMap.remove(uid)?.stop();
this.readyWidgets.delete(uid);
this.stopMessagingByUid(WidgetUtils.calcWidgetUid(widget.id, roomId));
}

public getMessaging(widget: Widget, roomId: string): ClientWidgetApi {
Expand All @@ -86,6 +85,8 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<unknown> {
*/
public stopMessagingByUid(widgetUid: string) {
this.widgetMap.remove(widgetUid)?.stop();
this.readyWidgets.delete(widgetUid);
this.emit(WidgetMessagingStoreEvent.StopMessaging, widgetUid);
}

/**
Expand Down
24 changes: 21 additions & 3 deletions test/stores/VideoChannelStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,28 +114,46 @@ describe("VideoChannelStore", () => {
expect(store.roomId).toBeFalsy();
expect(store.connected).toEqual(false);

store.connect("!1:example.org", null, null);
const connectPromise = store.connect("!1:example.org", null, null);
await confirmConnect();
await expect(connectPromise).resolves.toBeUndefined();
expect(store.roomId).toEqual("!1:example.org");
expect(store.connected).toEqual(true);

store.disconnect();
const disconnectPromise = store.disconnect();
await confirmDisconnect();
await expect(disconnectPromise).resolves.toBeUndefined();
expect(store.roomId).toBeFalsy();
expect(store.connected).toEqual(false);
WidgetMessagingStore.instance.stopMessaging(widget, "!1:example.org");
});

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);
widgetReady();
await confirmConnect();
await expect(connectPromise).resolves.toBeUndefined();
expect(store.roomId).toEqual("!1:example.org");
expect(store.connected).toEqual(true);

store.disconnect();
await confirmDisconnect();
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);
});
});