ffi: Turn EventTimelineItem into a record type
This improves parsing times in mobile Clients. On Android, this means a 5-10x faster parsing of timeline events. To do that I had to: - Make functions like `edit/redact/forward` take an identifier (EventId/TransactionId) instead of the actual event. This id will be used to look for the actual SDK timeline event in the timeline. This change will make these functions a bit less performant. - Make `InReplyToDetails` an object instead since a record can't recursively contain itself. - Turn `EventTimelineItem` into a record type. Do the same with `Message`, which is now `MessageContent`.
This commit is contained in:
committed by
Jorge Martin Espinosa
parent
e61fb45504
commit
67df36f733
@@ -1,6 +1,6 @@
|
||||
// TODO: target-os conditional would be good.
|
||||
|
||||
#![allow(unused_qualifications, clippy::new_without_default)]
|
||||
#![allow(unused_qualifications, clippy::new_without_default, unused_macros)]
|
||||
|
||||
macro_rules! unwrap_or_clone_arc_into_variant {
|
||||
(
|
||||
|
||||
@@ -702,8 +702,8 @@ impl RoomListItem {
|
||||
self.inner.is_encrypted().await.unwrap_or(false)
|
||||
}
|
||||
|
||||
async fn latest_event(&self) -> Option<Arc<EventTimelineItem>> {
|
||||
self.inner.latest_event().await.map(EventTimelineItem).map(Arc::new)
|
||||
async fn latest_event(&self) -> Option<EventTimelineItem> {
|
||||
self.inner.latest_event().await.map(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ use tracing::info;
|
||||
use crate::{
|
||||
error::{ClientError, MediaInfoError},
|
||||
helpers::unwrap_or_clone_arc,
|
||||
timeline::MessageContent,
|
||||
utils::u64_to_uint,
|
||||
};
|
||||
|
||||
@@ -227,6 +228,7 @@ pub impl RoomMessageEventContentWithoutRelationExt for RoomMessageEventContentWi
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mentions {
|
||||
pub user_ids: Vec<String>,
|
||||
pub room: bool,
|
||||
@@ -861,3 +863,13 @@ impl From<RumaPollKind> for PollKind {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`RoomMessageEventContentWithoutRelation`] given a
|
||||
/// [`MessageContent`] value.
|
||||
#[uniffi::export]
|
||||
pub fn content_without_relation_from_message(
|
||||
message: MessageContent,
|
||||
) -> Result<RoomMessageEventContentWithoutRelation, ClientError> {
|
||||
let msg_type = message.msg_type.try_into()?;
|
||||
Ok(RoomMessageEventContentWithoutRelation::new(msg_type))
|
||||
}
|
||||
|
||||
@@ -16,45 +16,40 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use matrix_sdk::{crypto::types::events::UtdCause, room::power_levels::power_level_user_changes};
|
||||
use matrix_sdk_ui::timeline::{PollResult, RoomPinnedEventsChange, TimelineDetails};
|
||||
use ruma::events::room::{message::RoomMessageEventContentWithoutRelation, MediaSource};
|
||||
use tracing::warn;
|
||||
use ruma::events::room::MediaSource;
|
||||
|
||||
use super::ProfileDetails;
|
||||
use crate::ruma::{ImageInfo, MessageType, PollKind};
|
||||
use crate::ruma::{ImageInfo, Mentions, MessageType, PollKind};
|
||||
|
||||
#[derive(Clone, uniffi::Object)]
|
||||
pub struct TimelineItemContent(pub(crate) matrix_sdk_ui::timeline::TimelineItemContent);
|
||||
|
||||
#[uniffi::export]
|
||||
impl TimelineItemContent {
|
||||
pub fn kind(&self) -> TimelineItemContentKind {
|
||||
impl From<&matrix_sdk_ui::timeline::TimelineItemContent> for TimelineItemContent {
|
||||
fn from(value: &matrix_sdk_ui::timeline::TimelineItemContent) -> Self {
|
||||
use matrix_sdk_ui::timeline::TimelineItemContent as Content;
|
||||
|
||||
match &self.0 {
|
||||
Content::Message(_) => TimelineItemContentKind::Message,
|
||||
match value {
|
||||
Content::Message(message) => TimelineItemContent::Message { content: message.into() },
|
||||
|
||||
Content::RedactedMessage => TimelineItemContentKind::RedactedMessage,
|
||||
Content::RedactedMessage => TimelineItemContent::RedactedMessage,
|
||||
|
||||
Content::Sticker(sticker) => {
|
||||
let content = sticker.content();
|
||||
TimelineItemContentKind::Sticker {
|
||||
TimelineItemContent::Sticker {
|
||||
body: content.body.clone(),
|
||||
info: (&content.info).into(),
|
||||
source: Arc::new(MediaSource::from(content.source.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
Content::Poll(poll_state) => TimelineItemContentKind::from(poll_state.results()),
|
||||
Content::Poll(poll_state) => TimelineItemContent::from(poll_state.results()),
|
||||
|
||||
Content::CallInvite => TimelineItemContentKind::CallInvite,
|
||||
Content::CallInvite => TimelineItemContent::CallInvite,
|
||||
|
||||
Content::CallNotify => TimelineItemContentKind::CallNotify,
|
||||
Content::CallNotify => TimelineItemContent::CallNotify,
|
||||
|
||||
Content::UnableToDecrypt(msg) => {
|
||||
TimelineItemContentKind::UnableToDecrypt { msg: EncryptedMessage::new(msg) }
|
||||
TimelineItemContent::UnableToDecrypt { msg: EncryptedMessage::new(msg) }
|
||||
}
|
||||
|
||||
Content::MembershipChange(membership) => TimelineItemContentKind::RoomMembership {
|
||||
Content::MembershipChange(membership) => TimelineItemContent::RoomMembership {
|
||||
user_id: membership.user_id().to_string(),
|
||||
user_display_name: membership.display_name(),
|
||||
change: membership.change().map(Into::into),
|
||||
@@ -74,7 +69,7 @@ impl TimelineItemContent {
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
TimelineItemContentKind::ProfileChange {
|
||||
TimelineItemContent::ProfileChange {
|
||||
display_name: display_name.flatten(),
|
||||
prev_display_name: prev_display_name.flatten(),
|
||||
avatar_url: avatar_url.flatten(),
|
||||
@@ -82,20 +77,20 @@ impl TimelineItemContent {
|
||||
}
|
||||
}
|
||||
|
||||
Content::OtherState(state) => TimelineItemContentKind::State {
|
||||
Content::OtherState(state) => TimelineItemContent::State {
|
||||
state_key: state.state_key().to_owned(),
|
||||
content: state.content().into(),
|
||||
},
|
||||
|
||||
Content::FailedToParseMessageLike { event_type, error } => {
|
||||
TimelineItemContentKind::FailedToParseMessageLike {
|
||||
TimelineItemContent::FailedToParseMessageLike {
|
||||
event_type: event_type.to_string(),
|
||||
error: error.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
Content::FailedToParseState { event_type, state_key, error } => {
|
||||
TimelineItemContentKind::FailedToParseState {
|
||||
TimelineItemContent::FailedToParseState {
|
||||
event_type: event_type.to_string(),
|
||||
state_key: state_key.to_string(),
|
||||
error: error.to_string(),
|
||||
@@ -103,16 +98,45 @@ impl TimelineItemContent {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_message(self: Arc<Self>) -> Option<Arc<Message>> {
|
||||
use matrix_sdk_ui::timeline::TimelineItemContent as Content;
|
||||
unwrap_or_clone_arc_into_variant!(self, .0, Content::Message(msg) => Arc::new(Message(msg)))
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct MessageContent {
|
||||
pub msg_type: MessageType,
|
||||
pub body: String,
|
||||
pub in_reply_to: Option<Arc<InReplyToDetails>>,
|
||||
pub thread_root: Option<String>,
|
||||
pub is_edited: bool,
|
||||
pub mentions: Option<Mentions>,
|
||||
}
|
||||
|
||||
impl From<&matrix_sdk_ui::timeline::Message> for MessageContent {
|
||||
fn from(value: &matrix_sdk_ui::timeline::Message) -> Self {
|
||||
Self {
|
||||
msg_type: value.msgtype().clone().into(),
|
||||
body: value.body().to_owned(),
|
||||
in_reply_to: value.in_reply_to().map(|r| Arc::new(r.into())),
|
||||
is_edited: value.is_edited(),
|
||||
thread_root: value.thread_root().map(|id| id.to_string()),
|
||||
mentions: value.mentions().cloned().map(|m| m.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Enum)]
|
||||
pub enum TimelineItemContentKind {
|
||||
Message,
|
||||
impl From<ruma::events::Mentions> for Mentions {
|
||||
fn from(value: ruma::events::Mentions) -> Self {
|
||||
Self {
|
||||
user_ids: value.user_ids.iter().map(|id| id.to_string()).collect(),
|
||||
room: value.room,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum TimelineItemContent {
|
||||
Message {
|
||||
content: MessageContent,
|
||||
},
|
||||
RedactedMessage,
|
||||
Sticker {
|
||||
body: String,
|
||||
@@ -160,36 +184,6 @@ pub enum TimelineItemContentKind {
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Object)]
|
||||
pub struct Message(matrix_sdk_ui::timeline::Message);
|
||||
|
||||
#[uniffi::export]
|
||||
impl Message {
|
||||
pub fn msgtype(&self) -> MessageType {
|
||||
self.0.msgtype().clone().into()
|
||||
}
|
||||
|
||||
pub fn body(&self) -> String {
|
||||
self.0.msgtype().body().to_owned()
|
||||
}
|
||||
|
||||
pub fn in_reply_to(&self) -> Option<InReplyToDetails> {
|
||||
self.0.in_reply_to().map(InReplyToDetails::from)
|
||||
}
|
||||
|
||||
pub fn is_threaded(&self) -> bool {
|
||||
self.0.is_threaded()
|
||||
}
|
||||
|
||||
pub fn is_edited(&self) -> bool {
|
||||
self.0.is_edited()
|
||||
}
|
||||
|
||||
pub fn content(&self) -> Arc<RoomMessageEventContentWithoutRelation> {
|
||||
Arc::new(RoomMessageEventContentWithoutRelation::new(self.0.msgtype().clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
pub struct InReplyToDetails {
|
||||
event_id: String,
|
||||
event: RepliedToEventDetails,
|
||||
@@ -201,6 +195,17 @@ impl InReplyToDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl InReplyToDetails {
|
||||
pub fn event_id(&self) -> String {
|
||||
self.event_id.clone()
|
||||
}
|
||||
|
||||
pub fn event(&self) -> RepliedToEventDetails {
|
||||
self.event.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&matrix_sdk_ui::timeline::InReplyToDetails> for InReplyToDetails {
|
||||
fn from(inner: &matrix_sdk_ui::timeline::InReplyToDetails) -> Self {
|
||||
let event_id = inner.event_id.to_string();
|
||||
@@ -208,7 +213,7 @@ impl From<&matrix_sdk_ui::timeline::InReplyToDetails> for InReplyToDetails {
|
||||
TimelineDetails::Unavailable => RepliedToEventDetails::Unavailable,
|
||||
TimelineDetails::Pending => RepliedToEventDetails::Pending,
|
||||
TimelineDetails::Ready(event) => RepliedToEventDetails::Ready {
|
||||
content: Arc::new(TimelineItemContent(event.content().to_owned())),
|
||||
content: event.content().into(),
|
||||
sender: event.sender().to_string(),
|
||||
sender_profile: event.sender_profile().into(),
|
||||
},
|
||||
@@ -221,11 +226,11 @@ impl From<&matrix_sdk_ui::timeline::InReplyToDetails> for InReplyToDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Enum)]
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum RepliedToEventDetails {
|
||||
Unavailable,
|
||||
Pending,
|
||||
Ready { content: Arc<TimelineItemContent>, sender: String, sender_profile: ProfileDetails },
|
||||
Ready { content: TimelineItemContent, sender: String, sender_profile: ProfileDetails },
|
||||
Error { message: String },
|
||||
}
|
||||
|
||||
@@ -419,15 +424,15 @@ impl From<&matrix_sdk_ui::timeline::AnyOtherFullStateEventContent> for OtherStat
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct PollAnswer {
|
||||
pub id: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl From<PollResult> for TimelineItemContentKind {
|
||||
impl From<PollResult> for TimelineItemContent {
|
||||
fn from(value: PollResult) -> Self {
|
||||
TimelineItemContentKind::Poll {
|
||||
TimelineItemContent::Poll {
|
||||
question: value.question,
|
||||
kind: PollKind::from(value.kind),
|
||||
max_selections: value.max_selections,
|
||||
|
||||
@@ -78,6 +78,8 @@ use crate::{
|
||||
|
||||
mod content;
|
||||
|
||||
pub use content::MessageContent;
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
#[repr(transparent)]
|
||||
pub struct Timeline {
|
||||
@@ -423,11 +425,11 @@ impl Timeline {
|
||||
|
||||
pub async fn send_poll_response(
|
||||
self: Arc<Self>,
|
||||
poll_start_id: String,
|
||||
poll_start_event_id: String,
|
||||
answers: Vec<String>,
|
||||
) -> Result<(), ClientError> {
|
||||
let poll_start_event_id =
|
||||
EventId::parse(poll_start_id).context("Failed to parse EventId")?;
|
||||
EventId::parse(poll_start_event_id).context("Failed to parse EventId")?;
|
||||
let poll_response_event_content =
|
||||
UnstablePollResponseEventContent::new(answers, poll_start_event_id);
|
||||
let event_content =
|
||||
@@ -442,11 +444,11 @@ impl Timeline {
|
||||
|
||||
pub fn end_poll(
|
||||
self: Arc<Self>,
|
||||
poll_start_id: String,
|
||||
poll_start_event_id: String,
|
||||
text: String,
|
||||
) -> Result<(), ClientError> {
|
||||
let poll_start_event_id =
|
||||
EventId::parse(poll_start_id).context("Failed to parse EventId")?;
|
||||
EventId::parse(poll_start_event_id).context("Failed to parse EventId")?;
|
||||
let poll_end_event_content = UnstablePollEndEventContent::new(text, poll_start_event_id);
|
||||
let event_content = AnyMessageLikeEventContent::UnstablePollEnd(poll_end_event_content);
|
||||
|
||||
@@ -486,13 +488,19 @@ impl Timeline {
|
||||
///
|
||||
/// Returns whether the edit did happen. It can only return false for
|
||||
/// local events that are being processed.
|
||||
pub async fn edit(
|
||||
&self,
|
||||
item: Arc<EventTimelineItem>,
|
||||
new_content: EditedContent,
|
||||
) -> Result<bool, ClientError> {
|
||||
let new_content: SdkEditedContent = new_content.try_into()?;
|
||||
self.inner.edit(&item.0, new_content).await.map_err(ClientError::from)
|
||||
pub async fn edit(&self, id: String, new_content: EditedContent) -> Result<bool, ClientError> {
|
||||
let event = if let Ok(event_id) = EventId::parse(&id) {
|
||||
self.inner.item_by_event_id(&event_id).await
|
||||
} else {
|
||||
let transaction_id: OwnedTransactionId = id.into();
|
||||
self.inner.local_item_by_transaction_id(&transaction_id).await
|
||||
};
|
||||
if let Some(event) = event {
|
||||
let new_content: SdkEditedContent = new_content.try_into()?;
|
||||
self.inner.edit(&event, new_content).await.map_err(ClientError::from)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_location(
|
||||
@@ -558,14 +566,14 @@ impl Timeline {
|
||||
pub async fn get_event_timeline_item_by_event_id(
|
||||
&self,
|
||||
event_id: String,
|
||||
) -> Result<Arc<EventTimelineItem>, ClientError> {
|
||||
) -> Result<EventTimelineItem, ClientError> {
|
||||
let event_id = EventId::parse(event_id)?;
|
||||
let item = self
|
||||
.inner
|
||||
.item_by_event_id(&event_id)
|
||||
.await
|
||||
.context("Item with given event ID not found")?;
|
||||
Ok(Arc::new(EventTimelineItem(item)))
|
||||
Ok(item.into())
|
||||
}
|
||||
|
||||
/// Get the current timeline item for the given transaction ID, if any.
|
||||
@@ -578,14 +586,14 @@ impl Timeline {
|
||||
pub async fn get_event_timeline_item_by_transaction_id(
|
||||
&self,
|
||||
transaction_id: String,
|
||||
) -> Result<Arc<EventTimelineItem>, ClientError> {
|
||||
) -> Result<EventTimelineItem, ClientError> {
|
||||
let transaction_id: OwnedTransactionId = transaction_id.into();
|
||||
let item = self
|
||||
.inner
|
||||
.local_item_by_transaction_id(&transaction_id)
|
||||
.await
|
||||
.context("Item with given transaction ID not found")?;
|
||||
Ok(Arc::new(EventTimelineItem(item)))
|
||||
Ok(item.into())
|
||||
}
|
||||
|
||||
/// Redacts an event from the timeline.
|
||||
@@ -600,16 +608,26 @@ impl Timeline {
|
||||
/// local events that are being processed.
|
||||
pub async fn redact_event(
|
||||
&self,
|
||||
item: Arc<EventTimelineItem>,
|
||||
id: String,
|
||||
reason: Option<String>,
|
||||
) -> Result<bool, ClientError> {
|
||||
let removed = self
|
||||
.inner
|
||||
.redact(&item.0, reason.as_deref())
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!(err))?;
|
||||
let event = if let Ok(event_id) = EventId::parse(&id) {
|
||||
self.inner.item_by_event_id(&event_id).await
|
||||
} else {
|
||||
let transaction_id: OwnedTransactionId = id.into();
|
||||
self.inner.local_item_by_transaction_id(&transaction_id).await
|
||||
};
|
||||
if let Some(event) = event {
|
||||
let removed = self
|
||||
.inner
|
||||
.redact(&event, reason.as_deref())
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!(err))?;
|
||||
|
||||
Ok(removed)
|
||||
Ok(removed)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the reply details for the given event id.
|
||||
@@ -640,7 +658,7 @@ impl Timeline {
|
||||
Ok(replied_to) => Ok(InReplyToDetails::new(
|
||||
event_id_str,
|
||||
RepliedToEventDetails::Ready {
|
||||
content: Arc::new(TimelineItemContent(replied_to.content().clone())),
|
||||
content: replied_to.content().into(),
|
||||
sender: replied_to.sender().to_string(),
|
||||
sender_profile: replied_to.sender_profile().into(),
|
||||
},
|
||||
@@ -672,6 +690,14 @@ impl Timeline {
|
||||
let event_id = EventId::parse(event_id).map_err(ClientError::from)?;
|
||||
self.inner.unpin_event(&event_id).await.map_err(ClientError::from)
|
||||
}
|
||||
|
||||
pub fn create_message_content(
|
||||
&self,
|
||||
msg_type: crate::ruma::MessageType,
|
||||
) -> Option<Arc<RoomMessageEventContentWithoutRelation>> {
|
||||
let msg_type: Option<MessageType> = msg_type.try_into().ok();
|
||||
msg_type.map(|m| Arc::new(RoomMessageEventContentWithoutRelation::new(m)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
@@ -871,9 +897,9 @@ impl TimelineItem {
|
||||
|
||||
#[uniffi::export]
|
||||
impl TimelineItem {
|
||||
pub fn as_event(self: Arc<Self>) -> Option<Arc<EventTimelineItem>> {
|
||||
pub fn as_event(self: Arc<Self>) -> Option<EventTimelineItem> {
|
||||
let event_item = self.0.as_event()?;
|
||||
Some(Arc::new(EventTimelineItem(event_item.clone())))
|
||||
Some(event_item.clone().into())
|
||||
}
|
||||
|
||||
pub fn as_virtual(self: Arc<Self>) -> Option<VirtualTimelineItem> {
|
||||
@@ -995,7 +1021,7 @@ fn event_send_state_from_sending_failed(error: &Error, is_recoverable: bool) ->
|
||||
|
||||
/// Recommended decorations for decrypted messages, representing the message's
|
||||
/// authenticity properties.
|
||||
#[derive(uniffi::Enum)]
|
||||
#[derive(uniffi::Enum, Clone)]
|
||||
pub enum ShieldState {
|
||||
/// A red shield with a tooltip containing the associated message should be
|
||||
/// presented.
|
||||
@@ -1021,100 +1047,73 @@ impl From<SdkShieldState> for ShieldState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct EventTimelineItem(pub(crate) matrix_sdk_ui::timeline::EventTimelineItem);
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct EventTimelineItem {
|
||||
is_local: bool,
|
||||
is_remote: bool,
|
||||
transaction_id: Option<String>,
|
||||
event_id: Option<String>,
|
||||
sender: String,
|
||||
sender_profile: ProfileDetails,
|
||||
is_own: bool,
|
||||
is_editable: bool,
|
||||
content: TimelineItemContent,
|
||||
timestamp: u64,
|
||||
reactions: Vec<Reaction>,
|
||||
debug_info: EventTimelineItemDebugInfo,
|
||||
local_send_state: Option<EventSendState>,
|
||||
read_receipts: HashMap<String, Receipt>,
|
||||
origin: Option<EventItemOrigin>,
|
||||
can_be_replied_to: bool,
|
||||
message_shield: Option<ShieldState>,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl EventTimelineItem {
|
||||
pub fn is_local(&self) -> bool {
|
||||
self.0.is_local_echo()
|
||||
}
|
||||
|
||||
pub fn is_remote(&self) -> bool {
|
||||
!self.0.is_local_echo()
|
||||
}
|
||||
|
||||
pub fn transaction_id(&self) -> Option<String> {
|
||||
self.0.transaction_id().map(ToString::to_string)
|
||||
}
|
||||
|
||||
pub fn event_id(&self) -> Option<String> {
|
||||
self.0.event_id().map(ToString::to_string)
|
||||
}
|
||||
|
||||
pub fn sender(&self) -> String {
|
||||
self.0.sender().to_string()
|
||||
}
|
||||
|
||||
pub fn sender_profile(&self) -> ProfileDetails {
|
||||
self.0.sender_profile().into()
|
||||
}
|
||||
|
||||
pub fn is_own(&self) -> bool {
|
||||
self.0.is_own()
|
||||
}
|
||||
|
||||
pub fn is_editable(&self) -> bool {
|
||||
self.0.is_editable()
|
||||
}
|
||||
|
||||
pub fn content(&self) -> Arc<TimelineItemContent> {
|
||||
Arc::new(TimelineItemContent(self.0.content().clone()))
|
||||
}
|
||||
|
||||
pub fn timestamp(&self) -> u64 {
|
||||
self.0.timestamp().0.into()
|
||||
}
|
||||
|
||||
pub fn reactions(&self) -> Vec<Reaction> {
|
||||
self.0
|
||||
impl From<matrix_sdk_ui::timeline::EventTimelineItem> for EventTimelineItem {
|
||||
fn from(value: matrix_sdk_ui::timeline::EventTimelineItem) -> Self {
|
||||
let reactions = value
|
||||
.reactions()
|
||||
.iter()
|
||||
.map(|(k, v)| Reaction {
|
||||
key: k.to_owned(),
|
||||
senders: v
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|(sender_id, info)| ReactionSenderData {
|
||||
sender_id: sender_id.to_string(),
|
||||
timestamp: info.timestamp.0.into(),
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn debug_info(&self) -> EventTimelineItemDebugInfo {
|
||||
EventTimelineItemDebugInfo {
|
||||
model: format!("{:#?}", self.0),
|
||||
original_json: self.0.original_json().map(|raw| raw.json().get().to_owned()),
|
||||
latest_edit_json: self.0.latest_edit_json().map(|raw| raw.json().get().to_owned()),
|
||||
.collect();
|
||||
let debug_info = EventTimelineItemDebugInfo {
|
||||
model: format!("{:#?}", value),
|
||||
original_json: value.original_json().map(|raw| raw.json().get().to_owned()),
|
||||
latest_edit_json: value.latest_edit_json().map(|raw| raw.json().get().to_owned()),
|
||||
};
|
||||
let read_receipts =
|
||||
value.read_receipts().iter().map(|(k, v)| (k.to_string(), v.clone().into())).collect();
|
||||
Self {
|
||||
is_local: value.is_local_echo(),
|
||||
is_remote: !value.is_local_echo(),
|
||||
transaction_id: value.transaction_id().map(|t| t.to_string()),
|
||||
event_id: value.event_id().map(|e| e.to_string()),
|
||||
sender: value.sender().to_string(),
|
||||
sender_profile: value.sender_profile().into(),
|
||||
is_own: value.is_own(),
|
||||
is_editable: value.is_editable(),
|
||||
content: value.content().into(),
|
||||
timestamp: value.timestamp().0.into(),
|
||||
reactions,
|
||||
debug_info,
|
||||
local_send_state: value.send_state().map(|s| s.into()),
|
||||
read_receipts,
|
||||
origin: value.origin(),
|
||||
can_be_replied_to: value.can_be_replied_to(),
|
||||
message_shield: value.get_shield(false).map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_send_state(&self) -> Option<EventSendState> {
|
||||
self.0.send_state().map(Into::into)
|
||||
}
|
||||
|
||||
pub fn read_receipts(&self) -> HashMap<String, Receipt> {
|
||||
self.0.read_receipts().iter().map(|(k, v)| (k.to_string(), v.clone().into())).collect()
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> Option<EventItemOrigin> {
|
||||
self.0.origin()
|
||||
}
|
||||
|
||||
pub fn can_be_replied_to(&self) -> bool {
|
||||
self.0.can_be_replied_to()
|
||||
}
|
||||
|
||||
/// Gets the [`ShieldState`] which can be used to decorate messages in the
|
||||
/// recommended way.
|
||||
pub fn get_shield(&self, strict: bool) -> Option<ShieldState> {
|
||||
self.0.get_shield(strict).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct Receipt {
|
||||
pub timestamp: Option<u64>,
|
||||
}
|
||||
@@ -1125,14 +1124,14 @@ impl From<ruma::events::receipt::Receipt> for Receipt {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct EventTimelineItemDebugInfo {
|
||||
model: String,
|
||||
original_json: Option<String>,
|
||||
latest_edit_json: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(uniffi::Enum)]
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum ProfileDetails {
|
||||
Unavailable,
|
||||
Pending,
|
||||
|
||||
@@ -117,6 +117,11 @@ impl Message {
|
||||
Self { msgtype, in_reply_to, thread_root, edited, mentions }
|
||||
}
|
||||
|
||||
/// Create a forwarded message from a [`MessageType`].
|
||||
pub fn forwarded(msgtype: MessageType) -> Self {
|
||||
Self { msgtype, in_reply_to: None, thread_root: None, edited: false, mentions: None }
|
||||
}
|
||||
|
||||
/// Get the `msgtype`-specific data of this message.
|
||||
pub fn msgtype(&self) -> &MessageType {
|
||||
&self.msgtype
|
||||
@@ -139,6 +144,11 @@ impl Message {
|
||||
self.thread_root.is_some()
|
||||
}
|
||||
|
||||
/// Get the [`OwnedEventId`] of the root event of a thread if it exists.
|
||||
pub fn thread_root(&self) -> Option<OwnedEventId> {
|
||||
self.thread_root.clone()
|
||||
}
|
||||
|
||||
/// Get the edit state of this message (has been edited: `true` /
|
||||
/// `false`).
|
||||
pub fn is_edited(&self) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user