import { useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { Icon } from '@/app/Icon'; import { useBootstrap } from '@/lib/api/hooks'; import { avatarBgClass, avatarLetter } from '@/lib/avatar'; import { formatMessagePreview } from '@/lib/messages/preview'; import { formatListTime } from '@/lib/messages/time'; import { useChatList } from '@/lib/tinode/hooks'; import { useSessionStore } from '@/stores/session'; /** * README §2.1's two always-pinned utility rows. Real, confirmed backend facts (docs/IM-前后端对接 * 文档.md:909): topic names are fixed ("slf"/"sys"), but 系统消息's unread comes from a separate * notifications table (not the topic itself) and 文件助手's own dedicated 3-column layout (README * §9.2) is Phase 8, not built yet -- so both route to the same SectionPlaceholder-backed "later * phase" destinations the rest of the nav already uses (contacts/favorites/calls/settings), rather * than a half-working ConversationView that assumes a regular P2P/group contact. */ const PINNED_ENTRIES = [ { key: 'system_messages' as const, badgeKey: 'system_unread_count' as const, label: '系统消息', icon: 'notifications', route: '/chat/system', colorClass: 'bg-warn', }, { key: 'file_assistant' as const, badgeKey: 'file_assistant_unread_count' as const, label: '文件助手', icon: 'folder', route: '/files', colorClass: 'bg-success', }, ]; /** Chat-list row sort: by `touched` (last activity) descending. Pinned-conversations-first isn't * wired yet -- that's driven by the P2P conversation-settings API (GET/PATCH /conversations/{topic}/ * settings {pinned}), which the detail panel doesn't build until its "置顶聊天" toggle lands. */ export function ChatListColumn() { const { topicId } = useParams(); const myUserId = useSessionStore((s) => s.myUserId); const contacts = useChatList(); const bootstrap = useBootstrap(); const [query, setQuery] = useState(''); const sorted = [...contacts].sort((a, b) => (b.touched?.getTime() ?? 0) - (a.touched?.getTime() ?? 0)); const trimmedQuery = query.trim(); const filtered = trimmedQuery ? sorted.filter((c) => (c.public?.fn || c.topic || '').toLowerCase().includes(trimmedQuery.toLowerCase())) : sorted; const pinned = PINNED_ENTRIES.filter((entry) => bootstrap.data?.entries?.[entry.key]?.enabled); return ( ); }