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 => { const [speakers, setSpeakers] = useState(new Set()); 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()); 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; };