1use std::collections::{HashMap, HashSet};
16
17use eyeball_im::VectorDiff;
18use itertools::Itertools as _;
19use matrix_sdk::deserialized_responses::{
20 ThreadSummaryStatus, TimelineEvent, TimelineEventKind, UnsignedEventLocation,
21};
22use ruma::{
23 EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId, UserId,
24 events::AnySyncTimelineEvent, push::Action, serde::Raw,
25};
26use tracing::{debug, instrument, trace, warn};
27
28use super::{
29 super::{
30 controller::ObservableItemsTransactionEntry,
31 date_dividers::DateDividerAdjuster,
32 event_handler::{Flow, TimelineEventContext, TimelineEventHandler, TimelineItemPosition},
33 event_item::RemoteEventOrigin,
34 traits::RoomDataProvider,
35 },
36 ObservableItems, ObservableItemsTransaction, TimelineMetadata, TimelineReadReceiptTracking,
37 TimelineSettings,
38 metadata::EventMeta,
39};
40use crate::timeline::{
41 EmbeddedEvent, Profile, ThreadSummary, TimelineDetails, VirtualTimelineItem,
42 controller::TimelineFocusKind,
43 event_handler::{FailedToParseEvent, RemovedItem, TimelineAction},
44};
45
46pub(in crate::timeline) struct TimelineStateTransaction<'a, P: RoomDataProvider> {
47 pub items: ObservableItemsTransaction<'a>,
50
51 number_of_items_when_transaction_started: usize,
53
54 pub meta: TimelineMetadata,
58
59 previous_meta: &'a mut TimelineMetadata,
61
62 pub focus: &'a TimelineFocusKind<P>,
64}
65
66impl<'a, P: RoomDataProvider> TimelineStateTransaction<'a, P> {
67 pub(super) fn new(
69 items: &'a mut ObservableItems,
70 meta: &'a mut TimelineMetadata,
71 focus: &'a TimelineFocusKind<P>,
72 ) -> Self {
73 let previous_meta = meta;
74 let meta = previous_meta.clone();
75 let items = items.transaction();
76
77 Self {
78 number_of_items_when_transaction_started: items.len(),
79 items,
80 previous_meta,
81 meta,
82 focus,
83 }
84 }
85
86 pub(super) async fn handle_remote_events_with_diffs(
88 &mut self,
89 diffs: Vec<VectorDiff<TimelineEvent>>,
90 origin: RemoteEventOrigin,
91 room_data_provider: &P,
92 settings: &TimelineSettings,
93 ) {
94 let mut date_divider_adjuster =
95 DateDividerAdjuster::new(settings.date_divider_mode.clone());
96
97 let mut cached_profiles: HashMap<OwnedUserId, Option<Profile>> = HashMap::new();
98
99 for diff in diffs {
100 match diff {
101 VectorDiff::Append { values: events } => {
102 for event in events {
103 self.handle_remote_event(
104 event,
105 TimelineItemPosition::End { origin },
106 room_data_provider,
107 settings,
108 &mut date_divider_adjuster,
109 &mut cached_profiles,
110 )
111 .await;
112 }
113 }
114
115 VectorDiff::PushFront { value: event } => {
116 self.handle_remote_event(
117 event,
118 TimelineItemPosition::Start { origin },
119 room_data_provider,
120 settings,
121 &mut date_divider_adjuster,
122 &mut cached_profiles,
123 )
124 .await;
125 }
126
127 VectorDiff::PushBack { value: event } => {
128 self.handle_remote_event(
129 event,
130 TimelineItemPosition::End { origin },
131 room_data_provider,
132 settings,
133 &mut date_divider_adjuster,
134 &mut cached_profiles,
135 )
136 .await;
137 }
138
139 VectorDiff::Insert { index: event_index, value: event } => {
140 self.handle_remote_event(
141 event,
142 TimelineItemPosition::At { event_index, origin },
143 room_data_provider,
144 settings,
145 &mut date_divider_adjuster,
146 &mut cached_profiles,
147 )
148 .await;
149 }
150
151 VectorDiff::Set { index: event_index, value: event } => {
152 if let Some(timeline_item_index) = self
153 .items
154 .all_remote_events()
155 .get(event_index)
156 .and_then(|meta| meta.timeline_item_index)
157 {
158 self.handle_remote_event(
159 event,
160 TimelineItemPosition::UpdateAt { timeline_item_index },
161 room_data_provider,
162 settings,
163 &mut date_divider_adjuster,
164 &mut cached_profiles,
165 )
166 .await;
167 } else {
168 warn!(
169 event_index,
170 "Set update dropped because there wasn't any attached timeline item index."
171 );
172 }
173 }
174
175 VectorDiff::Remove { index: event_index } => {
176 self.remove_timeline_item(event_index, &mut date_divider_adjuster);
177 }
178
179 VectorDiff::Clear => {
180 self.clear();
181 }
182
183 v => unimplemented!("{v:?}"),
184 }
185 }
186
187 self.adjust_date_dividers(date_divider_adjuster);
188 self.check_invariants();
189 }
190
191 async fn handle_remote_aggregation(
192 &mut self,
193 event: TimelineEvent,
194 position: TimelineItemPosition,
195 room_data_provider: &P,
196 date_divider_adjuster: &mut DateDividerAdjuster,
197 ) {
198 let raw_event = event.raw();
199
200 let deserialized = match raw_event.deserialize() {
201 Ok(deserialized) => deserialized,
202 Err(err) => {
203 warn!("Failed to deserialize timeline event: {err}");
204 return;
205 }
206 };
207
208 let sender = deserialized.sender().to_owned();
209 let timestamp = deserialized.origin_server_ts();
210 let event_id = deserialized.event_id().to_owned();
211 let txn_id = deserialized.transaction_id().map(ToOwned::to_owned);
212
213 if let Some(action @ TimelineAction::HandleAggregation { .. }) = TimelineAction::from_event(
214 deserialized,
215 raw_event,
216 room_data_provider,
217 None,
218 None,
219 None,
220 None,
221 )
222 .await
223 {
224 let encryption_info = event.kind.encryption_info().cloned();
225
226 let sender_profile = room_data_provider.profile_from_user_id(&sender).await;
227
228 let ctx = TimelineEventContext {
229 sender,
230 sender_profile,
231 timestamp,
232 read_receipts: Default::default(),
234 is_highlighted: false,
235 flow: Flow::Remote {
236 event_id: event_id.clone(),
237 raw_event: event.raw().clone(),
238 encryption_info,
239 txn_id,
240 position,
241 },
242 should_add_new_items: false,
244 };
245
246 TimelineEventHandler::new(self, ctx).handle_event(date_divider_adjuster, action).await;
247 }
248 }
249
250 pub(super) async fn handle_remote_aggregations(
257 &mut self,
258 diffs: Vec<VectorDiff<TimelineEvent>>,
259 origin: RemoteEventOrigin,
260 room_data_provider: &P,
261 settings: &TimelineSettings,
262 ) {
263 let mut date_divider_adjuster =
264 DateDividerAdjuster::new(settings.date_divider_mode.clone());
265
266 for diff in diffs {
267 match diff {
268 VectorDiff::Append { values: events } => {
269 for event in events {
270 self.handle_remote_aggregation(
271 event,
272 TimelineItemPosition::End { origin },
273 room_data_provider,
274 &mut date_divider_adjuster,
275 )
276 .await;
277 }
278 }
279
280 VectorDiff::PushFront { value: event } => {
281 self.handle_remote_aggregation(
282 event,
283 TimelineItemPosition::Start { origin },
284 room_data_provider,
285 &mut date_divider_adjuster,
286 )
287 .await;
288 }
289
290 VectorDiff::PushBack { value: event } => {
291 self.handle_remote_aggregation(
292 event,
293 TimelineItemPosition::End { origin },
294 room_data_provider,
295 &mut date_divider_adjuster,
296 )
297 .await;
298 }
299
300 VectorDiff::Insert { index: event_index, value: event } => {
301 self.handle_remote_aggregation(
302 event,
303 TimelineItemPosition::At { event_index, origin },
304 room_data_provider,
305 &mut date_divider_adjuster,
306 )
307 .await;
308 }
309
310 VectorDiff::Set { index: event_index, value: event } => {
311 if let Some(timeline_item_index) = self
312 .items
313 .all_remote_events()
314 .get(event_index)
315 .and_then(|meta| meta.timeline_item_index)
316 {
317 self.handle_remote_aggregation(
318 event,
319 TimelineItemPosition::UpdateAt { timeline_item_index },
320 room_data_provider,
321 &mut date_divider_adjuster,
322 )
323 .await;
324 } else {
325 warn!(
326 event_index,
327 "Set update dropped because there wasn't any attached timeline item index."
328 );
329 }
330 }
331
332 VectorDiff::Remove { .. } | VectorDiff::Clear => {
333 }
337
338 v => unimplemented!("{v:?}"),
339 }
340 }
341
342 self.adjust_date_dividers(date_divider_adjuster);
343 self.check_invariants();
344 }
345
346 fn check_invariants(&self) {
347 self.check_no_duplicate_read_receipts();
348 self.check_no_unused_unique_ids();
349 }
350
351 fn check_no_duplicate_read_receipts(&self) {
352 let mut by_user_id = HashMap::new();
353 let mut duplicates = HashSet::new();
354
355 for item in self.items.iter_remotes_region().filter_map(|(_, item)| item.as_event()) {
356 if let Some(event_id) = item.event_id() {
357 for (user_id, _read_receipt) in item.read_receipts() {
358 if let Some(prev_event_id) = by_user_id.insert(user_id, event_id) {
359 duplicates.insert((user_id.clone(), prev_event_id, event_id));
360 }
361 }
362 }
363 }
364
365 if !duplicates.is_empty() {
366 #[cfg(any(debug_assertions, test))]
367 panic!("duplicate read receipts in this timeline: {duplicates:?}\n{:?}", self.items);
368
369 #[cfg(not(any(debug_assertions, test)))]
370 tracing::error!(
371 ?duplicates,
372 items = ?self.items,
373 "duplicate read receipts in this timeline",
374 );
375 }
376 }
377
378 fn check_no_unused_unique_ids(&self) {
379 let duplicates = self
380 .items
381 .iter_all_regions()
382 .duplicates_by(|(_nth, item)| item.unique_id())
383 .map(|(_nth, item)| item.unique_id())
384 .collect::<Vec<_>>();
385
386 if !duplicates.is_empty() {
387 #[cfg(any(debug_assertions, test))]
388 panic!("duplicate unique ids in this timeline: {duplicates:?}\n{:?}", self.items);
389
390 #[cfg(not(any(debug_assertions, test)))]
391 tracing::error!(
392 ?duplicates,
393 items = ?self.items,
394 "duplicate unique ids in this timeline",
395 );
396 }
397 }
398
399 fn should_add_event_item(
401 &self,
402 room_data_provider: &P,
403 settings: &TimelineSettings,
404 event: &AnySyncTimelineEvent,
405 thread_root: Option<&EventId>,
406 position: TimelineItemPosition,
407 ) -> bool {
408 let rules = room_data_provider.room_version_rules();
409
410 if !(settings.event_filter)(event, &rules) {
411 return false;
413 }
414
415 match &self.focus {
416 TimelineFocusKind::PinnedEvents { .. } => {
417 room_data_provider.is_pinned_event(event.event_id())
419 }
420
421 TimelineFocusKind::Event { paginator } => {
422 let hide_threaded_events =
425 paginator.get().is_some_and(|paginator| paginator.hide_threaded_events());
426 if thread_root.is_some() && hide_threaded_events {
427 return false;
428 }
429
430 let origin = match position {
432 TimelineItemPosition::End { origin }
433 | TimelineItemPosition::Start { origin }
434 | TimelineItemPosition::At { origin, .. } => origin,
435
436 TimelineItemPosition::UpdateAt { timeline_item_index: idx } => self
437 .items
438 .get(idx)
439 .and_then(|item| item.as_event()?.as_remote())
440 .map_or(RemoteEventOrigin::Unknown, |item| item.origin),
441 };
442
443 match origin {
444 RemoteEventOrigin::Sync | RemoteEventOrigin::Unknown => false,
446 RemoteEventOrigin::Cache | RemoteEventOrigin::Pagination => true,
447 }
448 }
449
450 TimelineFocusKind::Live { hide_threaded_events } => {
451 thread_root.is_none() || !hide_threaded_events
454 }
455
456 TimelineFocusKind::Thread { root_event_id, .. } => {
457 event.event_id() == root_event_id
459 || thread_root.as_ref().is_some_and(|r| r == root_event_id)
460 }
461 }
462 }
463
464 fn can_show_read_receipts(
467 &self,
468 settings: &TimelineSettings,
469 event: &AnySyncTimelineEvent,
470 ) -> bool {
471 match event {
472 AnySyncTimelineEvent::State(_) => {
473 matches!(settings.track_read_receipts, TimelineReadReceiptTracking::AllEvents)
474 }
475 AnySyncTimelineEvent::MessageLike(_) => {
476 !matches!(settings.track_read_receipts, TimelineReadReceiptTracking::Disabled)
477 }
478 }
479 }
480
481 async fn maybe_add_error_item(
485 &mut self,
486 position: TimelineItemPosition,
487 room_data_provider: &P,
488 raw: &Raw<AnySyncTimelineEvent>,
489 deserialization_error: serde_json::Error,
490 settings: &TimelineSettings,
491 ) -> Option<(
492 OwnedEventId,
493 OwnedUserId,
494 MilliSecondsSinceUnixEpoch,
495 Option<OwnedTransactionId>,
496 Option<TimelineAction>,
497 Option<OwnedEventId>,
498 bool,
499 bool,
500 )> {
501 let state_key: Option<String> = raw.get_field("state_key").ok().flatten();
502
503 let event_type = if let Some(state_key) = state_key {
510 raw.get_field("type")
511 .ok()
512 .flatten()
513 .map(|event_type| FailedToParseEvent::State { event_type, state_key })
514 } else {
515 raw.get_field("type").ok().flatten().map(FailedToParseEvent::MsgLike)
516 };
517
518 let event_id: Option<OwnedEventId> = raw.get_field("event_id").ok().flatten();
519 let Some(event_id) = event_id else {
520 warn!(
522 ?event_type,
523 "Failed to deserialize timeline event (with no ID): {deserialization_error}"
524 );
525 return None;
526 };
527
528 let sender: Option<OwnedUserId> = raw.get_field("sender").ok().flatten();
529 let origin_server_ts: Option<MilliSecondsSinceUnixEpoch> =
530 raw.get_field("origin_server_ts").ok().flatten();
531
532 match (sender, origin_server_ts, event_type) {
533 (Some(sender), Some(origin_server_ts), Some(event_type))
534 if settings.add_failed_to_parse =>
535 {
536 #[derive(serde::Deserialize)]
539 struct Unsigned {
540 transaction_id: Option<OwnedTransactionId>,
541 }
542
543 let transaction_id: Option<OwnedTransactionId> = raw
544 .get_field::<Unsigned>("unsigned")
545 .ok()
546 .flatten()
547 .and_then(|unsigned| unsigned.transaction_id);
548
549 Some((
552 event_id,
553 sender,
554 origin_server_ts,
555 transaction_id,
556 Some(TimelineAction::failed_to_parse(event_type, deserialization_error)),
557 None,
558 true,
559 true,
560 ))
561 }
562
563 (sender, origin_server_ts, event_type) => {
564 warn!(
567 ?event_type,
568 ?event_id,
569 "Failed to deserialize timeline event: {deserialization_error}"
570 );
571
572 self.add_or_update_remote_event(
575 EventMeta::new(event_id, false, false, None),
576 sender.as_deref(),
577 origin_server_ts,
578 position,
579 room_data_provider,
580 settings,
581 )
582 .await;
583 None
584 }
585 }
586 }
587
588 #[instrument(skip(self, room_data_provider))]
591 async fn fetch_latest_thread_reply(
592 &mut self,
593 event_id: &EventId,
594 room_data_provider: &P,
595 ) -> Option<Box<EmbeddedEvent>> {
596 let event = RoomDataProvider::load_event(room_data_provider, event_id)
597 .await
598 .inspect_err(|err| {
599 warn!("Failed to load thread latest event: {err}");
600 })
601 .ok()?;
602
603 EmbeddedEvent::try_from_timeline_event(event, room_data_provider, &self.meta)
604 .await
605 .inspect_err(|err| {
606 warn!("Failed to extract thread latest event into a timeline item content: {err}");
607 })
608 .ok()
609 .flatten()
610 .map(Box::new)
611 }
612
613 pub(super) async fn handle_remote_event(
617 &mut self,
618 event: TimelineEvent,
619 position: TimelineItemPosition,
620 room_data_provider: &P,
621 settings: &TimelineSettings,
622 date_divider_adjuster: &mut DateDividerAdjuster,
623 profiles: &mut HashMap<OwnedUserId, Option<Profile>>,
624 ) -> RemovedItem {
625 let is_highlighted =
626 event.push_actions().is_some_and(|actions| actions.iter().any(Action::is_highlight));
627
628 let thread_summary = if let ThreadSummaryStatus::Some(summary) = event.thread_summary {
629 let latest_reply_item = if let Some(latest_reply) = summary.latest_reply {
630 self.fetch_latest_thread_reply(&latest_reply, room_data_provider).await
631 } else {
632 None
633 };
634 Some(ThreadSummary {
635 latest_event: TimelineDetails::from_initial_value(latest_reply_item),
636 num_replies: summary.num_replies,
637 })
638 } else {
639 None
640 };
641
642 let encryption_info = event.kind.encryption_info().cloned();
643
644 let bundled_edit_encryption_info = event.kind.unsigned_encryption_map().and_then(|map| {
645 map.get(&UnsignedEventLocation::RelationsReplace)?.encryption_info().cloned()
646 });
647
648 let (raw, utd_info) = match event.kind {
649 TimelineEventKind::UnableToDecrypt { utd_info, event } => (event, Some(utd_info)),
650 _ => (event.kind.into_raw(), None),
651 };
652
653 let (
654 event_id,
655 sender,
656 timestamp,
657 txn_id,
658 timeline_action,
659 thread_root,
660 should_add,
661 can_show_read_receipts,
662 ) = match raw.deserialize() {
663 Ok(event) => {
665 let (in_reply_to, thread_root) = self.meta.process_event_relations(
666 &event,
667 &raw,
668 bundled_edit_encryption_info,
669 &self.items,
670 self.focus.is_thread(),
671 );
672
673 let should_add = self.should_add_event_item(
674 room_data_provider,
675 settings,
676 &event,
677 thread_root.as_deref(),
678 position,
679 );
680
681 let can_show_read_receipts = self.can_show_read_receipts(settings, &event);
682
683 (
684 event.event_id().to_owned(),
685 event.sender().to_owned(),
686 event.origin_server_ts(),
687 event.transaction_id().map(ToOwned::to_owned),
688 TimelineAction::from_event(
689 event,
690 &raw,
691 room_data_provider,
692 utd_info
693 .map(|utd_info| (utd_info, self.meta.unable_to_decrypt_hook.as_ref())),
694 in_reply_to,
695 thread_root.clone(),
696 thread_summary,
697 )
698 .await,
699 thread_root,
700 should_add,
701 can_show_read_receipts,
702 )
703 }
704
705 Err(e) => {
707 if let Some(tuple) =
708 self.maybe_add_error_item(position, room_data_provider, &raw, e, settings).await
709 {
710 tuple
711 } else {
712 return false;
713 }
714 }
715 };
716
717 self.add_or_update_remote_event(
720 EventMeta::new(event_id.clone(), should_add, can_show_read_receipts, thread_root),
721 Some(&sender),
722 Some(timestamp),
723 position,
724 room_data_provider,
725 settings,
726 )
727 .await;
728
729 let item_added = if let Some(timeline_action) = timeline_action {
731 let sender_profile = if let Some(profile) = profiles.get(&sender) {
732 profile.clone()
733 } else {
734 let profile = room_data_provider.profile_from_user_id(&sender).await;
735 profiles.insert(sender.clone(), profile.clone());
736 profile
737 };
738
739 let ctx = TimelineEventContext {
740 sender,
741 sender_profile,
742 timestamp,
743 read_receipts: if settings.track_read_receipts.is_enabled()
744 && should_add
745 && can_show_read_receipts
746 {
747 self.meta.read_receipts.compute_event_receipts(
748 &event_id,
749 &mut self.items,
750 matches!(position, TimelineItemPosition::End { .. }),
751 )
752 } else {
753 Default::default()
754 },
755 is_highlighted,
756 flow: Flow::Remote {
757 event_id: event_id.clone(),
758 raw_event: raw,
759 encryption_info,
760 txn_id,
761 position,
762 },
763 should_add_new_items: should_add,
764 };
765
766 TimelineEventHandler::new(self, ctx)
767 .handle_event(date_divider_adjuster, timeline_action)
768 .await
769 } else {
770 false
772 };
773
774 let mut item_removed = false;
775
776 if !item_added {
777 trace!("No new item added");
778
779 if let TimelineItemPosition::UpdateAt { timeline_item_index } = position {
780 trace!("Removing UTD that was successfully retried");
783 self.items.remove(timeline_item_index);
784 item_removed = true;
785 }
786 }
787
788 item_removed
789 }
790
791 fn remove_timeline_item(
793 &mut self,
794 event_index: usize,
795 day_divider_adjuster: &mut DateDividerAdjuster,
796 ) {
797 day_divider_adjuster.mark_used();
798
799 if let Some(event_meta) = self.items.all_remote_events().get(event_index) {
809 if let Some(timeline_item_index) = event_meta.timeline_item_index {
811 let _ = self.items.remove(timeline_item_index);
812 }
813
814 self.items.remove_remote_event(event_index);
816 }
817 }
818
819 pub(super) fn clear(&mut self) {
820 if self.items.has_local() {
826 self.items.for_each(|entry| {
828 if entry.is_remote_event()
829 || entry.as_virtual().is_some_and(|vitem| match vitem {
830 VirtualTimelineItem::DateDivider(_) => false,
831 VirtualTimelineItem::ReadMarker | VirtualTimelineItem::TimelineStart => {
832 true
833 }
834 })
835 {
836 ObservableItemsTransactionEntry::remove(entry);
837 }
838 });
839
840 let mut idx = 0;
842 while idx < self.items.len() {
843 if self.items[idx].is_date_divider()
844 && self.items.get(idx + 1).is_none_or(|item| item.is_date_divider())
845 {
846 self.items.remove(idx);
847 } else {
849 idx += 1;
850 }
851 }
852 } else {
853 self.items.clear();
854 }
855
856 self.meta.clear();
857
858 debug!(remaining_items = self.items.len(), "Timeline cleared");
859 }
860
861 #[instrument(skip_all)]
862 pub(super) fn set_fully_read_event(&mut self, fully_read_event_id: OwnedEventId) {
863 if self.meta.fully_read_event.as_ref().is_some_and(|id| *id == fully_read_event_id) {
865 return;
866 }
867
868 self.meta.fully_read_event = Some(fully_read_event_id);
869 self.meta.update_read_marker(&mut self.items);
870 }
871
872 pub(super) fn commit(self) {
873 let previous_number_of_items = self.number_of_items_when_transaction_started;
875 let next_number_of_items = self.items.len();
876
877 if previous_number_of_items != next_number_of_items {
878 let count = self
879 .meta
880 .subscriber_skip_count
881 .compute_next(previous_number_of_items, next_number_of_items);
882 self.meta
883 .subscriber_skip_count
884 .update(count, matches!(self.focus, TimelineFocusKind::Live { .. }));
885 }
886
887 *self.previous_meta = self.meta;
889
890 self.items.commit();
891 }
892
893 async fn add_or_update_remote_event(
898 &mut self,
899 event_meta: EventMeta,
900 sender: Option<&UserId>,
901 timestamp: Option<MilliSecondsSinceUnixEpoch>,
902 position: TimelineItemPosition,
903 room_data_provider: &P,
904 settings: &TimelineSettings,
905 ) {
906 let event_id = event_meta.event_id.clone();
907
908 match position {
909 TimelineItemPosition::Start { .. } => self.items.push_front_remote_event(event_meta),
910
911 TimelineItemPosition::End { .. } => {
912 self.items.push_back_remote_event(event_meta);
913 }
914
915 TimelineItemPosition::At { event_index, .. } => {
916 self.items.insert_remote_event(event_index, event_meta);
917 }
918
919 TimelineItemPosition::UpdateAt { .. } => {
920 if let Some(event) =
921 self.items.get_remote_event_by_event_id_mut(&event_meta.event_id)
922 && (event.visible != event_meta.visible
923 || event.can_show_read_receipts != event_meta.can_show_read_receipts)
924 {
925 event.visible = event_meta.visible;
926 event.can_show_read_receipts = event_meta.can_show_read_receipts;
927
928 if settings.track_read_receipts.is_enabled() {
929 self.maybe_update_read_receipts_of_prev_event(&event_meta.event_id);
932 }
933 }
934 }
935 }
936
937 if settings.track_read_receipts.is_enabled()
938 && matches!(
939 position,
940 TimelineItemPosition::Start { .. }
941 | TimelineItemPosition::End { .. }
942 | TimelineItemPosition::At { .. }
943 )
944 {
945 self.load_read_receipts_for_event(&event_id, room_data_provider).await;
946
947 self.maybe_add_implicit_read_receipt(&event_id, sender, timestamp);
948 }
949 }
950
951 pub(super) fn adjust_date_dividers(&mut self, mut adjuster: DateDividerAdjuster) {
952 adjuster.run(&mut self.items, &mut self.meta);
953 }
954
955 pub(super) fn mark_all_events_as_encrypted(&mut self) {
959 for idx in 0..self.items.len() {
960 let item = &self.items[idx];
961
962 if let Some(event) = item.as_event() {
963 if event.is_room_encrypted {
964 continue;
965 }
966
967 let mut cloned_event = event.clone();
968 cloned_event.is_room_encrypted = true;
969
970 let item = item.with_kind(cloned_event);
972 self.items.replace(idx, item);
973 }
974 }
975 }
976}