diff --git a/web/src/features/messages/ChatListColumn.tsx b/web/src/features/messages/ChatListColumn.tsx index 97d84af5..99d9fcac 100644 --- a/web/src/features/messages/ChatListColumn.tsx +++ b/web/src/features/messages/ChatListColumn.tsx @@ -68,7 +68,14 @@ export function ChatListColumn() { const memberCountByTopic = new Map(groupsQuery.data?.map((g) => [g.topic, g.member_count])); const [query, setQuery] = useState(''); const [creatingGroup, setCreatingGroup] = useState(false); - const sorted = [...contacts].sort((a, b) => { + // `slf` (self-chat) is permanently represented by the pinned 文件助手 entry above (see + // PINNED_ENTRIES's own doc comment) -- it also comes back as an ordinary contact once it has any + // real message history (e.g. the very first time something gets sent through 文件助手), which + // without this filter renders it a SECOND time as its own chronologically-sorted row further down. + // Confirmed live: with both rows present, navigating to /messages/slf (via either one) satisfies + // both rows' own "am I the active one" check at once, highlighting them simultaneously -- a real, + // reproducible duplicate-representation bug, not just a cosmetic double-highlight. + const sorted = [...contacts].filter((c) => c.topic !== 'slf').sort((a, b) => { const pinDelta = (b.private?.pinned ? 1 : 0) - (a.private?.pinned ? 1 : 0); return pinDelta || (b.touched?.getTime() ?? 0) - (a.touched?.getTime() ?? 0); });