events: Improve usability and docs of m.room.message reply constructors

This commit is contained in:
Jonas Platte 2021-11-18 22:51:58 +01:00
parent 24950b208e
commit 49e5d45ac9
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
2 changed files with 81 additions and 18 deletions

View File

@ -20,6 +20,8 @@ pub mod feedback;
mod relation_serde;
mod reply;
pub use reply::ReplyBaseEvent;
/// The content of an `m.room.message` event.
///
/// This event is used when sending messages in a room.
@ -81,20 +83,28 @@ impl RoomMessageEventContent {
}
/// Creates a plain text reply to a message.
pub fn text_reply_plain(reply: impl fmt::Display, original_message: &RoomMessageEvent) -> Self {
pub fn text_reply_plain(
reply: impl fmt::Display,
original_message: &impl ReplyBaseEvent,
) -> Self {
let quoted = reply::get_plain_quote_fallback(original_message);
let body = format!("{}\n\n{}", quoted, reply);
Self {
relates_to: Some(Relation::Reply {
in_reply_to: InReplyTo { event_id: original_message.event_id.clone() },
in_reply_to: InReplyTo { event_id: original_message.event_id().clone() },
}),
..Self::text_plain(body)
}
}
/// Creates a html text reply to a message.
///
/// Different from `text_reply_plain`, this constructor requires specifically a
/// [`RoomMessageEvent`] since it creates a permalink to the previous message, for which the
/// room ID is required. If you want to reply to a [`SyncRoomMessageEvent`], you have to convert
/// it first by calling [`.into_full_event()`][crate::SyncMessageEvent::into_full_event].
pub fn text_reply_html(
reply: impl fmt::Display,
html_reply: impl fmt::Display,
@ -115,16 +125,21 @@ impl RoomMessageEventContent {
}
/// Creates a plain text notice reply to a message.
///
/// Different from `notice_reply_plain`, this constructor requires specifically a
/// [`RoomMessageEvent`] since it creates a permalink to the previous message, for which the
/// room ID is required. If you want to reply to a [`SyncRoomMessageEvent`], you have to convert
/// it first by calling [`.into_full_event()`][crate::SyncMessageEvent::into_full_event].
pub fn notice_reply_plain(
reply: impl fmt::Display,
original_message: &RoomMessageEvent,
original_message: &impl ReplyBaseEvent,
) -> Self {
let quoted = reply::get_plain_quote_fallback(original_message);
let body = format!("{}\n\n{}", quoted, reply);
Self {
relates_to: Some(Relation::Reply {
in_reply_to: InReplyTo { event_id: original_message.event_id.clone() },
in_reply_to: InReplyTo { event_id: original_message.event_id().clone() },
}),
..Self::notice_plain(body)
}

View File

@ -1,42 +1,90 @@
use indoc::formatdoc;
use ruma_identifiers::{EventId, UserId};
use super::{FormattedBody, MessageType, RoomMessageEvent};
use super::{
FormattedBody, MessageType, RoomMessageEvent, RoomMessageEventContent, SyncRoomMessageEvent,
};
pub fn get_plain_quote_fallback(original_message: &RoomMessageEvent) -> String {
match &original_message.content.msgtype {
/// An event that can be replied to.
///
/// This trait only exists to allow the plain-text `reply` constructors on `MessageEventContent` 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)]
fn sender(&self) -> &UserId;
#[doc(hidden)]
fn content(&self) -> &RoomMessageEventContent;
}
impl ReplyBaseEvent for RoomMessageEvent {
fn event_id(&self) -> &EventId {
&self.event_id
}
fn sender(&self) -> &UserId {
&self.sender
}
fn content(&self) -> &RoomMessageEventContent {
&self.content
}
}
impl ReplyBaseEvent for SyncRoomMessageEvent {
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(_) => {
format!("> <{}> sent an audio file.", original_message.sender)
format!("> <{}> sent an audio file.", sender)
}
MessageType::Emote(content) => {
format!("> * <{}> {}", original_message.sender, content.body)
format!("> * <{}> {}", sender, content.body)
}
MessageType::File(_) => {
format!("> <{}> sent a file.", original_message.sender)
format!("> <{}> sent a file.", sender)
}
MessageType::Image(_) => {
format!("> <{}> sent an image.", original_message.sender)
format!("> <{}> sent an image.", sender)
}
MessageType::Location(content) => {
format!("> <{}> {}", original_message.sender, content.body)
format!("> <{}> {}", sender, content.body)
}
MessageType::Notice(content) => {
format!("> <{}> {}", original_message.sender, content.body)
format!("> <{}> {}", sender, content.body)
}
MessageType::ServerNotice(content) => {
format!("> <{}> {}", original_message.sender, content.body)
format!("> <{}> {}", sender, content.body)
}
MessageType::Text(content) => {
format!("> <{}> {}", original_message.sender, content.body)
format!("> <{}> {}", sender, content.body)
}
MessageType::Video(_) => {
format!("> <{}> sent a video.", original_message.sender)
format!("> <{}> sent a video.", sender)
}
MessageType::_Custom(content) => {
format!("> <{}> {}", original_message.sender, content.body)
format!("> <{}> {}", sender, content.body)
}
#[cfg(feature = "unstable-pre-spec")]
MessageType::VerificationRequest(content) => {
format!("> <{}> {}", original_message.sender, content.body)
format!("> <{}> {}", sender, content.body)
}
}
.replace('\n', "\n> ")