feat(lotus): in-frame screenshare button where the host can't delegate (cinny #43)
Firefox, Safari and WebKitGTK have no Capability Delegation, so the host
can't start a share from its call bar: getDisplayMedia needs the click in
this frame. Until now the host clicked EC's hidden footer button through
the DOM, which only works while the frame is same-origin.
With lotusHostControls + lotusFrameScreenshare the fork now shows EC's own
screenshare button (floating bottom-right, footer still hidden), so the
click is native. controls_state reports frameScreenshare so the host hides
its own button. New toWidget io.lotus.set_frame_screenshare { visible }
lets the host hide it while the room's call policy forbids screensharing
(never while a share is live, so it can still be stopped).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dfb72bd9a5
commit
0f93368a7e
@@ -6,13 +6,13 @@ Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type IWidgetApiRequest } from "matrix-widget-api";
|
||||
import { combineLatest, of, type Subscription } from "rxjs";
|
||||
import { BehaviorSubject, combineLatest, of, type Subscription } from "rxjs";
|
||||
import { distinctUntilChanged, map, switchMap } from "rxjs/operators";
|
||||
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||
import { type LayoutMode } from "../state/LayoutSwitchViewModel";
|
||||
import { widget } from "../widget";
|
||||
import { LotusWidgetActions, lotusSendToHost } from "./lotusWidget";
|
||||
import { LotusWidgetActions, lotusFlag, lotusSendToHost } from "./lotusWidget";
|
||||
|
||||
/** Window event the reactions button listens for (see ReactionToggleButton). */
|
||||
export const LOTUS_TOGGLE_REACTIONS_EVENT = "lotus:toggle-reactions";
|
||||
@@ -25,6 +25,38 @@ export interface LotusControlsState {
|
||||
screenshareAction: true;
|
||||
/** 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.
|
||||
*/
|
||||
frameScreenshare: 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.
|
||||
*/
|
||||
export function lotusFrameScreenshareEnabled(): boolean {
|
||||
return lotusFlag("lotusHostControls") && lotusFlag("lotusFrameScreenshare");
|
||||
}
|
||||
|
||||
/**
|
||||
* Host-controlled visibility of the in-frame screenshare button
|
||||
* (`io.lotus.set_frame_screenshare`): the host hides it while the room's
|
||||
* call policy forbids screensharing, as it does its own call-bar button.
|
||||
*/
|
||||
export const lotusFrameScreenshareVisible$ = new BehaviorSubject(true);
|
||||
|
||||
/** `{ visible }` payload → the visibility to apply, or undefined. Exported for tests. */
|
||||
export function parseFrameScreensharePayload(
|
||||
data: unknown,
|
||||
): boolean | undefined {
|
||||
if (typeof data !== "object" || data === null) return undefined;
|
||||
const { visible } = data as { visible?: unknown };
|
||||
return typeof visible === "boolean" ? visible : undefined;
|
||||
}
|
||||
|
||||
/** `{ layout }` payload → a layout mode, or undefined if invalid. Exported for tests. */
|
||||
@@ -100,7 +132,17 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
w.lazyActions.on(LotusWidgetActions.SetLayout, onSetLayout);
|
||||
w.lazyActions.on(LotusWidgetActions.OpenSettings, onOpenSettings);
|
||||
w.lazyActions.on(LotusWidgetActions.ToggleReactions, onToggleReactions);
|
||||
const onSetFrameScreenshare = (ev: CustomEvent<IWidgetApiRequest>): void => {
|
||||
w.api.transport.reply(ev.detail, {});
|
||||
const visible = parseFrameScreensharePayload(ev.detail.data);
|
||||
if (visible !== undefined) lotusFrameScreenshareVisible$.next(visible);
|
||||
};
|
||||
|
||||
w.lazyActions.on(LotusWidgetActions.SetScreenshare, onSetScreenshare);
|
||||
w.lazyActions.on(
|
||||
LotusWidgetActions.SetFrameScreenshare,
|
||||
onSetFrameScreenshare,
|
||||
);
|
||||
|
||||
const sub: Subscription = combineLatest([
|
||||
vm.sharingScreen$,
|
||||
@@ -115,6 +157,7 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
layout,
|
||||
screenshareAction: true,
|
||||
hotkeys: true,
|
||||
frameScreenshare: lotusFrameScreenshareEnabled(),
|
||||
}),
|
||||
),
|
||||
distinctUntilChanged(
|
||||
@@ -131,5 +174,9 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
w.lazyActions.off(LotusWidgetActions.OpenSettings, onOpenSettings);
|
||||
w.lazyActions.off(LotusWidgetActions.ToggleReactions, onToggleReactions);
|
||||
w.lazyActions.off(LotusWidgetActions.SetScreenshare, onSetScreenshare);
|
||||
w.lazyActions.off(
|
||||
LotusWidgetActions.SetFrameScreenshare,
|
||||
onSetFrameScreenshare,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user