matrix_sdk_ui/timeline/event_item/content/
reply.rs1use std::sync::Arc;
16
17use imbl::Vector;
18use matrix_sdk::deserialized_responses::TimelineEvent;
19use ruma::{MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId};
20use tracing::{debug, instrument, warn};
21
22use super::TimelineItemContent;
23use crate::timeline::{
24 Error as TimelineError, MsgLikeContent, MsgLikeKind, PollState, TimelineEventItemId,
25 TimelineItem,
26 controller::TimelineMetadata,
27 event_handler::{HandleAggregationKind, TimelineAction},
28 event_item::{EventTimelineItem, Profile, TimelineDetails},
29 traits::RoomDataProvider,
30};
31
32#[derive(Clone, Debug)]
34pub struct InReplyToDetails {
35 pub event_id: OwnedEventId,
37
38 pub event: TimelineDetails<Box<EmbeddedEvent>>,
45}
46
47impl InReplyToDetails {
48 pub fn new(
49 event_id: OwnedEventId,
50 timeline_items: &Vector<Arc<TimelineItem>>,
51 ) -> InReplyToDetails {
52 let event = timeline_items
53 .iter()
54 .filter_map(|it| it.as_event())
55 .find(|it| it.event_id() == Some(&*event_id))
56 .map(|item| Box::new(EmbeddedEvent::from_timeline_item(item)));
57
58 InReplyToDetails { event_id, event: TimelineDetails::from_initial_value(event) }
59 }
60}
61
62#[derive(Clone, Debug)]
65pub struct EmbeddedEvent {
66 pub content: TimelineItemContent,
68 pub sender: OwnedUserId,
70 pub sender_profile: TimelineDetails<Profile>,
72 pub timestamp: MilliSecondsSinceUnixEpoch,
74 pub identifier: TimelineEventItemId,
79}
80
81impl EmbeddedEvent {
82 pub fn from_timeline_item(timeline_item: &EventTimelineItem) -> Self {
84 Self {
85 content: timeline_item.content.clone(),
86 sender: timeline_item.sender.clone(),
87 sender_profile: timeline_item.sender_profile.clone(),
88 timestamp: timeline_item.timestamp,
89 identifier: timeline_item.identifier(),
90 }
91 }
92
93 #[instrument(skip_all)]
94 pub(in crate::timeline) async fn try_from_timeline_event<P: RoomDataProvider>(
95 timeline_event: TimelineEvent,
96 room_data_provider: &P,
97 meta: &TimelineMetadata,
98 ) -> Result<Option<Self>, TimelineError> {
99 let (raw_event, unable_to_decrypt_info) = match timeline_event.kind {
100 matrix_sdk::deserialized_responses::TimelineEventKind::UnableToDecrypt {
101 utd_info,
102 event,
103 } => (event, Some(utd_info)),
104 _ => (timeline_event.kind.into_raw(), None),
105 };
106
107 let event = match raw_event.deserialize() {
108 Ok(event) => event,
109 Err(err) => {
110 warn!("can't get details, event couldn't be deserialized: {err}");
111 return Err(TimelineError::UnsupportedEvent);
112 }
113 };
114
115 debug!(event_type = %event.event_type(), "got deserialized event");
116
117 let in_reply_to = None;
120 let thread_root = None;
121 let thread_summary = None;
122
123 let sender = event.sender().to_owned();
124 let timestamp = event.origin_server_ts();
125 let identifier = TimelineEventItemId::EventId(event.event_id().to_owned());
126 let actions = TimelineAction::from_event(
127 event,
128 &raw_event,
129 room_data_provider,
130 unable_to_decrypt_info.map(|utd_info| (utd_info, meta.unable_to_decrypt_hook.as_ref())),
131 in_reply_to,
132 thread_root,
133 thread_summary,
134 )
135 .await;
136 match actions.as_slice() {
137 [TimelineAction::AddItem { content }] => {
138 let content = content.clone();
139 let sender_profile = TimelineDetails::from_initial_value(
140 room_data_provider.profile_from_user_id(&sender).await,
141 );
142 Ok(Some(Self { content, sender, sender_profile, timestamp, identifier }))
143 }
144 [TimelineAction::HandleAggregation { kind, .. }] => {
145 let reactions = Default::default();
151 let thread_root = None;
152 let in_reply_to = None;
153 let thread_summary = None;
154
155 let content = match kind {
156 HandleAggregationKind::Edit { replacement } => {
157 let msg = &replacement.new_content;
158 Some(TimelineItemContent::message(
159 msg.msgtype.clone(),
160 msg.mentions.clone(),
161 reactions,
162 thread_root,
163 in_reply_to,
164 thread_summary,
165 ))
166 }
167
168 HandleAggregationKind::PollEdit { replacement } => {
169 let msg = &replacement.new_content;
170 let poll_state = PollState::new(msg.poll_start.clone(), msg.text.clone());
171 Some(TimelineItemContent::MsgLike(MsgLikeContent {
172 kind: MsgLikeKind::Poll(poll_state),
173 reactions: Default::default(),
174 thread_root,
175 in_reply_to,
176 thread_summary,
177 }))
178 }
179
180 _ => {
181 warn!("embedded event is an aggregation: {}", kind.debug_string());
183 None
184 }
185 };
186
187 if let Some(content) = content {
188 let sender_profile = TimelineDetails::from_initial_value(
189 room_data_provider.profile_from_user_id(&sender).await,
190 );
191 Ok(Some(Self { content, sender, sender_profile, timestamp, identifier }))
192 } else {
193 Ok(None)
194 }
195 }
196 [] => {
197 warn!("No action for an embedded event");
198 Ok(None)
199 }
200 [_, _, ..] => {
201 warn!("Ignoring embedded event that produced multiple timeline actions");
205 Ok(None)
206 }
207 }
208 }
209}