fix(calls): "Focus camera" toggles to "Unfocus"; pin cleared on dispose; drop dead setPipMode
clearFocusParticipant() had no callers, so a spotlight pin was permanent. CallControl now tracks focusedUserId, the member menu toggles, and dispose() clears the pin. Removes _pipMode/setPipMode (never read). Fixes #56 Fixes #59 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -33,7 +33,10 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
// re-observe pass so a busy EC re-render doesn't thrash the control observer.
|
||||
private bodyMutationTimer?: ReturnType<typeof setTimeout>;
|
||||
|
||||
private _pipMode = false;
|
||||
// [Gitea #56] Tracks the participant currently pinned via focusCameraParticipant(),
|
||||
// so callers (MemberGlance) can render a "Focus camera" / "Unfocus camera" toggle
|
||||
// instead of a one-way pin with no way back. null == no manual pin (speaker-follows).
|
||||
private _focusedUserId: string | null = null;
|
||||
|
||||
// C-M3: last quality payload requested via setQuality(). Held so we can (re)send
|
||||
// it once joined (io.lotus.set_quality must not be sent before call-join — a
|
||||
@@ -422,10 +425,6 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
this.spotlightButton?.click();
|
||||
}
|
||||
|
||||
public setPipMode(pip: boolean) {
|
||||
this._pipMode = pip;
|
||||
}
|
||||
|
||||
public toggleReactions() {
|
||||
this.reactionsButton?.click();
|
||||
}
|
||||
@@ -446,17 +445,29 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
* participant has their camera off and EC didn't render a video tile for
|
||||
* them yet).
|
||||
*/
|
||||
public get focusedUserId(): string | null {
|
||||
return this._focusedUserId;
|
||||
}
|
||||
|
||||
public focusCameraParticipant(userId: string): void {
|
||||
// [lotus #4] Pin the participant via the fork's widget action instead of
|
||||
// DOM-poking tiles. EC's layout honors it — including surfacing the camera
|
||||
// alongside a screenshare (A5) — and it's version-stable. The fork always
|
||||
// acks, so the promise resolves regardless.
|
||||
this._focusedUserId = userId;
|
||||
this.call.transport.send('io.lotus.focus_participant', { userId }).catch(() => undefined);
|
||||
// [Gitea #56] Notify state-update listeners so the menu can flip to "Unfocus camera".
|
||||
this.emitStateUpdate();
|
||||
}
|
||||
|
||||
/** [lotus #4] Clear any manual spotlight pin and return to speaker-follows. */
|
||||
public clearFocusParticipant(): void {
|
||||
// [Gitea #56] No-op (and no redundant widget send) if nothing is pinned —
|
||||
// dispose() calls this unconditionally on every call teardown.
|
||||
if (this._focusedUserId === null) return;
|
||||
this._focusedUserId = null;
|
||||
this.call.transport.send('io.lotus.focus_participant', { userId: null }).catch(() => undefined);
|
||||
this.emitStateUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,6 +513,8 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
clearTimeout(this.bodyMutationTimer);
|
||||
this.bodyMutationTimer = undefined;
|
||||
}
|
||||
// [Gitea #56] Don't let a manual focus pin outlive the call.
|
||||
this.clearFocusParticipant();
|
||||
this.bodyMutationObserver.disconnect();
|
||||
this.controlMutationObserver.disconnect();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user