import { Room } from 'matrix-js-sdk'; import { MatrixRTCSession, MatrixRTCSessionEvent, } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSession'; import { MatrixRTCSessionManagerEvents } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSessionManager'; import { useEffect, useRef, useState } from 'react'; import { useAtomValue } from 'jotai'; import { useMatrixClient } from './useMatrixClient'; import { callEmbedAtom } from '../state/callEmbed'; export type IncomingDmCall = { room: Room; callerId: string; }; const isDmRoom = (room: Room): boolean => room.hasEncryptionStateEvent() && room.getMembers().length <= 2; export const useIncomingDmCall = (): [IncomingDmCall | null, () => void] => { const mx = useMatrixClient(); const callEmbed = useAtomValue(callEmbedAtom); const [incoming, setIncoming] = useState(null); const sessionRef = useRef(null); useEffect(() => { const myUserId = mx.getUserId(); const handleSessionStarted = (roomId: string, session: MatrixRTCSession) => { if (callEmbed) return; const room = mx.getRoom(roomId); if (!room || !isDmRoom(room)) return; const memberships = session.memberships ?? []; if (memberships.length === 0) return; const callerMembership = memberships.find((m) => m.sender !== myUserId); if (!callerMembership) return; if (memberships.find((m) => m.sender === myUserId)) return; sessionRef.current = session; setIncoming({ room, callerId: callerMembership.sender ?? callerMembership.deviceId }); }; const handleSessionEnded = (roomId: string) => { setIncoming((prev) => (prev?.room.roomId === roomId ? null : prev)); }; mx.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionStarted, handleSessionStarted); mx.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, handleSessionEnded); return () => { mx.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionStarted, handleSessionStarted); mx.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionEnded, handleSessionEnded); }; }, [mx, callEmbed]); // Dismiss if caller leaves before answered useEffect(() => { const session = sessionRef.current; if (!session || !incoming) return; const myUserId = mx.getUserId(); const check = () => { const memberships = session.memberships ?? []; if (!memberships.some((m) => m.sender !== myUserId)) setIncoming(null); }; session.on(MatrixRTCSessionEvent.MembershipsChanged, check); return () => { session.off(MatrixRTCSessionEvent.MembershipsChanged, check); }; }, [incoming, mx]); // Auto-dismiss after 30 seconds useEffect(() => { if (!incoming) return; const t = setTimeout(() => setIncoming(null), 30_000); return () => clearTimeout(t); }, [incoming]); // Dismiss when user joins a call useEffect(() => { if (callEmbed) setIncoming(null); }, [callEmbed]); const dismiss = () => { sessionRef.current = null; setIncoming(null); }; return [incoming, dismiss]; };