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 imbl::{HashSet, Vector};
27use matrix_sdk::{
28 deserialized_responses::TimelineEvent,
29 event_cache::{
30 DecryptionRetryRequest, EventCache, EventFocusedCache, PaginationStatus, PinnedEventsCache,
31 RoomEventCache, Subscriber as EventCacheSubscriber, ThreadEventCache, TimelineVectorDiffs,
32 },
33 send_queue::{
34 LocalEcho, LocalEchoContent, RoomSendQueueUpdate, SendHandle, SendReactionHandle,
35 },
36 task_monitor::BackgroundTaskHandle,
37};
38#[cfg(test)]
39use ruma::events::receipt::ReceiptEventContent;
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, AnySyncEphemeralRoomEvent, AnySyncMessageLikeEvent,
46 AnySyncTimelineEvent, MessageLikeEventType,
47 poll::unstable_start::UnstablePollStartEventContent,
48 reaction::ReactionEventContent,
49 receipt::{Receipt, ReceiptThread, ReceiptType},
50 relation::Annotation,
51 room::message::{MessageType, Relation},
52 },
53 room_version_rules::RoomVersionRules,
54 serde::Raw,
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::{ReactionStatus, 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 prev_status = item
580 .content()
581 .reactions()
582 .and_then(|map| Some(map.get(key)?.get(user_id)?.status.clone()));
583
584 let Some(prev_status) = prev_status else {
585 match item.handle() {
587 TimelineItemHandle::Local(send_handle) => {
588 if send_handle
589 .react(key.to_owned())
590 .await
591 .map_err(|err| Error::SendQueueError(err.into()))?
592 .is_some()
593 {
594 trace!("adding a reaction to a local echo");
595 return Ok(true);
596 }
597
598 warn!("couldn't toggle reaction for local echo");
599 return Ok(false);
600 }
601
602 TimelineItemHandle::Remote(event_id) => {
603 trace!("adding a reaction to a remote echo");
607 let annotation = Annotation::new(event_id.to_owned(), key.to_owned());
608 self.room_data_provider
609 .send(ReactionEventContent::from(annotation).into(), extra_content)
610 .await?;
611 return Ok(true);
612 }
613 }
614 };
615
616 trace!("removing a previous reaction");
617 match prev_status {
618 ReactionStatus::LocalToLocal(send_reaction_handle) => {
619 if let Some(handle) = send_reaction_handle {
620 if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? {
621 warn!("unexpectedly unable to abort sending of local reaction");
624 }
625 } else {
626 warn!("no send reaction handle (this should only happen in testing contexts)");
627 }
628 }
629
630 ReactionStatus::LocalToRemote(send_handle) => {
631 trace!("aborting send of the previous reaction that was a local echo");
634 if let Some(handle) = send_handle {
635 if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? {
636 warn!("unexpectedly unable to abort sending of local reaction");
639 }
640 } else {
641 warn!("no send handle (this should only happen in testing contexts)");
642 }
643 }
644
645 ReactionStatus::RemoteToRemote(event_id) => {
646 let Some(annotated_event_id) =
648 item.as_remote().map(|event_item| event_item.event_id.clone())
649 else {
650 warn!("remote reaction to remote event, but the associated item isn't remote");
651 return Ok(false);
652 };
653
654 let mut reactions = item.content().reactions().cloned().unwrap_or_default();
655 let reaction_info = reactions.remove_reaction(user_id, key);
656
657 if reaction_info.is_some() {
658 let new_item = item.with_reactions(reactions);
659 state.items.replace(item_pos, new_item);
660 } else {
661 warn!(
662 "reaction is missing on the item, not removing it locally, \
663 but sending redaction."
664 );
665 }
666
667 drop(state);
669
670 trace!("sending redact for a previous reaction");
671 if let Err(err) = self.room_data_provider.redact(&event_id, None, None).await {
672 if let Some(reaction_info) = reaction_info {
673 debug!("sending redact failed, adding the reaction back to the list");
674
675 let mut state = self.state.write().await;
676 if let Some((item_pos, item)) =
677 rfind_event_by_id(&state.items, &annotated_event_id)
678 {
679 let mut reactions =
681 item.content().reactions().cloned().unwrap_or_default();
682 reactions
683 .entry(key.to_owned())
684 .or_default()
685 .insert(user_id.to_owned(), reaction_info);
686 let new_item = item.with_reactions(reactions);
687 state.items.replace(item_pos, new_item);
688 } else {
689 warn!(
690 "couldn't find item to re-add reaction anymore; \
691 maybe it's been redacted?"
692 );
693 }
694 }
695
696 return Err(err);
697 }
698 }
699 }
700
701 Ok(false)
702 }
703
704 pub(super) async fn handle_remote_events_with_diffs(
706 &self,
707 diffs: Vec<VectorDiff<TimelineEvent>>,
708 origin: RemoteEventOrigin,
709 ) {
710 if diffs.is_empty() {
711 return;
712 }
713
714 let mut state = self.state.write().await;
715 state
716 .handle_remote_events_with_diffs(
717 diffs,
718 origin,
719 &self.room_data_provider,
720 &self.settings,
721 )
722 .await
723 }
724
725 pub(super) async fn handle_remote_aggregations(
727 &self,
728 diffs: Vec<VectorDiff<TimelineEvent>>,
729 origin: RemoteEventOrigin,
730 ) {
731 if diffs.is_empty() {
732 return;
733 }
734
735 let mut state = self.state.write().await;
736 state
737 .handle_remote_aggregations(diffs, origin, &self.room_data_provider, &self.settings)
738 .await
739 }
740
741 pub(super) async fn clear(&self) {
742 self.state.write().await.clear();
743 }
744
745 pub(super) async fn replace_with_initial_remote_events<Events>(
753 &self,
754 events: Events,
755 origin: RemoteEventOrigin,
756 ) where
757 Events: IntoIterator,
758 <Events as IntoIterator>::Item: Into<TimelineEvent>,
759 {
760 let mut state = self.state.write().await;
761
762 let track_read_markers = &self.settings.track_read_receipts;
763 if track_read_markers.is_enabled() {
764 state.populate_initial_user_receipt(&self.room_data_provider, ReceiptType::Read).await;
765 state
766 .populate_initial_user_receipt(&self.room_data_provider, ReceiptType::ReadPrivate)
767 .await;
768 }
769
770 let mut events = events.into_iter().peekable();
776 if !state.items.is_empty() || events.peek().is_some() {
777 state
778 .replace_with_remote_events(
779 events,
780 origin,
781 &self.room_data_provider,
782 &self.settings,
783 )
784 .await;
785 }
786
787 if track_read_markers.is_enabled() {
788 if let Some(fully_read_event_id) =
789 self.room_data_provider.load_fully_read_marker().await
790 {
791 state.handle_fully_read_marker(fully_read_event_id);
792 } else if let Some(latest_receipt_event_id) = state
793 .latest_user_read_receipt_timeline_event_id(self.room_data_provider.own_user_id())
794 {
795 debug!("no `m.fully_read` marker found, falling back to read receipt");
797 state.handle_fully_read_marker(latest_receipt_event_id);
798 }
799 }
800 }
801
802 pub(super) async fn handle_fully_read_marker(&self, fully_read_event_id: OwnedEventId) {
803 self.state.write().await.handle_fully_read_marker(fully_read_event_id);
804 }
805
806 pub(super) async fn handle_active_call_update(
807 &self,
808 maybe_active_call: Option<ActiveCallInfo>,
809 ) {
810 let mut state = self.state.write().await;
811 let mut txn = state.transaction();
812
813 txn.meta.active_call = maybe_active_call.clone();
815
816 if let Some(existing_event_id) = &txn.meta.active_rtc_notification_event_id {
817 let last_notification = rfind_event_by_id(&txn.items, existing_event_id);
819 if let Some((last_idx, last_notification)) = last_notification {
820 let updated_content = match last_notification.content() {
821 TimelineItemContent::RtcNotification {
822 call_intent,
823 declined_by,
824 active_call_info: _active_call_info,
825 } => Some(TimelineItemContent::RtcNotification {
826 call_intent: call_intent.to_owned(),
827 declined_by: declined_by.clone(),
828 active_call_info: maybe_active_call
829 .clone()
830 .map(|info| info.with_start_time(last_notification.timestamp.into())),
831 }),
832 _ => None,
833 };
834 if let Some(new_content) = updated_content {
835 let new_event_item = last_notification.inner.with_content(new_content);
836 let new_timeline_item =
837 TimelineItem::new(new_event_item, last_notification.internal_id.clone());
838 txn.items.replace(last_idx, new_timeline_item);
839 }
840
841 if maybe_active_call.is_none() {
842 txn.meta.active_rtc_notification_event_id = None;
844 }
845 }
846 }
847
848 txn.commit();
849 }
850
851 pub(super) async fn handle_ephemeral_events(
852 &self,
853 events: Vec<Raw<AnySyncEphemeralRoomEvent>>,
854 ) {
855 if events.is_empty() {
857 return;
858 }
859 let mut state = self.state.write().await;
860 state.handle_ephemeral_events(events, &self.room_data_provider).await;
861 }
862
863 #[instrument(skip_all)]
865 pub(super) async fn handle_local_event(
866 &self,
867 txn_id: OwnedTransactionId,
868 content: AnyMessageLikeEventContent,
869 send_handle: Option<SendHandle>,
870 ) {
871 let sender = self.room_data_provider.own_user_id().to_owned();
872 let profile = self.room_data_provider.profile_from_user_id(&sender).await;
873
874 let date_divider_mode = self.settings.date_divider_mode.clone();
875
876 let mut state = self.state.write().await;
877 state
878 .handle_local_event(sender, profile, date_divider_mode, txn_id, send_handle, content)
879 .await;
880 }
881
882 #[instrument(skip(self))]
887 pub(super) async fn update_event_send_state(
888 &self,
889 txn_id: &TransactionId,
890 send_state: EventSendState,
891 ) {
892 let mut state = self.state.write().await;
893 let mut txn = state.transaction();
894
895 let new_event_id: Option<&EventId> =
896 as_variant!(&send_state, EventSendState::Sent { event_id } => event_id);
897
898 if rfind_event_item(&txn.items, |it| {
901 new_event_id.is_some() && it.event_id() == new_event_id && it.as_remote().is_some()
902 })
903 .is_some()
904 {
905 trace!("Remote echo received before send-event response");
907
908 let local_echo = rfind_event_item(&txn.items, |it| it.transaction_id() == Some(txn_id));
909
910 if let Some((idx, _)) = local_echo {
914 warn!("Message echo got duplicated, removing the local one");
915 txn.items.remove(idx);
916
917 let mut adjuster =
919 DateDividerAdjuster::new(self.settings.date_divider_mode.clone());
920 adjuster.run(&mut txn.items, &mut txn.meta);
921 }
922
923 txn.commit();
924 return;
925 }
926
927 let result = rfind_event_item(&txn.items, |it| {
929 it.transaction_id() == Some(txn_id)
930 || new_event_id.is_some()
931 && it.event_id() == new_event_id
932 && it.as_local().is_some()
933 });
934
935 let Some((idx, item)) = result else {
936 if let Some(new_event_id) = new_event_id {
941 if txn.meta.aggregations.mark_aggregation_as_sent(
942 txn_id.to_owned(),
943 new_event_id.to_owned(),
944 &mut txn.items,
945 &txn.meta.room_version_rules,
946 ) {
947 trace!("Aggregation marked as sent");
948 txn.commit();
949 return;
950 }
951
952 trace!("Sent aggregation was not found");
953 }
954
955 warn!("Timeline item not found, can't update send state");
956 return;
957 };
958
959 let Some(local_item) = item.as_local() else {
960 warn!("We looked for a local item, but it transitioned to remote.");
961 return;
962 };
963
964 if let EventSendState::Sent { event_id: existing_event_id } = &local_item.send_state {
967 error!(?existing_event_id, ?new_event_id, "Local echo already marked as sent");
968 }
969
970 if let Some(new_event_id) = new_event_id {
973 txn.meta.aggregations.mark_target_as_sent(txn_id.to_owned(), new_event_id.to_owned());
974 }
975
976 let new_item = item.with_inner_kind(local_item.with_send_state(send_state));
977 txn.items.replace(idx, new_item);
978
979 txn.commit();
980 }
981
982 pub(super) async fn discard_local_echo(&self, txn_id: &TransactionId) -> bool {
983 let mut state = self.state.write().await;
984
985 if let Some((idx, _)) =
986 rfind_event_item(&state.items, |it| it.transaction_id() == Some(txn_id))
987 {
988 let mut txn = state.transaction();
989
990 txn.items.remove(idx);
991
992 let mut adjuster = DateDividerAdjuster::new(self.settings.date_divider_mode.clone());
995 adjuster.run(&mut txn.items, &mut txn.meta);
996
997 txn.meta.update_read_marker(&mut txn.items);
998
999 txn.commit();
1000
1001 debug!("discarded local echo");
1002 return true;
1003 }
1004
1005 let mut txn = state.transaction();
1008
1009 let found_aggregation = match txn.meta.aggregations.try_remove_aggregation(
1011 &TimelineEventItemId::TransactionId(txn_id.to_owned()),
1012 &mut txn.items,
1013 ) {
1014 Ok(val) => val,
1015 Err(err) => {
1016 warn!("error when discarding local echo for an aggregation: {err}");
1017 true
1019 }
1020 };
1021
1022 if found_aggregation {
1023 txn.commit();
1024 }
1025
1026 found_aggregation
1027 }
1028
1029 pub(super) async fn replace_local_echo(
1030 &self,
1031 txn_id: &TransactionId,
1032 content: AnyMessageLikeEventContent,
1033 ) -> bool {
1034 let AnyMessageLikeEventContent::RoomMessage(content) = content else {
1035 warn!("Replacing a local echo for a non-RoomMessage-like event NYI");
1040 return false;
1041 };
1042
1043 let mut state = self.state.write().await;
1044 let mut txn = state.transaction();
1045
1046 let Some((idx, prev_item)) =
1047 rfind_event_item(&txn.items, |it| it.transaction_id() == Some(txn_id))
1048 else {
1049 debug!("Can't find local echo to replace");
1050 return false;
1051 };
1052
1053 let ti_kind = {
1056 let Some(prev_local_item) = prev_item.as_local() else {
1057 warn!("We looked for a local item, but it transitioned as remote??");
1058 return false;
1059 };
1060 let progress = as_variant!(&prev_local_item.send_state,
1062 EventSendState::NotSentYet { progress } => progress.clone())
1063 .flatten();
1064 prev_local_item.with_send_state(EventSendState::NotSentYet { progress })
1065 };
1066
1067 let new_item = TimelineItem::new(
1069 prev_item.with_kind(ti_kind).with_content(TimelineItemContent::message(
1070 content.msgtype,
1071 content.mentions,
1072 prev_item.content().reactions().cloned().unwrap_or_default(),
1073 prev_item.content().thread_root(),
1074 prev_item.content().in_reply_to(),
1075 prev_item.content().thread_summary(),
1076 )),
1077 prev_item.internal_id.to_owned(),
1078 );
1079
1080 txn.items.replace(idx, new_item);
1081
1082 txn.commit();
1086
1087 debug!("Replaced local echo");
1088 true
1089 }
1090
1091 pub(super) async fn compute_redecryption_candidates(
1092 &self,
1093 ) -> (BTreeSet<String>, BTreeSet<String>) {
1094 let state = self.state.read().await;
1095 compute_redecryption_candidates(&state.items)
1096 }
1097
1098 pub(super) async fn set_sender_profiles_pending(&self) {
1099 self.set_non_ready_sender_profiles(TimelineDetails::Pending).await;
1100 }
1101
1102 pub(super) async fn set_sender_profiles_error(&self, error: Arc<matrix_sdk::Error>) {
1103 self.set_non_ready_sender_profiles(TimelineDetails::Error(error)).await;
1104 }
1105
1106 async fn set_non_ready_sender_profiles(&self, profile_state: TimelineDetails<Profile>) {
1107 self.state.write().await.items.for_each(|mut entry| {
1108 let Some(event_item) = entry.as_event() else { return };
1109 if !matches!(event_item.sender_profile(), TimelineDetails::Ready(_)) {
1110 let new_item = entry.with_kind(TimelineItemKind::Event(
1111 event_item.with_sender_profile(profile_state.clone()),
1112 ));
1113 ObservableItemsEntry::replace(&mut entry, new_item);
1114 }
1115 });
1116 }
1117
1118 pub(super) async fn update_missing_sender_profiles(&self) {
1119 trace!("Updating missing sender profiles");
1120
1121 let mut state = self.state.write().await;
1122 let mut entries = state.items.entries();
1123 while let Some(mut entry) = entries.next() {
1124 let Some(event_item) = entry.as_event() else { continue };
1125 let event_id = event_item.event_id().map(debug);
1126 let transaction_id = event_item.transaction_id().map(debug);
1127
1128 if event_item.sender_profile().is_ready() {
1129 trace!(event_id, transaction_id, "Profile already set");
1130 continue;
1131 }
1132
1133 match self.room_data_provider.profile_from_user_id(event_item.sender()).await {
1134 Some(profile) => {
1135 trace!(event_id, transaction_id, "Adding profile");
1136 let updated_item =
1137 event_item.with_sender_profile(TimelineDetails::Ready(profile));
1138 let new_item = entry.with_kind(updated_item);
1139 ObservableItemsEntry::replace(&mut entry, new_item);
1140 }
1141 None => {
1142 if !event_item.sender_profile().is_unavailable() {
1143 trace!(event_id, transaction_id, "Marking profile unavailable");
1144 let updated_item =
1145 event_item.with_sender_profile(TimelineDetails::Unavailable);
1146 let new_item = entry.with_kind(updated_item);
1147 ObservableItemsEntry::replace(&mut entry, new_item);
1148 } else {
1149 debug!(event_id, transaction_id, "Profile already marked unavailable");
1150 }
1151 }
1152 }
1153 }
1154
1155 trace!("Done updating missing sender profiles");
1156 }
1157
1158 pub(super) async fn force_update_sender_profiles(&self, sender_ids: &BTreeSet<&UserId>) {
1160 trace!("Forcing update of sender profiles: {sender_ids:?}");
1161
1162 let mut state = self.state.write().await;
1163 let mut entries = state.items.entries();
1164 while let Some(mut entry) = entries.next() {
1165 let Some(event_item) = entry.as_event() else { continue };
1166 if !sender_ids.contains(event_item.sender()) {
1167 continue;
1168 }
1169
1170 let event_id = event_item.event_id().map(debug);
1171 let transaction_id = event_item.transaction_id().map(debug);
1172
1173 match self.room_data_provider.profile_from_user_id(event_item.sender()).await {
1174 Some(profile) => {
1175 if matches!(event_item.sender_profile(), TimelineDetails::Ready(old_profile) if *old_profile == profile)
1176 {
1177 debug!(event_id, transaction_id, "Profile already up-to-date");
1178 } else {
1179 trace!(event_id, transaction_id, "Updating profile");
1180 let updated_item =
1181 event_item.with_sender_profile(TimelineDetails::Ready(profile));
1182 let new_item = entry.with_kind(updated_item);
1183 ObservableItemsEntry::replace(&mut entry, new_item);
1184 }
1185 }
1186 None => {
1187 if !event_item.sender_profile().is_unavailable() {
1188 trace!(event_id, transaction_id, "Marking profile unavailable");
1189 let updated_item =
1190 event_item.with_sender_profile(TimelineDetails::Unavailable);
1191 let new_item = entry.with_kind(updated_item);
1192 ObservableItemsEntry::replace(&mut entry, new_item);
1193 } else {
1194 debug!(event_id, transaction_id, "Profile already marked unavailable");
1195 }
1196 }
1197 }
1198 }
1199
1200 trace!("Done forcing update of sender profiles");
1201 }
1202
1203 #[cfg(test)]
1204 pub(super) async fn handle_read_receipts(&self, receipt_event_content: ReceiptEventContent) {
1205 let own_user_id = self.room_data_provider.own_user_id();
1206 self.state.write().await.handle_read_receipts(receipt_event_content, own_user_id);
1207 }
1208
1209 pub(super) async fn latest_user_read_receipt(
1213 &self,
1214 user_id: &UserId,
1215 ) -> Option<(OwnedEventId, Receipt)> {
1216 let receipt_thread = self.focus.receipt_thread();
1217
1218 self.state
1219 .read()
1220 .await
1221 .latest_user_read_receipt(
1222 user_id,
1223 receipt_thread,
1224 &self.room_data_provider,
1225 read_receipts::ImplicitReadReceipts::Include,
1226 )
1227 .await
1228 }
1229
1230 pub(super) async fn latest_user_read_receipt_timeline_event_id(
1233 &self,
1234 user_id: &UserId,
1235 ) -> Option<OwnedEventId> {
1236 self.state.read().await.latest_user_read_receipt_timeline_event_id(user_id)
1237 }
1238
1239 pub async fn subscribe_own_user_read_receipts_changed(
1241 &self,
1242 ) -> impl Stream<Item = ()> + use<P> {
1243 self.state.read().await.meta.read_receipts.subscribe_own_user_read_receipts_changed()
1244 }
1245
1246 pub(crate) async fn handle_local_echo(&self, echo: LocalEcho) {
1248 match echo.content {
1249 LocalEchoContent::Event { serialized_event, send_handle, send_error } => {
1250 let content = match serialized_event.deserialize() {
1251 Ok(d) => d,
1252 Err(err) => {
1253 warn!("error deserializing local echo: {err}");
1254 return;
1255 }
1256 };
1257
1258 self.handle_local_event(echo.transaction_id.clone(), content, Some(send_handle))
1259 .await;
1260
1261 if let Some(send_error) = send_error {
1262 self.update_event_send_state(
1263 &echo.transaction_id,
1264 EventSendState::SendingFailed {
1265 error: Arc::new(matrix_sdk::Error::SendQueueWedgeError(Box::new(
1266 send_error,
1267 ))),
1268 is_recoverable: false,
1269 },
1270 )
1271 .await;
1272 }
1273 }
1274
1275 LocalEchoContent::React { key, send_handle, applies_to } => {
1276 self.handle_local_reaction(key, send_handle, applies_to).await;
1277 }
1278
1279 LocalEchoContent::Redaction { redacts, send_error, .. } => {
1280 self.handle_local_redaction(echo.transaction_id.clone(), redacts).await;
1281
1282 if let Some(send_error) = send_error {
1283 self.update_event_send_state(
1284 &echo.transaction_id,
1285 EventSendState::SendingFailed {
1286 error: Arc::new(matrix_sdk::Error::SendQueueWedgeError(Box::new(
1287 send_error,
1288 ))),
1289 is_recoverable: false,
1290 },
1291 )
1292 .await;
1293 }
1294 }
1295 }
1296 }
1297
1298 #[instrument(skip(self, send_handle))]
1300 async fn handle_local_reaction(
1301 &self,
1302 reaction_key: String,
1303 send_handle: SendReactionHandle,
1304 applies_to: OwnedTransactionId,
1305 ) {
1306 let mut state = self.state.write().await;
1307 let mut tr = state.transaction();
1308
1309 let target = TimelineEventItemId::TransactionId(applies_to);
1310
1311 let reaction_txn_id = send_handle.transaction_id().to_owned();
1312 let reaction_status = ReactionStatus::LocalToLocal(Some(send_handle));
1313 let aggregation = Aggregation::new(
1314 TimelineEventItemId::TransactionId(reaction_txn_id),
1315 AggregationKind::Reaction {
1316 key: reaction_key.clone(),
1317 sender: self.room_data_provider.own_user_id().to_owned(),
1318 timestamp: MilliSecondsSinceUnixEpoch::now(),
1319 reaction_status,
1320 },
1321 );
1322
1323 tr.meta.aggregations.add(target.clone(), aggregation.clone());
1324 find_item_and_apply_aggregation(
1325 &tr.meta.aggregations,
1326 &mut tr.items,
1327 &target,
1328 aggregation,
1329 &tr.meta.room_version_rules,
1330 );
1331
1332 tr.commit();
1333 }
1334
1335 pub(super) async fn handle_local_redaction(
1337 &self,
1338 txn_id: OwnedTransactionId,
1339 redacts: OwnedEventId,
1340 ) {
1341 let mut state = self.state.write().await;
1342 let mut tr = state.transaction();
1343
1344 let target = TimelineEventItemId::EventId(redacts);
1345
1346 let aggregation = Aggregation::new(
1347 TimelineEventItemId::TransactionId(txn_id),
1348 AggregationKind::Redaction { is_local: true },
1349 );
1350
1351 tr.meta.aggregations.add(target.clone(), aggregation.clone());
1352 find_item_and_apply_aggregation(
1353 &tr.meta.aggregations,
1354 &mut tr.items,
1355 &target,
1356 aggregation,
1357 &tr.meta.room_version_rules,
1358 );
1359
1360 tr.commit();
1361 }
1362
1363 pub(crate) async fn handle_room_send_queue_update(&self, update: RoomSendQueueUpdate) {
1365 match update {
1366 RoomSendQueueUpdate::NewLocalEvent(echo) => {
1367 self.handle_local_echo(echo).await;
1368 }
1369
1370 RoomSendQueueUpdate::CancelledLocalEvent { transaction_id } => {
1371 if !self.discard_local_echo(&transaction_id).await {
1372 warn!("couldn't find the local echo to discard");
1373 }
1374 }
1375
1376 RoomSendQueueUpdate::ReplacedLocalEvent { transaction_id, new_content } => {
1377 let content = match new_content.deserialize() {
1378 Ok(d) => d,
1379 Err(err) => {
1380 warn!("error deserializing local echo (upon edit): {err}");
1381 return;
1382 }
1383 };
1384
1385 if !self.replace_local_echo(&transaction_id, content).await {
1386 warn!("couldn't find the local echo to replace");
1387 }
1388 }
1389
1390 RoomSendQueueUpdate::SendError { transaction_id, error, is_recoverable } => {
1391 self.update_event_send_state(
1392 &transaction_id,
1393 EventSendState::SendingFailed { error, is_recoverable },
1394 )
1395 .await;
1396 }
1397
1398 RoomSendQueueUpdate::RetryEvent { transaction_id } => {
1399 self.update_event_send_state(
1400 &transaction_id,
1401 EventSendState::NotSentYet { progress: None },
1402 )
1403 .await;
1404 }
1405
1406 RoomSendQueueUpdate::SentEvent { transaction_id, event_id } => {
1407 self.update_event_send_state(&transaction_id, EventSendState::Sent { event_id })
1408 .await;
1409 }
1410
1411 RoomSendQueueUpdate::MediaUpload { related_to, index, progress, .. } => {
1412 self.update_event_send_state(
1413 &related_to,
1414 EventSendState::NotSentYet {
1415 progress: Some(MediaUploadProgress { index, progress }),
1416 },
1417 )
1418 .await;
1419 }
1420 }
1421 }
1422
1423 pub async fn insert_timeline_start_if_missing(&self) {
1426 let mut state = self.state.write().await;
1427 let mut txn = state.transaction();
1428 txn.items.push_timeline_start_if_missing(
1429 txn.meta.new_timeline_item(VirtualTimelineItem::TimelineStart),
1430 );
1431 txn.commit();
1432 }
1433
1434 pub(super) async fn make_replied_to(
1440 &self,
1441 event: TimelineEvent,
1442 ) -> Result<Option<EmbeddedEvent>, Error> {
1443 let state = self.state.read().await;
1444 EmbeddedEvent::try_from_timeline_event(event, &self.room_data_provider, &state.meta).await
1445 }
1446}
1447
1448impl TimelineController {
1449 pub(super) fn room(&self) -> &Room {
1450 &self.room_data_provider
1451 }
1452
1453 pub(super) async fn init_focus(&self) -> Result<InitFocusResult, Error> {
1458 match self.focus.deref() {
1459 TimelineFocusKind::Live { event_cache, .. } => {
1460 let events = event_cache.events().await?;
1462
1463 let has_events = !events.is_empty();
1464
1465 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Cache).await;
1466
1467 match event_cache.pagination().status().get() {
1468 PaginationStatus::Idle { hit_timeline_start } => {
1469 if hit_timeline_start {
1470 self.insert_timeline_start_if_missing().await;
1473 }
1474 }
1475 PaginationStatus::Paginating => {}
1476 }
1477
1478 Ok(InitFocusResult { has_events, focus_task: None })
1479 }
1480
1481 TimelineFocusKind::Event {
1482 focused_event_id: event_id,
1483 thread_mode,
1484 thread_root: focus_thread_root,
1485 event_cache,
1486 ..
1487 } => {
1488 let (events, receiver) = event_cache.subscribe().await?;
1489
1490 let has_events = !events.is_empty();
1491
1492 if let Some(thread_root) = event_cache.thread_root().await? {
1495 focus_thread_root.get_or_init(|| thread_root);
1496 }
1497
1498 self.replace_with_initial_remote_events(events, RemoteEventOrigin::Pagination)
1499 .await;
1500
1501 let task = self
1502 .room_data_provider
1503 .client()
1504 .task_monitor()
1505 .spawn_infinite_task(
1506 "timeline::event_focused_cache_updates",
1507 event_focused_task(
1508 event_id.clone(),
1509 (*thread_mode).into(),
1510 event_cache.clone(),
1511 self.clone(),
1512 receiver,
1513 ),
1514 )
1515 .abort_on_drop();
1516
1517 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1518 }
1519
1520 TimelineFocusKind::Thread { event_cache, .. } => {
1521 let (has_events, subscriber) = self.init_with_thread_root(event_cache).await?;
1522
1523 let room = &self.room_data_provider;
1524 let span = info_span!(
1525 parent: Span::none(),
1526 "thread_live_update_handler",
1527 room_id = ?room.room_id(),
1528 );
1529 span.follows_from(Span::current());
1530
1531 let task = room
1532 .client()
1533 .task_monitor()
1534 .spawn_infinite_task(
1535 "timeline::thread_event_cache_updates",
1536 thread_updates_task(subscriber, event_cache.clone(), self.clone())
1537 .instrument(span),
1538 )
1539 .abort_on_drop();
1540
1541 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1542 }
1543
1544 TimelineFocusKind::PinnedEvents { event_cache } => {
1545 let (initial_events, pinned_events_recv) = event_cache.subscribe().await?;
1546
1547 let has_events = !initial_events.is_empty();
1548
1549 self.replace_with_initial_remote_events(
1550 initial_events,
1551 RemoteEventOrigin::Pagination,
1552 )
1553 .await;
1554
1555 let task = self
1556 .room_data_provider
1557 .client()
1558 .task_monitor()
1559 .spawn_infinite_task(
1560 "timeline::pinned_events_cache_updates",
1561 pinned_events_task(event_cache.clone(), self.clone(), pinned_events_recv),
1562 )
1563 .abort_on_drop();
1564
1565 Ok(InitFocusResult { has_events, focus_task: Some(task) })
1566 }
1567 }
1568 }
1569
1570 pub(super) async fn init_with_thread_root(
1577 &self,
1578 event_cache: &ThreadEventCache,
1579 ) -> Result<(bool, EventCacheSubscriber<TimelineVectorDiffs>), Error> {
1580 let (events, subscriber) = event_cache.subscribe().await?;
1581 let has_events = !events.is_empty();
1582
1583 let mut related_events = Vector::new();
1587 for event_id in events.iter().filter_map(|event| event.event_id()) {
1588 if let Some((_original, related)) =
1589 event_cache.find_event_with_relations(event_id, None).await?
1590 {
1591 related_events.extend(related);
1592 }
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
1950#[allow(clippy::too_many_arguments)]
1951async fn fetch_replied_to_event<P: RoomDataProvider>(
1952 mut state_guard: RwLockWriteGuard<'_, TimelineState<P>>,
1953 state_lock: &RwLock<TimelineState<P>>,
1954 index: usize,
1955 item: &EventTimelineItem,
1956 internal_id: TimelineUniqueId,
1957 msglike: &MsgLikeContent,
1958 in_reply_to: &EventId,
1959 room: &Room,
1960) -> Result<TimelineDetails<Box<EmbeddedEvent>>, Error> {
1961 if let Some((_, item)) = rfind_event_by_id(&state_guard.items, in_reply_to) {
1962 let details = TimelineDetails::Ready(Box::new(EmbeddedEvent::from_timeline_item(&item)));
1963 trace!("Found replied-to event locally");
1964 return Ok(details);
1965 }
1966
1967 trace!("Setting in-reply-to details to pending");
1970 let in_reply_to_details =
1971 InReplyToDetails { event_id: in_reply_to.to_owned(), event: TimelineDetails::Pending };
1972
1973 let event_item = item
1974 .with_content(TimelineItemContent::MsgLike(msglike.with_in_reply_to(in_reply_to_details)));
1975
1976 let new_timeline_item = TimelineItem::new(event_item, internal_id);
1977 state_guard.items.replace(index, new_timeline_item);
1978
1979 drop(state_guard);
1981
1982 trace!("Fetching replied-to event");
1983 let res = match room.load_or_fetch_event(in_reply_to, None).await {
1984 Ok(timeline_event) => {
1985 let state = state_lock.read().await;
1986
1987 let replied_to_item =
1988 EmbeddedEvent::try_from_timeline_event(timeline_event, room, &state.meta).await?;
1989
1990 if let Some(item) = replied_to_item {
1991 TimelineDetails::Ready(Box::new(item))
1992 } else {
1993 return Err(Error::UnsupportedEvent);
1995 }
1996 }
1997
1998 Err(e) => TimelineDetails::Error(Arc::new(e)),
1999 };
2000
2001 Ok(res)
2002}