1use std::{
16 collections::BTreeSet,
17 fmt,
18 ops::Deref,
19 sync::{Arc, OnceLock},
20};
21
22use as_variant::as_variant;
23use eyeball_im::{VectorDiff, VectorSubscriberStream};
24use eyeball_im_util::vector::{FilterMap, VectorObserverExt};
25use futures_core::Stream;
26use futures_util::future::try_join_all;
27use imbl::{HashSet, Vector};
28use matrix_sdk::{
29 deserialized_responses::{ThreadSummary, TimelineEvent},
30 event_cache::{
31 DecryptionRetryRequest, EventCache, EventFocusedCache, PaginationStatus, PinnedEventsCache,
32 RoomEventCache, Subscriber as EventCacheSubscriber, ThreadEventCache,
33 ThreadEventCacheUpdate,
34 },
35 send_queue::{LocalEcho, LocalEchoContent, RoomSendQueueUpdate, SendHandle},
36 task_monitor::BackgroundTaskHandle,
37};
38use ruma::{
39 EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId, RoomId,
40 TransactionId, UserId,
41 api::client::receipt::create_receipt::v3::ReceiptType as SendReceiptType,
42 events::{
43 AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncTimelineEvent,
44 MessageLikeEventType,
45 poll::unstable_start::UnstablePollStartEventContent,
46 reaction::ReactionEventContent,
47 receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
48 relation::{Annotation, RelationType},
49 room::message::{MessageType, Relation},
50 },
51 room_version_rules::RoomVersionRules,
52};
53use tokio::sync::{RwLock, RwLockWriteGuard};
54use tracing::{
55 Instrument as _, Span, debug, error, field::debug, info, info_span, instrument, trace, warn,
56};
57
58pub(super) use self::{
59 metadata::{RelativePosition, TimelineMetadata},
60 observable_items::{
61 AllRemoteEvents, ObservableItems, ObservableItemsEntry, ObservableItemsTransaction,
62 },
63 state::TimelineState,
64 state_transaction::TimelineStateTransaction,
65};
66use super::{
67 DateDividerMode, EmbeddedEvent, Error, EventSendState, EventTimelineItem, InReplyToDetails,
68 MediaUploadProgress, Profile, TimelineDetails, TimelineEventItemId, TimelineFocus,
69 TimelineItem, TimelineItemContent, TimelineItemKind, TimelineReadReceiptTracking,
70 VirtualTimelineItem,
71 algorithms::{rfind_event_by_id, rfind_event_item},
72 event_item::RemoteEventOrigin,
73 item::TimelineUniqueId,
74 subscriber::TimelineSubscriber,
75 traits::RoomDataProvider,
76};
77use crate::{
78 timeline::{
79 MsgLikeContent, MsgLikeKind, Room, SendTarget, TimelineEventFilterFn,
80 TimelineEventFocusThreadMode,
81 algorithms::rfind_event_by_item_id,
82 controller::decryption_retry_task::compute_redecryption_candidates,
83 date_dividers::DateDividerAdjuster,
84 event_item::TimelineItemHandle,
85 tasks::{event_focused_task, pinned_events_task, thread_updates_task},
86 },
87 unable_to_decrypt_hook::UtdHookManager,
88};
89
90pub(in crate::timeline) mod aggregations;
91mod decryption_retry_task;
92mod metadata;
93mod observable_items;
94mod read_receipts;
95mod state;
96mod state_transaction;
97
98pub(super) use aggregations::*;
99pub(super) use decryption_retry_task::{CryptoDropHandles, spawn_crypto_tasks};
100use matrix_sdk_base::{CallIntentConsensus, RoomInfo};
101
102pub(super) enum SendReceiptDecision {
104 DoNotSend,
106
107 SendTo(OwnedEventId),
112}
113
114#[derive(Debug)]
120pub(in crate::timeline) enum TimelineFocusKind {
121 Live {
123 hide_threaded_events: bool,
125
126 event_cache: RoomEventCache,
128 },
129
130 Event {
133 focused_event_id: OwnedEventId,
135
136 thread_root: OnceLock<OwnedEventId>,
142
143 thread_mode: TimelineEventFocusThreadMode,
146
147 event_cache: EventFocusedCache,
149 },
150
151 Thread {
153 thread_id: OwnedEventId,
155
156 event_cache: ThreadEventCache,
158 },
159
160 PinnedEvents {
161 event_cache: PinnedEventsCache,
163 },
164}
165
166impl TimelineFocusKind {
167 pub(super) fn room_id(&self) -> &RoomId {
169 match self {
170 TimelineFocusKind::Live { event_cache, .. } => event_cache.room_id(),
171 TimelineFocusKind::Thread { event_cache, .. } => event_cache.room_id(),
172 TimelineFocusKind::Event { event_cache, .. } => event_cache.room_id(),
173 TimelineFocusKind::PinnedEvents { event_cache } => event_cache.room_id(),
174 }
175 }
176 pub(super) fn receipt_thread(&self) -> ReceiptThread {
183 if let Some(thread_root) = self.thread_root() {
184 ReceiptThread::Thread(thread_root.to_owned())
185 } else if self.hide_threaded_events() {
186 ReceiptThread::Main
187 } else {
188 ReceiptThread::Unthreaded
189 }
190 }
191
192 fn hide_threaded_events(&self) -> bool {
194 match self {
195 TimelineFocusKind::Live { hide_threaded_events, .. } => *hide_threaded_events,
196 TimelineFocusKind::Event { thread_mode, .. } => {
197 matches!(
198 thread_mode,
199 TimelineEventFocusThreadMode::Automatic { hide_threaded_events: true }
200 )
201 }
202 TimelineFocusKind::Thread { .. } | TimelineFocusKind::PinnedEvents { .. } => false,
203 }
204 }
205
206 fn is_thread(&self) -> bool {
209 self.thread_root().is_some()
210 }
211
212 fn thread_root(&self) -> Option<&EventId> {
215 match self {
216 TimelineFocusKind::Event { thread_root, .. } => thread_root.get().map(|v| &**v),
217 TimelineFocusKind::Live { .. } | TimelineFocusKind::PinnedEvents { .. } => None,
218 TimelineFocusKind::Thread { thread_id, .. } => Some(thread_id),
219 }
220 }
221}
222
223#[derive(Clone, Debug)]
224pub(super) struct TimelineController<P: RoomDataProvider = Room> {
225 state: Arc<RwLock<TimelineState<P>>>,
227
228 focus: Arc<TimelineFocusKind>,
230
231 pub(crate) room_data_provider: P,
236
237 pub(super) settings: TimelineSettings,
239}
240
241#[derive(Clone)]
242pub(super) struct TimelineSettings {
243 pub(super) track_read_receipts: TimelineReadReceiptTracking,
246
247 pub(super) event_filter: Arc<TimelineEventFilterFn>,
250
251 pub(super) add_failed_to_parse: bool,
253
254 pub(super) date_divider_mode: DateDividerMode,
256}
257
258#[cfg(not(tarpaulin_include))]
259impl fmt::Debug for TimelineSettings {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 f.debug_struct("TimelineSettings")
262 .field("track_read_receipts", &self.track_read_receipts)
263 .field("add_failed_to_parse", &self.add_failed_to_parse)
264 .finish_non_exhaustive()
265 }
266}
267
268impl Default for TimelineSettings {
269 fn default() -> Self {
270 Self {
271 track_read_receipts: TimelineReadReceiptTracking::Disabled,
272 event_filter: Arc::new(default_event_filter),
273 add_failed_to_parse: true,
274 date_divider_mode: DateDividerMode::Daily,
275 }
276 }
277}
278
279pub fn default_event_filter(event: &AnySyncTimelineEvent, rules: &RoomVersionRules) -> bool {
289 match event {
290 AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomRedaction(ev)) => {
291 if ev.redacts(&rules.redaction).is_some() {
292 false
295 } else {
296 ev.event_type() != MessageLikeEventType::Reaction
299 }
300 }
301
302 AnySyncTimelineEvent::MessageLike(msg) => {
303 match msg.original_content() {
304 None => {
305 msg.event_type() != MessageLikeEventType::Reaction
308 }
309
310 Some(original_content) => {
311 match original_content {
312 AnyMessageLikeEventContent::RoomMessage(content) => {
313 if content
314 .relates_to
315 .as_ref()
316 .is_some_and(|rel| matches!(rel, Relation::Replacement(_)))
317 {
318 return false;
320 }
321
322 match content.msgtype {
323 MessageType::Audio(_)
324 | MessageType::Emote(_)
325 | MessageType::File(_)
326 | MessageType::Image(_)
327 | MessageType::Location(_)
328 | MessageType::Notice(_)
329 | MessageType::ServerNotice(_)
330 | MessageType::Text(_)
331 | MessageType::Video(_)
332 | MessageType::VerificationRequest(_) => true,
333 #[cfg(feature = "unstable-msc4274")]
334 MessageType::Gallery(_) => true,
335 _ => false,
336 }
337 }
338
339 AnyMessageLikeEventContent::Sticker(_)
340 | AnyMessageLikeEventContent::UnstablePollStart(
341 UnstablePollStartEventContent::New(_),
342 )
343 | AnyMessageLikeEventContent::CallInvite(_)
344 | AnyMessageLikeEventContent::RtcNotification(_)
345 | AnyMessageLikeEventContent::RoomEncrypted(_) => true,
346
347 AnyMessageLikeEventContent::Beacon(_) => false,
351 AnyMessageLikeEventContent::RtcDecline(_) => false,
354
355 _ => false,
356 }
357 }
358 }
359 }
360
361 AnySyncTimelineEvent::State(_) => {
362 true
364 }
365 }
366}
367
368pub(super) struct InitFocusResult {
370 pub has_events: bool,
372 pub focus_task: Option<BackgroundTaskHandle>,
375}
376
377#[derive(Clone, Debug, PartialEq)]
379pub struct ActiveCallInfo {
380 pub active_members: HashSet<OwnedUserId>,
382 pub call_intent: CallIntentConsensus,
384 pub is_joined: bool,
387 pub call_started_ts_millis: Option<MilliSecondsSinceUnixEpoch>,
391}
392
393impl ActiveCallInfo {
394 pub(crate) fn from_info(room_info: RoomInfo, owned_user_id: OwnedUserId) -> Option<Self> {
395 if room_info.has_active_room_call() {
396 Some(ActiveCallInfo {
397 active_members: HashSet::from(room_info.active_room_call_participants()),
398 call_intent: room_info.active_room_call_consensus_intent(),
399 is_joined: room_info.active_room_call_participants().contains(&owned_user_id),
400 call_started_ts_millis: None,
401 })
402 } else {
403 None
404 }
405 }
406
407 pub(crate) fn with_start_time(self, timestamp: Option<MilliSecondsSinceUnixEpoch>) -> Self {
408 Self { call_started_ts_millis: timestamp, ..self }
409 }
410}
411
412impl<P: RoomDataProvider> TimelineController<P> {
413 pub(super) async fn new(
414 room_data_provider: P,
415 focus: &TimelineFocus,
416 event_cache: &EventCache,
417 internal_id_prefix: Option<String>,
418 unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
419 is_room_encrypted: bool,
420 settings: TimelineSettings,
421 ) -> Result<Self, Error> {
422 let room_id = room_data_provider.room_id();
423
424 let focus = match focus {
425 TimelineFocus::Live { hide_threaded_events } => TimelineFocusKind::Live {
426 hide_threaded_events: *hide_threaded_events,
427 event_cache: event_cache.room(room_id).await?.0,
428 },
429
430 TimelineFocus::Event { target, thread_mode, num_context_events, .. } => {
431 TimelineFocusKind::Event {
432 event_cache: event_cache
433 .event_focused(room_id, target, (*thread_mode).into(), *num_context_events)
434 .await?
435 .0,
436 focused_event_id: target.clone(),
437 thread_root: OnceLock::new(),
439 thread_mode: *thread_mode,
440 }
441 }
442
443 TimelineFocus::Thread { thread_id, .. } => TimelineFocusKind::Thread {
444 event_cache: event_cache.thread(room_id, thread_id).await?.0,
445 thread_id: thread_id.clone(),
446 },
447
448 TimelineFocus::PinnedEvents => TimelineFocusKind::PinnedEvents {
449 event_cache: event_cache.pinned_events(room_id).await?.0,
450 },
451 };
452
453 let focus = Arc::new(focus);
454 let state = Arc::new(RwLock::new(TimelineState::new(
455 event_cache.clone(),
456 focus.clone(),
457 room_data_provider.own_user_id().to_owned(),
458 room_data_provider.room_version_rules(),
459 internal_id_prefix,
460 unable_to_decrypt_hook,
461 is_room_encrypted,
462 None,
463 )));
464
465 Ok(Self { state, focus, room_data_provider, settings })
466 }
467
468 pub async fn handle_encryption_state_changes(&self) {
472 let mut room_info = self.room_data_provider.room_info();
473
474 let mark_encrypted = || async {
476 let mut state = self.state.write().await;
477 state.meta.is_room_encrypted = true;
478 state.mark_all_events_as_encrypted();
479 };
480
481 if room_info.get().encryption_state().is_encrypted() {
482 mark_encrypted().await;
485 return;
486 }
487
488 while let Some(info) = room_info.next().await {
489 if info.encryption_state().is_encrypted() {
490 mark_encrypted().await;
491 break;
494 }
495 }
496 }
497
498 pub(super) async fn live_lazy_paginate_backwards(&self, num_events: u16) -> Option<usize> {
507 let state = self.state.read().await;
508
509 let (count, needs) = state
510 .meta
511 .subscriber_skip_count
512 .compute_next_when_paginating_backwards(num_events.into());
513
514 let is_live_timeline = true;
516 state.meta.subscriber_skip_count.update(count, is_live_timeline);
517
518 needs
519 }
520
521 pub(super) fn is_live(&self) -> bool {
523 matches!(&*self.focus, TimelineFocusKind::Live { .. })
524 }
525
526 pub(super) fn is_threaded(&self) -> bool {
528 self.focus.is_thread()
529 }
530
531 pub(super) fn thread_root(&self) -> Option<OwnedEventId> {
534 self.focus.thread_root().map(ToOwned::to_owned)
535 }
536
537 pub(super) async fn items(&self) -> Vector<Arc<TimelineItem>> {
541 self.state.read().await.items.clone_items()
542 }
543
544 #[cfg(test)]
545 pub(super) async fn subscribe_raw(
546 &self,
547 ) -> (Vector<Arc<TimelineItem>>, VectorSubscriberStream<Arc<TimelineItem>>) {
548 self.state.read().await.items.subscribe().into_values_and_stream()
549 }
550
551 pub(super) async fn subscribe(&self) -> (Vector<Arc<TimelineItem>>, TimelineSubscriber) {
552 let state = self.state.read().await;
553
554 TimelineSubscriber::new(&state.items, &state.meta.subscriber_skip_count)
555 }
556
557 pub(super) async fn subscribe_filter_map<U, F>(
558 &self,
559 f: F,
560 ) -> (Vector<U>, FilterMap<VectorSubscriberStream<Arc<TimelineItem>>, F>)
561 where
562 U: Clone,
563 F: Fn(Arc<TimelineItem>) -> Option<U>,
564 {
565 self.state.read().await.items.subscribe().filter_map(f)
566 }
567
568 #[instrument(skip_all)]
572 pub(super) async fn toggle_reaction_local(
573 &self,
574 item_id: &TimelineEventItemId,
575 key: &str,
576 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
577 ) -> Result<bool, Error> {
578 let mut state = self.state.write().await;
579
580 let Some((item_pos, item)) = rfind_event_by_item_id(&state.items, item_id) else {
581 warn!("Timeline item not found, can't add reaction");
582 return Err(Error::FailedToToggleReaction);
583 };
584
585 let user_id = self.room_data_provider.own_user_id();
586 let target = item.identifier();
587
588 let has_reaction =
591 item.reactions().get(key).is_some_and(|by_user| by_user.contains_key(user_id));
592 let previous = has_reaction
593 .then(|| state.meta.aggregations.find_reaction(&target, key, user_id).cloned())
594 .flatten();
595
596 if has_reaction && previous.is_none() {
597 warn!("reaction is on the item but unknown to the aggregations");
598 return Ok(false);
599 }
600
601 let Some(previous) = previous else {
602 match item.handle() {
604 TimelineItemHandle::Local(send_handle) => {
605 if send_handle
606 .react(key.to_owned())
607 .await
608 .map_err(|err| Error::SendQueueError(err.into()))?
609 .is_some()
610 {
611 trace!("adding a reaction to a local echo");
612 return Ok(true);
613 }
614
615 warn!("couldn't toggle reaction for local echo");
616 return Ok(false);
617 }
618
619 TimelineItemHandle::Remote(event_id) => {
620 trace!("adding a reaction to a remote echo");
624 let annotation = Annotation::new(event_id.to_owned(), key.to_owned());
625 self.room_data_provider
626 .send(ReactionEventContent::from(annotation).into(), extra_content)
627 .await?;
628 return Ok(true);
629 }
630 }
631 };
632
633 trace!("removing a previous reaction");
634
635 if previous.is_local() {
636 if let Some(handle) = &previous.send_handle {
639 if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? {
640 warn!("unexpectedly unable to abort sending of local reaction");
644 }
645 } else {
646 warn!("no send handle (this should only happen in testing contexts)");
647 }
648 return Ok(false);
649 }
650
651 let TimelineEventItemId::EventId(event_id) = previous.own_id else {
652 warn!("sent reaction without an event id");
653 return Ok(false);
654 };
655
656 let Some(annotated_event_id) =
659 item.as_remote().map(|event_item| event_item.event_id.clone())
660 else {
661 warn!("remote reaction to remote event, but the associated item isn't remote");
662 return Ok(false);
663 };
664
665 let mut reactions = item.reactions().clone();
666 let reaction_info = reactions.remove_reaction(user_id, key);
667
668 if reaction_info.is_some() {
669 let new_item = item.with_reactions(reactions);
670 state.items.replace(item_pos, new_item);
671 } else {
672 warn!(
673 "reaction is missing on the item, not removing it locally, \
674 but sending redaction."
675 );
676 }
677
678 drop(state);
680
681 trace!("sending redact for a previous reaction");
682 if let Err(err) = self.room_data_provider.redact(&event_id, None, None).await {
683 if let Some(reaction_info) = reaction_info {
684 debug!("sending redact failed, adding the reaction back to the list");
685
686 let mut state = self.state.write().await;
687 if let Some((item_pos, item)) = rfind_event_by_id(&state.items, &annotated_event_id)
688 {
689 let mut reactions = item.reactions().clone();
691 reactions
692 .entry(key.to_owned())
693 .or_default()
694 .insert(user_id.to_owned(), reaction_info);
695 let new_item = item.with_reactions(reactions);
696 state.items.replace(item_pos, new_item);
697 } else {
698 warn!(
699 "couldn't find item to re-add reaction anymore; \
700 maybe it's been redacted?"
701 );
702 }
703 }
704
705 return Err(err);
706 }
707
708 Ok(false)
709 }
710
711 pub(super) async fn pending_send_handle(
716 &self,
717 item_id: &TimelineEventItemId,
718 target: SendTarget,
719 ) -> Result<Option<SendHandle>, Error> {
720 let state = self.state.read().await;
721
722 let Some((_, item)) = rfind_event_by_item_id(&state.items, item_id) else {
723 return Err(Error::EventNotInTimeline(item_id.clone()));
724 };
725
726 let own_user_id = self.room_data_provider.own_user_id();
727 let target_id = item.identifier();
728 let aggregations = &state.meta.aggregations;
729
730 let handle = match &target {
731 SendTarget::Event => item.local_echo_send_handle(),
732 SendTarget::Edit => aggregations
733 .pending_send_handle(&target_id, |kind| matches!(kind, AggregationKind::Edit(_))),
734 SendTarget::Redaction => aggregations
735 .pending_send_handle(&target_id, |kind| matches!(kind, AggregationKind::Redaction { .. })),
736 SendTarget::Reaction { key } => {
737 aggregations.pending_send_handle(&target_id, |kind| {
738 matches!(kind, AggregationKind::Reaction { key: k, sender, .. } if k == key && sender == own_user_id)
739 })
740 }
741 };
742
743 Ok(handle)
744 }
745
746 pub(super) async fn handle_remote_events_with_diffs(
748 &self,
749 diffs: Vec<VectorDiff<TimelineEvent>>,
750 origin: RemoteEventOrigin,
751 ) {
752 if diffs.is_empty() {
753 return;
754 }
755
756 let mut state = self.state.write().await;
757 state
758 .handle_remote_events_with_diffs(
759 diffs,
760 origin,
761 &self.room_data_provider,
762 &self.settings,
763 )
764 .await
765 }
766
767 pub(super) async fn handle_remote_aggregations(
769 &self,
770 diffs: Vec<VectorDiff<TimelineEvent>>,
771 origin: RemoteEventOrigin,
772 ) {
773 if diffs.is_empty() {
774 return;
775 }
776
777 let mut state = self.state.write().await;
778 state
779 .handle_remote_aggregations(diffs, origin, &self.room_data_provider, &self.settings)
780 .await
781 }
782
783 pub(super) async fn handle_thread_summary(
786 &self,
787 thread_root: OwnedEventId,
788 thread_summary: ThreadSummary,
789 ) {
790 let mut state = self.state.write().await;
791 state.handle_thread_summary(thread_root, thread_summary, &self.room_data_provider).await
792 }
793
794 pub(super) async fn clear(&self) {
795 self.state.write().await.clear();
796 }
797
798 pub(super) async fn replace_with_initial_remote_events<Events>(
806 &self,
807 events: Events,
808 origin: RemoteEventOrigin,
809 ) where
810 Events: IntoIterator,
811 <Events as IntoIterator>::Item: Into<TimelineEvent>,
812 {
813 let mut state = self.state.write().await;
814
815 let track_read_markers = &self.settings.track_read_receipts;
816 if track_read_markers.is_enabled() {
817 state.populate_initial_user_receipt(&self.room_data_provider, ReceiptType::Read).await;
818 state
819 .populate_initial_user_receipt(&self.room_data_provider, ReceiptType::ReadPrivate)
820 .await;
821 }
822
823 let mut events = events.into_iter().peekable();
829 if !state.items.is_empty() || events.peek().is_some() {
830 state
831 .replace_with_remote_events(
832 events,
833 origin,
834 &self.room_data_provider,
835 &self.settings,
836 )
837 .await;
838 }
839
840 if track_read_markers.is_enabled() {
841 if let Some(fully_read_event_id) =
842 self.room_data_provider.load_fully_read_marker().await
843 {
844 state.handle_fully_read_marker(fully_read_event_id);
845 } else if let Some(latest_receipt_event_id) = state
846 .latest_user_read_receipt_timeline_event_id(self.room_data_provider.own_user_id())
847 {
848 debug!("no `m.fully_read` marker found, falling back to read receipt");
850 state.handle_fully_read_marker(latest_receipt_event_id);
851 }
852 }
853 }
854
855 pub(super) async fn handle_fully_read_marker(&self, fully_read_event_id: OwnedEventId) {
856 self.state.write().await.handle_fully_read_marker(fully_read_event_id);
857 }
858
859 pub(super) async fn handle_active_call_update(
860 &self,
861 maybe_active_call: Option<ActiveCallInfo>,
862 ) {
863 let mut state = self.state.write().await;
864 let mut txn = state.transaction();
865
866 txn.meta.active_call = maybe_active_call.clone();
869
870 if let Some(existing_event_id) = &txn.meta.active_rtc_notification_event_id {
871 let last_notification = rfind_event_by_id(&txn.items, existing_event_id);
873 if let Some((last_idx, last_notification)) = last_notification {
874 let updated_content = match last_notification.content() {
875 TimelineItemContent::RtcNotification {
876 call_intent,
877 declined_by,
878 active_call_info: _active_call_info,
879 } => Some(TimelineItemContent::RtcNotification {
880 call_intent: call_intent.to_owned(),
881 declined_by: declined_by.clone(),
882 active_call_info: maybe_active_call
883 .clone()
884 .map(|info| info.with_start_time(last_notification.timestamp.into())),
885 }),
886 _ => None,
887 };
888 if let Some(new_content) = updated_content {
889 let new_event_item = last_notification.inner.with_content(new_content);
890 let new_timeline_item =
891 TimelineItem::new(new_event_item, last_notification.internal_id.clone());
892 txn.items.replace(last_idx, new_timeline_item);
893 }
894
895 if maybe_active_call.is_none() {
896 txn.meta.active_rtc_notification_event_id = None;
898 }
899 }
900 }
901
902 txn.commit();
903 }
904
905 pub(super) async fn handle_read_receipt_event(&self, event: ReceiptEventContent) {
906 if event.is_empty() {
908 return;
909 }
910
911 let mut state = self.state.write().await;
912 state.handle_read_receipt(event, &self.room_data_provider).await;
913 }
914
915 #[instrument(skip_all)]
917 pub(super) async fn handle_local_event(
918 &self,
919 txn_id: OwnedTransactionId,
920 content: AnyMessageLikeEventContent,
921 send_handle: Option<SendHandle>,
922 ) {
923 let sender = self.room_data_provider.own_user_id().to_owned();
924 let profile = self.room_data_provider.profile_from_user_id(&sender).await;
925
926 let date_divider_mode = self.settings.date_divider_mode.clone();
927
928 let mut state = self.state.write().await;
929 state
930 .handle_local_event(sender, profile, date_divider_mode, txn_id, send_handle, content)
931 .await;
932 }
933
934 #[instrument(skip(self))]
939 pub(super) async fn update_event_send_state(
940 &self,
941 txn_id: &TransactionId,
942 send_state: EventSendState,
943 ) {
944 let mut state = self.state.write().await;
945 let mut txn = state.transaction();
946
947 let new_event_id: Option<&EventId> =
948 as_variant!(&send_state, EventSendState::Sent { event_id } => event_id);
949
950 if rfind_event_item(&txn.items, |it| {
953 new_event_id.is_some() && it.event_id() == new_event_id && it.as_remote().is_some()
954 })
955 .is_some()
956 {
957 trace!("Remote echo received before send-event response");
959
960 let local_echo = rfind_event_item(&txn.items, |it| it.transaction_id() == Some(txn_id));
961
962 if let Some((idx, _)) = local_echo {
966 warn!("Message echo got duplicated, removing the local one");
967 txn.items.remove(idx);
968
969 let mut adjuster =
971 DateDividerAdjuster::new(self.settings.date_divider_mode.clone());
972 adjuster.run(&mut txn.items, &mut txn.meta);
973 }
974
975 txn.commit();
976 return;
977 }
978
979 let result = rfind_event_item(&txn.items, |it| {
981 it.transaction_id() == Some(txn_id)
982 || new_event_id.is_some()
983 && it.event_id() == new_event_id
984 && it.as_local().is_some()
985 });
986
987 let Some((idx, item)) = result else {
988 if txn.meta.aggregations.update_send_state(
990 txn_id.to_owned(),
991 send_state,
992 &mut txn.items,
993 &txn.meta.room_version_rules,
994 ) {
995 trace!("Updated the send state of an aggregation");
996 txn.commit();
997 return;
998 }
999
1000 warn!("Timeline item not found, can't update send state");
1001 return;
1002 };
1003
1004 let Some(local_item) = item.as_local() else {
1005 warn!("We looked for a local item, but it transitioned to remote.");
1006 return;
1007 };
1008
1009 if let EventSendState::Sent { event_id: existing_event_id } = &local_item.send_state {
1012 error!(?existing_event_id, ?new_event_id, "Local echo already marked as sent");
1013 }
1014
1015 if let Some(new_event_id) = new_event_id {
1018 txn.meta.aggregations.mark_target_as_sent(txn_id.to_owned(), new_event_id.to_owned());
1019 }
1020
1021 let new_item = item.with_inner_kind(local_item.with_send_state(send_state));
1022 txn.items.replace(idx, new_item);
1023
1024 txn.commit();
1025 }
1026
1027 pub(super) async fn discard_local_echo(&self, txn_id: &TransactionId) -> bool {
1028 let mut state = self.state.write().await;
1029
1030 if let Some((idx, _)) =
1031 rfind_event_item(&state.items, |it| it.transaction_id() == Some(txn_id))
1032 {
1033 let mut txn = state.transaction();
1034
1035 txn.items.remove(idx);
1036
1037 let mut adjuster = DateDividerAdjuster::new(self.settings.date_divider_mode.clone());
1040 adjuster.run(&mut txn.items, &mut txn.meta);
1041
1042 txn.meta.update_read_marker(&mut txn.items);
1043
1044 txn.commit();
1045
1046 debug!("discarded local echo");
1047 return true;
1048 }
1049
1050 let mut txn = state.transaction();
1053
1054 let found_aggregation = match txn.meta.aggregations.try_remove_aggregation(
1056 &TimelineEventItemId::TransactionId(txn_id.to_owned()),
1057 &mut txn.items,
1058 ) {
1059 Ok(val) => val,
1060 Err(err) => {
1061 warn!("error when discarding local echo for an aggregation: {err}");
1062 true
1065 }
1066 };
1067
1068 if found_aggregation {
1069 txn.commit();
1070 }
1071
1072 found_aggregation
1073 }
1074
1075 pub(super) async fn replace_local_echo(
1076 &self,
1077 txn_id: &TransactionId,
1078 content: AnyMessageLikeEventContent,
1079 ) -> bool {
1080 let AnyMessageLikeEventContent::RoomMessage(content) = content else {
1081 warn!("Replacing a local echo for a non-RoomMessage-like event NYI");
1086 return false;
1087 };
1088
1089 let mut state = self.state.write().await;
1090 let mut txn = state.transaction();
1091
1092 let Some((idx, prev_item)) =
1093 rfind_event_item(&txn.items, |it| it.transaction_id() == Some(txn_id))
1094 else {
1095 if let Some(Relation::Replacement(replacement)) = content.relates_to
1097 && txn.meta.aggregations.replace_local_edit(
1098 txn_id,
1099 replacement,
1100 &mut txn.items,
1101 &txn.meta.room_version_rules,
1102 )
1103 {
1104 debug!("Replaced local echo of an edit");
1105 txn.commit();
1106 return true;
1107 }
1108
1109 debug!("Can't find local echo to replace");
1110 return false;
1111 };
1112
1113 let ti_kind = {
1116 let Some(prev_local_item) = prev_item.as_local() else {
1117 warn!("We looked for a local item, but it transitioned as remote??");
1118 return false;
1119 };
1120 let progress = as_variant!(&prev_local_item.send_state,
1122 EventSendState::NotSentYet { progress } => progress.clone())
1123 .flatten();
1124 prev_local_item.with_send_state(EventSendState::NotSentYet { progress })
1125 };
1126
1127 let new_item = TimelineItem::new(
1129 prev_item.with_kind(ti_kind).with_content(TimelineItemContent::message(
1130 content.msgtype,
1131 content.mentions,
1132 prev_item.content().thread_root(),
1133 prev_item.content().in_reply_to(),
1134 prev_item.content().thread_summary(),
1135 )),
1136 prev_item.internal_id.to_owned(),
1137 );
1138
1139 txn.items.replace(idx, new_item);
1140
1141 txn.commit();
1145
1146 debug!("Replaced local echo");
1147 true
1148 }
1149
1150 pub(super) async fn compute_redecryption_candidates(
1151 &self,
1152 ) -> (BTreeSet<String>, BTreeSet<String>) {
1153 let state = self.state.read().await;
1154 compute_redecryption_candidates(&state.items)
1155 }
1156
1157 pub(super) async fn set_sender_profiles_pending(&self) {
1158 self.set_non_ready_sender_profiles(TimelineDetails::Pending).await;
1159 }
1160
1161 pub(super) async fn set_sender_profiles_error(&self, error: Arc<matrix_sdk::Error>) {
1162 self.set_non_ready_sender_profiles(TimelineDetails::Error(error)).await;
1163 }
1164
1165 async fn set_non_ready_sender_profiles(&self, profile_state: TimelineDetails<Profile>) {
1166 self.state.write().await.items.for_each(|mut entry| {
1167 let Some(event_item) = entry.as_event() else { return };
1168 if !matches!(event_item.sender_profile(), TimelineDetails::Ready(_)) {
1169 let new_item = entry.with_kind(TimelineItemKind::Event(
1170 event_item.with_sender_profile(profile_state.clone()),
1171 ));
1172 ObservableItemsEntry::replace(&mut entry, new_item);
1173 }
1174 });
1175 }
1176
1177 pub(super) async fn update_missing_sender_profiles(&self) {
1178 trace!("Updating missing sender profiles");
1179
1180 let mut state = self.state.write().await;
1181 let mut entries = state.items.entries();
1182 while let Some(mut entry) = entries.next() {
1183 let Some(event_item) = entry.as_event() else { continue };
1184 let event_id = event_item.event_id().map(debug);
1185 let transaction_id = event_item.transaction_id().map(debug);
1186
1187 if event_item.sender_profile().is_ready() {
1188 trace!(event_id, transaction_id, "Profile already set");
1189 continue;
1190 }
1191
1192 match self.room_data_provider.profile_from_user_id(event_item.sender()).await {
1193 Some(profile) => {
1194 trace!(event_id, transaction_id, "Adding profile");
1195 let updated_item =
1196 event_item.with_sender_profile(TimelineDetails::Ready(profile));
1197 let new_item = entry.with_kind(updated_item);
1198 ObservableItemsEntry::replace(&mut entry, new_item);
1199 }
1200 None => {
1201 if !event_item.sender_profile().is_unavailable() {
1202 trace!(event_id, transaction_id, "Marking profile unavailable");
1203 let updated_item =
1204 event_item.with_sender_profile(TimelineDetails::Unavailable);
1205 let new_item = entry.with_kind(updated_item);
1206 ObservableItemsEntry::replace(&mut entry, new_item);
1207 } else {
1208 debug!(event_id, transaction_id, "Profile already marked unavailable");
1209 }
1210 }
1211 }
1212 }
1213
1214 trace!("Done updating missing sender profiles");
1215 }
1216
1217 pub(super) async fn force_update_sender_profiles(&self, sender_ids: &BTreeSet<&UserId>) {
1219 trace!("Forcing update of sender profiles: {sender_ids:?}");
1220
1221 let mut state = self.state.write().await;
1222 let mut entries = state.items.entries();
1223 while let Some(mut entry) = entries.next() {
1224 let Some(event_item) = entry.as_event() else { continue };
1225 if !sender_ids.contains(event_item.sender()) {
1226 continue;
1227 }
1228
1229 let event_id = event_item.event_id().map(debug);
1230 let transaction_id = event_item.transaction_id().map(debug);
1231
1232 match self.room_data_provider.profile_from_user_id(event_item.sender()).await {
1233 Some(profile) => {
1234 if matches!(event_item.sender_profile(), TimelineDetails::Ready(old_profile) if *old_profile == profile)
1235 {
1236 debug!(event_id, transaction_id, "Profile already up-to-date");
1237 } else {
1238 trace!(event_id, transaction_id, "Updating profile");
1239 let updated_item =
1240 event_item.with_sender_profile(TimelineDetails::Ready(profile));
1241 let new_item = entry.with_kind(updated_item);
1242 ObservableItemsEntry::replace(&mut entry, new_item);
1243 }
1244 }
1245 None => {
1246 if !event_item.sender_profile().is_unavailable() {
1247 trace!(event_id, transaction_id, "Marking profile unavailable");
1248 let updated_item =
1249 event_item.with_sender_profile(TimelineDetails::Unavailable);
1250 let new_item = entry.with_kind(updated_item);
1251 ObservableItemsEntry::replace(&mut entry, new_item);
1252 } else {
1253 debug!(event_id, transaction_id, "Profile already marked unavailable");
1254 }
1255 }
1256 }
1257 }
1258
1259 trace!("Done forcing update of sender profiles");
1260 }
1261
1262 #[cfg(test)]
1263 pub(super) async fn handle_read_receipts(&self, receipt_event_content: ReceiptEventContent) {
1264 let own_user_id = self.room_data_provider.own_user_id();
1265 self.state.write().await.handle_read_receipts(receipt_event_content, own_user_id);
1266 }
1267
1268 pub(super) async fn latest_user_read_receipt(
1272 &self,
1273 user_id: &UserId,
1274 ) -> Option<(OwnedEventId, Receipt)> {
1275 let receipt_thread = self.focus.receipt_thread();
1276
1277 self.state
1278 .read()
1279 .await
1280 .latest_user_read_receipt(
1281 user_id,
1282 receipt_thread,
1283 &self.room_data_provider,
1284 read_receipts::ImplicitReadReceipts::Include,
1285 )
1286 .await
1287 }
1288
1289 pub(super) async fn latest_user_read_receipt_timeline_event_id(
1292 &self,
1293 user_id: &UserId,
1294 ) -> Option<OwnedEventId> {
1295 self.state.read().await.latest_user_read_receipt_timeline_event_id(user_id)
1296 }
1297
1298 pub async fn subscribe_own_user_read_receipts_changed(
1300 &self,
1301 ) -> impl Stream<Item = ()> + use<P> {
1302 self.state.read().await.meta.read_receipts.subscribe_own_user_read_receipts_changed()
1303 }
1304
1305 pub(crate) async fn handle_local_echo(&self, echo: LocalEcho) {
1307 match echo.content {
1308 LocalEchoContent::Event { serialized_event, send_handle, send_error } => {
1309 let content = match serialized_event.deserialize() {
1310 Ok(d) => d,
1311 Err(err) => {
1312 warn!("error deserializing local echo: {err}");
1313 return;
1314 }
1315 };
1316
1317 self.handle_local_event(echo.transaction_id.clone(), content, Some(send_handle))
1318 .await;
1319
1320 if let Some(send_error) = send_error {
1321 self.update_event_send_state(
1322 &echo.transaction_id,
1323 EventSendState::SendingFailed {
1324 error: Arc::new(matrix_sdk::Error::SendQueueWedgeError(Box::new(
1325 send_error,
1326 ))),
1327 is_recoverable: false,
1328 },
1329 )
1330 .await;
1331 }
1332 }
1333
1334 LocalEchoContent::React { key, send_handle, applies_to } => {
1335 self.handle_local_reaction(key, send_handle, applies_to).await;
1336 }
1337
1338 LocalEchoContent::Redaction { redacts, reason, send_handle, send_error } => {
1339 self.handle_local_redaction(
1340 echo.transaction_id.clone(),
1341 redacts,
1342 reason,
1343 Some(send_handle),
1344 )
1345 .await;
1346
1347 if let Some(send_error) = send_error {
1348 self.update_event_send_state(
1349 &echo.transaction_id,
1350 EventSendState::SendingFailed {
1351 error: Arc::new(matrix_sdk::Error::SendQueueWedgeError(Box::new(
1352 send_error,
1353 ))),
1354 is_recoverable: false,
1355 },
1356 )
1357 .await;
1358 }
1359 }
1360 }
1361 }
1362
1363 #[instrument(skip(self, send_handle))]
1365 async fn handle_local_reaction(
1366 &self,
1367 reaction_key: String,
1368 send_handle: SendHandle,
1369 applies_to: OwnedTransactionId,
1370 ) {
1371 let mut state = self.state.write().await;
1372 let mut tr = state.transaction();
1373
1374 let target = TimelineEventItemId::TransactionId(applies_to);
1375
1376 let reaction_txn_id = send_handle.transaction_id().to_owned();
1377 let aggregation = Aggregation::new_local(
1378 TimelineEventItemId::TransactionId(reaction_txn_id),
1379 AggregationKind::Reaction {
1380 key: reaction_key.clone(),
1381 sender: self.room_data_provider.own_user_id().to_owned(),
1382 timestamp: MilliSecondsSinceUnixEpoch::now(),
1383 },
1384 Some(send_handle),
1385 );
1386
1387 tr.meta.aggregations.add(target.clone(), aggregation.clone());
1388 find_item_and_apply_aggregation(
1389 &tr.meta.aggregations,
1390 &mut tr.items,
1391 &target,
1392 aggregation,
1393 &tr.meta.room_version_rules,
1394 );
1395
1396 tr.commit();
1397 }
1398
1399 pub(super) async fn handle_local_redaction(
1401 &self,
1402 txn_id: OwnedTransactionId,
1403 redacts: OwnedEventId,
1404 reason: Option<String>,
1405 send_handle: Option<SendHandle>,
1406 ) {
1407 let mut state = self.state.write().await;
1408 let mut tr = state.transaction();
1409
1410 let target = TimelineEventItemId::EventId(redacts);
1411
1412 let aggregation = Aggregation::new_local(
1413 TimelineEventItemId::TransactionId(txn_id),
1414 AggregationKind::Redaction { reason },
1415 send_handle,
1416 );
1417
1418 tr.meta.aggregations.add(target.clone(), aggregation.clone());
1419 find_item_and_apply_aggregation(
1420 &tr.meta.aggregations,
1421 &mut tr.items,
1422 &target,
1423 aggregation,
1424 &tr.meta.room_version_rules,
1425 );
1426
1427 tr.commit();
1428 }
1429
1430 pub(crate) async fn handle_room_send_queue_update(&self, update: RoomSendQueueUpdate) {
1432 match update {
1433 RoomSendQueueUpdate::NewLocalEvent(echo) => {
1434 self.handle_local_echo(echo).await;
1435 }
1436
1437 RoomSendQueueUpdate::CancelledLocalEvent { transaction_id } => {
1438 if !self.discard_local_echo(&transaction_id).await {
1439 warn!("couldn't find the local echo to discard");
1440 }
1441 }
1442
1443 RoomSendQueueUpdate::ReplacedLocalEvent { transaction_id, new_content } => {
1444 let content = match new_content.deserialize() {
1445 Ok(d) => d,
1446 Err(err) => {
1447 warn!("error deserializing local echo (upon edit): {err}");
1448 return;
1449 }
1450 };
1451
1452 if !self.replace_local_echo(&transaction_id, content).await {
1453 warn!("couldn't find the local echo to replace");
1454 }
1455 }
1456
1457 RoomSendQueueUpdate::SendError { transaction_id, error, is_recoverable } => {
1458 self.update_event_send_state(
1459 &transaction_id,
1460 EventSendState::SendingFailed { error, is_recoverable },
1461 )
1462 .await;
1463 }
1464
1465 RoomSendQueueUpdate::RetryEvent { transaction_id } => {
1466 self.update_event_send_state(
1467 &transaction_id,
1468 EventSendState::NotSentYet { progress: None },
1469 )
1470 .await;
1471 }
1472
1473 RoomSendQueueUpdate::SentEvent { transaction_id, event_id } => {
1474 self.update_event_send_state(&transaction_id, EventSendState::Sent { event_id })
1475 .await;
1476 }
1477
1478 RoomSendQueueUpdate::MediaUpload { related_to, index, progress, .. } => {
1479 self.update_event_send_state(
1480 &related_to,
1481 EventSendState::NotSentYet {
1482 progress: Some(MediaUploadProgress { index, progress }),
1483 },
1484 )
1485 .await;
1486 }
1487 }
1488 }
1489
1490 pub async fn insert_timeline_start_if_missing(&self) {
1493 let mut state = self.state.write().await;
1494 let mut txn = state.transaction();
1495 txn.items.push_timeline_start_if_missing(
1496 txn.meta.new_timeline_item(VirtualTimelineItem::TimelineStart),
1497 );
1498 txn.commit();
1499 }
1500
1501 pub(super) async fn make_replied_to(
1507 &self,
1508 event: TimelineEvent,
1509 ) -> Result<Option<EmbeddedEvent>, Error> {
1510 let state = self.state.read().await;
1511 EmbeddedEvent::try_from_timeline_event(event, &self.room_data_provider, &state.meta).await
1512 }
1513}
1514
1515impl TimelineController {
1516 pub(super) fn room(&self) -> &Room {
1517 &self.room_data_provider
1518 }
1519
1520 pub(super) async fn init_focus(
1528 &self,
1529 initial_live_events: Vec<TimelineEvent>,
1530 ) -> Result<InitFocusResult, Error> {
1531 match self.focus.deref() {
1532 TimelineFocusKind::Live { event_cache, .. } => {
1533 let events = initial_live_events;
1535
1536 let has_events = !events.is_empty();
1537
1538 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Cache).await;
1539
1540 match event_cache.pagination().status().get() {
1541 PaginationStatus::Idle { hit_timeline_start } => {
1542 if hit_timeline_start {
1543 self.insert_timeline_start_if_missing().await;
1547 }
1548 }
1549 PaginationStatus::Paginating => {}
1550 }
1551
1552 Ok(InitFocusResult { has_events, focus_task: None })
1553 }
1554
1555 TimelineFocusKind::Event {
1556 focused_event_id: event_id,
1557 thread_mode,
1558 thread_root: focus_thread_root,
1559 event_cache,
1560 ..
1561 } => {
1562 let (events, receiver) = event_cache.subscribe().await?;
1563
1564 let has_events = !events.is_empty();
1565
1566 if let Some(thread_root) = event_cache.thread_root().await? {
1569 focus_thread_root.get_or_init(|| thread_root);
1570 }
1571
1572 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Pagination)
1573 .await;
1574
1575 let task = self
1576 .room_data_provider
1577 .client()
1578 .task_monitor()
1579 .spawn_infinite_task(
1580 "timeline::event_focused_cache_updates",
1581 event_focused_task(
1582 event_id.clone(),
1583 (*thread_mode).into(),
1584 event_cache.clone(),
1585 self.clone(),
1586 receiver,
1587 ),
1588 )
1589 .abort_on_drop();
1590
1591 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1592 }
1593
1594 TimelineFocusKind::Thread { event_cache, .. } => {
1595 let (has_events, subscriber) = self.init_with_thread_root(event_cache).await?;
1596
1597 let room = &self.room_data_provider;
1598 let span = info_span!(
1599 parent: Span::none(),
1600 "thread_live_update_handler",
1601 room_id = ?room.room_id(),
1602 );
1603 span.follows_from(Span::current());
1604
1605 let task = room
1606 .client()
1607 .task_monitor()
1608 .spawn_infinite_task(
1609 "timeline::thread_event_cache_updates",
1610 thread_updates_task(subscriber, event_cache.clone(), self.clone())
1611 .instrument(span),
1612 )
1613 .abort_on_drop();
1614
1615 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1616 }
1617
1618 TimelineFocusKind::PinnedEvents { event_cache } => {
1619 let (initial_events, pinned_events_recv) = event_cache.subscribe().await?;
1620
1621 let has_events = !initial_events.is_empty();
1622
1623 self.replace_with_initial_remote_events(
1624 initial_events,
1625 RemoteEventOrigin::Pagination,
1626 )
1627 .await;
1628
1629 let task = self
1630 .room_data_provider
1631 .client()
1632 .task_monitor()
1633 .spawn_infinite_task(
1634 "timeline::pinned_events_cache_updates",
1635 pinned_events_task(event_cache.clone(), self.clone(), pinned_events_recv),
1636 )
1637 .abort_on_drop();
1638
1639 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1640 }
1641 }
1642 }
1643
1644 pub(super) async fn init_with_thread_root(
1651 &self,
1652 event_cache: &ThreadEventCache,
1653 ) -> Result<(bool, EventCacheSubscriber<ThreadEventCacheUpdate>), Error> {
1654 let (events, subscriber) = event_cache.subscribe().await?;
1655 let has_events = !events.is_empty();
1656
1657 let lookups = events
1666 .iter()
1667 .filter_map(|event| event.event_id())
1668 .map(|event_id| event_cache.find_event_with_relations(event_id, None));
1669
1670 let mut related_events = Vector::new();
1671 for (_original, related) in try_join_all(lookups).await?.into_iter().flatten() {
1672 related_events.extend(related);
1673 }
1674
1675 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Cache).await;
1676
1677 if !related_events.is_empty() {
1679 self.handle_remote_aggregations(
1680 vec![VectorDiff::Append { values: related_events }],
1681 RemoteEventOrigin::Cache,
1682 )
1683 .await;
1684 }
1685
1686 Ok((has_events, subscriber))
1687 }
1688
1689 #[instrument(skip(self))]
1692 pub(super) async fn fetch_in_reply_to_details(&self, event_id: &EventId) -> Result<(), Error> {
1693 let state_guard = self.state.write().await;
1694 let (index, item) = rfind_event_by_id(&state_guard.items, event_id)
1695 .ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?;
1696 let remote_item = item
1697 .as_remote()
1698 .ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?
1699 .clone();
1700
1701 let TimelineItemContent::MsgLike(msglike) = item.content().clone() else {
1702 debug!("Event is not a message");
1703 return Ok(());
1704 };
1705 let Some(in_reply_to) = msglike.in_reply_to.clone() else {
1706 debug!("Event is not a reply");
1707 return Ok(());
1708 };
1709 if let TimelineDetails::Pending = &in_reply_to.event {
1710 debug!("Replied-to event is already being fetched");
1711 return Ok(());
1712 }
1713 if let TimelineDetails::Ready(_) = &in_reply_to.event {
1714 debug!("Replied-to event has already been fetched");
1715 return Ok(());
1716 }
1717
1718 let internal_id = item.internal_id.to_owned();
1719 let item = item.clone();
1720 let event = fetch_replied_to_event(
1721 state_guard,
1722 &self.state,
1723 index,
1724 &item,
1725 internal_id,
1726 &msglike,
1727 &in_reply_to.event_id,
1728 self.room(),
1729 )
1730 .await?;
1731
1732 let mut state = self.state.write().await;
1735 let (index, item) = rfind_event_by_id(&state.items, &remote_item.event_id)
1736 .ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?;
1737
1738 let TimelineItemContent::MsgLike(MsgLikeContent {
1741 kind: MsgLikeKind::Message(message),
1742 thread_root,
1743 in_reply_to,
1744 thread_summary,
1745 }) = item.content().clone()
1746 else {
1747 info!("Event is no longer a message (redacted?)");
1748 return Ok(());
1749 };
1750 let Some(in_reply_to) = in_reply_to else {
1751 warn!("Event no longer has a reply (bug?)");
1752 return Ok(());
1753 };
1754
1755 trace!("Updating in-reply-to details");
1758 let internal_id = item.internal_id.to_owned();
1759 let mut item = item.clone();
1760 item.set_content(TimelineItemContent::MsgLike(MsgLikeContent {
1761 kind: MsgLikeKind::Message(message),
1762 thread_root,
1763 in_reply_to: Some(InReplyToDetails { event_id: in_reply_to.event_id, event }),
1764 thread_summary,
1765 }));
1766 state.items.replace(index, TimelineItem::new(item, internal_id));
1767
1768 Ok(())
1769 }
1770
1771 pub(super) fn infer_thread_for_read_receipt(
1777 &self,
1778 receipt_type: &SendReceiptType,
1779 ) -> ReceiptThread {
1780 if matches!(receipt_type, SendReceiptType::FullyRead) {
1781 ReceiptThread::Unthreaded
1782 } else {
1783 self.focus.receipt_thread()
1784 }
1785 }
1786
1787 pub(super) async fn should_send_receipt(
1803 &self,
1804 receipt_type: &SendReceiptType,
1805 receipt_thread: &ReceiptThread,
1806 event_id: &EventId,
1807 is_marking_room_as_read: bool,
1808 ) -> SendReceiptDecision {
1809 let own_user_id = self.room().own_user_id();
1810 let state = self.state.read().await;
1811 let room = self.room();
1812 let all_remote_events = state.items.all_remote_events();
1813
1814 let target_event_id = match receipt_type {
1817 SendReceiptType::Read | SendReceiptType::ReadPrivate => {
1818 let is_own_event = all_remote_events
1819 .get_by_event_id(event_id)
1820 .and_then(|event_meta| event_meta.sender.as_deref())
1821 == Some(own_user_id);
1822
1823 if is_own_event {
1824 let filter_out_thread_events = match self.focus() {
1825 TimelineFocusKind::Thread { .. } | TimelineFocusKind::Event { .. } => false,
1826 TimelineFocusKind::Live { hide_threaded_events, .. } => {
1827 *hide_threaded_events
1828 }
1829 TimelineFocusKind::PinnedEvents { .. } => true,
1830 };
1831
1832 let previous_event = all_remote_events
1833 .iter()
1834 .rev()
1835 .skip_while(|event_meta| event_meta.event_id != *event_id)
1837 .skip(1)
1838 .filter(|event_meta| event_meta.sender.as_deref() != Some(own_user_id))
1840 .find_map(|event_meta| {
1841 if !filter_out_thread_events {
1842 Some(event_meta.event_id.clone())
1843 } else if event_meta.thread_root_id.is_none() {
1844 if let Some(TimelineEventItemId::EventId(aggregated_event_id)) =
1845 state.meta.aggregations.is_aggregation_of(
1846 &TimelineEventItemId::EventId(event_meta.event_id.clone()),
1847 )
1848 && let Some(target_meta) =
1849 all_remote_events.get_by_event_id(aggregated_event_id)
1850 && target_meta.thread_root_id.is_some()
1851 {
1852 None
1853 } else {
1854 Some(event_meta.event_id.clone())
1855 }
1856 } else {
1857 None
1858 }
1859 });
1860
1861 match previous_event {
1862 Some(event_id) => event_id,
1863 None if is_marking_room_as_read => event_id.to_owned(),
1868 None => return SendReceiptDecision::DoNotSend,
1869 }
1870 } else {
1871 event_id.to_owned()
1872 }
1873 }
1874
1875 _ => event_id.to_owned(),
1876 };
1877
1878 let previous_event_id = match receipt_type {
1880 SendReceiptType::Read => state
1881 .meta
1882 .user_receipt(
1883 own_user_id,
1884 ReceiptType::Read,
1885 receipt_thread.clone(),
1886 room,
1887 all_remote_events,
1888 read_receipts::ImplicitReadReceipts::Exclude,
1889 )
1890 .await
1891 .map(|(event_id, _)| event_id),
1892
1893 SendReceiptType::ReadPrivate => state
1897 .latest_user_read_receipt(
1898 own_user_id,
1899 receipt_thread.clone(),
1900 room,
1901 read_receipts::ImplicitReadReceipts::Exclude,
1902 )
1903 .await
1904 .map(|(event_id, _)| event_id),
1905
1906 SendReceiptType::FullyRead => self.room_data_provider.load_fully_read_marker().await,
1907
1908 _ => None,
1909 };
1910
1911 if let Some(previous_event_id) = previous_event_id {
1914 trace!(%previous_event_id, "found a previous receipt");
1915 if let Some(relative_pos) = TimelineMetadata::compare_events_positions(
1916 &previous_event_id,
1917 &target_event_id,
1918 all_remote_events,
1919 ) && relative_pos != RelativePosition::After
1920 {
1921 return SendReceiptDecision::DoNotSend;
1922 }
1923 }
1924
1925 SendReceiptDecision::SendTo(target_event_id)
1928 }
1929
1930 pub(crate) async fn latest_event_id(&self) -> Option<OwnedEventId> {
1933 let state = self.state.read().await;
1934 let filter_out_thread_events = match self.focus() {
1935 TimelineFocusKind::Thread { .. } => false,
1936 TimelineFocusKind::Live { hide_threaded_events, .. } => *hide_threaded_events,
1937 TimelineFocusKind::Event { .. } => {
1938 false
1941 }
1942 TimelineFocusKind::PinnedEvents { .. } => true,
1943 };
1944
1945 state
1946 .items
1947 .all_remote_events()
1948 .iter()
1949 .rev()
1950 .filter_map(|event_meta| {
1951 if !filter_out_thread_events {
1952 Some(event_meta.event_id.clone())
1955 } else if event_meta.thread_root_id.is_none() {
1956 if let Some(TimelineEventItemId::EventId(target_event_id)) =
1963 state.meta.aggregations.is_aggregation_of(&TimelineEventItemId::EventId(
1964 event_meta.event_id.clone(),
1965 ))
1966 && let Some(target_meta) =
1967 state.items.all_remote_events().get_by_event_id(target_event_id)
1968 && target_meta.thread_root_id.is_some()
1969 {
1970 None
1973 } else {
1974 Some(event_meta.event_id.clone())
1978 }
1979 } else {
1980 None
1983 }
1984 })
1985 .next()
1986 }
1987
1988 #[instrument(skip(self), fields(room_id = ?self.room().room_id()))]
1989 pub(super) async fn retry_event_decryption(&self, session_ids: Option<BTreeSet<String>>) {
1990 let (utds, decrypted) = self.compute_redecryption_candidates().await;
1991
1992 let request = DecryptionRetryRequest {
1993 room_id: self.room().room_id().to_owned(),
1994 utd_session_ids: utds,
1995 refresh_info_session_ids: decrypted,
1996 };
1997
1998 self.state.read().await.meta.event_cache.request_decryption(request);
1999 }
2000
2001 pub(super) async fn map_pagination_status(&self, status: PaginationStatus) -> PaginationStatus {
2009 match status {
2010 PaginationStatus::Idle { hit_timeline_start } => {
2011 if hit_timeline_start {
2012 let state = self.state.read().await;
2013 if state.meta.subscriber_skip_count.get() > 0 {
2018 return PaginationStatus::Idle { hit_timeline_start: false };
2019 }
2020 }
2021 }
2022 PaginationStatus::Paginating => {}
2023 }
2024
2025 status
2027 }
2028}
2029
2030impl<P: RoomDataProvider> TimelineController<P> {
2031 pub(super) fn focus(&self) -> &TimelineFocusKind {
2033 &self.focus
2034 }
2035
2036 pub(in crate::timeline) async fn find_event_with_relations(
2040 &self,
2041 event_id: &EventId,
2042 filter: Option<Vec<RelationType>>,
2043 ) -> Result<(TimelineEvent, Vec<TimelineEvent>), Error> {
2044 self.room_data_provider
2045 .load_or_fetch_event_with_relations(event_id, filter)
2046 .await
2047 .map_err(Into::into)
2048 }
2049}
2050
2051#[allow(clippy::too_many_arguments)]
2052async fn fetch_replied_to_event<P: RoomDataProvider>(
2053 mut state_guard: RwLockWriteGuard<'_, TimelineState<P>>,
2054 state_lock: &RwLock<TimelineState<P>>,
2055 index: usize,
2056 item: &EventTimelineItem,
2057 internal_id: TimelineUniqueId,
2058 msglike: &MsgLikeContent,
2059 in_reply_to: &EventId,
2060 room: &Room,
2061) -> Result<TimelineDetails<Box<EmbeddedEvent>>, Error> {
2062 if let Some((_, item)) = rfind_event_by_id(&state_guard.items, in_reply_to) {
2063 let details = TimelineDetails::Ready(Box::new(EmbeddedEvent::from_timeline_item(&item)));
2064 trace!("Found replied-to event locally");
2065 return Ok(details);
2066 }
2067
2068 trace!("Setting in-reply-to details to pending");
2071 let in_reply_to_details =
2072 InReplyToDetails { event_id: in_reply_to.to_owned(), event: TimelineDetails::Pending };
2073
2074 let event_item = item
2075 .with_content(TimelineItemContent::MsgLike(msglike.with_in_reply_to(in_reply_to_details)));
2076
2077 let new_timeline_item = TimelineItem::new(event_item, internal_id);
2078 state_guard.items.replace(index, new_timeline_item);
2079
2080 drop(state_guard);
2082
2083 trace!("Fetching replied-to event");
2084 let res = match room.load_or_fetch_event(in_reply_to, None).await {
2085 Ok(timeline_event) => {
2086 let state = state_lock.read().await;
2087
2088 let replied_to_item =
2089 EmbeddedEvent::try_from_timeline_event(timeline_event, room, &state.meta).await?;
2090
2091 if let Some(item) = replied_to_item {
2092 TimelineDetails::Ready(Box::new(item))
2093 } else {
2094 return Err(Error::UnsupportedEvent);
2096 }
2097 }
2098
2099 Err(e) => TimelineDetails::Error(Arc::new(e)),
2100 };
2101
2102 Ok(res)
2103}