修复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;
|
||||
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>;
|
||||
createAccountBasic(
|
||||
|
||||
@@ -253,22 +253,50 @@ export function ensureTopicAttached(topicName: string): Promise<Topic> {
|
||||
|
||||
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
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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[] {
|
||||
if (chatListSnapshot) return chatListSnapshot;
|
||||
const byTopic = new Map<string, Contact>();
|
||||
getTinode()
|
||||
.getMeTopic()
|
||||
.contacts((contact) => {
|
||||
if (!contact.topic) return;
|
||||
prefetchPreview(contact.topic);
|
||||
byTopic.set(contact.topic, contact);
|
||||
getTinode().mapTopics((topic) => {
|
||||
if (!topic.name || !isChatListTopicName(topic.name)) return;
|
||||
prefetchPreview(topic.name);
|
||||
// `.topic` (what every consumer of this list actually reads) normally arrives via
|
||||
// `_processMetaSubs` merging the server's own `sub.topic` field onto this same cached instance --
|
||||
// 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()];
|
||||
chatListSnapshot = rows;
|
||||
|
||||
Reference in New Issue
Block a user