import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { callParticipant, dmCallHistoryQueryKey, fetchDmCallHistory, type DmCall, } from '@/api/calls'; import type { Channel } from '@/api/types'; import { Avatar } from '@/components/ui/Avatar'; import { SkeletonLines } from '@/components/ui/Skeleton'; import { useAuthorDirectory } from '@/components/chat/useAuthorDirectory'; import { useSessionStore } from '@/stores/session'; /** * История звонков беседы (AGENT.md 7.8, Фаза 7): кто звонил, когда, сколько * длился разговор и кто не ответил. Данные приходят ручкой `GET /calls` — * отдельной от переписки, чтобы звонки не мешали непрочитанным сообщениям. */ export function DmCallHistoryPanel({ channel }: { channel: Channel }) { const { t } = useTranslation(); const currentUserId = useSessionStore((state) => state.user?.id ?? null); const query = useQuery({ queryKey: dmCallHistoryQueryKey(channel.id), queryFn: ({ signal }) => fetchDmCallHistory(channel.id, undefined, signal), staleTime: 30_000, }); const calls = query.data ?? []; const userIds = calls.flatMap((call) => call.participants.map((item) => item.user_id)); const authors = useAuthorDirectory(null, userIds); /** Длительность разговора: минуты и секунды, у гудков — прочерк. */ const formatDuration = (call: DmCall): string => { if (call.duration_seconds <= 0) { return '—'; } const minutes = Math.floor(call.duration_seconds / 60); const seconds = call.duration_seconds % 60; if (minutes === 0) { return t('friends.call.durationSeconds', { count: seconds }); } return t('friends.call.duration', { minutes, seconds }); }; if (query.isPending) { return (
); } if (calls.length === 0) { return (

{t('friends.call.historyEmpty')}

); } return (

{t('friends.call.history')}

); } /** Ключ подписи строки истории: зависит от того, чем звонок закончился. */ function callLabelKey(call: DmCall, myState: string | undefined): string { if (myState === 'missed') { return 'friends.call.entryMissed'; } if (call.end_reason === 'missed') { return 'friends.call.entryNoAnswer'; } if (call.end_reason === 'declined') { return 'friends.call.entryDeclined'; } if (call.end_reason === 'canceled') { return 'friends.call.entryCanceled'; } if (call.media === 'video') { return 'friends.call.entryVideo'; } return 'friends.call.entryAudio'; } /** Время звонка в локальном формате: дата и часы. */ function formatTime(value: string): string { const date = new Date(value); if (Number.isNaN(date.getTime())) { return ''; } return date.toLocaleString(); }