CI / Build & Quality Checks (push) Successful in 4m32s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 12s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 16m32s
Firefox, Safari and the WebKitGTK desktop app can't hand the user's click to the call frame (no Capability Delegation), and getDisplayMedia needs it. The host used to click EC's hidden footer button through the DOM instead, which dies with same-origin. Now (pins element-call-embedded 0.25.0-lotus.20): - those engines get `lotusFrameScreenshare`, the fork shows EC's own screenshare button in the frame, and the call bar and status bar hide theirs once controls_state reports `frameScreenshare`; the screenshare-audio mute stays; - the room's call policy is pushed with io.lotus.set_frame_screenshare, so the frame button hides where the server would refuse a share, like ours; - Chromium keeps the delegated io.lotus.set_screenshare from the host bar. Removed the fallbacks for forks older than lotus.14, which read or clicked EC's DOM: the screenshare/layout/settings/reactions/leave button lookups and their MutationObservers, the frame-window hotkey binding, and the speaking/muted tile scrape in useCallSpeakers (io.lotus.call_state is the only source now). getCallDocument is gone; the host's only handle on the frame is postMessage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { CallEmbed } from '../plugins/call';
|
|
import { nextSpeakerSet } from '../utils/speakerSet';
|
|
import { useCallMembers, useCallSession } from './useCall';
|
|
import { useCallJoined } from './useCallEmbed';
|
|
|
|
/**
|
|
* Returns the set of Matrix user IDs currently speaking in the call, from the
|
|
* fork's io.lotus.call_state reports ([lotus #2]).
|
|
*
|
|
* [Gitea #43] The fallback that scraped EC's rendered tiles (the speaking
|
|
* ring's `::before` background) is gone: the bundled fork always reports call
|
|
* state, and the host no longer reads the call frame's DOM.
|
|
*/
|
|
export const useCallSpeakers = (callEmbed: CallEmbed): Set<string> => {
|
|
const [speakers, setSpeakers] = useState(new Set<string>());
|
|
const callSession = useCallSession(callEmbed.room);
|
|
const callMembers = useCallMembers(callSession);
|
|
const joined = useCallJoined(callEmbed);
|
|
|
|
// C-L5 — depend on a STABLE boolean, not the callMembers array (whose identity
|
|
// changes on every membership change). The io.lotus.call_state subscription
|
|
// below already tracks participants joining/leaving live.
|
|
const hasCallMembers = callMembers.length > 0;
|
|
|
|
useEffect(() => {
|
|
if (!hasCallMembers || !joined) {
|
|
setSpeakers(new Set<string>());
|
|
return undefined;
|
|
}
|
|
|
|
const syncState = (): void => {
|
|
// #32 — bail out of setState (and the re-render it causes) when the
|
|
// derived set is unchanged from the previous one.
|
|
setSpeakers((prev) => nextSpeakerSet(prev, callEmbed.getLotusParticipants() ?? []));
|
|
};
|
|
|
|
syncState();
|
|
return callEmbed.onLotusCallState(syncState);
|
|
}, [callEmbed, hasCallMembers, joined]);
|
|
|
|
return speakers;
|
|
};
|
|
|
|
/**
|
|
* Returns true when there is at least one REMOTE participant and every one of
|
|
* them has their microphone muted, from the fork's io.lotus.call_state reports.
|
|
*/
|
|
export const useRemoteAllMuted = (callEmbed: CallEmbed | undefined): boolean => {
|
|
const [muted, setMuted] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!callEmbed) return undefined;
|
|
|
|
const localUserId = callEmbed.room.client?.getUserId() ?? '';
|
|
|
|
const syncState = (): void => {
|
|
const remote = (callEmbed.getLotusParticipants() ?? []).filter(
|
|
(p) => p.userId !== localUserId,
|
|
);
|
|
setMuted(remote.length > 0 && remote.every((p) => !p.audioEnabled));
|
|
};
|
|
|
|
syncState();
|
|
return callEmbed.onLotusCallState(syncState);
|
|
}, [callEmbed]);
|
|
|
|
return muted;
|
|
};
|