被拉入新群时标题栏/桌面通知没有任何提示——扩展兜底同步机制覆盖这个场景

reconcileMeSubs()本来就能发现"此前完全不认识的话题"(今天早些时候为解决群聊
被动成员看不到新群加的兜底机制),但发现后只是原样信任服务端返回的read/unread
字段——服务端对一个刚加入的新成员,默认给的读游标经常是"视为已读"(read==seq),
如果这个群目前恰好一条消息都没有,unread就会是0,导致今天新加的标题栏滚动/
favicon角标(由同一个未读数驱动)完全没有反应,被拉群这件事本身悄无声息。

改成:对每一个新发现的话题,不管服务端给的read游标是什么,一律按"我自己完全
没确认过"处理(read清零,unread按当前seq计算);如果是群,额外通过
subscribeNewGroupDiscovered(新增的发布-订阅,push.ts订阅它去弹真正的桌面
通知"你被拉入了这个群聊",因为topics.ts不能反向依赖push.ts)触发一条独立的
桌面通知,不依赖未读数是否恰好大于0。

实机验证:systest04建一个只邀请systest05的全新群,systest05这边模拟切回
标签页触发兜底同步后,确认收到桌面通知(title/body/tag均正确)且标题栏
未读数正确增加。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-16 22:25:55 +08:00
co-authored by Claude Sonnet 5
parent f570483b62
commit 3fab8b08b9
2 changed files with 64 additions and 11 deletions
+27 -1
View File
@@ -99,6 +99,18 @@ export function subscribeTopicInfo(callback: (topicName: string, info: RawTopicI
const goneListeners = new Set<(topicName: string) => void>();
const newGroupListeners = new Set<(topicName: string) => void>();
/** Fires when reconcileMeSubs() (below) discovers a group topic this client has never seen before --
* the "you were just added to a group" case (Zentao/user-reported: "被拉入群时title的消息没有提示").
* Consumed by lib/notifications/push.ts to fire a desktop notification -- can't live there itself
* since push.ts already depends on topics.ts and the reverse would be a cycle, same reasoning as
* setActiveConversationTopic living here instead of in push.ts. */
export function subscribeNewGroupDiscovered(callback: (topicName: string) => void): () => void {
newGroupListeners.add(callback);
return () => newGroupListeners.delete(callback);
}
/** Fires once a topic is confirmed gone (group dissolved by anyone, or removed from this account) --
* the SDK's own `Topic#_gone()` clears the topic's public/private/messages and calls
* `onDeleteTopic()` (confirmed reading tinode.dev.js). Without ConversationView reacting to this,
@@ -481,7 +493,21 @@ function reconcileMeSubs() {
for (const c of getChatListSnapshot()) {
if (!c.topic) continue;
const prev = before.get(c.topic);
if (!prev) continue; // brand-new topic to this session -- exactly what this mitigation is for
if (!prev) {
// Brand-new topic to this session -- exactly what this mitigation is for. A fresh member's
// sub record can legitimately come back "already caught up" (read === seq) from the
// server's own default, which would otherwise leave being newly added to a group producing
// NO unread signal at all whenever it happens to have zero messages so far -- real,
// reported UX gap ("被拉入群时title的消息没有提示"): the tab-title/favicon-badge (see lib/
// notifications/tabIndicator.ts) are driven by this same unread count, so nothing tells the
// user anything happened. Force it to read as genuinely unread from this client's own
// perspective regardless of what the server defaulted it to.
c.read = 0;
c.unread = c.seq || 0;
notifyTopic(c.topic);
if (c.topic.startsWith('grp')) newGroupListeners.forEach((cb) => cb(c.topic!));
continue;
}
const seq = c.seq || 0;
const unread = c.unread || 0;
if (seq <= prev.seq && unread > prev.unread) {