diff --git a/web/src/lib/tinode/send.ts b/web/src/lib/tinode/send.ts index e70377ca..0b8f81fd 100644 --- a/web/src/lib/tinode/send.ts +++ b/web/src/lib/tinode/send.ts @@ -206,8 +206,19 @@ export function sendReply(topic: Topic, text: string, quoted: StoredMessage): Pr * Single-message forward, sent directly to one target topic. Simplified from the old app's own * flow (which stages the forwarded content and lets the user add a comment via the normal composer * before sending) -- this sends immediately on picking a target, trading that one nicety for a - * much smaller "pick a contact" dialog. `head.forwarded` records the source for traceability; it - * has no reader anywhere yet, matching the old app (no confirmed call site reads it back either). + * much smaller "pick a contact" dialog. + * + * `head['x-im-forwarded']`/`head['x-im-forward-source']` (not the bare `forwarded: "topic:seq"` + * string this used to write): confirmed via direct DB inspection (the same investigation that found + * `flatForwardPayload`'s root cause) that mobile's OWN forwards use exactly this two-field shape -- + * `{"x-im-forwarded":true,"x-im-forward-source":{"seq":N,"from":"usr...","topic":"..."}}` -- a real, + * live example from a genuine mobile-to-mobile forward chain, not a guess. The bare `forwarded` + * string this web app used to write instead had zero readers anywhere in this codebase (confirmed: + * grepped for it), so nothing here regresses by switching -- but writing it meant every message this + * app forwards was tagged in a shape mobile's own client has no reason to recognize, which would + * silently fail to show a "转发自 XXX" indicator (or whatever mobile's UI does with this) for any + * message forwarded from web, even though the message CONTENT itself displays fine either way + * (content and forward-provenance are two independent things carried in the same packet). */ export function forwardMessage(sourceTopic: string, msg: StoredMessage, target: Topic): Promise { const flat = flatForwardPayload(msg); @@ -216,6 +227,10 @@ export function forwardMessage(sourceTopic: string, msg: StoredMessage, target: // lib/tinode/calls.ts's own identical cast) passes a plain object straight through as literal // `content` -- true for the attachment/contact-card/call-result branches of `flatForwardPayload`. const newMsg = target.createMessage(content as unknown as DraftyContent, false); - newMsg.head = Object.assign(newMsg.head || {}, flat?.headOverrides, { forwarded: `${sourceTopic}:${msg.seq}`, 'x-im-client-mid': newClientMid() }); + newMsg.head = Object.assign(newMsg.head || {}, flat?.headOverrides, { + 'x-im-forwarded': true, + 'x-im-forward-source': { seq: msg.seq, from: msg.from, topic: sourceTopic }, + 'x-im-client-mid': newClientMid(), + }); return publishWithReattach(target, newMsg); }