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