From cb03aac4cf719cdc4ca6b0de4525f246cc1e2584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 14 Jul 2023 22:51:24 +0200 Subject: [PATCH] Switch to the new `session` API for screen-sharing (#11266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/electron/electron/pull/30702 - this has the benefit of the js-sdk and LiveKit not having to add custom logic for Electron Signed-off-by: Šimon Brandner --- .../elements/DesktopCapturerSourcePicker.tsx | 9 +-- src/components/views/voip/LegacyCallView.tsx | 15 +--- src/models/Call.ts | 26 ------ src/stores/widgets/ElementWidgetActions.ts | 13 --- test/models/Call-test.ts | 79 ------------------- 5 files changed, 5 insertions(+), 137 deletions(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index fd9e019ee6..c5aaa1ce1b 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -79,10 +79,10 @@ export class ExistingSource extends React.Component { export interface PickerIState { selectedTab: Tabs; sources: Array; - selectedSource: DesktopCapturerSource | null; + selectedSource?: DesktopCapturerSource; } export interface PickerIProps { - onFinished(sourceId?: string): void; + onFinished(source?: DesktopCapturerSource): void; } type TabId = "screen" | "window"; @@ -96,7 +96,6 @@ export default class DesktopCapturerSourcePicker extends React.Component { - this.props.onFinished(this.state.selectedSource?.id); + this.props.onFinished(this.state.selectedSource); }; private onTabChange = (): void => { - this.setState({ selectedSource: null }); + this.setState({ selectedSource: undefined }); }; private onCloseClick = (): void => { diff --git a/src/components/views/voip/LegacyCallView.tsx b/src/components/views/voip/LegacyCallView.tsx index 4d90539708..b61a0a8dda 100644 --- a/src/components/views/voip/LegacyCallView.tsx +++ b/src/components/views/voip/LegacyCallView.tsx @@ -30,12 +30,9 @@ import VideoFeed from "./VideoFeed"; import RoomAvatar from "../avatars/RoomAvatar"; import AccessibleButton from "../elements/AccessibleButton"; import { avatarUrlForMember } from "../../../Avatar"; -import DesktopCapturerSourcePicker from "../elements/DesktopCapturerSourcePicker"; -import Modal from "../../../Modal"; import LegacyCallViewSidebar from "./LegacyCallViewSidebar"; import LegacyCallViewHeader from "./LegacyCallView/LegacyCallViewHeader"; import LegacyCallViewButtons from "./LegacyCallView/LegacyCallViewButtons"; -import PlatformPeg from "../../../PlatformPeg"; import { ActionPayload } from "../../../dispatcher/payloads"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; @@ -289,17 +286,7 @@ export default class LegacyCallView extends React.Component { if (this.state.screensharing) { isScreensharing = await this.props.call.setScreensharingEnabled(false); } else { - if (PlatformPeg.get()?.supportsDesktopCapturer()) { - const { finished } = Modal.createDialog(DesktopCapturerSourcePicker); - const [source] = await finished; - if (!source) return; - - isScreensharing = await this.props.call.setScreensharingEnabled(true, { - desktopCapturerSourceId: source, - }); - } else { - isScreensharing = await this.props.call.setScreensharingEnabled(true); - } + isScreensharing = await this.props.call.setScreensharingEnabled(true); } this.setState({ diff --git a/src/models/Call.ts b/src/models/Call.ts index c87f0e6f8e..cc0b08466b 100644 --- a/src/models/Call.ts +++ b/src/models/Call.ts @@ -48,10 +48,7 @@ import { ElementWidgetActions } from "../stores/widgets/ElementWidgetActions"; import WidgetStore from "../stores/WidgetStore"; import { WidgetMessagingStore, WidgetMessagingStoreEvent } from "../stores/widgets/WidgetMessagingStore"; import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../stores/ActiveWidgetStore"; -import PlatformPeg from "../PlatformPeg"; import { getCurrentLanguage } from "../languageHandler"; -import DesktopCapturerSourcePicker from "../components/views/elements/DesktopCapturerSourcePicker"; -import Modal from "../Modal"; import { FontWatcher } from "../settings/watchers/FontWatcher"; import { PosthogAnalytics } from "../PosthogAnalytics"; @@ -762,7 +759,6 @@ 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.ScreenshareRequest}`, this.onScreenshareRequest); } protected async performDisconnection(): Promise { @@ -777,7 +773,6 @@ 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.ScreenshareRequest}`, this.onScreenshareRequest); super.setDisconnected(); this.groupCall.enteredViaAnotherSession = false; } @@ -878,25 +873,4 @@ export class ElementCall extends Call { this.layout = Layout.Spotlight; await this.messaging!.transport.reply(ev.detail, {}); // ack }; - - private onScreenshareRequest = async (ev: CustomEvent): Promise => { - ev.preventDefault(); - - if (PlatformPeg.get()?.supportsDesktopCapturer()) { - await this.messaging!.transport.reply(ev.detail, { pending: true }); - - const { finished } = Modal.createDialog(DesktopCapturerSourcePicker); - const [source] = await finished; - - 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, { pending: false }); - } - }; } diff --git a/src/stores/widgets/ElementWidgetActions.ts b/src/stores/widgets/ElementWidgetActions.ts index 1d0437a2ce..36b6e2b6fc 100644 --- a/src/stores/widgets/ElementWidgetActions.ts +++ b/src/stores/widgets/ElementWidgetActions.ts @@ -27,19 +27,6 @@ export enum ElementWidgetActions { 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", diff --git a/test/models/Call-test.ts b/test/models/Call-test.ts index 9eb8d1ed0d..110121dc2b 100644 --- a/test/models/Call-test.ts +++ b/test/models/Call-test.ts @@ -39,8 +39,6 @@ import { WidgetMessagingStore } from "../../src/stores/widgets/WidgetMessagingSt import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../../src/stores/ActiveWidgetStore"; import { ElementWidgetActions } from "../../src/stores/widgets/ElementWidgetActions"; import SettingsStore from "../../src/settings/SettingsStore"; -import Modal, { IHandle } from "../../src/Modal"; -import PlatformPeg from "../../src/PlatformPeg"; import { PosthogAnalytics } from "../../src/PosthogAnalytics"; jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({ @@ -947,83 +945,6 @@ describe("ElementCall", () => { 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); - jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(true); - - await call.connect(); - - messaging.emit( - `action:${ElementWidgetActions.ScreenshareRequest}`, - new CustomEvent("widgetapirequest", { detail: {} }), - ); - - await waitFor(() => { - expect(messaging!.transport.reply).toHaveBeenCalledWith( - expect.objectContaining({}), - expect.objectContaining({ pending: true }), - ); - }); - - await waitFor(() => { - expect(messaging!.transport.send).toHaveBeenCalledWith( - "io.element.screenshare_start", - expect.objectContaining({ desktopCapturerSourceId: sourceId }), - ); - }); - }); - - 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); - jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(true); - - await call.connect(); - - messaging.emit( - `action:${ElementWidgetActions.ScreenshareRequest}`, - new CustomEvent("widgetapirequest", { detail: {} }), - ); - - await waitFor(() => { - expect(messaging!.transport.reply).toHaveBeenCalledWith( - expect.objectContaining({}), - expect.objectContaining({ pending: true }), - ); - }); - - await waitFor(() => { - expect(messaging!.transport.send).toHaveBeenCalledWith( - "io.element.screenshare_stop", - expect.objectContaining({}), - ); - }); - }); - - 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.ScreenshareRequest}`, - new CustomEvent("widgetapirequest", { detail: {} }), - ); - - await waitFor(() => { - expect(messaging!.transport.reply).toHaveBeenCalledWith( - expect.objectContaining({}), - expect.objectContaining({ pending: false }), - ); - }); - }); - }); - it("ends the call immediately if we're the last participant to leave", async () => { await call.connect(); const onDestroy = jest.fn();