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

Fix embedded Element Call screen sharing #9485

Merged
merged 2 commits into from
Oct 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
21 changes: 13 additions & 8 deletions src/models/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ export class ElementCall extends Call {
this.messaging!.on(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
this.messaging!.on(`action:${ElementWidgetActions.TileLayout}`, this.onTileLayout);
this.messaging!.on(`action:${ElementWidgetActions.SpotlightLayout}`, this.onSpotlightLayout);
this.messaging!.on(`action:${ElementWidgetActions.Screenshare}`, this.onScreenshare);
this.messaging!.on(`action:${ElementWidgetActions.ScreenshareRequest}`, this.onScreenshareRequest);
}

protected async performDisconnection(): Promise<void> {
Expand All @@ -832,7 +832,7 @@ export class ElementCall extends Call {
this.messaging!.off(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
this.messaging!.off(`action:${ElementWidgetActions.TileLayout}`, this.onTileLayout);
this.messaging!.off(`action:${ElementWidgetActions.SpotlightLayout}`, this.onSpotlightLayout);
this.messaging!.off(`action:${ElementWidgetActions.Screenshare}`, this.onSpotlightLayout);
this.messaging!.off(`action:${ElementWidgetActions.ScreenshareRequest}`, this.onScreenshareRequest);
super.setDisconnected();
}

Expand Down Expand Up @@ -952,19 +952,24 @@ export class ElementCall extends Call {
await this.messaging!.transport.reply(ev.detail, {}); // ack
};

private onScreenshare = async (ev: CustomEvent<IWidgetApiRequest>) => {
private onScreenshareRequest = async (ev: CustomEvent<IWidgetApiRequest>) => {
ev.preventDefault();

if (PlatformPeg.get().supportsDesktopCapturer()) {
await this.messaging!.transport.reply(ev.detail, { pending: true });

const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
const [source] = await finished;

await this.messaging!.transport.reply(ev.detail, {
failed: !source,
desktopCapturerSourceId: source,
});
if (source) {
await this.messaging!.transport.send(ElementWidgetActions.ScreenshareStart, {
desktopCapturerSourceId: source,
});
} else {
await this.messaging!.transport.send(ElementWidgetActions.ScreenshareStop, {});
}
} else {
await this.messaging!.transport.reply(ev.detail, {});
await this.messaging!.transport.reply(ev.detail, { pending: false });
}
};
}
15 changes: 14 additions & 1 deletion src/stores/widgets/ElementWidgetActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,23 @@ export enum ElementWidgetActions {
MuteVideo = "io.element.mute_video",
UnmuteVideo = "io.element.unmute_video",
StartLiveStream = "im.vector.start_live_stream",

// Element Call -> host requesting to start a screenshare
// (ie. expects a ScreenshareStart once the user has picked a source)
// replies with { pending } where pending is true if the host has asked
// the user to choose a window and false if not (ie. if the host isn't
// running within Electron)
ScreenshareRequest = "io.element.screenshare_request",
// host -> Element Call telling EC to start screen sharing with
// the given source
ScreenshareStart = "io.element.screenshare_start",
// host -> Element Call telling EC to stop screen sharing, or that
// the user cancelled when selecting a source after a ScreenshareRequest
ScreenshareStop = "io.element.screenshare_stop",

// Actions for switching layouts
TileLayout = "io.element.tile_layout",
SpotlightLayout = "io.element.spotlight_layout",
Screenshare = "io.element.screenshare",

OpenIntegrationManager = "integration_manager_open",

Expand Down
34 changes: 23 additions & 11 deletions test/models/Call-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -820,19 +820,25 @@ describe("ElementCall", () => {
await call.connect();

messaging.emit(
`action:${ElementWidgetActions.Screenshare}`,
`action:${ElementWidgetActions.ScreenshareRequest}`,
new CustomEvent("widgetapirequest", { detail: {} }),
);

waitFor(() => {
await waitFor(() => {
expect(messaging!.transport.reply).toHaveBeenCalledWith(
expect.objectContaining({}),
expect.objectContaining({ desktopCapturerSourceId: sourceId }),
expect.objectContaining({ pending: true }),
);
});

await waitFor(() => {
expect(messaging!.transport.send).toHaveBeenCalledWith(
"io.element.screenshare_start", expect.objectContaining({ desktopCapturerSourceId: sourceId }),
);
});
});

it("passes failed if we couldn't get a source id", async () => {
it("sends ScreenshareStop if we couldn't get a source id", async () => {
jest.spyOn(Modal, "createDialog").mockReturnValue(
{ finished: new Promise((r) => r([null])) } as IHandle<any[]>,
);
Expand All @@ -841,32 +847,38 @@ describe("ElementCall", () => {
await call.connect();

messaging.emit(
`action:${ElementWidgetActions.Screenshare}`,
`action:${ElementWidgetActions.ScreenshareRequest}`,
new CustomEvent("widgetapirequest", { detail: {} }),
);

waitFor(() => {
await waitFor(() => {
expect(messaging!.transport.reply).toHaveBeenCalledWith(
expect.objectContaining({}),
expect.objectContaining({ failed: true }),
expect.objectContaining({ pending: true }),
);
});

await waitFor(() => {
expect(messaging!.transport.send).toHaveBeenCalledWith(
"io.element.screenshare_stop", expect.objectContaining({ }),
);
});
});

it("passes an empty object if we don't support desktop capturer", async () => {
it("replies with pending: false if we don't support desktop capturer", async () => {
jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(false);

await call.connect();

messaging.emit(
`action:${ElementWidgetActions.Screenshare}`,
`action:${ElementWidgetActions.ScreenshareRequest}`,
new CustomEvent("widgetapirequest", { detail: {} }),
);

waitFor(() => {
await waitFor(() => {
expect(messaging!.transport.reply).toHaveBeenCalledWith(
expect.objectContaining({}),
expect.objectContaining({}),
expect.objectContaining({ pending: false }),
);
});
});
Expand Down