import React, { useCallback, useEffect, useRef } from 'react'; import { Avatar, Box, Button, Text } from 'folds'; import { UserAvatar } from './user-avatar'; import { useMatrixClient } from '../hooks/useMatrixClient'; import { useCallStart } from '../hooks/useCallEmbed'; import { useSetting } from '../state/hooks/settings'; import { settingsAtom } from '../state/settings'; import { useIncomingDmCall } from '../hooks/useIncomingDmCall'; import { mxcUrlToHttp } from '../utils/matrix'; function useRingTone(active: boolean) { const stopRef = useRef<(() => void) | null>(null); useEffect(() => { if (!active) { stopRef.current?.(); stopRef.current = null; return; } let ctx: AudioContext; try { ctx = new AudioContext(); } catch { return; } let cancelled = false; const pulse = (t: number, freq: number, dur: number) => { const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.frequency.value = freq; osc.type = 'sine'; gain.gain.setValueAtTime(0, t); gain.gain.linearRampToValueAtTime(0.22, t + 0.04); gain.gain.setValueAtTime(0.22, t + dur - 0.04); gain.gain.linearRampToValueAtTime(0, t + dur); osc.start(t); osc.stop(t + dur); }; const ring = () => { if (cancelled) return; const now = ctx.currentTime; pulse(now, 880, 0.18); pulse(now + 0.28, 880, 0.18); setTimeout(ring, 2200); }; ring(); stopRef.current = () => { cancelled = true; ctx.close(); }; return () => { cancelled = true; ctx.close(); stopRef.current = null; }; }, [active]); return stopRef; } function IncomingCallCard() { const mx = useMatrixClient(); const startCall = useCallStart(true); const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal'); const [incoming, dismiss] = useIncomingDmCall(); const stopRingRef = useRingTone(!!incoming); const stopAndDismiss = useCallback(() => { stopRingRef.current?.(); dismiss(); }, [dismiss, stopRingRef]); const handleAnswer = useCallback(() => { if (!incoming) return; stopRingRef.current?.(); startCall(incoming.room); dismiss(); }, [incoming, startCall, dismiss, stopRingRef]); if (!incoming) return null; const callerUser = mx.getUser(incoming.callerId); const callerName = callerUser?.displayName ?? incoming.callerId; const avatarMxc = callerUser?.avatarUrl; const avatarUrl = avatarMxc ? mxcUrlToHttp(mx, avatarMxc, undefined, 64, 64, 'crop') ?? undefined : undefined; if (lotusTerminal) { return (