events: Add convenience constructors for threads
This commit is contained in:
parent
1db716f643
commit
a2df988c23
@ -112,11 +112,9 @@ impl RoomMessageEventContent {
|
|||||||
reply: impl fmt::Display,
|
reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let quoted = reply::get_plain_quote_fallback(original_message);
|
let formatted: Option<&str> = None;
|
||||||
let quoted_html = reply::get_html_quote_fallback(original_message);
|
let (body, html_body) =
|
||||||
|
reply::plain_and_formatted_reply_body(reply, formatted, original_message);
|
||||||
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 {
|
||||||
@ -137,11 +135,8 @@ impl RoomMessageEventContent {
|
|||||||
html_reply: impl fmt::Display,
|
html_reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let quoted = reply::get_plain_quote_fallback(original_message);
|
let (body, html_body) =
|
||||||
let quoted_html = reply::get_html_quote_fallback(original_message);
|
reply::plain_and_formatted_reply_body(reply, Some(html_reply), original_message);
|
||||||
|
|
||||||
let body = format!("{}\n\n{}", quoted, reply);
|
|
||||||
let html_body = format!("{}\n\n{}", quoted_html, html_reply);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
relates_to: Some(Relation::Reply {
|
relates_to: Some(Relation::Reply {
|
||||||
@ -161,11 +156,9 @@ impl RoomMessageEventContent {
|
|||||||
reply: impl fmt::Display,
|
reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let quoted = reply::get_plain_quote_fallback(original_message);
|
let formatted: Option<&str> = None;
|
||||||
let quoted_html = reply::get_html_quote_fallback(original_message);
|
let (body, html_body) =
|
||||||
|
reply::plain_and_formatted_reply_body(reply, formatted, original_message);
|
||||||
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 {
|
||||||
@ -186,11 +179,8 @@ impl RoomMessageEventContent {
|
|||||||
html_reply: impl fmt::Display,
|
html_reply: impl fmt::Display,
|
||||||
original_message: &OriginalRoomMessageEvent,
|
original_message: &OriginalRoomMessageEvent,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let quoted = reply::get_plain_quote_fallback(original_message);
|
let (body, html_body) =
|
||||||
let quoted_html = reply::get_html_quote_fallback(original_message);
|
reply::plain_and_formatted_reply_body(reply, Some(html_reply), original_message);
|
||||||
|
|
||||||
let body = format!("{}\n\n{}", quoted, reply);
|
|
||||||
let html_body = format!("{}\n\n{}", quoted_html, html_reply);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
relates_to: Some(Relation::Reply {
|
relates_to: Some(Relation::Reply {
|
||||||
@ -200,6 +190,106 @@ impl RoomMessageEventContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new reply with the given message and optionally forwards the [`Relation::Thread`].
|
||||||
|
///
|
||||||
|
/// If `message` is a text or notice message, it is modified to include the rich reply fallback.
|
||||||
|
#[cfg(feature = "unstable-msc3440")]
|
||||||
|
pub fn reply(
|
||||||
|
message: MessageType,
|
||||||
|
original_message: &OriginalRoomMessageEvent,
|
||||||
|
forward_thread: ForwardThread,
|
||||||
|
) -> Self {
|
||||||
|
let msgtype = match message {
|
||||||
|
MessageType::Text(TextMessageEventContent { body, formatted, .. }) => {
|
||||||
|
let (body, html_body) = reply::plain_and_formatted_reply_body(
|
||||||
|
body,
|
||||||
|
formatted.map(|f| f.body),
|
||||||
|
original_message,
|
||||||
|
);
|
||||||
|
|
||||||
|
MessageType::Text(TextMessageEventContent::html(body, html_body))
|
||||||
|
}
|
||||||
|
MessageType::Notice(NoticeMessageEventContent { body, formatted, .. }) => {
|
||||||
|
let (body, html_body) = reply::plain_and_formatted_reply_body(
|
||||||
|
body,
|
||||||
|
formatted.map(|f| f.body),
|
||||||
|
original_message,
|
||||||
|
);
|
||||||
|
|
||||||
|
MessageType::Notice(NoticeMessageEventContent::html(body, html_body))
|
||||||
|
}
|
||||||
|
_ => message,
|
||||||
|
};
|
||||||
|
|
||||||
|
let relates_to = if let Some(Relation::Thread(Thread { event_id, .. })) = original_message
|
||||||
|
.content
|
||||||
|
.relates_to
|
||||||
|
.as_ref()
|
||||||
|
.filter(|_| forward_thread == ForwardThread::Yes)
|
||||||
|
{
|
||||||
|
Relation::Thread(Thread::reply(event_id.clone(), original_message.event_id.clone()))
|
||||||
|
} else {
|
||||||
|
Relation::Reply {
|
||||||
|
in_reply_to: InReplyTo { event_id: original_message.event_id.clone() },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { msgtype, relates_to: Some(relates_to) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create 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
|
||||||
|
/// created.
|
||||||
|
///
|
||||||
|
/// If `message` is a text or notice message, it is modified to include the rich reply fallback.
|
||||||
|
#[cfg(feature = "unstable-msc3440")]
|
||||||
|
pub fn for_thread(
|
||||||
|
message: MessageType,
|
||||||
|
previous_message: &OriginalRoomMessageEvent,
|
||||||
|
is_reply: ReplyInThread,
|
||||||
|
) -> Self {
|
||||||
|
let msgtype = match message {
|
||||||
|
MessageType::Text(TextMessageEventContent { body, formatted, .. }) => {
|
||||||
|
let (body, html_body) = reply::plain_and_formatted_reply_body(
|
||||||
|
body,
|
||||||
|
formatted.map(|f| f.body),
|
||||||
|
previous_message,
|
||||||
|
);
|
||||||
|
|
||||||
|
MessageType::Text(TextMessageEventContent::html(body, html_body))
|
||||||
|
}
|
||||||
|
MessageType::Notice(NoticeMessageEventContent { body, formatted, .. }) => {
|
||||||
|
let (body, html_body) = reply::plain_and_formatted_reply_body(
|
||||||
|
body,
|
||||||
|
formatted.map(|f| f.body),
|
||||||
|
previous_message,
|
||||||
|
);
|
||||||
|
|
||||||
|
MessageType::Notice(NoticeMessageEventContent::html(body, html_body))
|
||||||
|
}
|
||||||
|
_ => message,
|
||||||
|
};
|
||||||
|
|
||||||
|
let thread_root = if let Some(Relation::Thread(Thread { event_id, .. })) =
|
||||||
|
&previous_message.content.relates_to
|
||||||
|
{
|
||||||
|
event_id.clone()
|
||||||
|
} else {
|
||||||
|
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 == ReplyInThread::No,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a reference to the `msgtype` string.
|
/// Returns a reference to the `msgtype` string.
|
||||||
///
|
///
|
||||||
/// If you want to access the message type-specific data rather than the message type itself,
|
/// If you want to access the message type-specific data rather than the message type itself,
|
||||||
@ -325,6 +415,42 @@ impl From<VoiceEventContent> for RoomMessageEventContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether or not to forward a [`Relation::Thread`] when sending a reply.
|
||||||
|
#[cfg(feature = "unstable-msc3440")]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[allow(clippy::exhaustive_enums)]
|
||||||
|
pub enum ForwardThread {
|
||||||
|
/// The thread relation in the original message is forwarded if it exists.
|
||||||
|
///
|
||||||
|
/// This should be set if your client doesn't support threads (see [MSC3440]).
|
||||||
|
///
|
||||||
|
/// [MSC3440]: https://github.com/matrix-org/matrix-spec-proposals/pull/3440
|
||||||
|
Yes,
|
||||||
|
|
||||||
|
/// Create a reply in the main conversation even if the original message is in a thread.
|
||||||
|
///
|
||||||
|
/// This should be used if you client supports threads and you explicitly want that behavior.
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether or not the message is a reply inside a thread.
|
||||||
|
#[cfg(feature = "unstable-msc3440")]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[allow(clippy::exhaustive_enums)]
|
||||||
|
pub enum ReplyInThread {
|
||||||
|
/// This is a reply.
|
||||||
|
///
|
||||||
|
/// Create a proper reply _in_ the thread.
|
||||||
|
Yes,
|
||||||
|
|
||||||
|
/// This is not a reply.
|
||||||
|
///
|
||||||
|
/// Create a regular message in the thread, with a reply fallback, according to [MSC3440].
|
||||||
|
///
|
||||||
|
/// [MSC3440]: https://github.com/matrix-org/matrix-spec-proposals/pull/3440
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
/// The content that is specific to each message type variant.
|
/// The content that is specific to each message type variant.
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@ -566,13 +692,13 @@ impl Thread {
|
|||||||
/// Convenience method to create a regular `Thread` with the given event ID and latest
|
/// Convenience method to create a regular `Thread` with the given event ID and latest
|
||||||
/// message-like event ID.
|
/// message-like event ID.
|
||||||
pub fn plain(event_id: Box<EventId>, latest_event_id: Box<EventId>) -> Self {
|
pub fn plain(event_id: Box<EventId>, latest_event_id: Box<EventId>) -> Self {
|
||||||
Self { event_id, in_reply_to: InReplyTo::new(latest_event_id), is_falling_back: false }
|
Self { event_id, in_reply_to: InReplyTo::new(latest_event_id), is_falling_back: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method to create a reply `Thread` with the given event ID and replied-to event
|
/// Convenience method to create a reply `Thread` with the given event ID and replied-to event
|
||||||
/// ID.
|
/// ID.
|
||||||
pub fn reply(event_id: Box<EventId>, reply_to_event_id: Box<EventId>) -> Self {
|
pub fn reply(event_id: Box<EventId>, reply_to_event_id: Box<EventId>) -> Self {
|
||||||
Self { event_id, in_reply_to: InReplyTo::new(reply_to_event_id), is_falling_back: true }
|
Self { event_id, in_reply_to: InReplyTo::new(reply_to_event_id), is_falling_back: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
|
|
||||||
use super::{FormattedBody, MessageType, OriginalRoomMessageEvent};
|
use super::{FormattedBody, MessageType, OriginalRoomMessageEvent};
|
||||||
@ -250,6 +252,27 @@ fn formatted_or_plain_body<'a>(formatted: &'a Option<FormattedBody>, body: &'a s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the plain and formatted body for a rich reply.
|
||||||
|
///
|
||||||
|
/// Returns a `(plain, html)` tuple.
|
||||||
|
pub fn plain_and_formatted_reply_body(
|
||||||
|
body: impl fmt::Display,
|
||||||
|
formatted: Option<impl fmt::Display>,
|
||||||
|
original_message: &OriginalRoomMessageEvent,
|
||||||
|
) -> (String, String) {
|
||||||
|
let quoted = get_plain_quote_fallback(original_message);
|
||||||
|
let quoted_html = get_html_quote_fallback(original_message);
|
||||||
|
|
||||||
|
let plain = format!("{}\n\n{}", quoted, body);
|
||||||
|
let html = if let Some(formatted) = formatted {
|
||||||
|
format!("{}\n\n{}", quoted_html, formatted)
|
||||||
|
} else {
|
||||||
|
format!("{}\n\n{}", quoted_html, body)
|
||||||
|
};
|
||||||
|
|
||||||
|
(plain, html)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -178,6 +178,7 @@ fn thread_plain_serialize() {
|
|||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
"event_id": "$latesteventid",
|
"event_id": "$latesteventid",
|
||||||
},
|
},
|
||||||
|
"io.element.show_reply": true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -195,6 +196,7 @@ fn thread_plain_serialize() {
|
|||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
"event_id": "$latesteventid",
|
"event_id": "$latesteventid",
|
||||||
},
|
},
|
||||||
|
"io.element.show_reply": true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -229,7 +231,6 @@ fn thread_reply_serialize() {
|
|||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
"event_id": "$repliedtoeventid",
|
"event_id": "$repliedtoeventid",
|
||||||
},
|
},
|
||||||
"io.element.show_reply": true,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -247,7 +248,6 @@ fn thread_reply_serialize() {
|
|||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
"event_id": "$repliedtoeventid",
|
"event_id": "$repliedtoeventid",
|
||||||
},
|
},
|
||||||
"io.element.show_reply": true,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user