import { useInfiniteQuery } from '@tanstack/react-query'; import { Link, useNavigate } from 'react-router-dom'; import { Icon } from '@/app/Icon'; import { getCallHistory, type CallHistoryItem } from '@/lib/api/calls'; import { avatarBgClass, avatarLetter } from '@/lib/avatar'; import { formatEventTimestamp } from '@/lib/messages/time'; import { useChatList } from '@/lib/tinode/hooks'; import { useCallStore } from '@/stores/calls'; function formatDuration(ms: number | undefined): string { const totalSeconds = Math.round((ms || 0) / 1000); return `${String(Math.floor(totalSeconds / 60)).padStart(2, '0')}:${String(totalSeconds % 60).padStart(2, '0')}`; } /** Direction-dependent, same reasoning as dispatch.ts's callResultDisplay: a `missed` result reads * as "未接听" from the callee's own history and "已取消" from the caller's. */ function statusLabel(item: CallHistoryItem): string { const result = item.result || item.status; const incoming = item.direction === 'incoming'; if (result === 'finished') return formatDuration(item.duration_ms); if (result === 'missed') return incoming ? '未接听' : '已取消'; if (result === 'declined') return '已拒绝'; if (result === 'disconnected') return '异常结束'; return result; } /** README §7 doesn't spec a dedicated call-history screen beyond the "最近通话" idea implicit in * NavRail's own 通话记录 entry -- this is a plain list off the confirmed real `GET /v1/im/calls` * endpoint, whose response shape (see calls.ts's own comment) turned out completely different * from the doc's own minimal example once checked live: `peer_user_id` (a real `usr...` id, unlike * `topic`, which comes back `p2p...`-hashed and unusable for navigation/re-dialing) is resolved to * a display name via the chat list, same lookup ChatListColumn itself uses. */ export function CallHistoryView() { const navigate = useNavigate(); const contacts = useChatList(); const query = useInfiniteQuery({ queryKey: ['call-history'], queryFn: ({ pageParam }) => getCallHistory(pageParam), initialPageParam: '', getNextPageParam: (lastPage) => lastPage.next_cursor || undefined, }); const items = query.data?.pages.flatMap((p) => p.items) ?? []; return (
{/* `/calls`, like `/favorites`, is only reachable on a narrow viewport via 设置(我的)>通话记录 (SettingsLayout.tsx's MOBILE_SHORTCUTS) -- same "back returns there" convention FavoritesLayout's own mobile header already uses, and the same gap this file didn't have until now (there was no way back from here at all short of a system back gesture). */}
通话记录
通话记录
{query.isLoading ? (
加载中…
) : query.isError ? (
加载失败,请重试
) : items.length === 0 ? (
暂无通话记录
) : ( items.map((item) => { const result = item.result || item.status; const incoming = item.direction === 'incoming'; // Mock only reds out a genuine "未接听" (a call that rang and was never answered) -- // "已取消"(I cancelled before pickup)/"对方忙线中"/"异常结束" are neutral outcomes, not // something the viewer missed and should feel warned about. This used to redden EVERY // non-'finished' result alike. const isMissed = result === 'missed' && incoming; const name = contacts.find((c) => c.topic === item.peer_user_id)?.public?.fn || item.peer_user_id; const ts = item.started_at || item.created_at; return (
); }) )} {query.hasNextPage ? ( ) : null}
); }