fix(web): 修复自聊天显示为原始topic id、主发送/重试路径未重新订阅话题

自聊天(slf)会话头部/侧边栏/聊天列表此前直接回退显示原始topic id "slf"
(而不是自己的昵称),且会话头部还错误显示了通话/视频按钮和"离线"状态——
这三者都源自同一处:peerName的计算只查好友列表(不含自己),没有对slf
做特殊处理。DetailColumn此前已经用isSelf正确处理了详情栏内部的各项操
作(commit 9271a0a9),但ConversationView自己的头部渲染和ChatListColumn
的列表行都没有跟进。改为isSelf时读取AppShell/SettingsLayout已经在用的
同一个['me']缓存查询,取自己的真实昵称,并隐藏通话按钮和在线状态行。

真实两账号联调时发现更严重的问题:主输入框发送消息(ConversationView
submit())和失败重试(MessageBubble retry())都直接调用sendText/
retryMessage,从未像转发流程(见ensureTopicAttached自己的注释)那样先
确保话题处于attached状态——连接抖动一次(断线重连会重置每个话题的
attached状态)之后,不管是正常发送还是点击重试都会直接失败,不会自
动恢复。转发流程本来就有这个防护,这次给主发送路径和重试路径补上同
一个防护。

用两个真实测试账号(asn494180 与新建的e2etester1,跨web.118im.com/
web2.118im.com两个域名分别登录)完整验证:加好友、接受好友申请、
P2P发消息全链路,复现问题后确认修复生效。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-16 10:44:19 +08:00
co-authored by Claude Sonnet 5
parent e78437cb2a
commit c3e8f91364
3 changed files with 53 additions and 17 deletions
+12 -2
View File
@@ -8,6 +8,7 @@ import { CreateGroupModal } from '@/features/groups/CreateGroupModal';
import { fetchAllPages } from '@/lib/api/client';
import { getGroups } from '@/lib/api/groups';
import { useBootstrap } from '@/lib/api/hooks';
import { getMe } from '@/lib/api/profile';
import { avatarBgClass, avatarLetter } from '@/lib/avatar';
import { formatMessagePreview } from '@/lib/messages/preview';
import { formatListTime } from '@/lib/messages/time';
@@ -55,6 +56,9 @@ export function ChatListColumn() {
const location = useLocation();
const navigate = useNavigate();
const myUserId = useSessionStore((s) => s.myUserId);
// `slf` (chat with yourself) never has its own `.public.fn` -- shares AppShell's `['me']` query
// key so this is a cache read, not an extra request, in the common case that already mounted it.
const meQuery = useQuery({ queryKey: ['me'], queryFn: getMe });
const tinode = useTinode();
const contacts = useChatList();
const bootstrap = useBootstrap();
@@ -70,7 +74,13 @@ export function ChatListColumn() {
return pinDelta || (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 filtered = trimmedQuery
? sorted.filter((c) =>
(c.topic === 'slf' ? meQuery.data?.profile.nickname || '我' : c.public?.fn || c.topic || '')
.toLowerCase()
.includes(trimmedQuery.toLowerCase()),
)
: sorted;
const pinned = PINNED_ENTRIES.filter((entry) => bootstrap.data?.entries?.[entry.key]?.enabled);
return (
@@ -127,7 +137,7 @@ export function ChatListColumn() {
</div>
) : (
filtered.map((contact) => {
const name = contact.public?.fn || contact.topic;
const name = contact.topic === 'slf' ? meQuery.data?.profile.nickname || '我' : contact.public?.fn || contact.topic;
const preview = formatMessagePreview(contact.latestMessage?.(), myUserId);
const active = topicId === contact.topic;
const isGroup = tinode.getTopic(contact.topic).isGroupType();