88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
|
|
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<IncomingDmCall | null>(null);
|
||
|
|
const sessionRef = useRef<MatrixRTCSession | null>(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];
|
||
|
|
};
|