Files
cinny/src/app/hooks/useCallSpeakers.ts
T

70 lines
2.4 KiB
TypeScript
Raw Normal View History

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);
2026-05-23 17:20:41 +05:30
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;
};