修复P2P会话完全不出现在聊天列表、标题栏显示原始ID+离线的根因
根因:tinode-sdk自带的Topic.topicType()前缀表只认'usr'为P2P类型前缀,没有 'p2p'这个条目,而本后端真实分配的P2P话题名是`p2p<hash>`形式——导致 isCommType()对每一个P2P会话都恒定返回false,MeTopic.contacts()因此把所有 单聊会话过滤掉。用真实账号实测确认这不是新会话/顶号账号特有的问题:任何 账号的任何P2P好友关系都受影响,只是群聊和"slf"自聊天不受影响所以此前一直 没被发现。 聊天列表改用Tinode#mapTopics()(不做类型过滤的原始遍历)+ 本项目自己正确 的前缀判断(p2p/grp/slf),不再依赖SDK自身的isCommType()。 实测(e2etester1/e2etester2真实新建好友关系,此前从未聊过天):修复前 标题栏显示原始topic hash+"离线"(对方实际在线)、侧边栏"暂无会话"(消息 其实已正确送达,只是不可见);修复后标题栏正确显示对方昵称+"在线",侧边栏 正确出现该会话及消息预览。
This commit is contained in:
Vendored
+4
@@ -302,6 +302,10 @@ declare module 'tinode-sdk' {
|
|||||||
|
|
||||||
getMeTopic(): MeTopic;
|
getMeTopic(): MeTopic;
|
||||||
getTopic(topicName: string): Topic;
|
getTopic(topicName: string): Topic;
|
||||||
|
/** Iterates EVERY locally-cached topic (any type, including `me`/`fnd`), unlike `MeTopic.contacts()`
|
||||||
|
* which additionally filters by `isCommType()` -- see topics.ts's `getChatListSnapshot` for why
|
||||||
|
* that filter can't be trusted for this backend's P2P topics. */
|
||||||
|
mapTopics(callback: (topic: Topic, key: string) => void): void;
|
||||||
|
|
||||||
account(userId: string, scheme: string, secret: string, login: boolean, desc?: unknown): Promise<unknown>;
|
account(userId: string, scheme: string, secret: string, login: boolean, desc?: unknown): Promise<unknown>;
|
||||||
createAccountBasic(
|
createAccountBasic(
|
||||||
|
|||||||
@@ -253,22 +253,50 @@ export function ensureTopicAttached(topicName: string): Promise<Topic> {
|
|||||||
|
|
||||||
let chatListSnapshot: Contact[] | null = null;
|
let chatListSnapshot: Contact[] | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Real, addressable chat-list topic types this backend actually issues -- deliberately NOT using
|
||||||
|
* `Topic#isCommType()`/`isP2PType()` (confirmed live by reading tinode-sdk's own `Topic.topicType()`
|
||||||
|
* table: it maps prefix `'usr'` to P2P, with NO entry for `'p2p'` at all). That table is right for a
|
||||||
|
* peer ALIAS (`usr<peerId>`, used before a P2P topic has ever been subscribed), but this backend's
|
||||||
|
* REAL, server-assigned P2P topic name is `p2p<hash>` (confirmed via raw `{meta topic:"me"}` capture:
|
||||||
|
* every subscription the server reports uses this form, never the alias) -- so `isCommType()` returns
|
||||||
|
* false for every P2P topic once its real name is known, silently filtering ALL of them out of
|
||||||
|
* `MeTopic.contacts()`. This has nothing to do with account age or how a conversation was opened --
|
||||||
|
* verified by direct fresh-login inspection that even a heavily-used account with many real, working
|
||||||
|
* P2P friendships had ZERO P2P entries in `contacts()`; every P2P row the chat list has ever shown
|
||||||
|
* this session was actually the special-cased `slf` self-chat entry.
|
||||||
|
*/
|
||||||
|
function isChatListTopicName(name: string): boolean {
|
||||||
|
return name.startsWith('p2p') || name.startsWith('grp') || name === 'slf';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached the same way getMessagesSnapshot is, but invalidated by ANY topic's change (a new message
|
* Cached the same way getMessagesSnapshot is, but invalidated by ANY topic's change (a new message
|
||||||
* on a contact that isn't currently open still needs to move that row and refresh its preview).
|
* on a contact that isn't currently open still needs to move that row and refresh its preview).
|
||||||
* Triggers each contact's one-shot preview prefetch as a side effect of being read -- cheap and
|
* Triggers each contact's one-shot preview prefetch as a side effect of being read -- cheap and
|
||||||
* idempotent (prefetchPreview no-ops after the first call per topic), so it's fine to do this on
|
* idempotent (prefetchPreview no-ops after the first call per topic), so it's fine to do this on
|
||||||
* every rebuild rather than needing a separate "wire up previews" pass.
|
* every rebuild rather than needing a separate "wire up previews" pass.
|
||||||
|
*
|
||||||
|
* Uses `Tinode#mapTopics` (every locally-cached topic, unfiltered) + `isChatListTopicName` instead of
|
||||||
|
* `MeTopic#contacts()` -- see that function's own comment for why `contacts()`'s built-in filter can't
|
||||||
|
* be trusted here. `mapTopics` still only sees topics the SDK has locally cached, which `me`'s own
|
||||||
|
* subscribe (`subscribeMeTopic`'s `withLaterSub()`) and any direct `openConversation()` both populate.
|
||||||
*/
|
*/
|
||||||
export function getChatListSnapshot(): Contact[] {
|
export function getChatListSnapshot(): Contact[] {
|
||||||
if (chatListSnapshot) return chatListSnapshot;
|
if (chatListSnapshot) return chatListSnapshot;
|
||||||
const byTopic = new Map<string, Contact>();
|
const byTopic = new Map<string, Contact>();
|
||||||
getTinode()
|
getTinode().mapTopics((topic) => {
|
||||||
.getMeTopic()
|
if (!topic.name || !isChatListTopicName(topic.name)) return;
|
||||||
.contacts((contact) => {
|
prefetchPreview(topic.name);
|
||||||
if (!contact.topic) return;
|
// `.topic` (what every consumer of this list actually reads) normally arrives via
|
||||||
prefetchPreview(contact.topic);
|
// `_processMetaSubs` merging the server's own `sub.topic` field onto this same cached instance --
|
||||||
byTopic.set(contact.topic, contact);
|
// but a brand-new P2P topic reached through `openConversation()` is cached here the moment its
|
||||||
|
// OWN subscribe resolves, potentially before `me`'s subscription list has ever reported it. Set
|
||||||
|
// explicitly rather than assuming that merge already happened; kept as the same live instance
|
||||||
|
// (not a clone) so prototype methods like `latestMessage()` survive.
|
||||||
|
const contact = topic as unknown as Contact;
|
||||||
|
contact.topic = topic.name;
|
||||||
|
byTopic.set(topic.name, contact);
|
||||||
});
|
});
|
||||||
const rows = [...byTopic.values()];
|
const rows = [...byTopic.values()];
|
||||||
chatListSnapshot = rows;
|
chatListSnapshot = rows;
|
||||||
|
|||||||
Reference in New Issue
Block a user