import React from 'react'; import { Avatar, Box, Icon, IconButton, Icons, Text, Tooltip, TooltipProvider } from 'folds'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { Room } from 'matrix-js-sdk'; import { useSetAtom } from 'jotai'; import { useNavigate } from 'react-router-dom'; import * as css from './styles.css'; import { ContainerColor } from '../../styles/ContainerColor.css'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { UserAvatar } from '../../components/user-avatar'; import { getMemberAvatarMxc, getMemberDisplayName } from '../../utils/room'; import { getCanonicalAliasOrRoomId, getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; import { useSwitchOrStartDmCall } from '../../hooks/useSwitchOrStartDmCall'; import { getDirectRoomPath } from '../../pages/pathUtils'; import { IncomingCall, incomingCallsAtom } from '../../state/incomingCalls'; import { getIncomingCallKey } from '../../utils/rtcNotification'; const DECLINE_RETRY_DELAY_MS = 500; type IncomingCallStripProps = { call: IncomingCall; room: Room; }; export function IncomingCallStrip({ call, room }: IncomingCallStripProps) { const { t } = useTranslation(); const mx = useMatrixClient(); const navigate = useNavigate(); const useAuthentication = useMediaAuthentication(); const switchOrStartDmCall = useSwitchOrStartDmCall(); const setIncoming = useSetAtom(incomingCallsAtom); const senderId = call.notifEvent.getSender(); const displayName = (senderId && getMemberDisplayName(room, senderId)) || (senderId && getMxIdLocalPart(senderId)) || senderId || t('Call.unknown_caller'); const avatarMxc = senderId ? getMemberAvatarMxc(room, senderId) : undefined; const avatarUrl = avatarMxc ? mxcUrlToHttp(mx, avatarMxc, useAuthentication, 96, 96, 'crop') ?? undefined : undefined; const callKey = getIncomingCallKey(call.callId, call.roomId); const handleAnswer = () => { navigate(getDirectRoomPath(getCanonicalAliasOrRoomId(mx, call.roomId))); switchOrStartDmCall(call.roomId) .then(() => { const evId = call.notifEvent.getId(); if (evId) { setIncoming({ type: 'REMOVE_BY_NOTIF_ID', notifEventId: evId }); return; } setIncoming({ type: 'REMOVE', key: callKey }); }) .catch((err: unknown) => { // eslint-disable-next-line no-console console.warn('[call] strip answer switch/start failed', err); }); }; const handleDecline = async () => { setIncoming({ type: 'REMOVE', key: callKey }); const evId = call.notifEvent.getId(); if (!evId) return; try { await mx.sendRtcDecline(call.roomId, evId); } catch (err) { await new Promise((resolve) => { setTimeout(resolve, DECLINE_RETRY_DELAY_MS); }); try { await mx.sendRtcDecline(call.roomId, evId); } catch (retryErr) { // eslint-disable-next-line no-console console.warn('[call] sendRtcDecline failed after retry', retryErr); } } }; return ( } /> {displayName} {t('Call.incoming')} {t('Call.decline')} } > {(anchorRef) => ( )} {t('Call.answer')} } > {(anchorRef) => ( )} ); }