diff --git a/src/views/messages-view.jsx b/src/views/messages-view.jsx index 82cf05b7..932854d2 100644 --- a/src/views/messages-view.jsx +++ b/src/views/messages-view.jsx @@ -1594,7 +1594,24 @@ class MessagesView extends React.Component { msg = Drafty.parse(msg); this.applyMentionResolution(msg); } - msg = Drafty.append(Drafty.sanitizeEntities(this.state.reply.content), msg); + // Both tinode.dev.js helpers below mutate their first argument in place and return that + // same reference (Drafty.sanitizeEntities: `return content` after patching content.ent[i] + // itself; Drafty.append: `first.txt += ...`, `first.fmt.push(...)`, then `return first`) -- + // neither ever clones. Passing `this.state.reply.content` straight through used to hand this + // live React-state object to both, so every call here permanently welded whatever `msg` this + // particular attempt carried onto the quoted content the *reply itself* points to. Invisible + // on an ordinary send since handleCancelReply() clears state.reply right after, discarding the + // now-mutated copy along with it -- but the URL risk dialog above defers doSend (and this + // append) until *after* the check resolves, and cancelling it deliberately leaves state.reply + // alone so the reply survives (see doSend's own comment). Confirmed live: reply to a message, + // type a flagged link, send, click "取消发送", send the exact same text again, confirm through + // the dialog this time -- the composed message quoted the reply once but then repeated the + // typed text twice, because the first (cancelled) attempt had already appended it into + // state.reply.content, and the second attempt appended onto *that*. A deep clone here keeps + // both helpers' in-place mutations confined to a throwaway copy every time, however many times + // sendMessage runs for the same still-open reply. + const replyContentCopy = JSON.parse(JSON.stringify(this.state.reply.content)); + msg = Drafty.append(Drafty.sanitizeEntities(replyContentCopy), msg); } } else if (typeof msg == 'string' && this.mentionedUsers && this.mentionedUsers.length) { // 4.2-4.3 @提醒:Drafty.parse 本来就会把纯文本里的"@word"自动识别成 MN 实体,但