feat(call): drive layout / settings / reactions over the widget API (#43)
Once the fork reports io.lotus.controls_state (element-call v0.25.0-lotus.14), the call bar's Spotlight/Grid View, Reactions and Settings send io.lotus.set_layout / toggle_reactions / open_settings instead of clicking Element Call's hidden buttons, and the call bar's screenshare and layout state come from that report instead of a MutationObserver on EC's DOM. Older forks never send the report and keep the DOM path. Screensharing itself is still started/stopped via EC's button: it needs the user's click to reach the frame (Capability Delegation, step 3 of #43). Verified in a two-person local call (Synapse + LiveKit) against a locally built fork: controls_state arrives on join; Spotlight View / Grid View flip EC's layout and the reported layout; Reactions opens EC's reactions menu; Settings opens EC's settings modal; the four actions go over postMessage; starting and stopping a screenshare flips the host's button via the report. 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
a932b1999a
commit
a0d1c565d0
@@ -330,10 +330,32 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
}
|
||||
|
||||
// [Gitea #43] Set once the fork reports io.lotus.controls_state: from then on
|
||||
// layout / settings / reactions go over the widget API and screenshare +
|
||||
// layout state come from that report, not from EC's DOM. Older forks never
|
||||
// send it and keep the DOM path below.
|
||||
private forkControls = false;
|
||||
|
||||
/** [Gitea #43] The fork's `io.lotus.controls_state` report. */
|
||||
public onControlsState(data: unknown) {
|
||||
if (typeof data !== 'object' || data === null) return;
|
||||
const { screensharing, layout } = data as { screensharing?: unknown; layout?: unknown };
|
||||
this.forkControls = true;
|
||||
this.applyControls(
|
||||
typeof screensharing === 'boolean' ? screensharing : this.screenshare,
|
||||
layout === 'spotlight' || layout === 'grid' ? layout === 'spotlight' : this.spotlight,
|
||||
);
|
||||
}
|
||||
|
||||
private onControlMutation() {
|
||||
const wasScreensharing = this.screenshare;
|
||||
if (this.forkControls) return;
|
||||
const screenshare: boolean = this.screenshareButton?.getAttribute('data-kind') === 'primary';
|
||||
const spotlight: boolean = this.spotlightButton?.checked ?? false;
|
||||
this.applyControls(screenshare, spotlight);
|
||||
}
|
||||
|
||||
private applyControls(screenshare: boolean, spotlight: boolean) {
|
||||
const wasScreensharing = this.screenshare;
|
||||
|
||||
// C-M6: when a screenshare stops, clear the screenshare-audio mute so a
|
||||
// later screenshare doesn't start pre-muted.
|
||||
@@ -436,6 +458,12 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
|
||||
public toggleSpotlight() {
|
||||
if (this.forkControls) {
|
||||
this.sendForkAction('io.lotus.set_layout', {
|
||||
layout: this.spotlight ? 'grid' : 'spotlight',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.spotlight) {
|
||||
this.gridButton?.click();
|
||||
return;
|
||||
@@ -444,13 +472,26 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
}
|
||||
|
||||
public toggleReactions() {
|
||||
if (this.forkControls) {
|
||||
this.sendForkAction('io.lotus.toggle_reactions', {});
|
||||
return;
|
||||
}
|
||||
this.reactionsButton?.click();
|
||||
}
|
||||
|
||||
public toggleSettings() {
|
||||
if (this.forkControls) {
|
||||
// Same as clicking EC's settings button: opens the modal.
|
||||
this.sendForkAction('io.lotus.open_settings', { open: true });
|
||||
return;
|
||||
}
|
||||
this.settingsButton?.click();
|
||||
}
|
||||
|
||||
private sendForkAction(action: string, data: Record<string, unknown>): void {
|
||||
this.call.transport.send(action, data).catch(() => undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus a specific participant's camera tile in Element Call.
|
||||
*
|
||||
|
||||
@@ -417,6 +417,13 @@ export class CallEmbed {
|
||||
}
|
||||
}),
|
||||
);
|
||||
// [Gitea #43] Screenshare + layout state from the fork; also switches the
|
||||
// call bar's layout / settings / reactions buttons to widget actions.
|
||||
this.disposables.push(
|
||||
this.listenAction('io.lotus.controls_state', (evt) => {
|
||||
this.control.onControlsState((evt.detail as { data?: unknown } | undefined)?.data);
|
||||
}),
|
||||
);
|
||||
this.disposables.push(
|
||||
this.listenAction('io.lotus.call_state', (evt) => {
|
||||
const data = (evt.detail as { data?: { participants?: unknown } } | undefined)?.data;
|
||||
|
||||
Reference in New Issue
Block a user