import { useEffect, useRef } from 'react'; import { useSetAtom } from 'jotai'; import { CallEmbed, useCallControlState } from '../plugins/call'; import { useSetting } from '../state/hooks/settings'; import { settingsAtom } from '../state/settings'; import { dismissToastAtom, toastQueueAtom } from '../state/toast'; import { useMatrixClient } from './useMatrixClient'; const HOLD_MS = 1500; const CHECK_MS = 250; /** * [Gitea #117] "You're muted" — the fork reports `speakingWhileMuted` for the * local participant in io.lotus.call_state (a level tap on the muted track; * cinny captures no audio). Once it has been true for ≥ 1.5 s, one sticky * toast with an Unmute action, at most once per mute session (re-arms after * unmute → mute). Never in Push-to-Talk mode — being muted between holds is * the point. */ export function useMutedTalkWarning(embed: CallEmbed | undefined, joined: boolean): void { const mx = useMatrixClient(); const [enabled] = useSetting(settingsAtom, 'mutedTalkWarning'); const [pttMode] = useSetting(settingsAtom, 'pttMode'); const { microphone } = useCallControlState(embed?.control); const setToast = useSetAtom(toastQueueAtom); const dismissToast = useSetAtom(dismissToastAtom); const warnedThisMute = useRef(false); const toastIdRef = useRef(null); // Unmuting (by any means) re-arms the warning and retires the toast. useEffect(() => { if (!microphone) return; warnedThisMute.current = false; if (toastIdRef.current) { dismissToast(toastIdRef.current); toastIdRef.current = null; } }, [microphone, dismissToast]); useEffect(() => { if (!embed || !joined || !enabled || pttMode || microphone) return undefined; const me = mx.getSafeUserId(); let since: number | null = null; const id = window.setInterval(() => { if (warnedThisMute.current) return; const local = embed.getLotusParticipants()?.find((p) => p.userId === me); if (!local?.speakingWhileMuted) { since = null; return; } if (since === null) since = Date.now(); if (Date.now() - since < HOLD_MS) return; warnedThisMute.current = true; const toastId = `muted-talk-${Date.now()}`; toastIdRef.current = toastId; setToast({ id: toastId, displayName: 'Lotus Chat', body: "You're muted — click to unmute", roomName: embed.room.name ?? 'Voice call', roomId: embed.roomId, sticky: true, onClick: () => { embed.control.setMicrophone(true); }, }); }, CHECK_MS); return () => window.clearInterval(id); }, [embed, joined, enabled, pttMode, microphone, mx, setToast]); }