/* 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): void => { // Always reply so the host transport doesn't time out. w.api.transport.reply(ev.detail, {}); const target = parseFocusPayload(ev.detail.data); if (target !== undefined) vm.setManualSpotlight(target); }; w.lazyActions.on(LotusWidgetActions.FocusParticipant, handler); return () => w.lazyActions.off(LotusWidgetActions.FocusParticipant, handler); }