Files
element-call/src/lotus/lotusFocus.ts
T

58 lines
2.3 KiB
TypeScript
Raw Normal View History

/*
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 IWidgetApiRequest } from "matrix-widget-api";
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { widget } from "../widget";
import { LotusWidgetActions } from "./lotusActions";
import { type ManualSpotlight } from "./lotusSpotlight";
/**
* Parse a `focus_participant` payload into a pin, `null` to clear, or
* `undefined` to leave the current pin alone (#30). Exported for tests.
*
* Mirror deafen's partial-payload semantics: a payload that OMITS `userId`
* must keep the current spotlight, not clear it. Only act when the key is
* actually present — an explicit `null` clears, a string pins that user. An
* optional `id` (EC media id `${userId}:${deviceId}`, as sent to the host in
* `io.lotus.call_state`) selects a specific device of that user.
*/
export function parseFocusPayload(
data: unknown,
): ManualSpotlight | null | undefined {
if (typeof data !== "object" || data === null || !("userId" in data))
return undefined;
const { userId, id } = data as { userId?: unknown; id?: unknown };
if (typeof userId !== "string") return null;
return { userId, id: typeof id === "string" ? id : null };
}
/**
* Handle the host's `io.lotus.focus_participant` toWidget action (#4): pin a
* participant to the spotlight by Matrix user id (and optionally media id), or
* clear it with `{ userId: null }`. This replaces cinny's old DOM `.click()`
* tile-selector hack with a real, layout-aware spotlight override.
*
* No effect unless the host actually sends the action, so registering the
* handler whenever we're a widget is safe. Returns a teardown function.
*/
export function startLotusFocus(vm: CallViewModel): () => void {
const w = widget;
if (!w) return () => undefined;
const handler = (ev: CustomEvent<IWidgetApiRequest>): void => {
// Always reply so the host transport doesn't time out.
2026-09-12 11:35:57 -04:00
w.api.transport.reply(ev.detail, {});
const target = parseFocusPayload(ev.detail.data);
if (target !== undefined) vm.setManualSpotlight(target);
};
w.lazyActions.on(LotusWidgetActions.FocusParticipant, handler);
2026-09-12 11:35:57 -04:00
return () => w.lazyActions.off(LotusWidgetActions.FocusParticipant, handler);
}