Make Element Call screensharing work on desktop (#9476)
This commit is contained in:
parent
9d405a905d
commit
f464682967
3 changed files with 88 additions and 6 deletions
|
@ -43,6 +43,8 @@ import { WidgetMessagingStore, WidgetMessagingStoreEvent } from "../stores/widge
|
||||||
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../stores/ActiveWidgetStore";
|
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../stores/ActiveWidgetStore";
|
||||||
import PlatformPeg from "../PlatformPeg";
|
import PlatformPeg from "../PlatformPeg";
|
||||||
import { getCurrentLanguage } from "../languageHandler";
|
import { getCurrentLanguage } from "../languageHandler";
|
||||||
|
import DesktopCapturerSourcePicker from "../components/views/elements/DesktopCapturerSourcePicker";
|
||||||
|
import Modal from "../Modal";
|
||||||
|
|
||||||
const TIMEOUT_MS = 16000;
|
const TIMEOUT_MS = 16000;
|
||||||
|
|
||||||
|
@ -639,10 +641,6 @@ export class ElementCall extends Call {
|
||||||
baseUrl: client.baseUrl,
|
baseUrl: client.baseUrl,
|
||||||
lang: getCurrentLanguage().replace("_", "-"),
|
lang: getCurrentLanguage().replace("_", "-"),
|
||||||
});
|
});
|
||||||
// Currently, the screen-sharing support is the same is it is for Jitsi
|
|
||||||
if (!PlatformPeg.get().supportsJitsiScreensharing()) {
|
|
||||||
params.append("hideScreensharing", "");
|
|
||||||
}
|
|
||||||
url.hash = `#?${params.toString()}`;
|
url.hash = `#?${params.toString()}`;
|
||||||
|
|
||||||
// To use Element Call without touching room state, we create a virtual
|
// To use Element Call without touching room state, we create a virtual
|
||||||
|
@ -818,6 +816,7 @@ export class ElementCall extends Call {
|
||||||
this.messaging!.on(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
|
this.messaging!.on(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
|
||||||
this.messaging!.on(`action:${ElementWidgetActions.TileLayout}`, this.onTileLayout);
|
this.messaging!.on(`action:${ElementWidgetActions.TileLayout}`, this.onTileLayout);
|
||||||
this.messaging!.on(`action:${ElementWidgetActions.SpotlightLayout}`, this.onSpotlightLayout);
|
this.messaging!.on(`action:${ElementWidgetActions.SpotlightLayout}`, this.onSpotlightLayout);
|
||||||
|
this.messaging!.on(`action:${ElementWidgetActions.Screenshare}`, this.onScreenshare);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async performDisconnection(): Promise<void> {
|
protected async performDisconnection(): Promise<void> {
|
||||||
|
@ -831,8 +830,9 @@ export class ElementCall extends Call {
|
||||||
public setDisconnected() {
|
public setDisconnected() {
|
||||||
this.client.off(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
this.client.off(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
||||||
this.messaging!.off(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
|
this.messaging!.off(`action:${ElementWidgetActions.HangupCall}`, this.onHangup);
|
||||||
this.messaging!.on(`action:${ElementWidgetActions.TileLayout}`, this.onTileLayout);
|
this.messaging!.off(`action:${ElementWidgetActions.TileLayout}`, this.onTileLayout);
|
||||||
this.messaging!.on(`action:${ElementWidgetActions.SpotlightLayout}`, this.onSpotlightLayout);
|
this.messaging!.off(`action:${ElementWidgetActions.SpotlightLayout}`, this.onSpotlightLayout);
|
||||||
|
this.messaging!.off(`action:${ElementWidgetActions.Screenshare}`, this.onSpotlightLayout);
|
||||||
super.setDisconnected();
|
super.setDisconnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,4 +951,20 @@ export class ElementCall extends Call {
|
||||||
this.layout = Layout.Spotlight;
|
this.layout = Layout.Spotlight;
|
||||||
await this.messaging!.transport.reply(ev.detail, {}); // ack
|
await this.messaging!.transport.reply(ev.detail, {}); // ack
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onScreenshare = async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
if (PlatformPeg.get().supportsDesktopCapturer()) {
|
||||||
|
const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
|
||||||
|
const [source] = await finished;
|
||||||
|
|
||||||
|
await this.messaging!.transport.reply(ev.detail, {
|
||||||
|
failed: !source,
|
||||||
|
desktopCapturerSourceId: source,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.messaging!.transport.reply(ev.detail, {});
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ export enum ElementWidgetActions {
|
||||||
// Actions for switching layouts
|
// Actions for switching layouts
|
||||||
TileLayout = "io.element.tile_layout",
|
TileLayout = "io.element.tile_layout",
|
||||||
SpotlightLayout = "io.element.spotlight_layout",
|
SpotlightLayout = "io.element.spotlight_layout",
|
||||||
|
Screenshare = "io.element.screenshare",
|
||||||
|
|
||||||
OpenIntegrationManager = "integration_manager_open",
|
OpenIntegrationManager = "integration_manager_open",
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ import { WidgetMessagingStore } from "../../src/stores/widgets/WidgetMessagingSt
|
||||||
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../../src/stores/ActiveWidgetStore";
|
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../../src/stores/ActiveWidgetStore";
|
||||||
import { ElementWidgetActions } from "../../src/stores/widgets/ElementWidgetActions";
|
import { ElementWidgetActions } from "../../src/stores/widgets/ElementWidgetActions";
|
||||||
import SettingsStore from "../../src/settings/SettingsStore";
|
import SettingsStore from "../../src/settings/SettingsStore";
|
||||||
|
import Modal, { IHandle } from "../../src/Modal";
|
||||||
|
import PlatformPeg from "../../src/PlatformPeg";
|
||||||
|
|
||||||
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
|
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
|
||||||
[MediaDeviceKindEnum.AudioInput]: [
|
[MediaDeviceKindEnum.AudioInput]: [
|
||||||
|
@ -807,6 +809,69 @@ describe("ElementCall", () => {
|
||||||
call.off(CallEvent.Layout, onLayout);
|
call.off(CallEvent.Layout, onLayout);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("screensharing", () => {
|
||||||
|
it("passes source id if we can get it", async () => {
|
||||||
|
const sourceId = "source_id";
|
||||||
|
jest.spyOn(Modal, "createDialog").mockReturnValue(
|
||||||
|
{ finished: new Promise((r) => r([sourceId])) } as IHandle<any[]>,
|
||||||
|
);
|
||||||
|
jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(true);
|
||||||
|
|
||||||
|
await call.connect();
|
||||||
|
|
||||||
|
messaging.emit(
|
||||||
|
`action:${ElementWidgetActions.Screenshare}`,
|
||||||
|
new CustomEvent("widgetapirequest", { detail: {} }),
|
||||||
|
);
|
||||||
|
|
||||||
|
waitFor(() => {
|
||||||
|
expect(messaging!.transport.reply).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({}),
|
||||||
|
expect.objectContaining({ desktopCapturerSourceId: sourceId }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes failed if we couldn't get a source id", async () => {
|
||||||
|
jest.spyOn(Modal, "createDialog").mockReturnValue(
|
||||||
|
{ finished: new Promise((r) => r([null])) } as IHandle<any[]>,
|
||||||
|
);
|
||||||
|
jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(true);
|
||||||
|
|
||||||
|
await call.connect();
|
||||||
|
|
||||||
|
messaging.emit(
|
||||||
|
`action:${ElementWidgetActions.Screenshare}`,
|
||||||
|
new CustomEvent("widgetapirequest", { detail: {} }),
|
||||||
|
);
|
||||||
|
|
||||||
|
waitFor(() => {
|
||||||
|
expect(messaging!.transport.reply).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({}),
|
||||||
|
expect.objectContaining({ failed: true }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes an empty object if we don't support desktop capturer", async () => {
|
||||||
|
jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(false);
|
||||||
|
|
||||||
|
await call.connect();
|
||||||
|
|
||||||
|
messaging.emit(
|
||||||
|
`action:${ElementWidgetActions.Screenshare}`,
|
||||||
|
new CustomEvent("widgetapirequest", { detail: {} }),
|
||||||
|
);
|
||||||
|
|
||||||
|
waitFor(() => {
|
||||||
|
expect(messaging!.transport.reply).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({}),
|
||||||
|
expect.objectContaining({}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("ends the call immediately if we're the last participant to leave", async () => {
|
it("ends the call immediately if we're the last participant to leave", async () => {
|
||||||
await call.connect();
|
await call.connect();
|
||||||
const onDestroy = jest.fn();
|
const onDestroy = jest.fn();
|
||||||
|
|
Loading…
Reference in a new issue