From c9f4615c40aad4aa998c9f2749c0d957d8f2d7ea Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 8 Sep 2026 04:48:53 +0800 Subject: [PATCH] fix(web): code review round 2 -- messaging UI bugs and design fidelity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DetailColumn: remark-edit draft and the history-search modal's open state weren't reset on contact switch (no key={topicId}, same long- lived instance) -- could silently save a typed remark against the WRONG contact. History-search state lifted to MessagesLayout so ConversationView's header search icon (previously wired to nothing) can open the same modal - ConversationView: loadOlder's in-flight "restore scroll position" callback and its loading flag weren't guarded against a conversation switch happening mid-request, and the scroll-to-bottom effect keyed only on messages.length so two conversations that happened to load the same page size never got scrolled down on open - ChatListColumn: pinned 系统消息/文件助手 rows used solid-color-square avatars like regular contacts; the actual design-code source (ChatList.dc.html) and reference screenshots use a light tinted background with a colored icon instead - MessageBubble: own ("我") messages hash-colored their avatar like any contact instead of the design's fixed accent blue - Every send/recall/favorite/forward action swallowed its own failure silently (empty .catch or, for forward, no catch at all) -- moved Toast from features/auth into app/ (shared) and wired real error feedback through it - MessageContextMenu: added the recall countdown label, Escape-to-close, and viewport-edge clamping (a right-click near the window edge could render partially off-screen) - EmojiPanel now defaults to 最近 per the reference (was defaulting to 表情) - HistorySearchModal: pagination (searchTopicMessages/getTopicMedia both supported cursor/next_cursor already, nothing used it -- silently truncated at one page), Escape-to-close, and a brief "没有找到" flash on the first keystroke before the debounce caught up - A handful of design-token corrections (--shadow-btn, NavRail's border/ spacing, LoginPage's logo radius/brand-panel padding, a placeholder icon color) verified against screens/*.dc.html's exact pixel values Co-Authored-By: Claude Sonnet 5 --- web/src/app/AppShell.tsx | 5 +- web/src/app/NavRail.tsx | 4 +- web/src/app/Toast.tsx | 38 +++++++ .../features/auth/EnterpriseRegisterPage.tsx | 12 ++- web/src/features/auth/LoginPage.tsx | 6 +- web/src/features/auth/ResetPasswordPage.tsx | 2 +- web/src/features/auth/TenantEntryPage.tsx | 4 +- web/src/features/auth/TenantFeedbackPage.tsx | 12 ++- .../components/VerificationCodeButton.tsx | 8 ++ .../features/messages/AttachmentPreview.tsx | 2 +- web/src/features/messages/ChatListColumn.tsx | 6 +- .../features/messages/ConversationView.tsx | 78 ++++++++++---- web/src/features/messages/DetailColumn.tsx | 37 +++++-- web/src/features/messages/EmojiPanel.tsx | 2 +- .../features/messages/ForwardPickerModal.tsx | 2 + .../features/messages/HistorySearchModal.tsx | 101 ++++++++++++------ web/src/features/messages/MessageBubble.tsx | 6 +- .../features/messages/MessageContextMenu.tsx | 38 ++++++- web/src/features/messages/MessagesLayout.tsx | 19 +++- web/src/index.css | 2 +- 20 files changed, 287 insertions(+), 97 deletions(-) create mode 100644 web/src/app/Toast.tsx diff --git a/web/src/app/AppShell.tsx b/web/src/app/AppShell.tsx index 25b9ab49..4a2796d5 100644 --- a/web/src/app/AppShell.tsx +++ b/web/src/app/AppShell.tsx @@ -15,8 +15,11 @@ import { NavRail } from './NavRail'; export function AppShell() { const contacts = useChatList(); const bootstrap = useBootstrap(); + // "sys"/"slf" have their own authoritative unread source in entry_badges (bootstrap.ts) -- excluded + // here in case they ever also show up as regular Tinode subscriptions in `contacts`, which would + // otherwise double-count the same unread messages into this badge. const unreadMessages = - contacts.reduce((sum, c) => sum + (c.unread || 0), 0) + + contacts.reduce((sum, c) => (c.topic === 'sys' || c.topic === 'slf' ? sum : sum + (c.unread || 0)), 0) + (bootstrap.data?.entry_badges?.system_unread_count || 0) + (bootstrap.data?.entry_badges?.file_assistant_unread_count || 0); diff --git a/web/src/app/NavRail.tsx b/web/src/app/NavRail.tsx index f8fcc5d3..3760659b 100644 --- a/web/src/app/NavRail.tsx +++ b/web/src/app/NavRail.tsx @@ -45,8 +45,8 @@ function NavRailButton({ item }: { item: NavRailItem }) { /** Persistent 64px icon rail, visible at >=640px (see App.tsx for the <640 bottom-tab fallback). */ export function NavRail({ selfInitial = '我', unreadMessages = 0 }: { selfInitial?: string; unreadMessages?: number }) { return ( -