events: Add RoomMessageEventContent::make_reply_to
… and deprecate reply constructors.
This commit is contained in:
parent
5c3610b9b7
commit
b7b7d043f3
@ -1,5 +1,5 @@
|
|||||||
<!-- Keep this comment so the content is always included as a new paragraph -->
|
<!-- Keep this comment so the content is always included as a new paragraph -->
|
||||||
This constructor requires an [`OriginalRoomMessageEvent`] since it creates a permalink to
|
This function 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
|
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
|
[`OriginalSyncRoomMessageEvent`], you have to convert it first by calling
|
||||||
[`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event].
|
[`.into_full_event()`][crate::events::OriginalSyncMessageLikeEvent::into_full_event].
|
||||||
|
@ -103,76 +103,116 @@ impl RoomMessageEventContent {
|
|||||||
Self::new(MessageType::Notice(NoticeMessageEventContent::markdown(body)))
|
Self::new(MessageType::Notice(NoticeMessageEventContent::markdown(body)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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`.
|
||||||
|
#[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 {
|
||||||
|
let empty_formatted_body = || FormattedBody::html(String::new());
|
||||||
|
|
||||||
|
let (body, formatted) = {
|
||||||
|
match &mut self.msgtype {
|
||||||
|
MessageType::Emote(m) => {
|
||||||
|
(&mut m.body, Some(m.formatted.get_or_insert_with(empty_formatted_body)))
|
||||||
|
}
|
||||||
|
MessageType::Notice(m) => {
|
||||||
|
(&mut m.body, Some(m.formatted.get_or_insert_with(empty_formatted_body)))
|
||||||
|
}
|
||||||
|
MessageType::Text(m) => {
|
||||||
|
(&mut m.body, Some(m.formatted.get_or_insert_with(empty_formatted_body)))
|
||||||
|
}
|
||||||
|
MessageType::Audio(m) => (&mut m.body, None),
|
||||||
|
MessageType::File(m) => (&mut m.body, None),
|
||||||
|
MessageType::Image(m) => (&mut m.body, None),
|
||||||
|
MessageType::Location(m) => (&mut m.body, None),
|
||||||
|
MessageType::ServerNotice(m) => (&mut m.body, None),
|
||||||
|
MessageType::Video(m) => (&mut m.body, None),
|
||||||
|
MessageType::VerificationRequest(m) => (&mut m.body, None),
|
||||||
|
MessageType::_Custom(m) => (&mut m.body, None),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(f) = formatted {
|
||||||
|
assert_eq!(
|
||||||
|
f.format,
|
||||||
|
MessageFormat::Html,
|
||||||
|
"make_reply_to can't handle non-HTML formatted messages"
|
||||||
|
);
|
||||||
|
|
||||||
|
let formatted_body = &mut f.body;
|
||||||
|
|
||||||
|
(*body, *formatted_body) = reply::plain_and_formatted_reply_body(
|
||||||
|
body.as_str(),
|
||||||
|
(!formatted_body.is_empty()).then(|| formatted_body.as_str()),
|
||||||
|
original_message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.relates_to = Some(Relation::Reply {
|
||||||
|
in_reply_to: InReplyTo { event_id: original_message.event_id.to_owned() },
|
||||||
|
});
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a plain text reply to a message.
|
/// Creates a plain text reply to a message.
|
||||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
||||||
|
#[deprecated = "\
|
||||||
|
use [`Self::text_plain`](#method.text_plain)`(reply).`\
|
||||||
|
[`make_reply_to`](#method.make_reply_to)`(original_message)` instead\
|
||||||
|
"]
|
||||||
pub fn text_reply_plain(
|
pub fn text_reply_plain(
|
||||||
reply: impl fmt::Display,
|
reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let formatted: Option<&str> = None;
|
Self::text_plain(reply.to_string()).make_reply_to(original_message)
|
||||||
let (body, html_body) =
|
|
||||||
reply::plain_and_formatted_reply_body(reply, formatted, original_message);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
relates_to: Some(Relation::Reply {
|
|
||||||
in_reply_to: InReplyTo { event_id: original_message.event_id.to_owned() },
|
|
||||||
}),
|
|
||||||
..Self::text_html(body, html_body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a html text reply to a message.
|
/// Creates a html text reply to a message.
|
||||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
||||||
|
#[deprecated = "\
|
||||||
|
use [`Self::text_html`](#method.text_html)`(reply, html_reply).`\
|
||||||
|
[`make_reply_to`](#method.make_reply_to)`(original_message)` instead\
|
||||||
|
"]
|
||||||
pub fn text_reply_html(
|
pub fn text_reply_html(
|
||||||
reply: impl fmt::Display,
|
reply: impl fmt::Display,
|
||||||
html_reply: impl fmt::Display,
|
html_reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (body, html_body) =
|
Self::text_html(reply.to_string(), html_reply.to_string()).make_reply_to(original_message)
|
||||||
reply::plain_and_formatted_reply_body(reply, Some(html_reply), original_message);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
relates_to: Some(Relation::Reply {
|
|
||||||
in_reply_to: InReplyTo { event_id: original_message.event_id.clone() },
|
|
||||||
}),
|
|
||||||
..Self::text_html(body, html_body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a plain text notice reply to a message.
|
/// Creates a plain text notice reply to a message.
|
||||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
||||||
|
#[deprecated = "\
|
||||||
|
use [`Self::notice_plain`](#method.notice_plain)`(reply).`\
|
||||||
|
[`make_reply_to`](#method.make_reply_to)`(original_message)` instead\
|
||||||
|
"]
|
||||||
pub fn notice_reply_plain(
|
pub fn notice_reply_plain(
|
||||||
reply: impl fmt::Display,
|
reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let formatted: Option<&str> = None;
|
Self::notice_plain(reply.to_string()).make_reply_to(original_message)
|
||||||
let (body, html_body) =
|
|
||||||
reply::plain_and_formatted_reply_body(reply, formatted, original_message);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
relates_to: Some(Relation::Reply {
|
|
||||||
in_reply_to: InReplyTo { event_id: original_message.event_id.to_owned() },
|
|
||||||
}),
|
|
||||||
..Self::notice_html(body, html_body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a html text notice reply to a message.
|
/// Creates a html text notice reply to a message.
|
||||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/rich_reply.md"))]
|
||||||
|
#[deprecated = "\
|
||||||
|
use [`Self::notice_html`](#method.notice_html)`(reply, html_reply).`\
|
||||||
|
[`make_reply_to`](#method.make_reply_to)`(original_message)` instead\
|
||||||
|
"]
|
||||||
pub fn notice_reply_html(
|
pub fn notice_reply_html(
|
||||||
reply: impl fmt::Display,
|
reply: impl fmt::Display,
|
||||||
html_reply: impl fmt::Display,
|
html_reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (body, html_body) =
|
Self::notice_html(reply.to_string(), html_reply.to_string()).make_reply_to(original_message)
|
||||||
reply::plain_and_formatted_reply_body(reply, Some(html_reply), original_message);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
relates_to: Some(Relation::Reply {
|
|
||||||
in_reply_to: InReplyTo { event_id: original_message.event_id.clone() },
|
|
||||||
}),
|
|
||||||
..Self::notice_html(body, html_body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new reply with the given message and optionally forwards the [`Relation::Thread`].
|
/// Create a new reply with the given message and optionally forwards the [`Relation::Thread`].
|
||||||
|
@ -8,37 +8,7 @@ use super::{
|
|||||||
use super::{sanitize_html, HtmlSanitizerMode, RemoveReplyFallback};
|
use super::{sanitize_html, HtmlSanitizerMode, RemoveReplyFallback};
|
||||||
|
|
||||||
fn get_message_quote_fallbacks(original_message: &OriginalRoomMessageEvent) -> (String, String) {
|
fn get_message_quote_fallbacks(original_message: &OriginalRoomMessageEvent) -> (String, String) {
|
||||||
match &original_message.content.msgtype {
|
let get_quotes = |body: &str, formatted: Option<&FormattedBody>, is_emote: bool| {
|
||||||
MessageType::Audio(_) => get_quotes("sent an audio file.", None, original_message, false),
|
|
||||||
MessageType::Emote(content) => {
|
|
||||||
get_quotes(&content.body, content.formatted.as_ref(), original_message, true)
|
|
||||||
}
|
|
||||||
MessageType::File(_) => get_quotes("sent a file.", None, original_message, false),
|
|
||||||
MessageType::Image(_) => get_quotes("sent an image.", None, original_message, false),
|
|
||||||
MessageType::Location(_) => get_quotes("sent a location.", None, original_message, false),
|
|
||||||
MessageType::Notice(content) => {
|
|
||||||
get_quotes(&content.body, content.formatted.as_ref(), original_message, false)
|
|
||||||
}
|
|
||||||
MessageType::ServerNotice(content) => {
|
|
||||||
get_quotes(&content.body, None, original_message, false)
|
|
||||||
}
|
|
||||||
MessageType::Text(content) => {
|
|
||||||
get_quotes(&content.body, content.formatted.as_ref(), original_message, false)
|
|
||||||
}
|
|
||||||
MessageType::Video(_) => get_quotes("sent a video.", None, original_message, false),
|
|
||||||
MessageType::_Custom(content) => get_quotes(&content.body, None, original_message, false),
|
|
||||||
MessageType::VerificationRequest(content) => {
|
|
||||||
get_quotes(&content.body, None, original_message, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_quotes(
|
|
||||||
body: &str,
|
|
||||||
formatted: Option<&FormattedBody>,
|
|
||||||
original_message: &OriginalRoomMessageEvent,
|
|
||||||
is_emote: bool,
|
|
||||||
) -> (String, String) {
|
|
||||||
let OriginalRoomMessageEvent { room_id, event_id, sender, content, .. } = original_message;
|
let OriginalRoomMessageEvent { room_id, event_id, sender, content, .. } = original_message;
|
||||||
let is_reply = matches!(content.relates_to, Some(Relation::Reply { .. }));
|
let is_reply = matches!(content.relates_to, Some(Relation::Reply { .. }));
|
||||||
let emote_sign = is_emote.then(|| "* ").unwrap_or_default();
|
let emote_sign = is_emote.then(|| "* ").unwrap_or_default();
|
||||||
@ -61,6 +31,21 @@ fn get_quotes(
|
|||||||
</mx-reply>"
|
</mx-reply>"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
match &original_message.content.msgtype {
|
||||||
|
MessageType::Audio(_) => get_quotes("sent an audio file.", None, false),
|
||||||
|
MessageType::Emote(c) => get_quotes(&c.body, c.formatted.as_ref(), true),
|
||||||
|
MessageType::File(_) => get_quotes("sent a file.", None, false),
|
||||||
|
MessageType::Image(_) => get_quotes("sent an image.", None, false),
|
||||||
|
MessageType::Location(_) => get_quotes("sent a location.", None, false),
|
||||||
|
MessageType::Notice(c) => get_quotes(&c.body, c.formatted.as_ref(), false),
|
||||||
|
MessageType::ServerNotice(c) => get_quotes(&c.body, None, false),
|
||||||
|
MessageType::Text(c) => get_quotes(&c.body, c.formatted.as_ref(), false),
|
||||||
|
MessageType::Video(_) => get_quotes("sent a video.", None, false),
|
||||||
|
MessageType::VerificationRequest(content) => get_quotes(&content.body, None, false),
|
||||||
|
MessageType::_Custom(content) => get_quotes(&content.body, None, false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn formatted_or_plain_body(
|
fn formatted_or_plain_body(
|
||||||
@ -119,10 +104,9 @@ pub fn plain_and_formatted_reply_body(
|
|||||||
let (quoted, quoted_html) = get_message_quote_fallbacks(original_message);
|
let (quoted, quoted_html) = get_message_quote_fallbacks(original_message);
|
||||||
|
|
||||||
let plain = format!("{quoted}\n{body}");
|
let plain = format!("{quoted}\n{body}");
|
||||||
let html = if let Some(formatted) = formatted {
|
let html = match formatted {
|
||||||
format!("{quoted_html}{formatted}")
|
Some(formatted) => format!("{quoted_html}{formatted}"),
|
||||||
} else {
|
None => format!("{quoted_html}{body}"),
|
||||||
format!("{quoted_html}{body}")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
(plain, html)
|
(plain, html)
|
||||||
@ -160,7 +144,7 @@ mod tests {
|
|||||||
<br>\
|
<br>\
|
||||||
multi<br>line\
|
multi<br>line\
|
||||||
</blockquote>\
|
</blockquote>\
|
||||||
</mx-reply>"
|
</mx-reply>",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,22 +500,22 @@ fn reply_sanitize() {
|
|||||||
unsigned: MessageLikeUnsigned::default(),
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
};
|
};
|
||||||
let second_message = OriginalRoomMessageEvent {
|
let second_message = OriginalRoomMessageEvent {
|
||||||
content: RoomMessageEventContent::text_reply_html(
|
content: RoomMessageEventContent::text_html(
|
||||||
"This is the _second_ message",
|
"This is the _second_ message",
|
||||||
"This is the <em>second</em> message",
|
"This is the <em>second</em> message",
|
||||||
&first_message,
|
)
|
||||||
),
|
.make_reply_to(&first_message),
|
||||||
event_id: event_id!("$143273582443PhrSn:example.org").to_owned(),
|
event_id: event_id!("$143273582443PhrSn:example.org").to_owned(),
|
||||||
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(10_000)),
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(10_000)),
|
||||||
room_id: room_id!("!testroomid:example.org").to_owned(),
|
room_id: room_id!("!testroomid:example.org").to_owned(),
|
||||||
sender: user_id!("@user:example.org").to_owned(),
|
sender: user_id!("@user:example.org").to_owned(),
|
||||||
unsigned: MessageLikeUnsigned::default(),
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
};
|
};
|
||||||
let final_reply = RoomMessageEventContent::text_reply_html(
|
let final_reply = RoomMessageEventContent::text_html(
|
||||||
"This is **my** reply",
|
"This is **my** reply",
|
||||||
"This is <strong>my</strong> reply",
|
"This is <strong>my</strong> reply",
|
||||||
&second_message,
|
)
|
||||||
);
|
.make_reply_to(&second_message);
|
||||||
|
|
||||||
let (body, formatted) = assert_matches!(
|
let (body, formatted) = assert_matches!(
|
||||||
first_message.content.msgtype,
|
first_message.content.msgtype,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user