diff --git a/web/src/lib/tinode/tinode-sdk.d.ts b/web/src/lib/tinode/tinode-sdk.d.ts index c8b8d9af..d4bf2f67 100644 --- a/web/src/lib/tinode/tinode-sdk.d.ts +++ b/web/src/lib/tinode/tinode-sdk.d.ts @@ -270,7 +270,7 @@ declare module 'tinode-sdk' { /** Single-slot callbacks the app wires fresh per "currently open" topic and tears down on leave. */ onData?: (data?: StoredMessage) => void; onAllMessagesReceived?: (count: number) => void; - onInfo?: (info: { what: 'read' | 'recv' | 'kp' | 'call'; seq?: number; from?: string; event?: string; payload?: Record }) => void; + onInfo?: (info: { what: 'read' | 'recv' | 'kp' | 'call' | 'im'; seq?: number; from?: string; event?: string; payload?: Record }) => void; onMetaDesc?: (desc: unknown) => void; /** `me`-topic-only in practice (verified tinode.dev.js:4654-4657): fired with the just-subscribed * Topic itself the moment a brand-new contact's subscribe resolves -- notably including the diff --git a/web/src/lib/tinode/topics.ts b/web/src/lib/tinode/topics.ts index 84e4e08b..6a8895a7 100644 --- a/web/src/lib/tinode/topics.ts +++ b/web/src/lib/tinode/topics.ts @@ -110,7 +110,7 @@ export function subscribeAnyTopicChange(callback: () => void): () => void { } export interface RawTopicInfo { - what: 'read' | 'recv' | 'kp' | 'call'; + what: 'read' | 'recv' | 'kp' | 'call' | 'im'; seq?: number; from?: string; event?: string; @@ -240,6 +240,24 @@ function wireTopicCallbacks(topic: Topic) { if (groupEvent?.what === 'group' && groupEvent.event === 'group.dissolved') { markTopicGoneAndNotify(topic.name); } + // docs/IM-前后端对接文档.md:416,1256: recall/edit push a custom `{info what:"im", + // event:"message.recalled"|"message.edited", payload:{seq,version}}` to every other ONLINE + // topic member -- explicitly NOT a native Tinode mechanism (`_routeInfo`'s switch has no `"im"` + // case, confirmed reading tinode.dev.js the same way the group.dissolved gap above was), so + // nothing here ever picked it up before. `notify()` above still fired (it's unconditional), so a + // component watching this topic WOULD re-render -- but the SDK's own local message cache was + // never told the content changed, so it re-rendered the SAME stale pre-recall/pre-edit bubble. + // The doc's own instruction is explicit: "按 topic+seq+version 刷新本地消息,不要等待用户重新 + // 进入会话才更新" -- live-confirmed this was exactly what was happening instead (recall/edit only + // ever showed up for the OTHER party after something unrelated caused a full resync, e.g. + // reopening the conversation). Reuses the exact `getMeta(...).withData(seq,seq+1,1)` targeted + // single-message refetch ConversationView's own jump-to-message feature already established. + if (info?.what === 'im' && (info.event === 'message.recalled' || info.event === 'message.edited')) { + const seq = info.payload?.seq; + if (typeof seq === 'number') { + topic.getMeta(topic.startMetaQuery().withData(seq, seq + 1, 1).build()).catch(() => {}); + } + } }; topic.onMetaDesc = notify; // Only ever actually invoked on the `me` topic (the SDK reads `me.onMetaSub`/`me.onSubsUpdated`