use std::sync::Arc;
use imbl::Vector;
use matrix_sdk::deserialized_responses::TimelineEvent;
use ruma::{MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId};
use tracing::{debug, instrument, warn};
use super::TimelineItemContent;
use crate::timeline::{
Error as TimelineError, MsgLikeContent, MsgLikeKind, PollState, TimelineEventItemId,
TimelineItem,
controller::TimelineMetadata,
event_handler::{HandleAggregationKind, TimelineAction},
event_item::{EventTimelineItem, Profile, TimelineDetails},
traits::RoomDataProvider,
};
#[derive(Clone, Debug)]
pub struct InReplyToDetails {
pub event_id: OwnedEventId,
pub event: TimelineDetails<Box<EmbeddedEvent>>,
}
impl InReplyToDetails {
pub fn new(
event_id: OwnedEventId,
timeline_items: &Vector<Arc<TimelineItem>>,
) -> InReplyToDetails {
let event = timeline_items
.iter()
.filter_map(|it| it.as_event())
.find(|it| it.event_id() == Some(&*event_id))
.map(|item| Box::new(EmbeddedEvent::from_timeline_item(item)));
InReplyToDetails { event_id, event: TimelineDetails::from_initial_value(event) }
}
}
#[derive(Clone, Debug)]
pub struct EmbeddedEvent {
pub content: TimelineItemContent,
pub sender: OwnedUserId,
pub sender_profile: TimelineDetails<Profile>,
pub timestamp: MilliSecondsSinceUnixEpoch,
pub identifier: TimelineEventItemId,
}
impl EmbeddedEvent {
pub fn from_timeline_item(timeline_item: &EventTimelineItem) -> Self {
Self {
content: timeline_item.content.clone(),
sender: timeline_item.sender.clone(),
sender_profile: timeline_item.sender_profile.clone(),
timestamp: timeline_item.timestamp,
identifier: timeline_item.identifier(),
}
}
#[instrument(skip_all)]
pub(in crate::timeline) async fn try_from_timeline_event<P: RoomDataProvider>(
timeline_event: TimelineEvent,
room_data_provider: &P,
meta: &TimelineMetadata,
) -> Result<Option<Self>, TimelineError> {
let (raw_event, unable_to_decrypt_info) = match timeline_event.kind {
matrix_sdk::deserialized_responses::TimelineEventKind::UnableToDecrypt {
utd_info,
event,
} => (event, Some(utd_info)),
_ => (timeline_event.kind.into_raw(), None),
};
let event = match raw_event.deserialize() {
Ok(event) => event,
Err(err) => {
warn!("can't get details, event couldn't be deserialized: {err}");
return Err(TimelineError::UnsupportedEvent);
}
};
debug!(event_type = %event.event_type(), "got deserialized event");
let in_reply_to = None;
let thread_root = None;
let thread_summary = None;
let sender = event.sender().to_owned();
let timestamp = event.origin_server_ts();
let identifier = TimelineEventItemId::EventId(event.event_id().to_owned());
let action = TimelineAction::from_event(
event,
&raw_event,
room_data_provider,
unable_to_decrypt_info.map(|utd_info| (utd_info, meta.unable_to_decrypt_hook.as_ref())),
in_reply_to,
thread_root,
thread_summary,
)
.await;
match action {
Some(TimelineAction::AddItem { content }) => {
let sender_profile = TimelineDetails::from_initial_value(
room_data_provider.profile_from_user_id(&sender).await,
);
Ok(Some(Self { content, sender, sender_profile, timestamp, identifier }))
}
Some(TimelineAction::HandleAggregation { kind, .. }) => {
let reactions = Default::default();
let thread_root = None;
let in_reply_to = None;
let thread_summary = None;
let content = match kind {
HandleAggregationKind::Edit { replacement } => {
let msg = replacement.new_content;
Some(TimelineItemContent::message(
msg.msgtype,
msg.mentions,
reactions,
thread_root,
in_reply_to,
thread_summary,
))
}
HandleAggregationKind::PollEdit { replacement } => {
let msg = replacement.new_content;
let poll_state = PollState::new(msg.poll_start, msg.text);
Some(TimelineItemContent::MsgLike(MsgLikeContent {
kind: MsgLikeKind::Poll(poll_state),
reactions: Default::default(),
thread_root,
in_reply_to,
thread_summary,
}))
}
_ => {
warn!("embedded event is an aggregation: {}", kind.debug_string());
None
}
};
if let Some(content) = content {
let sender_profile = TimelineDetails::from_initial_value(
room_data_provider.profile_from_user_id(&sender).await,
);
Ok(Some(Self { content, sender, sender_profile, timestamp, identifier }))
} else {
Ok(None)
}
}
None => {
warn!("embedded event lead to no action (neither an aggregation nor a new item)");
Ok(None)
}
}
}
}