Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b0050e8bb | ||
|
|
41b45bef25 | ||
|
|
bb675fa50e |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lotusguild/element-call-embedded",
|
||||
"version": "0.25.0-lotus.21",
|
||||
"version": "0.25.0-lotus.22",
|
||||
"files": [
|
||||
"README.md",
|
||||
"LICENSE-AGPL-3.0",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/* [cinny #43] EC's screenshare button, floating in the frame's bottom-right
|
||||
corner while the host draws the rest of the call bar. */
|
||||
.floating {
|
||||
position: fixed;
|
||||
right: var(--cpd-space-4x);
|
||||
bottom: calc(env(safe-area-inset-bottom) + var(--cpd-space-4x));
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
Copyright 2026 Lotus Guild
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||
import { LotusFrameScreenshare } from "./LotusFrameScreenshare";
|
||||
import {
|
||||
lotusFrameScreenshareEnabled,
|
||||
lotusFrameScreenshareVisible$,
|
||||
parseFrameScreensharePayload,
|
||||
} from "./lotusControls";
|
||||
|
||||
// lotusFlag memoizes the URL params on first read, so set them once here.
|
||||
window.history.pushState(
|
||||
{},
|
||||
"",
|
||||
"/?lotusHostControls=1&lotusFrameScreenshare=1",
|
||||
);
|
||||
|
||||
const mockVm = (
|
||||
toggle: (() => void) | null,
|
||||
sharing = new BehaviorSubject(false),
|
||||
): CallViewModel =>
|
||||
({
|
||||
sharingScreen$: sharing,
|
||||
toggleScreenSharing: toggle,
|
||||
}) as unknown as CallViewModel;
|
||||
|
||||
const renderButton = (vm: CallViewModel): void => {
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<LotusFrameScreenshare vm={vm} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
describe("LotusFrameScreenshare", () => {
|
||||
it("is enabled by lotusHostControls + lotusFrameScreenshare", () => {
|
||||
expect(lotusFrameScreenshareEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it("toggles the share from a click inside the frame", async () => {
|
||||
const toggle = vi.fn();
|
||||
renderButton(mockVm(toggle));
|
||||
await userEvent.click(screen.getByTestId("lotus_frame_screenshare"));
|
||||
expect(toggle).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("follows the sharing state", () => {
|
||||
const sharing = new BehaviorSubject(false);
|
||||
renderButton(mockVm(vi.fn(), sharing));
|
||||
const button = screen.getByTestId("lotus_frame_screenshare");
|
||||
expect(button).toHaveAttribute("aria-checked", "false");
|
||||
act(() => sharing.next(true));
|
||||
expect(button).toHaveAttribute("aria-checked", "true");
|
||||
});
|
||||
|
||||
it("hides while the host's policy forbids sharing, unless a share is live", () => {
|
||||
const sharing = new BehaviorSubject(false);
|
||||
act(() => lotusFrameScreenshareVisible$.next(false));
|
||||
renderButton(mockVm(vi.fn(), sharing));
|
||||
expect(screen.queryByTestId("lotus_frame_screenshare")).toBeNull();
|
||||
act(() => sharing.next(true));
|
||||
expect(screen.getByTestId("lotus_frame_screenshare")).toBeInTheDocument();
|
||||
act(() => lotusFrameScreenshareVisible$.next(true));
|
||||
});
|
||||
|
||||
it("parses the set_frame_screenshare payload", () => {
|
||||
expect(parseFrameScreensharePayload({ visible: false })).toBe(false);
|
||||
expect(parseFrameScreensharePayload({ visible: true })).toBe(true);
|
||||
expect(parseFrameScreensharePayload({})).toBeUndefined();
|
||||
expect(parseFrameScreensharePayload(null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("renders nothing when screensharing is unavailable", () => {
|
||||
renderButton(mockVm(null));
|
||||
expect(screen.queryByTestId("lotus_frame_screenshare")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright 2026 Lotus Guild
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC } from "react";
|
||||
|
||||
import { ShareScreenButton } from "../button";
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||
import { useBehavior } from "../useBehavior";
|
||||
import {
|
||||
lotusFrameScreenshareEnabled,
|
||||
lotusFrameScreenshareVisible$,
|
||||
} from "./lotusControls";
|
||||
import styles from "./LotusFrameScreenshare.module.css";
|
||||
|
||||
/**
|
||||
* [cinny #43] EC's screenshare button, shown inside the frame when the host
|
||||
* can't start a share for us (`lotusFrameScreenshare`). The rest of EC's
|
||||
* footer stays hidden behind the host's call bar.
|
||||
*/
|
||||
export const LotusFrameScreenshare: FC<{ vm: CallViewModel }> = ({ vm }) => {
|
||||
const sharing = useBehavior(vm.sharingScreen$);
|
||||
const visible = useBehavior(lotusFrameScreenshareVisible$);
|
||||
if (!lotusFrameScreenshareEnabled() || !vm.toggleScreenSharing) return null;
|
||||
// Hidden by the host's call policy, but never while a share is live: the
|
||||
// user must still be able to stop it.
|
||||
if (!visible && !sharing) return null;
|
||||
return (
|
||||
<div className={styles.floating}>
|
||||
<ShareScreenButton
|
||||
size="md"
|
||||
enabled={sharing}
|
||||
onClick={vm.toggleScreenSharing}
|
||||
data-testid="lotus_frame_screenshare"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
/* [cinny #43] "Share your screen?" card, drawn inside the frame just above the
|
||||
host's call bar (the frame's bottom edge). */
|
||||
.prompt {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: calc(env(safe-area-inset-bottom) + var(--cpd-space-4x));
|
||||
transform: translateX(-50%);
|
||||
z-index: 2;
|
||||
box-sizing: border-box;
|
||||
width: max-content;
|
||||
max-width: min(360px, calc(100% - 2 * var(--cpd-space-2x)));
|
||||
padding: var(--cpd-space-4x);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--cpd-space-2x);
|
||||
background: var(--cpd-color-bg-canvas-default);
|
||||
border: 1px solid var(--cpd-color-border-interactive-secondary);
|
||||
border-radius: var(--cpd-space-3x);
|
||||
box-shadow: 0 8px 32px rgb(0 0 0 / 35%);
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: var(--cpd-space-2x);
|
||||
}
|
||||
|
||||
/* The call's picture-in-picture window is only ~158px tall: keep the card to
|
||||
the question and the two buttons. */
|
||||
@media (max-height: 240px) {
|
||||
.prompt {
|
||||
bottom: var(--cpd-space-2x);
|
||||
padding: var(--cpd-space-2x) var(--cpd-space-3x);
|
||||
gap: var(--cpd-space-1-5x);
|
||||
}
|
||||
.description {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2026 Lotus Guild
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||
import { LotusScreensharePrompt } from "./LotusScreensharePrompt";
|
||||
import {
|
||||
lotusFrameScreenshareEnabled,
|
||||
lotusFrameScreenshareVisible$,
|
||||
lotusScreensharePrompt$,
|
||||
shouldPromptScreenshare,
|
||||
} from "./lotusControls";
|
||||
|
||||
// lotusFlag memoizes the URL params on first read, so set them once here.
|
||||
window.history.pushState(
|
||||
{},
|
||||
"",
|
||||
"/?lotusHostControls=1&lotusFrameScreenshare=1",
|
||||
);
|
||||
|
||||
const mockVm = (
|
||||
toggle: (() => void) | null,
|
||||
sharing = new BehaviorSubject(false),
|
||||
): CallViewModel =>
|
||||
({
|
||||
sharingScreen$: sharing,
|
||||
toggleScreenSharing: toggle,
|
||||
}) as unknown as CallViewModel;
|
||||
|
||||
const renderPrompt = (vm: CallViewModel): void => {
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<LotusScreensharePrompt vm={vm} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
};
|
||||
const prompt = (): HTMLElement | null =>
|
||||
screen.queryByTestId("lotus_screenshare_prompt");
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
lotusScreensharePrompt$.next(false);
|
||||
lotusFrameScreenshareVisible$.next(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("LotusScreensharePrompt", () => {
|
||||
it("is enabled by lotusHostControls + lotusFrameScreenshare", () => {
|
||||
expect(lotusFrameScreenshareEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it("stays hidden until the host asks", () => {
|
||||
renderPrompt(mockVm(vi.fn()));
|
||||
expect(prompt()).toBeNull();
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
expect(prompt()).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("alertdialog", { name: "Share your screen?" }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("focuses Share, and Share starts the share and closes", async () => {
|
||||
const toggle = vi.fn();
|
||||
renderPrompt(mockVm(toggle));
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
const share = screen.getByTestId("lotus_screenshare_prompt_share");
|
||||
expect(share).toHaveFocus();
|
||||
await userEvent.click(share);
|
||||
expect(toggle).toHaveBeenCalledTimes(1);
|
||||
expect(prompt()).toBeNull();
|
||||
});
|
||||
|
||||
it("Cancel and Escape close without sharing", async () => {
|
||||
const toggle = vi.fn();
|
||||
renderPrompt(mockVm(toggle));
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
await userEvent.click(screen.getByRole("button", { name: "Cancel" }));
|
||||
expect(prompt()).toBeNull();
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
await userEvent.keyboard("{Escape}");
|
||||
expect(prompt()).toBeNull();
|
||||
expect(toggle).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("closes when a share starts some other way", () => {
|
||||
const sharing = new BehaviorSubject(false);
|
||||
renderPrompt(mockVm(vi.fn(), sharing));
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
act(() => sharing.next(true));
|
||||
expect(prompt()).toBeNull();
|
||||
});
|
||||
|
||||
it("closes when the room's policy stops allowing screensharing", () => {
|
||||
renderPrompt(mockVm(vi.fn()));
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
act(() => lotusFrameScreenshareVisible$.next(false));
|
||||
expect(prompt()).toBeNull();
|
||||
});
|
||||
|
||||
it("renders nothing when screensharing is unavailable", () => {
|
||||
renderPrompt(mockVm(null));
|
||||
act(() => lotusScreensharePrompt$.next(true));
|
||||
expect(prompt()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("shouldPromptScreenshare", () => {
|
||||
const ok = { enabled: true, allowed: true, canShare: true, sharing: false };
|
||||
it("prompts only in prompt mode, when allowed, able and not sharing", () => {
|
||||
expect(shouldPromptScreenshare(ok)).toBe(true);
|
||||
expect(shouldPromptScreenshare({ ...ok, enabled: false })).toBe(false);
|
||||
expect(shouldPromptScreenshare({ ...ok, allowed: false })).toBe(false);
|
||||
expect(shouldPromptScreenshare({ ...ok, canShare: false })).toBe(false);
|
||||
expect(shouldPromptScreenshare({ ...ok, sharing: true })).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2026 Lotus Guild
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC, useCallback, useEffect, useId, useRef } from "react";
|
||||
import { Button, Text } from "@vector-im/compound-web";
|
||||
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||
import { useBehavior } from "../useBehavior";
|
||||
import {
|
||||
lotusFrameScreenshareEnabled,
|
||||
lotusFrameScreenshareVisible$,
|
||||
lotusScreensharePrompt$,
|
||||
} from "./lotusControls";
|
||||
import styles from "./LotusScreensharePrompt.module.css";
|
||||
|
||||
/**
|
||||
* [cinny #43] "Share your screen?" asked inside the call frame, for hosts that
|
||||
* can't hand the user's click to this frame (Firefox, Safari, WebKitGTK). The
|
||||
* host's call-bar button sends `io.lotus.prompt_screenshare`; the Share click
|
||||
* lands here, so getDisplayMedia runs with this frame's own activation. Same
|
||||
* two clicks as the host's own confirm on Chromium.
|
||||
*/
|
||||
export const LotusScreensharePrompt: FC<{ vm: CallViewModel }> = ({ vm }) => {
|
||||
const open = useBehavior(lotusScreensharePrompt$);
|
||||
const sharing = useBehavior(vm.sharingScreen$);
|
||||
const allowed = useBehavior(lotusFrameScreenshareVisible$);
|
||||
const shareRef = useRef<HTMLButtonElement>(null);
|
||||
const titleId = useId();
|
||||
const descriptionId = useId();
|
||||
|
||||
const close = useCallback(() => lotusScreensharePrompt$.next(false), []);
|
||||
|
||||
// Stale once a share is running (started some other way) or the room's
|
||||
// policy stops allowing it.
|
||||
useEffect(() => {
|
||||
if (open && (sharing || !allowed)) close();
|
||||
}, [open, sharing, allowed, close]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) shareRef.current?.focus();
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return undefined;
|
||||
const onKeyDown = (e: KeyboardEvent): void => {
|
||||
if (e.key !== "Escape") return;
|
||||
e.preventDefault();
|
||||
close();
|
||||
};
|
||||
window.addEventListener("keydown", onKeyDown, true);
|
||||
return (): void => window.removeEventListener("keydown", onKeyDown, true);
|
||||
}, [open, close]);
|
||||
|
||||
const toggle = vm.toggleScreenSharing;
|
||||
if (!open || !lotusFrameScreenshareEnabled() || !toggle) return null;
|
||||
|
||||
const onShare = (): void => {
|
||||
close();
|
||||
// Synchronously inside this click: getDisplayMedia needs its activation.
|
||||
toggle();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.prompt}
|
||||
role="alertdialog"
|
||||
aria-labelledby={titleId}
|
||||
aria-describedby={descriptionId}
|
||||
data-testid="lotus_screenshare_prompt"
|
||||
>
|
||||
<Text
|
||||
as="h2"
|
||||
id={titleId}
|
||||
size="md"
|
||||
weight="semibold"
|
||||
className={styles.title}
|
||||
>
|
||||
Share your screen?
|
||||
</Text>
|
||||
<Text id={descriptionId} size="md" className={styles.description}>
|
||||
Your screen will be visible to everyone in this call.
|
||||
</Text>
|
||||
<div className={styles.buttons}>
|
||||
<Button
|
||||
ref={shareRef}
|
||||
size="md"
|
||||
onClick={onShare}
|
||||
data-testid="lotus_screenshare_prompt_share"
|
||||
>
|
||||
Share
|
||||
</Button>
|
||||
<Button size="md" kind="secondary" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -33,6 +33,7 @@ describe("LotusWidgetActions", () => {
|
||||
LotusWidgetActions.SetScreenshare,
|
||||
LotusWidgetActions.SetHotkeys,
|
||||
LotusWidgetActions.SetFrameScreenshare,
|
||||
LotusWidgetActions.PromptScreenshare,
|
||||
];
|
||||
|
||||
expect(new Set(LOTUS_TO_WIDGET_ACTIONS)).toEqual(new Set(expectedToWidget));
|
||||
|
||||
@@ -92,6 +92,12 @@ export enum LotusWidgetActions {
|
||||
* policy forbids screensharing (cinny #43). Defaults to visible.
|
||||
*/
|
||||
SetFrameScreenshare = "io.lotus.set_frame_screenshare",
|
||||
/**
|
||||
* toWidget: `{}` — show the "Share your screen?" prompt inside this frame
|
||||
* (cinny #43). For hosts that can't delegate the user's click: the prompt's
|
||||
* Share button is clicked here, so getDisplayMedia gets its activation.
|
||||
*/
|
||||
PromptScreenshare = "io.lotus.prompt_screenshare",
|
||||
/**
|
||||
* fromWidget: a watched key went down/up here, or this window's focus
|
||||
* changed; see `LotusHotkeyReport` (cinny #43).
|
||||
@@ -99,7 +105,8 @@ export enum LotusWidgetActions {
|
||||
Hotkey = "io.lotus.hotkey",
|
||||
/**
|
||||
* fromWidget: `{ screensharing: boolean, layout: "grid" | "spotlight" | null,
|
||||
* screenshareAction: true, hotkeys: true, frameScreenshare: boolean }`
|
||||
* screenshareAction: true, hotkeys: true, frameScreenshare: false,
|
||||
* screensharePrompt: boolean, screensharePromptOpen: boolean }`
|
||||
* whenever either changes (cinny #43), so the host stops reading EC's DOM for
|
||||
* them. Its arrival also tells the host this fork supports the actions above.
|
||||
*/
|
||||
@@ -125,4 +132,5 @@ export const LOTUS_TO_WIDGET_ACTIONS: LotusWidgetActions[] = [
|
||||
LotusWidgetActions.SetScreenshare,
|
||||
LotusWidgetActions.SetHotkeys,
|
||||
LotusWidgetActions.SetFrameScreenshare,
|
||||
LotusWidgetActions.PromptScreenshare,
|
||||
];
|
||||
|
||||
@@ -26,23 +26,39 @@ export interface LotusControlsState {
|
||||
/** This fork handles `io.lotus.set_hotkeys` (always true). */
|
||||
hotkeys: true;
|
||||
/**
|
||||
* EC's own screenshare button is shown inside the frame (see
|
||||
* `lotusFrameScreenshare`), so the host should hide its own.
|
||||
* EC's own screenshare button is shown inside the frame, so the host should
|
||||
* hide its own. Always false since the prompt replaced that button; kept so
|
||||
* a host that still reads it doesn't hide its button.
|
||||
*/
|
||||
frameScreenshare: boolean;
|
||||
frameScreenshare: false;
|
||||
/**
|
||||
* The host should start a share with `io.lotus.prompt_screenshare` (the
|
||||
* in-frame prompt) instead of `set_screenshare`: see
|
||||
* `lotusFrameScreenshareEnabled`.
|
||||
*/
|
||||
screensharePrompt: boolean;
|
||||
/**
|
||||
* The prompt is showing. The host must let clicks through to this frame
|
||||
* meanwhile (e.g. its picture-in-picture overlay).
|
||||
*/
|
||||
screensharePromptOpen: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* [cinny #43] `lotusFrameScreenshare=1` (with `lotusHostControls`): the host
|
||||
* can't hand the user's click into this frame (no Capability Delegation:
|
||||
* Firefox, Safari, WebKitGTK), and `getDisplayMedia` needs that click here.
|
||||
* So EC's own screenshare button stays visible in the frame, where the click
|
||||
* is native, instead of the host clicking it through the DOM.
|
||||
* The host's call-bar button then asks for `io.lotus.prompt_screenshare`, and
|
||||
* the "Share your screen?" prompt is drawn inside this frame, where the Share
|
||||
* click is native.
|
||||
*/
|
||||
export function lotusFrameScreenshareEnabled(): boolean {
|
||||
return lotusFlag("lotusHostControls") && lotusFlag("lotusFrameScreenshare");
|
||||
}
|
||||
|
||||
/** Whether the in-frame "Share your screen?" prompt is showing. */
|
||||
export const lotusScreensharePrompt$ = new BehaviorSubject(false);
|
||||
|
||||
/**
|
||||
* Host-controlled visibility of the in-frame screenshare button
|
||||
* (`io.lotus.set_frame_screenshare`): the host hides it while the room's
|
||||
@@ -50,6 +66,21 @@ export function lotusFrameScreenshareEnabled(): boolean {
|
||||
*/
|
||||
export const lotusFrameScreenshareVisible$ = new BehaviorSubject(true);
|
||||
|
||||
/**
|
||||
* Whether `io.lotus.prompt_screenshare` should show the prompt: only in
|
||||
* prompt mode, where the room's policy allows sharing, EC can share, and no
|
||||
* share is running (stopping goes through `set_screenshare`, which needs no
|
||||
* click). Exported for tests.
|
||||
*/
|
||||
export function shouldPromptScreenshare(s: {
|
||||
enabled: boolean;
|
||||
allowed: boolean;
|
||||
canShare: boolean;
|
||||
sharing: boolean;
|
||||
}): boolean {
|
||||
return s.enabled && s.allowed && s.canShare && !s.sharing;
|
||||
}
|
||||
|
||||
/** `{ visible }` payload → the visibility to apply, or undefined. Exported for tests. */
|
||||
export function parseFrameScreensharePayload(
|
||||
data: unknown,
|
||||
@@ -138,30 +169,51 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
if (visible !== undefined) lotusFrameScreenshareVisible$.next(visible);
|
||||
};
|
||||
|
||||
const onPromptScreenshare = (ev: CustomEvent<IWidgetApiRequest>): void => {
|
||||
w.api.transport.reply(ev.detail, {});
|
||||
if (
|
||||
shouldPromptScreenshare({
|
||||
enabled: lotusFrameScreenshareEnabled(),
|
||||
allowed: lotusFrameScreenshareVisible$.value,
|
||||
canShare: !!vm.toggleScreenSharing,
|
||||
sharing: vm.sharingScreen$.value,
|
||||
})
|
||||
) {
|
||||
lotusScreensharePrompt$.next(true);
|
||||
}
|
||||
};
|
||||
|
||||
w.lazyActions.on(LotusWidgetActions.SetScreenshare, onSetScreenshare);
|
||||
w.lazyActions.on(
|
||||
LotusWidgetActions.SetFrameScreenshare,
|
||||
onSetFrameScreenshare,
|
||||
);
|
||||
w.lazyActions.on(LotusWidgetActions.PromptScreenshare, onPromptScreenshare);
|
||||
|
||||
const sub: Subscription = combineLatest([
|
||||
vm.sharingScreen$,
|
||||
vm.layoutSwitchVm$.pipe(
|
||||
switchMap((l) => (l ? l.layout$ : of<LayoutMode | null>(null))),
|
||||
),
|
||||
lotusScreensharePrompt$,
|
||||
])
|
||||
.pipe(
|
||||
map(
|
||||
([screensharing, layout]): LotusControlsState => ({
|
||||
([screensharing, layout, promptOpen]): LotusControlsState => ({
|
||||
screensharing,
|
||||
layout,
|
||||
screenshareAction: true,
|
||||
hotkeys: true,
|
||||
frameScreenshare: lotusFrameScreenshareEnabled(),
|
||||
frameScreenshare: false,
|
||||
screensharePrompt: lotusFrameScreenshareEnabled(),
|
||||
screensharePromptOpen: promptOpen,
|
||||
}),
|
||||
),
|
||||
distinctUntilChanged(
|
||||
(a, b) => a.screensharing === b.screensharing && a.layout === b.layout,
|
||||
(a, b) =>
|
||||
a.screensharing === b.screensharing &&
|
||||
a.layout === b.layout &&
|
||||
a.screensharePromptOpen === b.screensharePromptOpen,
|
||||
),
|
||||
)
|
||||
.subscribe((state) => {
|
||||
@@ -178,5 +230,10 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
LotusWidgetActions.SetFrameScreenshare,
|
||||
onSetFrameScreenshare,
|
||||
);
|
||||
w.lazyActions.off(
|
||||
LotusWidgetActions.PromptScreenshare,
|
||||
onPromptScreenshare,
|
||||
);
|
||||
lotusScreensharePrompt$.next(false);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import { startLotusCallState } from "../lotus/lotusCallState";
|
||||
import { startLotusFocus } from "../lotus/lotusFocus";
|
||||
import { startLotusControls } from "../lotus/lotusControls";
|
||||
import { startLotusHotkeys } from "../lotus/lotusHotkeys";
|
||||
import { LotusFrameScreenshare } from "../lotus/LotusFrameScreenshare";
|
||||
import { LotusScreensharePrompt } from "../lotus/LotusScreensharePrompt";
|
||||
import { startLotusMicLevel } from "../lotus/lotusMicLevel";
|
||||
import { startLotusAudioInject } from "../lotus/lotusAudioInject";
|
||||
import { startLotusQuality } from "../lotus/lotusQuality";
|
||||
@@ -693,7 +693,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
{earpieceOverlay}
|
||||
<ReactionsOverlay vm={vm} />
|
||||
{footer}
|
||||
<LotusFrameScreenshare vm={vm} />
|
||||
<LotusScreensharePrompt vm={vm} />
|
||||
{showModals && (
|
||||
<>
|
||||
<RageshakeRequestModal {...rageshakeRequestModalProps} />
|
||||
|
||||
Reference in New Issue
Block a user