fix: recall/edit not syncing to the other party in real time

docs/IM-前后端对接文档.md:416,1256 documents a custom {info what:"im",
event:"message.recalled"|"message.edited"} push to every other online
topic member, with an explicit instruction to refresh that message by
topic+seq+version rather than waiting for the user to re-enter the
conversation. Nothing recognized "im" as a valid `what` value (the
native SDK's own _routeInfo has no such case either, same gap as the
existing group.dissolved handling right above this fix), so the push
arrived over the wire but never triggered anything -- the peer's local
message cache kept the stale pre-recall/pre-edit content until some
unrelated full resync happened to refetch it.

Widen the `what` union (both the app-level RawTopicInfo and the SDK's
own onInfo callback type) to include 'im', and on that event issue the
same targeted single-message getMeta(...).withData(seq,seq+1,1) refetch
ConversationView's jump-to-message feature already established.

Live-verified: recalling a message now shows "⚠ 消息已删除" on the
other party's screen immediately, with no manual refresh.
This commit is contained in:
Your Name
2026-09-18 16:09:52 +08:00
parent 1a4a96461a
commit f5a512b8c9
2 changed files with 20 additions and 2 deletions
+1 -1
View File
@@ -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<string, unknown> }) => void;
onInfo?: (info: { what: 'read' | 'recv' | 'kp' | 'call' | 'im'; seq?: number; from?: string; event?: string; payload?: Record<string, unknown> }) => 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
+19 -1
View File
@@ -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`