From d1c81d432ede230f122eccbc00904aacdd8f9395 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 9 Sep 2026 22:01:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E6=96=87=E4=BB=B6=E5=8A=A9?= =?UTF-8?q?=E6=89=8B=20duplicated=20as=20its=20own=20chat-list=20row=20onc?= =?UTF-8?q?e=20slf=20has=20real=20history?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 文件助手 is a permanent pinned entry that redirects into a real conversation on the slf topic (see PINNED_ENTRIES's own doc comment -- there's no dedicated file-browser API to back a real one). Once that topic has any message history it also comes back from useChatList() as an ordinary contact, so it rendered a second time as its own chronologically-sorted row further down -- confirmed live: navigating to /messages/slf via either row satisfied both rows' own "am I active" check at once, highlighting both simultaneously. Filters the slf topic out of the ordinary contacts list; 文件助手 stays the one and only way it's represented there. Co-Authored-By: Claude Sonnet 5 --- web/src/features/messages/ChatListColumn.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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); });