events: Fix plain text reply constructors

Send a formatted_body, according to the spec
This commit is contained in:
Kévin Commaille 2022-04-05 19:44:21 +02:00 committed by Kévin Commaille
parent 002d863738
commit 1e1fa06342
3 changed files with 38 additions and 70 deletions

View File

@ -19,6 +19,9 @@ Breaking changes:
* Change `events::room` media types to use `Duration` where applicable * Change `events::room` media types to use `Duration` where applicable
* Move `prev_content` into `unsigned` * Move `prev_content` into `unsigned`
* Rename `identifiers::Error` to `IdParseError` * Rename `identifiers::Error` to `IdParseError`
* Fix the `RoomMessageEventContent::*_reply_plain` methods that now return a
message with a `formatted_body`, according to the spec. Therefore, they only
accept `OriginalRoomMessageEvent`s like their HTML counterparts.
Improvements: Improvements:

View File

@ -42,8 +42,6 @@ pub mod feedback;
mod relation_serde; mod relation_serde;
mod reply; mod reply;
pub use reply::ReplyBaseEvent;
/// The content of an `m.room.message` event. /// The content of an `m.room.message` event.
/// ///
/// This event is used when sending messages in a room. /// This event is used when sending messages in a room.
@ -105,28 +103,34 @@ impl RoomMessageEventContent {
} }
/// Creates a plain text reply to a message. /// Creates a plain text reply to a message.
///
/// This constructor requires an [`OriginalRoomMessageEvent`] since it creates a permalink to
/// the previous message, for which the room ID is required. If you want to reply to an
/// [`OriginalSyncRoomMessageEvent`], you have to convert it first by calling
/// [`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event].
pub fn text_reply_plain( pub fn text_reply_plain(
reply: impl fmt::Display, reply: impl fmt::Display,
original_message: &impl ReplyBaseEvent, original_message: &OriginalRoomMessageEvent,
) -> Self { ) -> Self {
let quoted = reply::get_plain_quote_fallback(original_message); let quoted = reply::get_plain_quote_fallback(original_message);
let quoted_html = reply::get_html_quote_fallback(original_message);
let body = format!("{}\n\n{}", quoted, reply); let body = format!("{}\n\n{}", quoted, reply);
let html_body = format!("{}\n\n{}", quoted_html, reply);
Self { Self {
relates_to: Some(Relation::Reply { relates_to: Some(Relation::Reply {
in_reply_to: InReplyTo { event_id: original_message.event_id().to_owned() }, in_reply_to: InReplyTo { event_id: original_message.event_id.to_owned() },
}), }),
..Self::text_plain(body) ..Self::text_html(body, html_body)
} }
} }
/// Creates a html text reply to a message. /// Creates a html text reply to a message.
/// ///
/// Different from `text_reply_plain`, this constructor requires specifically a /// This constructor requires an [`OriginalRoomMessageEvent`] since it creates a permalink to
/// [`RoomMessageEvent`] since it creates a permalink to the previous message, for which the /// the previous message, for which the room ID is required. If you want to reply to an
/// room ID is required. If you want to reply to a [`SyncRoomMessageEvent`], you have to convert /// [`OriginalSyncRoomMessageEvent`], you have to convert it first by calling
/// it first by calling
/// [`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event]. /// [`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event].
pub fn text_reply_html( pub fn text_reply_html(
reply: impl fmt::Display, reply: impl fmt::Display,
@ -148,27 +152,34 @@ impl RoomMessageEventContent {
} }
/// Creates a plain text notice reply to a message. /// Creates a plain text notice reply to a message.
///
/// This constructor requires an [`OriginalRoomMessageEvent`] since it creates a permalink to
/// the previous message, for which the room ID is required. If you want to reply to an
/// [`OriginalSyncRoomMessageEvent`], you have to convert it first by calling
/// [`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event].
pub fn notice_reply_plain( pub fn notice_reply_plain(
reply: impl fmt::Display, reply: impl fmt::Display,
original_message: &impl ReplyBaseEvent, original_message: &OriginalRoomMessageEvent,
) -> Self { ) -> Self {
let quoted = reply::get_plain_quote_fallback(original_message); let quoted = reply::get_plain_quote_fallback(original_message);
let quoted_html = reply::get_html_quote_fallback(original_message);
let body = format!("{}\n\n{}", quoted, reply); let body = format!("{}\n\n{}", quoted, reply);
let html_body = format!("{}\n\n{}", quoted_html, reply);
Self { Self {
relates_to: Some(Relation::Reply { relates_to: Some(Relation::Reply {
in_reply_to: InReplyTo { event_id: original_message.event_id().to_owned() }, in_reply_to: InReplyTo { event_id: original_message.event_id.to_owned() },
}), }),
..Self::notice_plain(body) ..Self::notice_html(body, html_body)
} }
} }
/// Creates a html text notice reply to a message. /// Creates a html text notice reply to a message.
/// ///
/// Different from `notice_reply_plain`, this constructor requires specifically a /// This constructor requires an [`OriginalRoomMessageEvent`] since it creates a permalink to
/// [`RoomMessageEvent`] since it creates a permalink to the previous message, for which the /// the previous message, for which the room ID is required. If you want to reply to an
/// room ID is required. If you want to reply to a [`SyncRoomMessageEvent`], you have to convert /// [`OriginalSyncRoomMessageEvent`], you have to convert it first by calling
/// it first by calling
/// [`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event]. /// [`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event].
pub fn notice_reply_html( pub fn notice_reply_html(
reply: impl fmt::Display, reply: impl fmt::Display,

View File

@ -1,59 +1,11 @@
use indoc::formatdoc; use indoc::formatdoc;
use super::{ use super::{FormattedBody, MessageType, OriginalRoomMessageEvent};
FormattedBody, MessageType, OriginalRoomMessageEvent, OriginalSyncRoomMessageEvent,
RoomMessageEventContent,
};
use crate::{EventId, UserId};
/// An event that can be replied to. pub fn get_plain_quote_fallback(original_message: &OriginalRoomMessageEvent) -> String {
/// let sender = &original_message.sender;
/// This trait only exists to allow the plain-text `reply` constructors on `MessageLikeEventContent`
/// to use either a [`RoomMessageEvent`] or a [`SyncRoomMessageEvent`] as the event being replied
/// to.
pub trait ReplyBaseEvent {
#[doc(hidden)]
fn event_id(&self) -> &EventId;
#[doc(hidden)] match &original_message.content.msgtype {
fn sender(&self) -> &UserId;
#[doc(hidden)]
fn content(&self) -> &RoomMessageEventContent;
}
impl ReplyBaseEvent for OriginalRoomMessageEvent {
fn event_id(&self) -> &EventId {
&self.event_id
}
fn sender(&self) -> &UserId {
&self.sender
}
fn content(&self) -> &RoomMessageEventContent {
&self.content
}
}
impl ReplyBaseEvent for OriginalSyncRoomMessageEvent {
fn event_id(&self) -> &EventId {
&self.event_id
}
fn sender(&self) -> &UserId {
&self.sender
}
fn content(&self) -> &RoomMessageEventContent {
&self.content
}
}
pub fn get_plain_quote_fallback(original_message: &impl ReplyBaseEvent) -> String {
let sender = original_message.sender();
match &original_message.content().msgtype {
MessageType::Audio(_) => { MessageType::Audio(_) => {
format!("> <{}> sent an audio file.", sender) format!("> <{}> sent an audio file.", sender)
} }
@ -301,10 +253,12 @@ fn formatted_or_plain_body<'a>(formatted: &'a Option<FormattedBody>, body: &'a s
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
event_id, events::MessageLikeUnsigned, room_id, user_id, MilliSecondsSinceUnixEpoch, event_id,
events::{room::message::RoomMessageEventContent, MessageLikeUnsigned},
room_id, user_id, MilliSecondsSinceUnixEpoch,
}; };
use super::{OriginalRoomMessageEvent, RoomMessageEventContent}; use super::OriginalRoomMessageEvent;
#[test] #[test]
fn plain_quote_fallback_multiline() { fn plain_quote_fallback_multiline() {