import { useEffect } from 'react'; import { useAtomValue } from 'jotai'; import { CallEmbed } from '../plugins/call'; import { settingsAtom } from '../state/settings'; import { useStateEvent } from './useStateEvent'; import { StateEvent } from '../../types/matrix/room'; import { buildQualityPayload, RoomQualityContent } from '../utils/callQuality'; /** * [P5-31] Apply the user's call quality settings (clamped by any room-level * cap) to the Element Call fork via the `io.lotus.set_quality` widget action. * * The fork stores the settings and re-applies them on every (re)publish and * reconnect, so we only need to (re)send when the payload changes or the widget * becomes ready — no need to poll the track lifecycle here. */ export function useCallQuality(embed: CallEmbed): void { const { callAudioBitrate, screenshareBitrate, screenshareFramerate } = useAtomValue(settingsAtom); const roomQualityEvent = useStateEvent(embed.room, StateEvent.LotusRoomQuality); const roomCaps = roomQualityEvent?.getContent(); // Depend on the primitive cap values (not the event object) so re-renders // don't resend needlessly. const audioCap = roomCaps?.audio_max_kbps; const ssCap = roomCaps?.screenshare_max_kbps; const fpsCap = roomCaps?.screenshare_max_fps; useEffect(() => { const payload = buildQualityPayload( { callAudioBitrate, screenshareBitrate, screenshareFramerate }, { audio_max_kbps: audioCap, screenshare_max_kbps: ssCap, screenshare_max_fps: fpsCap }, ); const send = (): void => embed.control.setQuality(payload); // Send now (settings are sticky fork-side even if tracks aren't up yet) and // again once the widget signals ready, in case the transport wasn't up. send(); const off = embed.onReady(send); return off; }, [embed, callAudioBitrate, screenshareBitrate, screenshareFramerate, audioCap, ssCap, fpsCap]); }