matrix_sdk_ui/timeline/controller/
state.rs1use std::{future::Future, sync::Arc};
16
17use eyeball_im::VectorDiff;
18use matrix_sdk::{deserialized_responses::TimelineEvent, send_queue::SendHandle};
19#[cfg(test)]
20use ruma::events::receipt::ReceiptEventContent;
21use ruma::{
22 events::{
23 poll::unstable_start::NewUnstablePollStartEventContentWithoutRelation,
24 relation::Replacement, room::message::RoomMessageEventContentWithoutRelation,
25 AnySyncEphemeralRoomEvent, AnySyncTimelineEvent,
26 },
27 serde::Raw,
28 EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
29 RoomVersionId, UserId,
30};
31use tracing::{instrument, trace, warn};
32
33use super::{
34 super::{
35 date_dividers::DateDividerAdjuster,
36 event_handler::{
37 Flow, TimelineEventContext, TimelineEventHandler, TimelineEventKind,
38 TimelineItemPosition,
39 },
40 event_item::RemoteEventOrigin,
41 traits::RoomDataProvider,
42 Profile, TimelineItem,
43 },
44 metadata::EventMeta,
45 observable_items::ObservableItems,
46 DateDividerMode, TimelineFocusKind, TimelineMetadata, TimelineSettings,
47 TimelineStateTransaction,
48};
49use crate::unable_to_decrypt_hook::UtdHookManager;
50
51#[derive(Debug)]
52pub(in crate::timeline) struct TimelineState {
53 pub items: ObservableItems,
54 pub meta: TimelineMetadata,
55
56 pub timeline_focus: TimelineFocusKind,
58}
59
60impl TimelineState {
61 pub(super) fn new(
62 timeline_focus: TimelineFocusKind,
63 own_user_id: OwnedUserId,
64 room_version: RoomVersionId,
65 internal_id_prefix: Option<String>,
66 unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
67 is_room_encrypted: bool,
68 ) -> Self {
69 Self {
70 items: ObservableItems::new(),
71 meta: TimelineMetadata::new(
72 own_user_id,
73 room_version,
74 internal_id_prefix,
75 unable_to_decrypt_hook,
76 is_room_encrypted,
77 ),
78 timeline_focus,
79 }
80 }
81
82 pub(super) async fn handle_remote_events_with_diffs<RoomData>(
84 &mut self,
85 diffs: Vec<VectorDiff<TimelineEvent>>,
86 origin: RemoteEventOrigin,
87 room_data: &RoomData,
88 settings: &TimelineSettings,
89 ) where
90 RoomData: RoomDataProvider,
91 {
92 if diffs.is_empty() {
93 return;
94 }
95
96 let mut transaction = self.transaction();
97 transaction.handle_remote_events_with_diffs(diffs, origin, room_data, settings).await;
98 transaction.commit();
99 }
100
101 pub(super) fn handle_fully_read_marker(&mut self, fully_read_event_id: OwnedEventId) {
104 let mut txn = self.transaction();
105 txn.set_fully_read_event(fully_read_event_id);
106 txn.commit();
107 }
108
109 #[instrument(skip_all)]
110 pub(super) async fn handle_ephemeral_events<P: RoomDataProvider>(
111 &mut self,
112 events: Vec<Raw<AnySyncEphemeralRoomEvent>>,
113 room_data_provider: &P,
114 ) {
115 if events.is_empty() {
116 return;
117 }
118
119 let mut txn = self.transaction();
120
121 trace!("Handling ephemeral room events");
122 let own_user_id = room_data_provider.own_user_id();
123 for raw_event in events {
124 match raw_event.deserialize() {
125 Ok(AnySyncEphemeralRoomEvent::Receipt(ev)) => {
126 txn.handle_explicit_read_receipts(ev.content, own_user_id);
127 }
128 Ok(_) => {}
129 Err(e) => {
130 let event_type = raw_event.get_field::<String>("type").ok().flatten();
131 warn!(event_type, "Failed to deserialize ephemeral event: {e}");
132 }
133 }
134 }
135
136 txn.commit();
137 }
138
139 #[allow(clippy::too_many_arguments)]
141 #[instrument(skip_all)]
142 pub(super) async fn handle_local_event(
143 &mut self,
144 own_user_id: OwnedUserId,
145 own_profile: Option<Profile>,
146 should_add_new_items: bool,
147 date_divider_mode: DateDividerMode,
148 txn_id: OwnedTransactionId,
149 send_handle: Option<SendHandle>,
150 content: TimelineEventKind,
151 ) {
152 let ctx = TimelineEventContext {
153 sender: own_user_id,
154 sender_profile: own_profile,
155 timestamp: MilliSecondsSinceUnixEpoch::now(),
156 is_own_event: true,
157 read_receipts: Default::default(),
158 is_highlighted: false,
160 flow: Flow::Local { txn_id, send_handle },
161 should_add_new_items,
162 };
163
164 let mut txn = self.transaction();
165
166 let mut date_divider_adjuster = DateDividerAdjuster::new(date_divider_mode);
167
168 TimelineEventHandler::new(&mut txn, ctx)
169 .handle_event(&mut date_divider_adjuster, content)
170 .await;
171
172 txn.adjust_date_dividers(date_divider_adjuster);
173
174 txn.commit();
175 }
176
177 pub(super) async fn retry_event_decryption<P: RoomDataProvider, Fut>(
178 &mut self,
179 retry_one: impl Fn(Arc<TimelineItem>) -> Fut,
180 retry_indices: Vec<usize>,
181 push_rules_context: Option<(ruma::push::Ruleset, ruma::push::PushConditionRoomCtx)>,
182 room_data_provider: &P,
183 settings: &TimelineSettings,
184 ) where
185 Fut: Future<Output = Option<TimelineEvent>>,
186 {
187 let mut txn = self.transaction();
188
189 let mut date_divider_adjuster =
190 DateDividerAdjuster::new(settings.date_divider_mode.clone());
191
192 let mut offset = 0;
196 for idx in retry_indices {
197 let idx = idx - offset;
198 let Some(mut event) = retry_one(txn.items[idx].clone()).await else {
199 continue;
200 };
201
202 event.push_actions = push_rules_context.as_ref().map(|(push_rules, push_context)| {
203 push_rules.get_actions(event.raw(), push_context).to_owned()
204 });
205
206 let handle_one_res = txn
207 .handle_remote_event(
208 event,
209 TimelineItemPosition::UpdateAt { timeline_item_index: idx },
210 room_data_provider,
211 settings,
212 &mut date_divider_adjuster,
213 )
214 .await;
215
216 if handle_one_res.item_removed {
219 offset += 1;
220 }
221 }
222
223 txn.adjust_date_dividers(date_divider_adjuster);
224
225 txn.commit();
226 }
227
228 #[cfg(test)]
229 pub(super) fn handle_read_receipts(
230 &mut self,
231 receipt_event_content: ReceiptEventContent,
232 own_user_id: &UserId,
233 ) {
234 let mut txn = self.transaction();
235 txn.handle_explicit_read_receipts(receipt_event_content, own_user_id);
236 txn.commit();
237 }
238
239 pub(super) fn clear(&mut self) {
240 let mut txn = self.transaction();
241 txn.clear();
242 txn.commit();
243 }
244
245 pub(super) async fn replace_with_remote_events<Events, RoomData>(
251 &mut self,
252 events: Events,
253 origin: RemoteEventOrigin,
254 room_data_provider: &RoomData,
255 settings: &TimelineSettings,
256 ) where
257 Events: IntoIterator,
258 Events::Item: Into<TimelineEvent>,
259 RoomData: RoomDataProvider,
260 {
261 let mut txn = self.transaction();
262 txn.clear();
263 txn.handle_remote_events_with_diffs(
264 vec![VectorDiff::Append { values: events.into_iter().map(Into::into).collect() }],
265 origin,
266 room_data_provider,
267 settings,
268 )
269 .await;
270 txn.commit();
271 }
272
273 pub(super) fn mark_all_events_as_encrypted(&mut self) {
274 let mut txn = self.transaction();
277 txn.mark_all_events_as_encrypted();
278 txn.commit();
279 }
280
281 pub(super) fn transaction(&mut self) -> TimelineStateTransaction<'_> {
282 TimelineStateTransaction::new(&mut self.items, &mut self.meta, self.timeline_focus)
283 }
284}
285
286#[derive(Clone)]
287pub(in crate::timeline) enum PendingEditKind {
288 RoomMessage(Replacement<RoomMessageEventContentWithoutRelation>),
289 Poll(Replacement<NewUnstablePollStartEventContentWithoutRelation>),
290}
291
292#[derive(Clone)]
293pub(in crate::timeline) struct PendingEdit {
294 pub kind: PendingEditKind,
295 pub event_json: Raw<AnySyncTimelineEvent>,
296}
297
298impl PendingEdit {
299 pub fn edited_event(&self) -> &EventId {
300 match &self.kind {
301 PendingEditKind::RoomMessage(Replacement { event_id, .. })
302 | PendingEditKind::Poll(Replacement { event_id, .. }) => event_id,
303 }
304 }
305}
306
307#[cfg(not(tarpaulin_include))]
308impl std::fmt::Debug for PendingEdit {
309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310 match &self.kind {
311 PendingEditKind::RoomMessage(_) => {
312 f.debug_struct("RoomMessage").finish_non_exhaustive()
313 }
314 PendingEditKind::Poll(_) => f.debug_struct("Poll").finish_non_exhaustive(),
315 }
316 }
317}
318
319pub(crate) struct FullEventMeta<'a> {
323 pub event_id: &'a EventId,
325 pub visible: bool,
327 pub sender: Option<&'a UserId>,
329 pub is_own_event: bool,
331 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
333}
334
335impl FullEventMeta<'_> {
336 pub(super) fn base_meta(&self) -> EventMeta {
337 EventMeta {
338 event_id: self.event_id.to_owned(),
339 visible: self.visible,
340 timeline_item_index: None,
341 }
342 }
343}