From 1d383b4e93da30c49731527c9233f8c65718074d Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 25 Sep 2026 11:34:44 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BD=AC=E5=8F=91=E6=B6=88=E6=81=AF=E7=9A=84?= =?UTF-8?q?=E6=9D=A5=E6=BA=90=E6=A0=87=E8=AE=B0=E6=94=B9=E7=94=A8=E6=89=8B?= =?UTF-8?q?=E6=9C=BA=E7=AB=AF=E5=90=8C=E6=AC=BE=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BD=91=E9=A1=B5=E8=BD=AC=E5=8F=91=E5=9C=A8?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E7=AB=AF=E8=AE=A4=E4=B8=8D=E5=87=BA"?= =?UTF-8?q?=E8=BD=AC=E5=8F=91"=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 网页版转发功能此前一直用自造的 head.forwarded: "topic:seq" 单字符串字段记录 来源,且这个字段在整个网页项目里从未被任何地方读取过(纯写入,无消费者)。 数据库排查(排查转发内容丢失bug时顺带确认)发现手机端自己的转发用的是完全 不同的字段:head['x-im-forwarded']:true + head['x-im-forward-source']: {seq,from,topic},是真实双端数据。网页版这个自造格式手机端根本不认识, 意味着从网页转发出去的消息,即使内容显示正常,手机端也大概率认不出"这是 一条转发消息",不会显示对应的转发来源提示(如果手机端UI有这个功能的话)。 改成手机端同款的两字段格式。确认这不影响网页自己的任何逻辑(旧字段确认无 读取方)。 Co-Authored-By: Claude Sonnet 5 --- web/src/lib/tinode/send.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) 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); }