diff --git a/crates/ruma-common/CHANGELOG.md b/crates/ruma-common/CHANGELOG.md index 81d5f3670e..efecd6aadf 100644 --- a/crates/ruma-common/CHANGELOG.md +++ b/crates/ruma-common/CHANGELOG.md @@ -15,6 +15,8 @@ Improvements: * Stabilize support for private read receipts * Add stable support for threads * Move `Relation::Thread` and associated types and methods out of `unstable-msc3440` + * Add parameter to `RoomMessageEventContent::make_reply_to` to be thread-aware + * Add `RoomMessageEventContent::make_for_reply` # 0.10.3 diff --git a/crates/ruma-common/src/events/room/message.rs b/crates/ruma-common/src/events/room/message.rs index dd350ad074..de2b2069ba 100644 --- a/crates/ruma-common/src/events/room/message.rs +++ b/crates/ruma-common/src/events/room/message.rs @@ -106,14 +106,20 @@ impl RoomMessageEventContent { /// Turns `self` into a reply to the given message. /// /// Takes the `body` / `formatted_body` (if any) in `self` for the main text and prepends a - /// quoted version of `original_message`. Also sets the `in_reply_to` field inside `relates_to`. + /// quoted version of `original_message`. Also sets the `in_reply_to` field inside `relates_to`, + /// and optionally the `rel_type` to `m.thread` if the `original_message is in a thread and + /// thread forwarding is enabled. #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))] /// /// # Panics /// /// Panics if `self` has a `formatted_body` with a format other than HTML. #[track_caller] - pub fn make_reply_to(mut self, original_message: &OriginalRoomMessageEvent) -> Self { + pub fn make_reply_to( + mut self, + original_message: &OriginalRoomMessageEvent, + forward_thread: ForwardThread, + ) -> Self { let empty_formatted_body = || FormattedBody::html(String::new()); let (body, formatted) = { @@ -154,43 +160,6 @@ impl RoomMessageEventContent { ); } - self.relates_to = Some(Relation::Reply { - in_reply_to: InReplyTo { event_id: original_message.event_id.to_owned() }, - }); - - self - } - - /// Create a new reply with the given message and optionally forwards the [`Relation::Thread`]. - /// - /// If `message` is a text, an emote or a notice message, it is modified to include the rich - /// reply fallback. - #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))] - pub fn reply( - message: MessageType, - original_message: &OriginalRoomMessageEvent, - forward_thread: ForwardThread, - ) -> Self { - let make_reply = |body, formatted: Option| { - reply::plain_and_formatted_reply_body(body, formatted.map(|f| f.body), original_message) - }; - - let msgtype = match message { - MessageType::Text(TextMessageEventContent { body, formatted, .. }) => { - let (body, html_body) = make_reply(body, formatted); - MessageType::Text(TextMessageEventContent::html(body, html_body)) - } - MessageType::Emote(EmoteMessageEventContent { body, formatted, .. }) => { - let (body, html_body) = make_reply(body, formatted); - MessageType::Emote(EmoteMessageEventContent::html(body, html_body)) - } - MessageType::Notice(NoticeMessageEventContent { body, formatted, .. }) => { - let (body, html_body) = make_reply(body, formatted); - MessageType::Notice(NoticeMessageEventContent::html(body, html_body)) - } - _ => message, - }; - let relates_to = if let Some(Relation::Thread(Thread { event_id, .. })) = original_message .content .relates_to @@ -203,48 +172,34 @@ impl RoomMessageEventContent { in_reply_to: InReplyTo { event_id: original_message.event_id.clone() }, } }; + self.relates_to = Some(relates_to); - Self { msgtype, relates_to: Some(relates_to) } + self } - /// Create a new message for a thread that is optionally a reply. + /// Turns `self` into a new message for a thread, that is optionally a reply. /// - /// Looks for a [`Relation::Thread`] in `previous_message`. If it exists, a message for the same - /// thread is created. If it doesn't, a new thread with `previous_message` as the root is + /// Looks for a [`Relation::Thread`] in `previous_message`. If it exists, this message will be + /// in the same thread. If it doesn't, a new thread with `previous_message` as the root is /// created. /// - /// If `message` is a text, an emote or a notice message, and this is a reply in the thread, it - /// is modified to include the rich reply fallback. + /// If this is a reply within the thread, takes the `body` / `formatted_body` (if any) in `self` + /// for the main text and prepends a quoted version of `previous_message`. Also sets the + /// `in_reply_to` field inside `relates_to`. #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))] - pub fn for_thread( - message: MessageType, + /// + /// # Panics + /// + /// Panics if this is a reply within the thread and `self` has a `formatted_body` with a format + /// other than HTML. + pub fn make_for_thread( + mut self, previous_message: &OriginalRoomMessageEvent, is_reply: ReplyWithinThread, ) -> Self { - let make_reply = |body, formatted: Option| { - reply::plain_and_formatted_reply_body(body, formatted.map(|f| f.body), previous_message) - }; - - let msgtype = if is_reply == ReplyWithinThread::Yes { - // If this is a real reply, add the rich reply fallback. - match message { - MessageType::Text(TextMessageEventContent { body, formatted, .. }) => { - let (body, html_body) = make_reply(body, formatted); - MessageType::Text(TextMessageEventContent::html(body, html_body)) - } - MessageType::Emote(EmoteMessageEventContent { body, formatted, .. }) => { - let (body, html_body) = make_reply(body, formatted); - MessageType::Emote(EmoteMessageEventContent::html(body, html_body)) - } - MessageType::Notice(NoticeMessageEventContent { body, formatted, .. }) => { - let (body, html_body) = make_reply(body, formatted); - MessageType::Notice(NoticeMessageEventContent::html(body, html_body)) - } - _ => message, - } - } else { - message - }; + if is_reply == ReplyWithinThread::Yes { + self = self.make_reply_to(previous_message, ForwardThread::No); + } let thread_root = if let Some(Relation::Thread(Thread { event_id, .. })) = &previous_message.content.relates_to @@ -254,14 +209,13 @@ impl RoomMessageEventContent { previous_message.event_id.clone() }; - Self { - msgtype, - relates_to: Some(Relation::Thread(Thread { - event_id: thread_root, - in_reply_to: InReplyTo { event_id: previous_message.event_id.clone() }, - is_falling_back: is_reply == ReplyWithinThread::No, - })), - } + self.relates_to = Some(Relation::Thread(Thread { + event_id: thread_root, + in_reply_to: InReplyTo { event_id: previous_message.event_id.clone() }, + is_falling_back: is_reply == ReplyWithinThread::No, + })); + + self } /// Returns a reference to the `msgtype` string. diff --git a/crates/ruma-common/tests/events/room_message.rs b/crates/ruma-common/tests/events/room_message.rs index 506ab87e6a..71a43d5083 100644 --- a/crates/ruma-common/tests/events/room_message.rs +++ b/crates/ruma-common/tests/events/room_message.rs @@ -486,7 +486,7 @@ fn content_deserialization_failure() { #[test] #[cfg(feature = "unstable-sanitize")] fn reply_sanitize() { - use ruma_common::events::room::message::TextMessageEventContent; + use ruma_common::events::room::message::{ForwardThread, TextMessageEventContent}; let first_message = OriginalRoomMessageEvent { content: RoomMessageEventContent::text_html( @@ -504,7 +504,7 @@ fn reply_sanitize() { "This is the _second_ message", "This is the second message", ) - .make_reply_to(&first_message), + .make_reply_to(&first_message, ForwardThread::Yes), event_id: event_id!("$143273582443PhrSn:example.org").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(10_000)), room_id: room_id!("!testroomid:example.org").to_owned(), @@ -515,7 +515,7 @@ fn reply_sanitize() { "This is **my** reply", "This is my reply", ) - .make_reply_to(&second_message); + .make_reply_to(&second_message, ForwardThread::Yes); let (body, formatted) = assert_matches!( first_message.content.msgtype,