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::TimelineEvent,
30 event_cache::{
31 DecryptionRetryRequest, EventCache, EventFocusedCache, PaginationStatus, PinnedEventsCache,
32 RoomEventCache, Subscriber as EventCacheSubscriber, ThreadEventCache,
33 ThreadEventCacheUpdate,
34 },
35 send_queue::{
36 LocalEcho, LocalEchoContent, RoomSendQueueUpdate, SendHandle, SendReactionHandle,
37 },
38 task_monitor::BackgroundTaskHandle,
39};
40use ruma::{
41 EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
42 TransactionId, UserId,
43 api::client::receipt::create_receipt::v3::ReceiptType as SendReceiptType,
44 events::{
45 AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncTimelineEvent,
46 MessageLikeEventType,
47 poll::unstable_start::UnstablePollStartEventContent,
48 reaction::ReactionEventContent,
49 receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
50 relation::{Annotation, RelationType},
51 room::message::{MessageType, Relation},
52 },
53 room_version_rules::RoomVersionRules,
54};
55use tokio::sync::{RwLock, RwLockWriteGuard};
56use tracing::{
57 Instrument as _, Span, debug, error, field::debug, info, info_span, instrument, trace, warn,
58};
59
60pub(super) use self::{
61 metadata::{RelativePosition, TimelineMetadata},
62 observable_items::{
63 AllRemoteEvents, ObservableItems, ObservableItemsEntry, ObservableItemsTransaction,
64 ObservableItemsTransactionEntry,
65 },
66 state::TimelineState,
67 state_transaction::TimelineStateTransaction,
68};
69use super::{
70 DateDividerMode, EmbeddedEvent, Error, EventSendState, EventTimelineItem, InReplyToDetails,
71 MediaUploadProgress, Profile, TimelineDetails, TimelineEventItemId, TimelineFocus,
72 TimelineItem, TimelineItemContent, TimelineItemKind, TimelineReadReceiptTracking,
73 VirtualTimelineItem,
74 algorithms::{rfind_event_by_id, rfind_event_item},
75 event_item::{ReactionStatus, RemoteEventOrigin},
76 item::TimelineUniqueId,
77 subscriber::TimelineSubscriber,
78 traits::RoomDataProvider,
79};
80use crate::{
81 timeline::{
82 MsgLikeContent, MsgLikeKind, Room, TimelineEventFilterFn, TimelineEventFocusThreadMode,
83 algorithms::rfind_event_by_item_id,
84 controller::decryption_retry_task::compute_redecryption_candidates,
85 date_dividers::DateDividerAdjuster,
86 event_item::TimelineItemHandle,
87 tasks::{event_focused_task, pinned_events_task, thread_updates_task},
88 },
89 unable_to_decrypt_hook::UtdHookManager,
90};
91
92pub(in crate::timeline) mod aggregations;
93mod decryption_retry_task;
94mod metadata;
95mod observable_items;
96mod read_receipts;
97mod state;
98mod state_transaction;
99
100pub(super) use aggregations::*;
101pub(super) use decryption_retry_task::{CryptoDropHandles, spawn_crypto_tasks};
102use matrix_sdk_base::{CallIntentConsensus, RoomInfo};
103
104pub(super) enum SendReceiptDecision {
106 DoNotSend,
108
109 SendTo(OwnedEventId),
114}
115
116#[derive(Debug)]
122pub(in crate::timeline) enum TimelineFocusKind {
123 Live {
125 hide_threaded_events: bool,
127
128 event_cache: RoomEventCache,
130 },
131
132 Event {
135 focused_event_id: OwnedEventId,
137
138 thread_root: OnceLock<OwnedEventId>,
144
145 thread_mode: TimelineEventFocusThreadMode,
148
149 event_cache: EventFocusedCache,
151 },
152
153 Thread {
155 root_event_id: OwnedEventId,
157
158 event_cache: ThreadEventCache,
160 },
161
162 PinnedEvents {
163 event_cache: PinnedEventsCache,
165 },
166}
167
168impl TimelineFocusKind {
169 pub(super) fn receipt_thread(&self) -> ReceiptThread {
176 if let Some(thread_root) = self.thread_root() {
177 ReceiptThread::Thread(thread_root.to_owned())
178 } else if self.hide_threaded_events() {
179 ReceiptThread::Main
180 } else {
181 ReceiptThread::Unthreaded
182 }
183 }
184
185 fn hide_threaded_events(&self) -> bool {
187 match self {
188 TimelineFocusKind::Live { hide_threaded_events, .. } => *hide_threaded_events,
189 TimelineFocusKind::Event { thread_mode, .. } => {
190 matches!(
191 thread_mode,
192 TimelineEventFocusThreadMode::Automatic { hide_threaded_events: true }
193 )
194 }
195 TimelineFocusKind::Thread { .. } | TimelineFocusKind::PinnedEvents { .. } => false,
196 }
197 }
198
199 fn is_thread(&self) -> bool {
202 self.thread_root().is_some()
203 }
204
205 fn thread_root(&self) -> Option<&EventId> {
207 match self {
208 TimelineFocusKind::Event { thread_root, .. } => thread_root.get().map(|v| &**v),
209 TimelineFocusKind::Live { .. } | TimelineFocusKind::PinnedEvents { .. } => None,
210 TimelineFocusKind::Thread { root_event_id, .. } => Some(root_event_id),
211 }
212 }
213}
214
215#[derive(Clone, Debug)]
216pub(super) struct TimelineController<P: RoomDataProvider = Room> {
217 state: Arc<RwLock<TimelineState<P>>>,
219
220 focus: Arc<TimelineFocusKind>,
222
223 pub(crate) room_data_provider: P,
228
229 pub(super) settings: TimelineSettings,
231}
232
233#[derive(Clone)]
234pub(super) struct TimelineSettings {
235 pub(super) track_read_receipts: TimelineReadReceiptTracking,
238
239 pub(super) event_filter: Arc<TimelineEventFilterFn>,
242
243 pub(super) add_failed_to_parse: bool,
245
246 pub(super) date_divider_mode: DateDividerMode,
248}
249
250#[cfg(not(tarpaulin_include))]
251impl fmt::Debug for TimelineSettings {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 f.debug_struct("TimelineSettings")
254 .field("track_read_receipts", &self.track_read_receipts)
255 .field("add_failed_to_parse", &self.add_failed_to_parse)
256 .finish_non_exhaustive()
257 }
258}
259
260impl Default for TimelineSettings {
261 fn default() -> Self {
262 Self {
263 track_read_receipts: TimelineReadReceiptTracking::Disabled,
264 event_filter: Arc::new(default_event_filter),
265 add_failed_to_parse: true,
266 date_divider_mode: DateDividerMode::Daily,
267 }
268 }
269}
270
271pub fn default_event_filter(event: &AnySyncTimelineEvent, rules: &RoomVersionRules) -> bool {
281 match event {
282 AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomRedaction(ev)) => {
283 if ev.redacts(&rules.redaction).is_some() {
284 false
287 } else {
288 ev.event_type() != MessageLikeEventType::Reaction
291 }
292 }
293
294 AnySyncTimelineEvent::MessageLike(msg) => {
295 match msg.original_content() {
296 None => {
297 msg.event_type() != MessageLikeEventType::Reaction
300 }
301
302 Some(original_content) => {
303 match original_content {
304 AnyMessageLikeEventContent::RoomMessage(content) => {
305 if content
306 .relates_to
307 .as_ref()
308 .is_some_and(|rel| matches!(rel, Relation::Replacement(_)))
309 {
310 return false;
312 }
313
314 match content.msgtype {
315 MessageType::Audio(_)
316 | MessageType::Emote(_)
317 | MessageType::File(_)
318 | MessageType::Image(_)
319 | MessageType::Location(_)
320 | MessageType::Notice(_)
321 | MessageType::ServerNotice(_)
322 | MessageType::Text(_)
323 | MessageType::Video(_)
324 | MessageType::VerificationRequest(_) => true,
325 #[cfg(feature = "unstable-msc4274")]
326 MessageType::Gallery(_) => true,
327 _ => false,
328 }
329 }
330
331 AnyMessageLikeEventContent::Sticker(_)
332 | AnyMessageLikeEventContent::UnstablePollStart(
333 UnstablePollStartEventContent::New(_),
334 )
335 | AnyMessageLikeEventContent::CallInvite(_)
336 | AnyMessageLikeEventContent::RtcNotification(_)
337 | AnyMessageLikeEventContent::RoomEncrypted(_) => true,
338
339 AnyMessageLikeEventContent::Beacon(_) => false,
343 AnyMessageLikeEventContent::RtcDecline(_) => false,
346
347 _ => false,
348 }
349 }
350 }
351 }
352
353 AnySyncTimelineEvent::State(_) => {
354 true
356 }
357 }
358}
359
360pub(super) struct InitFocusResult {
362 pub has_events: bool,
364 pub focus_task: Option<BackgroundTaskHandle>,
367}
368
369#[derive(Clone, Debug, PartialEq)]
371pub struct ActiveCallInfo {
372 pub active_members: HashSet<OwnedUserId>,
374 pub call_intent: CallIntentConsensus,
376 pub is_joined: bool,
379 pub call_started_ts_millis: Option<MilliSecondsSinceUnixEpoch>,
383}
384
385impl ActiveCallInfo {
386 pub(crate) fn from_info(room_info: RoomInfo, owned_user_id: OwnedUserId) -> Option<Self> {
387 if room_info.has_active_room_call() {
388 Some(ActiveCallInfo {
389 active_members: HashSet::from(room_info.active_room_call_participants()),
390 call_intent: room_info.active_room_call_consensus_intent(),
391 is_joined: room_info.active_room_call_participants().contains(&owned_user_id),
392 call_started_ts_millis: None,
393 })
394 } else {
395 None
396 }
397 }
398
399 pub(crate) fn with_start_time(self, timestamp: Option<MilliSecondsSinceUnixEpoch>) -> Self {
400 Self { call_started_ts_millis: timestamp, ..self }
401 }
402}
403
404impl<P: RoomDataProvider> TimelineController<P> {
405 pub(super) async fn new(
406 room_data_provider: P,
407 focus: &TimelineFocus,
408 event_cache: &EventCache,
409 internal_id_prefix: Option<String>,
410 unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
411 is_room_encrypted: bool,
412 settings: TimelineSettings,
413 ) -> Result<Self, Error> {
414 let room_id = room_data_provider.room_id();
415
416 let focus = match focus {
417 TimelineFocus::Live { hide_threaded_events } => TimelineFocusKind::Live {
418 hide_threaded_events: *hide_threaded_events,
419 event_cache: event_cache.room(room_id).await?.0,
420 },
421
422 TimelineFocus::Event { target, thread_mode, num_context_events, .. } => {
423 TimelineFocusKind::Event {
424 event_cache: event_cache
425 .event_focused(room_id, target, (*thread_mode).into(), *num_context_events)
426 .await?
427 .0,
428 focused_event_id: target.clone(),
429 thread_root: OnceLock::new(),
431 thread_mode: *thread_mode,
432 }
433 }
434
435 TimelineFocus::Thread { root_event_id, .. } => TimelineFocusKind::Thread {
436 event_cache: event_cache.thread(room_id, root_event_id).await?.0,
437 root_event_id: root_event_id.clone(),
438 },
439
440 TimelineFocus::PinnedEvents => TimelineFocusKind::PinnedEvents {
441 event_cache: event_cache.pinned_events(room_id).await?.0,
442 },
443 };
444
445 let focus = Arc::new(focus);
446 let state = Arc::new(RwLock::new(TimelineState::new(
447 focus.clone(),
448 room_data_provider.own_user_id().to_owned(),
449 room_data_provider.room_version_rules(),
450 internal_id_prefix,
451 unable_to_decrypt_hook,
452 is_room_encrypted,
453 None,
454 )));
455
456 Ok(Self { state, focus, room_data_provider, settings })
457 }
458
459 pub async fn handle_encryption_state_changes(&self) {
464 let mut room_info = self.room_data_provider.room_info();
465
466 let mark_encrypted = || async {
468 let mut state = self.state.write().await;
469 state.meta.is_room_encrypted = true;
470 state.mark_all_events_as_encrypted();
471 };
472
473 if room_info.get().encryption_state().is_encrypted() {
474 mark_encrypted().await;
477 return;
478 }
479
480 while let Some(info) = room_info.next().await {
481 if info.encryption_state().is_encrypted() {
482 mark_encrypted().await;
483 break;
486 }
487 }
488 }
489
490 pub(super) async fn live_lazy_paginate_backwards(&self, num_events: u16) -> Option<usize> {
499 let state = self.state.read().await;
500
501 let (count, needs) = state
502 .meta
503 .subscriber_skip_count
504 .compute_next_when_paginating_backwards(num_events.into());
505
506 let is_live_timeline = true;
508 state.meta.subscriber_skip_count.update(count, is_live_timeline);
509
510 needs
511 }
512
513 pub(super) fn is_live(&self) -> bool {
515 matches!(&*self.focus, TimelineFocusKind::Live { .. })
516 }
517
518 pub(super) fn is_threaded(&self) -> bool {
520 self.focus.is_thread()
521 }
522
523 pub(super) fn thread_root(&self) -> Option<OwnedEventId> {
526 self.focus.thread_root().map(ToOwned::to_owned)
527 }
528
529 pub(super) async fn items(&self) -> Vector<Arc<TimelineItem>> {
533 self.state.read().await.items.clone_items()
534 }
535
536 #[cfg(test)]
537 pub(super) async fn subscribe_raw(
538 &self,
539 ) -> (Vector<Arc<TimelineItem>>, VectorSubscriberStream<Arc<TimelineItem>>) {
540 self.state.read().await.items.subscribe().into_values_and_stream()
541 }
542
543 pub(super) async fn subscribe(&self) -> (Vector<Arc<TimelineItem>>, TimelineSubscriber) {
544 let state = self.state.read().await;
545
546 TimelineSubscriber::new(&state.items, &state.meta.subscriber_skip_count)
547 }
548
549 pub(super) async fn subscribe_filter_map<U, F>(
550 &self,
551 f: F,
552 ) -> (Vector<U>, FilterMap<VectorSubscriberStream<Arc<TimelineItem>>, F>)
553 where
554 U: Clone,
555 F: Fn(Arc<TimelineItem>) -> Option<U>,
556 {
557 self.state.read().await.items.subscribe().filter_map(f)
558 }
559
560 #[instrument(skip_all)]
564 pub(super) async fn toggle_reaction_local(
565 &self,
566 item_id: &TimelineEventItemId,
567 key: &str,
568 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
569 ) -> Result<bool, Error> {
570 let mut state = self.state.write().await;
571
572 let Some((item_pos, item)) = rfind_event_by_item_id(&state.items, item_id) else {
573 warn!("Timeline item not found, can't add reaction");
574 return Err(Error::FailedToToggleReaction);
575 };
576
577 let user_id = self.room_data_provider.own_user_id();
578 let prev_status = item
579 .content()
580 .reactions()
581 .and_then(|map| Some(map.get(key)?.get(user_id)?.status.clone()));
582
583 let Some(prev_status) = prev_status else {
584 match item.handle() {
586 TimelineItemHandle::Local(send_handle) => {
587 if send_handle
588 .react(key.to_owned())
589 .await
590 .map_err(|err| Error::SendQueueError(err.into()))?
591 .is_some()
592 {
593 trace!("adding a reaction to a local echo");
594 return Ok(true);
595 }
596
597 warn!("couldn't toggle reaction for local echo");
598 return Ok(false);
599 }
600
601 TimelineItemHandle::Remote(event_id) => {
602 trace!("adding a reaction to a remote echo");
606 let annotation = Annotation::new(event_id.to_owned(), key.to_owned());
607 self.room_data_provider
608 .send(ReactionEventContent::from(annotation).into(), extra_content)
609 .await?;
610 return Ok(true);
611 }
612 }
613 };
614
615 trace!("removing a previous reaction");
616 match prev_status {
617 ReactionStatus::LocalToLocal(send_reaction_handle) => {
618 if let Some(handle) = send_reaction_handle {
619 if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? {
620 warn!("unexpectedly unable to abort sending of local reaction");
623 }
624 } else {
625 warn!("no send reaction handle (this should only happen in testing contexts)");
626 }
627 }
628
629 ReactionStatus::LocalToRemote(send_handle) => {
630 trace!("aborting send of the previous reaction that was a local echo");
633 if let Some(handle) = send_handle {
634 if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? {
635 warn!("unexpectedly unable to abort sending of local reaction");
638 }
639 } else {
640 warn!("no send handle (this should only happen in testing contexts)");
641 }
642 }
643
644 ReactionStatus::RemoteToRemote(event_id) => {
645 let Some(annotated_event_id) =
647 item.as_remote().map(|event_item| event_item.event_id.clone())
648 else {
649 warn!("remote reaction to remote event, but the associated item isn't remote");
650 return Ok(false);
651 };
652
653 let mut reactions = item.content().reactions().cloned().unwrap_or_default();
654 let reaction_info = reactions.remove_reaction(user_id, key);
655
656 if reaction_info.is_some() {
657 let new_item = item.with_reactions(reactions);
658 state.items.replace(item_pos, new_item);
659 } else {
660 warn!(
661 "reaction is missing on the item, not removing it locally, \
662 but sending redaction."
663 );
664 }
665
666 drop(state);
668
669 trace!("sending redact for a previous reaction");
670 if let Err(err) = self.room_data_provider.redact(&event_id, None, None).await {
671 if let Some(reaction_info) = reaction_info {
672 debug!("sending redact failed, adding the reaction back to the list");
673
674 let mut state = self.state.write().await;
675 if let Some((item_pos, item)) =
676 rfind_event_by_id(&state.items, &annotated_event_id)
677 {
678 let mut reactions =
680 item.content().reactions().cloned().unwrap_or_default();
681 reactions
682 .entry(key.to_owned())
683 .or_default()
684 .insert(user_id.to_owned(), reaction_info);
685 let new_item = item.with_reactions(reactions);
686 state.items.replace(item_pos, new_item);
687 } else {
688 warn!(
689 "couldn't find item to re-add reaction anymore; \
690 maybe it's been redacted?"
691 );
692 }
693 }
694
695 return Err(err);
696 }
697 }
698 }
699
700 Ok(false)
701 }
702
703 pub(super) async fn handle_remote_events_with_diffs(
705 &self,
706 diffs: Vec<VectorDiff<TimelineEvent>>,
707 origin: RemoteEventOrigin,
708 ) {
709 if diffs.is_empty() {
710 return;
711 }
712
713 let mut state = self.state.write().await;
714 state
715 .handle_remote_events_with_diffs(
716 diffs,
717 origin,
718 &self.room_data_provider,
719 &self.settings,
720 )
721 .await
722 }
723
724 pub(super) async fn handle_remote_aggregations(
726 &self,
727 diffs: Vec<VectorDiff<TimelineEvent>>,
728 origin: RemoteEventOrigin,
729 ) {
730 if diffs.is_empty() {
731 return;
732 }
733
734 let mut state = self.state.write().await;
735 state
736 .handle_remote_aggregations(diffs, origin, &self.room_data_provider, &self.settings)
737 .await
738 }
739
740 pub(super) async fn clear(&self) {
741 self.state.write().await.clear();
742 }
743
744 pub(super) async fn replace_with_initial_remote_events<Events>(
752 &self,
753 events: Events,
754 origin: RemoteEventOrigin,
755 ) where
756 Events: IntoIterator,
757 <Events as IntoIterator>::Item: Into<TimelineEvent>,
758 {
759 let mut state = self.state.write().await;
760
761 let track_read_markers = &self.settings.track_read_receipts;
762 if track_read_markers.is_enabled() {
763 state.populate_initial_user_receipt(&self.room_data_provider, ReceiptType::Read).await;
764 state
765 .populate_initial_user_receipt(&self.room_data_provider, ReceiptType::ReadPrivate)
766 .await;
767 }
768
769 let mut events = events.into_iter().peekable();
775 if !state.items.is_empty() || events.peek().is_some() {
776 state
777 .replace_with_remote_events(
778 events,
779 origin,
780 &self.room_data_provider,
781 &self.settings,
782 )
783 .await;
784 }
785
786 if track_read_markers.is_enabled() {
787 if let Some(fully_read_event_id) =
788 self.room_data_provider.load_fully_read_marker().await
789 {
790 state.handle_fully_read_marker(fully_read_event_id);
791 } else if let Some(latest_receipt_event_id) = state
792 .latest_user_read_receipt_timeline_event_id(self.room_data_provider.own_user_id())
793 {
794 debug!("no `m.fully_read` marker found, falling back to read receipt");
796 state.handle_fully_read_marker(latest_receipt_event_id);
797 }
798 }
799 }
800
801 pub(super) async fn handle_fully_read_marker(&self, fully_read_event_id: OwnedEventId) {
802 self.state.write().await.handle_fully_read_marker(fully_read_event_id);
803 }
804
805 pub(super) async fn handle_active_call_update(
806 &self,
807 maybe_active_call: Option<ActiveCallInfo>,
808 ) {
809 let mut state = self.state.write().await;
810 let mut txn = state.transaction();
811
812 txn.meta.active_call = maybe_active_call.clone();
814
815 if let Some(existing_event_id) = &txn.meta.active_rtc_notification_event_id {
816 let last_notification = rfind_event_by_id(&txn.items, existing_event_id);
818 if let Some((last_idx, last_notification)) = last_notification {
819 let updated_content = match last_notification.content() {
820 TimelineItemContent::RtcNotification {
821 call_intent,
822 declined_by,
823 active_call_info: _active_call_info,
824 } => Some(TimelineItemContent::RtcNotification {
825 call_intent: call_intent.to_owned(),
826 declined_by: declined_by.clone(),
827 active_call_info: maybe_active_call
828 .clone()
829 .map(|info| info.with_start_time(last_notification.timestamp.into())),
830 }),
831 _ => None,
832 };
833 if let Some(new_content) = updated_content {
834 let new_event_item = last_notification.inner.with_content(new_content);
835 let new_timeline_item =
836 TimelineItem::new(new_event_item, last_notification.internal_id.clone());
837 txn.items.replace(last_idx, new_timeline_item);
838 }
839
840 if maybe_active_call.is_none() {
841 txn.meta.active_rtc_notification_event_id = None;
843 }
844 }
845 }
846
847 txn.commit();
848 }
849
850 pub(super) async fn handle_read_receipt_event(&self, event: ReceiptEventContent) {
851 if event.is_empty() {
853 return;
854 }
855
856 let mut state = self.state.write().await;
857 state.handle_read_receipt(event, &self.room_data_provider).await;
858 }
859
860 #[instrument(skip_all)]
862 pub(super) async fn handle_local_event(
863 &self,
864 txn_id: OwnedTransactionId,
865 content: AnyMessageLikeEventContent,
866 send_handle: Option<SendHandle>,
867 ) {
868 let sender = self.room_data_provider.own_user_id().to_owned();
869 let profile = self.room_data_provider.profile_from_user_id(&sender).await;
870
871 let date_divider_mode = self.settings.date_divider_mode.clone();
872
873 let mut state = self.state.write().await;
874 state
875 .handle_local_event(sender, profile, date_divider_mode, txn_id, send_handle, content)
876 .await;
877 }
878
879 #[instrument(skip(self))]
884 pub(super) async fn update_event_send_state(
885 &self,
886 txn_id: &TransactionId,
887 send_state: EventSendState,
888 ) {
889 let mut state = self.state.write().await;
890 let mut txn = state.transaction();
891
892 let new_event_id: Option<&EventId> =
893 as_variant!(&send_state, EventSendState::Sent { event_id } => event_id);
894
895 if rfind_event_item(&txn.items, |it| {
898 new_event_id.is_some() && it.event_id() == new_event_id && it.as_remote().is_some()
899 })
900 .is_some()
901 {
902 trace!("Remote echo received before send-event response");
904
905 let local_echo = rfind_event_item(&txn.items, |it| it.transaction_id() == Some(txn_id));
906
907 if let Some((idx, _)) = local_echo {
911 warn!("Message echo got duplicated, removing the local one");
912 txn.items.remove(idx);
913
914 let mut adjuster =
916 DateDividerAdjuster::new(self.settings.date_divider_mode.clone());
917 adjuster.run(&mut txn.items, &mut txn.meta);
918 }
919
920 txn.commit();
921 return;
922 }
923
924 let result = rfind_event_item(&txn.items, |it| {
926 it.transaction_id() == Some(txn_id)
927 || new_event_id.is_some()
928 && it.event_id() == new_event_id
929 && it.as_local().is_some()
930 });
931
932 let Some((idx, item)) = result else {
933 if let Some(new_event_id) = new_event_id {
938 if txn.meta.aggregations.mark_aggregation_as_sent(
939 txn_id.to_owned(),
940 new_event_id.to_owned(),
941 &mut txn.items,
942 &txn.meta.room_version_rules,
943 ) {
944 trace!("Aggregation marked as sent");
945 txn.commit();
946 return;
947 }
948
949 trace!("Sent aggregation was not found");
950 }
951
952 warn!("Timeline item not found, can't update send state");
953 return;
954 };
955
956 let Some(local_item) = item.as_local() else {
957 warn!("We looked for a local item, but it transitioned to remote.");
958 return;
959 };
960
961 if let EventSendState::Sent { event_id: existing_event_id } = &local_item.send_state {
964 error!(?existing_event_id, ?new_event_id, "Local echo already marked as sent");
965 }
966
967 if let Some(new_event_id) = new_event_id {
970 txn.meta.aggregations.mark_target_as_sent(txn_id.to_owned(), new_event_id.to_owned());
971 }
972
973 let new_item = item.with_inner_kind(local_item.with_send_state(send_state));
974 txn.items.replace(idx, new_item);
975
976 txn.commit();
977 }
978
979 pub(super) async fn discard_local_echo(&self, txn_id: &TransactionId) -> bool {
980 let mut state = self.state.write().await;
981
982 if let Some((idx, _)) =
983 rfind_event_item(&state.items, |it| it.transaction_id() == Some(txn_id))
984 {
985 let mut txn = state.transaction();
986
987 txn.items.remove(idx);
988
989 let mut adjuster = DateDividerAdjuster::new(self.settings.date_divider_mode.clone());
992 adjuster.run(&mut txn.items, &mut txn.meta);
993
994 txn.meta.update_read_marker(&mut txn.items);
995
996 txn.commit();
997
998 debug!("discarded local echo");
999 return true;
1000 }
1001
1002 let mut txn = state.transaction();
1005
1006 let found_aggregation = match txn.meta.aggregations.try_remove_aggregation(
1008 &TimelineEventItemId::TransactionId(txn_id.to_owned()),
1009 &mut txn.items,
1010 ) {
1011 Ok(val) => val,
1012 Err(err) => {
1013 warn!("error when discarding local echo for an aggregation: {err}");
1014 true
1016 }
1017 };
1018
1019 if found_aggregation {
1020 txn.commit();
1021 }
1022
1023 found_aggregation
1024 }
1025
1026 pub(super) async fn replace_local_echo(
1027 &self,
1028 txn_id: &TransactionId,
1029 content: AnyMessageLikeEventContent,
1030 ) -> bool {
1031 let AnyMessageLikeEventContent::RoomMessage(content) = content else {
1032 warn!("Replacing a local echo for a non-RoomMessage-like event NYI");
1037 return false;
1038 };
1039
1040 let mut state = self.state.write().await;
1041 let mut txn = state.transaction();
1042
1043 let Some((idx, prev_item)) =
1044 rfind_event_item(&txn.items, |it| it.transaction_id() == Some(txn_id))
1045 else {
1046 debug!("Can't find local echo to replace");
1047 return false;
1048 };
1049
1050 let ti_kind = {
1053 let Some(prev_local_item) = prev_item.as_local() else {
1054 warn!("We looked for a local item, but it transitioned as remote??");
1055 return false;
1056 };
1057 let progress = as_variant!(&prev_local_item.send_state,
1059 EventSendState::NotSentYet { progress } => progress.clone())
1060 .flatten();
1061 prev_local_item.with_send_state(EventSendState::NotSentYet { progress })
1062 };
1063
1064 let new_item = TimelineItem::new(
1066 prev_item.with_kind(ti_kind).with_content(TimelineItemContent::message(
1067 content.msgtype,
1068 content.mentions,
1069 prev_item.content().reactions().cloned().unwrap_or_default(),
1070 prev_item.content().thread_root(),
1071 prev_item.content().in_reply_to(),
1072 prev_item.content().thread_summary(),
1073 )),
1074 prev_item.internal_id.to_owned(),
1075 );
1076
1077 txn.items.replace(idx, new_item);
1078
1079 txn.commit();
1083
1084 debug!("Replaced local echo");
1085 true
1086 }
1087
1088 pub(super) async fn compute_redecryption_candidates(
1089 &self,
1090 ) -> (BTreeSet<String>, BTreeSet<String>) {
1091 let state = self.state.read().await;
1092 compute_redecryption_candidates(&state.items)
1093 }
1094
1095 pub(super) async fn set_sender_profiles_pending(&self) {
1096 self.set_non_ready_sender_profiles(TimelineDetails::Pending).await;
1097 }
1098
1099 pub(super) async fn set_sender_profiles_error(&self, error: Arc<matrix_sdk::Error>) {
1100 self.set_non_ready_sender_profiles(TimelineDetails::Error(error)).await;
1101 }
1102
1103 async fn set_non_ready_sender_profiles(&self, profile_state: TimelineDetails<Profile>) {
1104 self.state.write().await.items.for_each(|mut entry| {
1105 let Some(event_item) = entry.as_event() else { return };
1106 if !matches!(event_item.sender_profile(), TimelineDetails::Ready(_)) {
1107 let new_item = entry.with_kind(TimelineItemKind::Event(
1108 event_item.with_sender_profile(profile_state.clone()),
1109 ));
1110 ObservableItemsEntry::replace(&mut entry, new_item);
1111 }
1112 });
1113 }
1114
1115 pub(super) async fn update_missing_sender_profiles(&self) {
1116 trace!("Updating missing sender profiles");
1117
1118 let mut state = self.state.write().await;
1119 let mut entries = state.items.entries();
1120 while let Some(mut entry) = entries.next() {
1121 let Some(event_item) = entry.as_event() else { continue };
1122 let event_id = event_item.event_id().map(debug);
1123 let transaction_id = event_item.transaction_id().map(debug);
1124
1125 if event_item.sender_profile().is_ready() {
1126 trace!(event_id, transaction_id, "Profile already set");
1127 continue;
1128 }
1129
1130 match self.room_data_provider.profile_from_user_id(event_item.sender()).await {
1131 Some(profile) => {
1132 trace!(event_id, transaction_id, "Adding profile");
1133 let updated_item =
1134 event_item.with_sender_profile(TimelineDetails::Ready(profile));
1135 let new_item = entry.with_kind(updated_item);
1136 ObservableItemsEntry::replace(&mut entry, new_item);
1137 }
1138 None => {
1139 if !event_item.sender_profile().is_unavailable() {
1140 trace!(event_id, transaction_id, "Marking profile unavailable");
1141 let updated_item =
1142 event_item.with_sender_profile(TimelineDetails::Unavailable);
1143 let new_item = entry.with_kind(updated_item);
1144 ObservableItemsEntry::replace(&mut entry, new_item);
1145 } else {
1146 debug!(event_id, transaction_id, "Profile already marked unavailable");
1147 }
1148 }
1149 }
1150 }
1151
1152 trace!("Done updating missing sender profiles");
1153 }
1154
1155 pub(super) async fn force_update_sender_profiles(&self, sender_ids: &BTreeSet<&UserId>) {
1157 trace!("Forcing update of sender profiles: {sender_ids:?}");
1158
1159 let mut state = self.state.write().await;
1160 let mut entries = state.items.entries();
1161 while let Some(mut entry) = entries.next() {
1162 let Some(event_item) = entry.as_event() else { continue };
1163 if !sender_ids.contains(event_item.sender()) {
1164 continue;
1165 }
1166
1167 let event_id = event_item.event_id().map(debug);
1168 let transaction_id = event_item.transaction_id().map(debug);
1169
1170 match self.room_data_provider.profile_from_user_id(event_item.sender()).await {
1171 Some(profile) => {
1172 if matches!(event_item.sender_profile(), TimelineDetails::Ready(old_profile) if *old_profile == profile)
1173 {
1174 debug!(event_id, transaction_id, "Profile already up-to-date");
1175 } else {
1176 trace!(event_id, transaction_id, "Updating profile");
1177 let updated_item =
1178 event_item.with_sender_profile(TimelineDetails::Ready(profile));
1179 let new_item = entry.with_kind(updated_item);
1180 ObservableItemsEntry::replace(&mut entry, new_item);
1181 }
1182 }
1183 None => {
1184 if !event_item.sender_profile().is_unavailable() {
1185 trace!(event_id, transaction_id, "Marking profile unavailable");
1186 let updated_item =
1187 event_item.with_sender_profile(TimelineDetails::Unavailable);
1188 let new_item = entry.with_kind(updated_item);
1189 ObservableItemsEntry::replace(&mut entry, new_item);
1190 } else {
1191 debug!(event_id, transaction_id, "Profile already marked unavailable");
1192 }
1193 }
1194 }
1195 }
1196
1197 trace!("Done forcing update of sender profiles");
1198 }
1199
1200 #[cfg(test)]
1201 pub(super) async fn handle_read_receipts(&self, receipt_event_content: ReceiptEventContent) {
1202 let own_user_id = self.room_data_provider.own_user_id();
1203 self.state.write().await.handle_read_receipts(receipt_event_content, own_user_id);
1204 }
1205
1206 pub(super) async fn latest_user_read_receipt(
1210 &self,
1211 user_id: &UserId,
1212 ) -> Option<(OwnedEventId, Receipt)> {
1213 let receipt_thread = self.focus.receipt_thread();
1214
1215 self.state
1216 .read()
1217 .await
1218 .latest_user_read_receipt(
1219 user_id,
1220 receipt_thread,
1221 &self.room_data_provider,
1222 read_receipts::ImplicitReadReceipts::Include,
1223 )
1224 .await
1225 }
1226
1227 pub(super) async fn latest_user_read_receipt_timeline_event_id(
1230 &self,
1231 user_id: &UserId,
1232 ) -> Option<OwnedEventId> {
1233 self.state.read().await.latest_user_read_receipt_timeline_event_id(user_id)
1234 }
1235
1236 pub async fn subscribe_own_user_read_receipts_changed(
1238 &self,
1239 ) -> impl Stream<Item = ()> + use<P> {
1240 self.state.read().await.meta.read_receipts.subscribe_own_user_read_receipts_changed()
1241 }
1242
1243 pub(crate) async fn handle_local_echo(&self, echo: LocalEcho) {
1245 match echo.content {
1246 LocalEchoContent::Event { serialized_event, send_handle, send_error } => {
1247 let content = match serialized_event.deserialize() {
1248 Ok(d) => d,
1249 Err(err) => {
1250 warn!("error deserializing local echo: {err}");
1251 return;
1252 }
1253 };
1254
1255 self.handle_local_event(echo.transaction_id.clone(), content, Some(send_handle))
1256 .await;
1257
1258 if let Some(send_error) = send_error {
1259 self.update_event_send_state(
1260 &echo.transaction_id,
1261 EventSendState::SendingFailed {
1262 error: Arc::new(matrix_sdk::Error::SendQueueWedgeError(Box::new(
1263 send_error,
1264 ))),
1265 is_recoverable: false,
1266 },
1267 )
1268 .await;
1269 }
1270 }
1271
1272 LocalEchoContent::React { key, send_handle, applies_to } => {
1273 self.handle_local_reaction(key, send_handle, applies_to).await;
1274 }
1275
1276 LocalEchoContent::Redaction { redacts, send_error, .. } => {
1277 self.handle_local_redaction(echo.transaction_id.clone(), redacts).await;
1278
1279 if let Some(send_error) = send_error {
1280 self.update_event_send_state(
1281 &echo.transaction_id,
1282 EventSendState::SendingFailed {
1283 error: Arc::new(matrix_sdk::Error::SendQueueWedgeError(Box::new(
1284 send_error,
1285 ))),
1286 is_recoverable: false,
1287 },
1288 )
1289 .await;
1290 }
1291 }
1292 }
1293 }
1294
1295 #[instrument(skip(self, send_handle))]
1297 async fn handle_local_reaction(
1298 &self,
1299 reaction_key: String,
1300 send_handle: SendReactionHandle,
1301 applies_to: OwnedTransactionId,
1302 ) {
1303 let mut state = self.state.write().await;
1304 let mut tr = state.transaction();
1305
1306 let target = TimelineEventItemId::TransactionId(applies_to);
1307
1308 let reaction_txn_id = send_handle.transaction_id().to_owned();
1309 let reaction_status = ReactionStatus::LocalToLocal(Some(send_handle));
1310 let aggregation = Aggregation::new(
1311 TimelineEventItemId::TransactionId(reaction_txn_id),
1312 AggregationKind::Reaction {
1313 key: reaction_key.clone(),
1314 sender: self.room_data_provider.own_user_id().to_owned(),
1315 timestamp: MilliSecondsSinceUnixEpoch::now(),
1316 reaction_status,
1317 },
1318 );
1319
1320 tr.meta.aggregations.add(target.clone(), aggregation.clone());
1321 find_item_and_apply_aggregation(
1322 &tr.meta.aggregations,
1323 &mut tr.items,
1324 &target,
1325 aggregation,
1326 &tr.meta.room_version_rules,
1327 );
1328
1329 tr.commit();
1330 }
1331
1332 pub(super) async fn handle_local_redaction(
1334 &self,
1335 txn_id: OwnedTransactionId,
1336 redacts: OwnedEventId,
1337 ) {
1338 let mut state = self.state.write().await;
1339 let mut tr = state.transaction();
1340
1341 let target = TimelineEventItemId::EventId(redacts);
1342
1343 let aggregation = Aggregation::new(
1344 TimelineEventItemId::TransactionId(txn_id),
1345 AggregationKind::Redaction { is_local: true },
1346 );
1347
1348 tr.meta.aggregations.add(target.clone(), aggregation.clone());
1349 find_item_and_apply_aggregation(
1350 &tr.meta.aggregations,
1351 &mut tr.items,
1352 &target,
1353 aggregation,
1354 &tr.meta.room_version_rules,
1355 );
1356
1357 tr.commit();
1358 }
1359
1360 pub(crate) async fn handle_room_send_queue_update(&self, update: RoomSendQueueUpdate) {
1362 match update {
1363 RoomSendQueueUpdate::NewLocalEvent(echo) => {
1364 self.handle_local_echo(echo).await;
1365 }
1366
1367 RoomSendQueueUpdate::CancelledLocalEvent { transaction_id } => {
1368 if !self.discard_local_echo(&transaction_id).await {
1369 warn!("couldn't find the local echo to discard");
1370 }
1371 }
1372
1373 RoomSendQueueUpdate::ReplacedLocalEvent { transaction_id, new_content } => {
1374 let content = match new_content.deserialize() {
1375 Ok(d) => d,
1376 Err(err) => {
1377 warn!("error deserializing local echo (upon edit): {err}");
1378 return;
1379 }
1380 };
1381
1382 if !self.replace_local_echo(&transaction_id, content).await {
1383 warn!("couldn't find the local echo to replace");
1384 }
1385 }
1386
1387 RoomSendQueueUpdate::SendError { transaction_id, error, is_recoverable } => {
1388 self.update_event_send_state(
1389 &transaction_id,
1390 EventSendState::SendingFailed { error, is_recoverable },
1391 )
1392 .await;
1393 }
1394
1395 RoomSendQueueUpdate::RetryEvent { transaction_id } => {
1396 self.update_event_send_state(
1397 &transaction_id,
1398 EventSendState::NotSentYet { progress: None },
1399 )
1400 .await;
1401 }
1402
1403 RoomSendQueueUpdate::SentEvent { transaction_id, event_id } => {
1404 self.update_event_send_state(&transaction_id, EventSendState::Sent { event_id })
1405 .await;
1406 }
1407
1408 RoomSendQueueUpdate::MediaUpload { related_to, index, progress, .. } => {
1409 self.update_event_send_state(
1410 &related_to,
1411 EventSendState::NotSentYet {
1412 progress: Some(MediaUploadProgress { index, progress }),
1413 },
1414 )
1415 .await;
1416 }
1417 }
1418 }
1419
1420 pub async fn insert_timeline_start_if_missing(&self) {
1423 let mut state = self.state.write().await;
1424 let mut txn = state.transaction();
1425 txn.items.push_timeline_start_if_missing(
1426 txn.meta.new_timeline_item(VirtualTimelineItem::TimelineStart),
1427 );
1428 txn.commit();
1429 }
1430
1431 pub(super) async fn make_replied_to(
1437 &self,
1438 event: TimelineEvent,
1439 ) -> Result<Option<EmbeddedEvent>, Error> {
1440 let state = self.state.read().await;
1441 EmbeddedEvent::try_from_timeline_event(event, &self.room_data_provider, &state.meta).await
1442 }
1443}
1444
1445impl TimelineController {
1446 pub(super) fn room(&self) -> &Room {
1447 &self.room_data_provider
1448 }
1449
1450 pub(super) async fn init_focus(&self) -> Result<InitFocusResult, Error> {
1455 match self.focus.deref() {
1456 TimelineFocusKind::Live { event_cache, .. } => {
1457 let events = event_cache.events().await?;
1459
1460 let has_events = !events.is_empty();
1461
1462 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Cache).await;
1463
1464 match event_cache.pagination().status().get() {
1465 PaginationStatus::Idle { hit_timeline_start } => {
1466 if hit_timeline_start {
1467 self.insert_timeline_start_if_missing().await;
1470 }
1471 }
1472 PaginationStatus::Paginating => {}
1473 }
1474
1475 Ok(InitFocusResult { has_events, focus_task: None })
1476 }
1477
1478 TimelineFocusKind::Event {
1479 focused_event_id: event_id,
1480 thread_mode,
1481 thread_root: focus_thread_root,
1482 event_cache,
1483 ..
1484 } => {
1485 let (events, receiver) = event_cache.subscribe().await?;
1486
1487 let has_events = !events.is_empty();
1488
1489 if let Some(thread_root) = event_cache.thread_root().await? {
1492 focus_thread_root.get_or_init(|| thread_root);
1493 }
1494
1495 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Pagination)
1496 .await;
1497
1498 let task = self
1499 .room_data_provider
1500 .client()
1501 .task_monitor()
1502 .spawn_infinite_task(
1503 "timeline::event_focused_cache_updates",
1504 event_focused_task(
1505 event_id.clone(),
1506 (*thread_mode).into(),
1507 event_cache.clone(),
1508 self.clone(),
1509 receiver,
1510 ),
1511 )
1512 .abort_on_drop();
1513
1514 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1515 }
1516
1517 TimelineFocusKind::Thread { event_cache, .. } => {
1518 let (has_events, subscriber) = self.init_with_thread_root(event_cache).await?;
1519
1520 let room = &self.room_data_provider;
1521 let span = info_span!(
1522 parent: Span::none(),
1523 "thread_live_update_handler",
1524 room_id = ?room.room_id(),
1525 );
1526 span.follows_from(Span::current());
1527
1528 let task = room
1529 .client()
1530 .task_monitor()
1531 .spawn_infinite_task(
1532 "timeline::thread_event_cache_updates",
1533 thread_updates_task(subscriber, event_cache.clone(), self.clone())
1534 .instrument(span),
1535 )
1536 .abort_on_drop();
1537
1538 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1539 }
1540
1541 TimelineFocusKind::PinnedEvents { event_cache } => {
1542 let (initial_events, pinned_events_recv) = event_cache.subscribe().await?;
1543
1544 let has_events = !initial_events.is_empty();
1545
1546 self.replace_with_initial_remote_events(
1547 initial_events,
1548 RemoteEventOrigin::Pagination,
1549 )
1550 .await;
1551
1552 let task = self
1553 .room_data_provider
1554 .client()
1555 .task_monitor()
1556 .spawn_infinite_task(
1557 "timeline::pinned_events_cache_updates",
1558 pinned_events_task(event_cache.clone(), self.clone(), pinned_events_recv),
1559 )
1560 .abort_on_drop();
1561
1562 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1563 }
1564 }
1565 }
1566
1567 pub(super) async fn init_with_thread_root(
1574 &self,
1575 event_cache: &ThreadEventCache,
1576 ) -> Result<(bool, EventCacheSubscriber<ThreadEventCacheUpdate>), Error> {
1577 let (events, subscriber) = event_cache.subscribe().await?;
1578 let has_events = !events.is_empty();
1579
1580 let lookups = events
1589 .iter()
1590 .filter_map(|event| event.event_id())
1591 .map(|event_id| event_cache.find_event_with_relations(event_id, None));
1592
1593 let mut related_events = Vector::new();
1594 for (_original, related) in try_join_all(lookups).await?.into_iter().flatten() {
1595 related_events.extend(related);
1596 }
1597
1598 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Cache).await;
1599
1600 if !related_events.is_empty() {
1602 self.handle_remote_aggregations(
1603 vec![VectorDiff::Append { values: related_events }],
1604 RemoteEventOrigin::Cache,
1605 )
1606 .await;
1607 }
1608
1609 Ok((has_events, subscriber))
1610 }
1611
1612 #[instrument(skip(self))]
1615 pub(super) async fn fetch_in_reply_to_details(&self, event_id: &EventId) -> Result<(), Error> {
1616 let state_guard = self.state.write().await;
1617 let (index, item) = rfind_event_by_id(&state_guard.items, event_id)
1618 .ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?;
1619 let remote_item = item
1620 .as_remote()
1621 .ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?
1622 .clone();
1623
1624 let TimelineItemContent::MsgLike(msglike) = item.content().clone() else {
1625 debug!("Event is not a message");
1626 return Ok(());
1627 };
1628 let Some(in_reply_to) = msglike.in_reply_to.clone() else {
1629 debug!("Event is not a reply");
1630 return Ok(());
1631 };
1632 if let TimelineDetails::Pending = &in_reply_to.event {
1633 debug!("Replied-to event is already being fetched");
1634 return Ok(());
1635 }
1636 if let TimelineDetails::Ready(_) = &in_reply_to.event {
1637 debug!("Replied-to event has already been fetched");
1638 return Ok(());
1639 }
1640
1641 let internal_id = item.internal_id.to_owned();
1642 let item = item.clone();
1643 let event = fetch_replied_to_event(
1644 state_guard,
1645 &self.state,
1646 index,
1647 &item,
1648 internal_id,
1649 &msglike,
1650 &in_reply_to.event_id,
1651 self.room(),
1652 )
1653 .await?;
1654
1655 let mut state = self.state.write().await;
1658 let (index, item) = rfind_event_by_id(&state.items, &remote_item.event_id)
1659 .ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?;
1660
1661 let TimelineItemContent::MsgLike(MsgLikeContent {
1664 kind: MsgLikeKind::Message(message),
1665 reactions,
1666 thread_root,
1667 in_reply_to,
1668 thread_summary,
1669 }) = item.content().clone()
1670 else {
1671 info!("Event is no longer a message (redacted?)");
1672 return Ok(());
1673 };
1674 let Some(in_reply_to) = in_reply_to else {
1675 warn!("Event no longer has a reply (bug?)");
1676 return Ok(());
1677 };
1678
1679 trace!("Updating in-reply-to details");
1682 let internal_id = item.internal_id.to_owned();
1683 let mut item = item.clone();
1684 item.set_content(TimelineItemContent::MsgLike(MsgLikeContent {
1685 kind: MsgLikeKind::Message(message),
1686 reactions,
1687 thread_root,
1688 in_reply_to: Some(InReplyToDetails { event_id: in_reply_to.event_id, event }),
1689 thread_summary,
1690 }));
1691 state.items.replace(index, TimelineItem::new(item, internal_id));
1692
1693 Ok(())
1694 }
1695
1696 pub(super) fn infer_thread_for_read_receipt(
1702 &self,
1703 receipt_type: &SendReceiptType,
1704 ) -> ReceiptThread {
1705 if matches!(receipt_type, SendReceiptType::FullyRead) {
1706 ReceiptThread::Unthreaded
1707 } else {
1708 self.focus.receipt_thread()
1709 }
1710 }
1711
1712 pub(super) async fn should_send_receipt(
1728 &self,
1729 receipt_type: &SendReceiptType,
1730 receipt_thread: &ReceiptThread,
1731 event_id: &EventId,
1732 is_marking_room_as_read: bool,
1733 ) -> SendReceiptDecision {
1734 let own_user_id = self.room().own_user_id();
1735 let state = self.state.read().await;
1736 let room = self.room();
1737 let all_remote_events = state.items.all_remote_events();
1738
1739 let target_event_id = match receipt_type {
1742 SendReceiptType::Read | SendReceiptType::ReadPrivate => {
1743 let is_own_event = all_remote_events
1744 .get_by_event_id(event_id)
1745 .and_then(|event_meta| event_meta.sender.as_deref())
1746 == Some(own_user_id);
1747
1748 if is_own_event {
1749 let filter_out_thread_events = match self.focus() {
1750 TimelineFocusKind::Thread { .. } | TimelineFocusKind::Event { .. } => false,
1751 TimelineFocusKind::Live { hide_threaded_events, .. } => {
1752 *hide_threaded_events
1753 }
1754 TimelineFocusKind::PinnedEvents { .. } => true,
1755 };
1756
1757 let previous_event = all_remote_events
1758 .iter()
1759 .rev()
1760 .skip_while(|event_meta| event_meta.event_id != *event_id)
1762 .skip(1)
1763 .filter(|event_meta| event_meta.sender.as_deref() != Some(own_user_id))
1765 .find_map(|event_meta| {
1766 if !filter_out_thread_events {
1767 Some(event_meta.event_id.clone())
1768 } else if event_meta.thread_root_id.is_none() {
1769 if let Some(TimelineEventItemId::EventId(aggregated_event_id)) =
1770 state.meta.aggregations.is_aggregation_of(
1771 &TimelineEventItemId::EventId(event_meta.event_id.clone()),
1772 )
1773 && let Some(target_meta) =
1774 all_remote_events.get_by_event_id(aggregated_event_id)
1775 && target_meta.thread_root_id.is_some()
1776 {
1777 None
1778 } else {
1779 Some(event_meta.event_id.clone())
1780 }
1781 } else {
1782 None
1783 }
1784 });
1785
1786 match previous_event {
1787 Some(event_id) => event_id,
1788 None if is_marking_room_as_read => event_id.to_owned(),
1792 None => return SendReceiptDecision::DoNotSend,
1793 }
1794 } else {
1795 event_id.to_owned()
1796 }
1797 }
1798
1799 _ => event_id.to_owned(),
1800 };
1801
1802 let previous_event_id = match receipt_type {
1804 SendReceiptType::Read => state
1805 .meta
1806 .user_receipt(
1807 own_user_id,
1808 ReceiptType::Read,
1809 receipt_thread.clone(),
1810 room,
1811 all_remote_events,
1812 read_receipts::ImplicitReadReceipts::Exclude,
1813 )
1814 .await
1815 .map(|(event_id, _)| event_id),
1816
1817 SendReceiptType::ReadPrivate => state
1820 .latest_user_read_receipt(
1821 own_user_id,
1822 receipt_thread.clone(),
1823 room,
1824 read_receipts::ImplicitReadReceipts::Exclude,
1825 )
1826 .await
1827 .map(|(event_id, _)| event_id),
1828
1829 SendReceiptType::FullyRead => self.room_data_provider.load_fully_read_marker().await,
1830
1831 _ => None,
1832 };
1833
1834 if let Some(previous_event_id) = previous_event_id {
1836 trace!(%previous_event_id, "found a previous receipt");
1837 if let Some(relative_pos) = TimelineMetadata::compare_events_positions(
1838 &previous_event_id,
1839 &target_event_id,
1840 all_remote_events,
1841 ) && relative_pos != RelativePosition::After
1842 {
1843 return SendReceiptDecision::DoNotSend;
1844 }
1845 }
1846
1847 SendReceiptDecision::SendTo(target_event_id)
1850 }
1851
1852 pub(crate) async fn latest_event_id(&self) -> Option<OwnedEventId> {
1855 let state = self.state.read().await;
1856 let filter_out_thread_events = match self.focus() {
1857 TimelineFocusKind::Thread { .. } => false,
1858 TimelineFocusKind::Live { hide_threaded_events, .. } => *hide_threaded_events,
1859 TimelineFocusKind::Event { .. } => {
1860 false
1862 }
1863 TimelineFocusKind::PinnedEvents { .. } => true,
1864 };
1865
1866 state
1867 .items
1868 .all_remote_events()
1869 .iter()
1870 .rev()
1871 .filter_map(|event_meta| {
1872 if !filter_out_thread_events {
1873 Some(event_meta.event_id.clone())
1875 } else if event_meta.thread_root_id.is_none() {
1876 if let Some(TimelineEventItemId::EventId(target_event_id)) =
1882 state.meta.aggregations.is_aggregation_of(&TimelineEventItemId::EventId(
1883 event_meta.event_id.clone(),
1884 ))
1885 && let Some(target_meta) =
1886 state.items.all_remote_events().get_by_event_id(target_event_id)
1887 && target_meta.thread_root_id.is_some()
1888 {
1889 None
1891 } else {
1892 Some(event_meta.event_id.clone())
1895 }
1896 } else {
1897 None
1900 }
1901 })
1902 .next()
1903 }
1904
1905 #[instrument(skip(self), fields(room_id = ?self.room().room_id()))]
1906 pub(super) async fn retry_event_decryption(&self, session_ids: Option<BTreeSet<String>>) {
1907 let (utds, decrypted) = self.compute_redecryption_candidates().await;
1908
1909 let request = DecryptionRetryRequest {
1910 room_id: self.room().room_id().to_owned(),
1911 utd_session_ids: utds,
1912 refresh_info_session_ids: decrypted,
1913 };
1914
1915 self.room().client().event_cache().request_decryption(request);
1916 }
1917
1918 pub(super) async fn map_pagination_status(&self, status: PaginationStatus) -> PaginationStatus {
1926 match status {
1927 PaginationStatus::Idle { hit_timeline_start } => {
1928 if hit_timeline_start {
1929 let state = self.state.read().await;
1930 if state.meta.subscriber_skip_count.get() > 0 {
1934 return PaginationStatus::Idle { hit_timeline_start: false };
1935 }
1936 }
1937 }
1938 PaginationStatus::Paginating => {}
1939 }
1940
1941 status
1943 }
1944}
1945
1946impl<P: RoomDataProvider> TimelineController<P> {
1947 pub(super) fn focus(&self) -> &TimelineFocusKind {
1949 &self.focus
1950 }
1951
1952 pub(in crate::timeline) async fn find_event_with_relations(
1956 &self,
1957 event_id: &EventId,
1958 filter: Option<Vec<RelationType>>,
1959 ) -> Result<(TimelineEvent, Vec<TimelineEvent>), Error> {
1960 self.room_data_provider
1961 .load_or_fetch_event_with_relations(event_id, filter)
1962 .await
1963 .map_err(Into::into)
1964 }
1965}
1966
1967#[allow(clippy::too_many_arguments)]
1968async fn fetch_replied_to_event<P: RoomDataProvider>(
1969 mut state_guard: RwLockWriteGuard<'_, TimelineState<P>>,
1970 state_lock: &RwLock<TimelineState<P>>,
1971 index: usize,
1972 item: &EventTimelineItem,
1973 internal_id: TimelineUniqueId,
1974 msglike: &MsgLikeContent,
1975 in_reply_to: &EventId,
1976 room: &Room,
1977) -> Result<TimelineDetails<Box<EmbeddedEvent>>, Error> {
1978 if let Some((_, item)) = rfind_event_by_id(&state_guard.items, in_reply_to) {
1979 let details = TimelineDetails::Ready(Box::new(EmbeddedEvent::from_timeline_item(&item)));
1980 trace!("Found replied-to event locally");
1981 return Ok(details);
1982 }
1983
1984 trace!("Setting in-reply-to details to pending");
1987 let in_reply_to_details =
1988 InReplyToDetails { event_id: in_reply_to.to_owned(), event: TimelineDetails::Pending };
1989
1990 let event_item = item
1991 .with_content(TimelineItemContent::MsgLike(msglike.with_in_reply_to(in_reply_to_details)));
1992
1993 let new_timeline_item = TimelineItem::new(event_item, internal_id);
1994 state_guard.items.replace(index, new_timeline_item);
1995
1996 drop(state_guard);
1998
1999 trace!("Fetching replied-to event");
2000 let res = match room.load_or_fetch_event(in_reply_to, None).await {
2001 Ok(timeline_event) => {
2002 let state = state_lock.read().await;
2003
2004 let replied_to_item =
2005 EmbeddedEvent::try_from_timeline_event(timeline_event, room, &state.meta).await?;
2006
2007 if let Some(item) = replied_to_item {
2008 TimelineDetails::Ready(Box::new(item))
2009 } else {
2010 return Err(Error::UnsupportedEvent);
2012 }
2013 }
2014
2015 Err(e) => TimelineDetails::Error(Arc::new(e)),
2016 };
2017
2018 Ok(res)
2019}