fix(web): 隐藏文件助手入口,修复自聊天详情栏错误操作

后端目前没有支持文件助手所需的配额/分类等能力,只是把它当作
普通话题处理;与其做一个功能不全的界面,先从导航栏、会话列表
置顶区、通讯录固定入口、设置页移动端快捷入口中移除该入口。slf
话题现在按普通会话行为处理(排序、未读计数均不再特殊处理)。

同时修复 DetailColumn 对自聊天(slf)显示语音/视频通话、备注、
拉黑、举报、删除好友等对自己无意义操作的问题,改为仅显示适用
的发消息/查找聊天记录/置顶/免打扰/清空聊天记录等项。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-09 23:35:58 +08:00
co-authored by Claude Sonnet 5
parent 3065e232be
commit 9271a0a9c1
6 changed files with 97 additions and 98 deletions
+8 -6
View File
@@ -24,13 +24,15 @@ export function AppShell() {
useEffect(() => {
initCallSignaling();
}, []);
// "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.
// "sys" has its own authoritative unread source in entry_badges (bootstrap.ts) -- excluded here
// in case it ever also shows up as a regular Tinode subscription in `contacts`, which would
// otherwise double-count the same unread messages into this badge. "slf" gets no such exclusion:
// 文件助手 (which used to own its own entry_badges count, file_assistant_unread_count) has been
// removed as a UI entry point entirely -- see ChatListColumn.tsx's PINNED_ENTRIES doc comment --
// so "slf" is now just an ordinary contact row like any other, and should count normally here too.
const unreadMessages =
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);
contacts.reduce((sum, c) => (c.topic === 'sys' ? sum : sum + (c.unread || 0)), 0) +
(bootstrap.data?.entry_badges?.system_unread_count || 0);
return (
<div className="flex h-dvh w-full overflow-hidden bg-surface-page">
+4 -14
View File
@@ -13,7 +13,6 @@ const TOP_ITEMS: NavRailItem[] = [
{ to: '/messages', icon: 'chat_bubble', label: '消息' },
{ to: '/contacts', icon: 'contacts', label: '通讯录' },
{ to: '/favorites', icon: 'inventory_2', label: '收藏' },
{ to: '/files', icon: 'folder', label: '文件助手' },
];
const BOTTOM_ITEMS: NavRailItem[] = [
@@ -21,14 +20,8 @@ const BOTTOM_ITEMS: NavRailItem[] = [
{ to: '/settings', icon: 'settings', label: '设置' },
];
/** `/files` is `<Navigate to="/messages/slf" replace />` (App.tsx) -- the real URL right after
* clicking "文件助手" is `/messages/slf`, which a plain (non-`end`) NavLink match makes "消息"
* light up for (a same-topicId prefix match) while "文件助手" itself (`to: '/files'`, a path that
* no longer exists once redirected) never matches at all. Overriding both items' active state off
* the real pathname instead of trusting NavLink's own prefix match fixes both sides of the mix-up. */
function isNavItemActive(to: string, pathname: string): boolean {
if (to === '/files') return pathname === '/messages/slf';
if (to === '/messages') return pathname.startsWith('/messages') && pathname !== '/messages/slf';
if (to === '/messages') return pathname.startsWith('/messages');
return pathname === to || pathname.startsWith(`${to}/`);
}
@@ -39,12 +32,9 @@ function NavRailButton({ item }: { item: NavRailItem }) {
// Plain Link, not NavLink -- NavLink computes its OWN `aria-current` from ITS OWN prefix-match
// logic unconditionally (confirmed by reading react-router's source: passing an `aria-current`
// prop only changes the STRING used, not whether it's applied at all, which stays gated on
// NavLink's internal isActive). That's exactly `/files`'s own problem above -- NavLink's naive
// match would light up "消息" for `/messages/slf`, not "文件助手" -- so a second, independent
// aria-current computation fighting the same override this file already needed for `className`
// is a real, verifiable a11y bug (screen readers and sighted highlighting would disagree on
// which icon is current). Driving both off the one already-correct `isActive` avoids that
// entirely.
// NavLink's internal isActive), so a second, independent aria-current computation could in
// principle disagree with the `className` highlight computed by `isActive` below. Driving both
// off the one shared `isActive` value keeps them impossible to desync.
<Link
to={item.to}
title={item.label}