修复Zentao批量报告的6个前端bug:来电UI不弹出/黑名单发送无提示/群名校验/群人数与通话记录显示uid
用户提供了一份Zentao导出的bug清单(#21/23/24/25/26/27/28共7条),逐条实机复现
排查,修复了其中6个真正的前端bug,1个是纯后端问题(禁言不生效)已记录到
docs/IM-后端问题清单.md,1个(好友申请404/500)未能独立复现只记录了测试团队
的原始报告,另1个(离线状态推送)实测后确认并非报告描述的"永久不更新",只是
有正常的心跳超时延迟。
- **#23 来电UI完全不弹出**(根因最深的一个):initCallSignaling用的是
`me.getMeTopic().contacts()`枚举联系人再查最新消息——但这个方法只反映`me`
话题自己的订阅同步进度,一个P2P话题可能早就真正订阅上、能收到消息,`me`的
同步却还没跟上,导致这个联系人在枚举里直接消失。用两个真实账号实机验证:
被叫方`me.contacts()`只枚举出1个群,主叫方完全没出现,来电消息静默丢失。
topics.ts里`getChatListSnapshot()`自己的注释早就写明了这个坑、并且已经改
用`mapTopics()`规避,只是`stores/calls.ts`这个监听器当时没有跟着改。现在
统一复用`getChatListSnapshot()`,实机验证来电卡片正确弹出。
- **#28 拉黑后发送消息无原因提示**:读tinode-sdk源码确认发送失败的具体原因
从架构上就拿不到——`Topic#publishMessage`自己的`.catch()`把服务端拒绝原因
吞掉、只留一个`_failed`标记,不会重新抛出。改成事前判断:新增
`getConversationContext()`(P2P专用,`relationship`字段区分
`blocked_by_me`/`blocked_by_peer`),发送区直接换成对应文案的横幅,两个
方向都拿真实账号互相拉黑验证过。
- **#26 群名超长/留空自动名两个校验问题**:`MAX_TITLE_LENGTH`一直是60,但
后端`im_http.go`真实校验的是32字(读源码确认),导致33-59字之间的群名能
输入、能提交,然后被后端拒绝只显示通用错误。改成32,从输入框这一层就不
再放行超长名。另外群名留空时自动拼接的成员昵称组合,理论上可能被内容审核
拒绝——这种情况下用户根本不知道"名字"是问题所在,补了针对性提示。
- **#27 通话记录/群列表显示原始uid、群人数显示0人**(2处独立bug):
1. `CallHistoryView.tsx`按`peer_user_id`(真实usr id)去匹配聊天列表的
`contact.topic`(建立过真实会话后已经是服务端重命名过的p2p哈希),两个
不是同一个id空间,永远匹配不上,一直显示原始id。改成用`item.topic`
(已确认和chatlist的topic是同一个值)去匹配。
2. 实测发现`GET /groups`列表接口的`member_count`会返回0,但同一个群
`GET /groups/{topic}`单独查询正确返回4——两个接口的计数器不一致(后端
已知问题,见问题8),前端这次统一改成"读到0就不显示人数"而不是显示一
个已确认错误的数字。
以上改动均跑过`tsc -b --noEmit`/`oxlint`/`vite build`,除#26外全部用两个真实
账号(systest02/systest04)互相操作实机验证。
This commit is contained in:
@@ -19,7 +19,7 @@ import { JoinRequestsModal } from '@/features/groups/JoinRequestsModal';
|
||||
import { useBootstrap } from '@/lib/api/hooks';
|
||||
import { favoriteMessages, getTopicDraft, type MediaType, putTopicDraft, recallMessage } from '@/lib/api/messages';
|
||||
import { getAllGroupMembers, getAllGroupMutes, getGroup, getGroupGovernance, getGroupSummary } from '@/lib/api/groups';
|
||||
import { getUserProfile } from '@/lib/api/friends';
|
||||
import { getConversationContext, getUserProfile } from '@/lib/api/friends';
|
||||
import { getMe } from '@/lib/api/profile';
|
||||
import { FETCH_PAGE_TRIGGER, MESSAGES_PAGE } from '@/lib/config';
|
||||
import { useClickOutside } from '@/lib/hooks';
|
||||
@@ -482,6 +482,28 @@ export function ConversationView() {
|
||||
queryFn: () => getUserProfile(peerUserId || ''),
|
||||
enabled: !!peerUserId,
|
||||
});
|
||||
// Live-confirmed real bug this closes (Zentao #28): sending into a P2P conversation blocked in
|
||||
// EITHER direction showed only the generic per-bubble "发送失败,点击重试", no reason, on both sides
|
||||
// -- reading tinode-sdk source confirmed the specific rejection reason can never be recovered
|
||||
// after a failed send (Topic#publishMessage's own .catch swallows it into a bare `_failed` flag and
|
||||
// never rethrows), so the only real fix is checking BEFORE attempting to send, not reacting after.
|
||||
// This endpoint is P2P-only and reports the relationship from either side (`blocked_by_me` on the
|
||||
// blocker's own view, `blocked_by_peer` on the blocked side) -- see getConversationContext's own
|
||||
// doc comment for why `getUserProfile`'s similar-looking `capabilities.can_send` can't be reused for
|
||||
// this (403s entirely from the blocked party's own perspective instead of reporting false).
|
||||
const conversationContextQuery = useQuery({
|
||||
queryKey: ['conversation-context', topicId],
|
||||
queryFn: () => getConversationContext(topicId || ''),
|
||||
enabled: !!topicId && !isGroup && !isSelf,
|
||||
});
|
||||
const blockedNotice =
|
||||
conversationContextQuery.data?.relationship === 'blocked_by_me'
|
||||
? '你已将对方加入黑名单,无法发送消息'
|
||||
: conversationContextQuery.data?.relationship === 'blocked_by_peer'
|
||||
? '对方已将你加入黑名单,暂时无法发送消息'
|
||||
: conversationContextQuery.data?.can_send === false
|
||||
? '暂时无法向对方发送消息'
|
||||
: null;
|
||||
const membersQuery = useQuery({
|
||||
queryKey: ['group-members', topicId],
|
||||
queryFn: () => getAllGroupMembers(topicId || ''),
|
||||
@@ -973,6 +995,10 @@ export function ConversationView() {
|
||||
</div>
|
||||
) : (
|
||||
<div ref={mentionContainerRef} className="flex-none border-t border-border-panel bg-white px-4 pb-3 pt-2">
|
||||
{blockedNotice ? (
|
||||
<div className="flex h-18 items-center justify-center text-sm text-text-tertiary-3">{blockedNotice}</div>
|
||||
) : (
|
||||
<>
|
||||
{replyTo ? (
|
||||
<div className="mb-2 flex items-center justify-between rounded-lg bg-surface-input px-3 py-1.5 text-xs text-text-tertiary-1">
|
||||
<span className="truncate">
|
||||
@@ -1101,6 +1127,8 @@ export function ConversationView() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user