diff --git a/src/widgets/chat-message.jsx b/src/widgets/chat-message.jsx index 586061f3..cca076a5 100644 --- a/src/widgets/chat-message.jsx +++ b/src/widgets/chat-message.jsx @@ -374,6 +374,23 @@ class BaseChatMessage extends React.PureComponent { // Content consists of 1-5 emoji characters. Count how many and use it to increase the font size. textSizeClass += ' emoji-' + (content || '').match(/\p{RGI_Emoji}/vg).length; } + } else if (content == null) { + // Tinode's wire protocol marks a recalled message by nulling its `content` in place (the seq + // slot stays, everything else is gone) -- see server's message.recalled handling and + // IM-前后端对接文档.md 1256行. That's a *different* deletion shape from the msg.hi-flagged kind + // messages-view.jsx already handles by rendering a dedicated + // instead of a ChatMessage at all (deletedText is that component's own prop, not this one's -- + // there's no equivalent plumbed in here). Recalled messages still go through ordinary ChatMessage, + // so without this branch a null content fell all the way through to the generic "invalid content" + // case below -- confirmed live: recalling a message left the *other* party's chat showing a bare + // "⚠ invalid content" (无效内容) row where "消息已删除" belongs (the recalling side just drops the + // row from its own list locally, unaffected by this). Reuses the exact same i18n id/defaultMessage + // messages-view.jsx's `messages.message_deleted` already registers (translated zh string: + // "消息已删除"), not a new string. `content == null` (not `===`) intentionally also catches + // `undefined`, same as every other falsy-content branch here. + content = <>block + ; } else { content = <>warning_amber