matrix_sdk_ui/timeline/controller/
state.rs1use std::sync::Arc;
16
17use eyeball_im::VectorDiff;
18use matrix_sdk::{deserialized_responses::TimelineEvent, send_queue::SendHandle};
19use ruma::{
20 MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
21 events::{AnyMessageLikeEventContent, receipt::ReceiptEventContent},
22 room_version_rules::RoomVersionRules,
23};
24use tracing::{instrument, trace};
25
26use super::{
27 super::{
28 Profile,
29 date_dividers::DateDividerAdjuster,
30 event_handler::{Flow, TimelineAction, TimelineEventContext, TimelineEventHandler},
31 event_item::RemoteEventOrigin,
32 traits::RoomDataProvider,
33 },
34 ActiveCallInfo, DateDividerMode, TimelineMetadata, TimelineSettings, TimelineStateTransaction,
35 observable_items::ObservableItems,
36};
37use crate::{timeline::controller::TimelineFocusKind, unable_to_decrypt_hook::UtdHookManager};
38
39#[derive(Debug)]
40pub(in crate::timeline) struct TimelineState<P: RoomDataProvider> {
41 pub items: ObservableItems,
42 pub meta: TimelineMetadata,
43
44 pub(super) focus: Arc<TimelineFocusKind>,
46
47 _phantom: std::marker::PhantomData<P>,
49}
50
51impl<P: RoomDataProvider> TimelineState<P> {
52 pub(super) fn new(
53 focus: Arc<TimelineFocusKind>,
54 own_user_id: OwnedUserId,
55 room_version_rules: RoomVersionRules,
56 internal_id_prefix: Option<String>,
57 unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
58 is_room_encrypted: bool,
59 active_call: Option<ActiveCallInfo>,
60 ) -> Self {
61 Self {
62 items: ObservableItems::new(),
63 meta: TimelineMetadata::new(
64 own_user_id,
65 room_version_rules,
66 internal_id_prefix,
67 unable_to_decrypt_hook,
68 is_room_encrypted,
69 )
70 .with_active_call_info(active_call),
71 focus,
72 _phantom: std::marker::PhantomData,
73 }
74 }
75
76 pub(super) async fn handle_remote_events_with_diffs(
78 &mut self,
79 diffs: Vec<VectorDiff<TimelineEvent>>,
80 origin: RemoteEventOrigin,
81 room_data: &P,
82 settings: &TimelineSettings,
83 ) {
84 if diffs.is_empty() {
85 return;
86 }
87
88 let mut transaction = self.transaction();
89 transaction.handle_remote_events_with_diffs(diffs, origin, room_data, settings).await;
90 transaction.commit();
91 }
92
93 pub(super) async fn handle_remote_aggregations(
95 &mut self,
96 diffs: Vec<VectorDiff<TimelineEvent>>,
97 origin: RemoteEventOrigin,
98 room_data: &P,
99 settings: &TimelineSettings,
100 ) {
101 if diffs.is_empty() {
102 return;
103 }
104
105 let mut transaction = self.transaction();
106 transaction.handle_remote_aggregations(diffs, origin, room_data, settings).await;
107 transaction.commit();
108 }
109
110 pub(super) fn handle_fully_read_marker(&mut self, fully_read_event_id: OwnedEventId) {
113 let mut txn = self.transaction();
114 txn.set_fully_read_event(fully_read_event_id);
115 txn.commit();
116 }
117
118 #[instrument(skip_all)]
119 pub(super) async fn handle_read_receipt(
120 &mut self,
121 event: ReceiptEventContent,
122 room_data_provider: &P,
123 ) {
124 if event.is_empty() {
125 return;
126 }
127
128 trace!("Handling ephemeral room events");
129
130 let mut txn = self.transaction();
131 txn.handle_explicit_read_receipts(event, room_data_provider.own_user_id());
132 txn.commit();
133 }
134
135 #[allow(clippy::too_many_arguments)]
137 #[instrument(skip_all)]
138 pub(super) async fn handle_local_event(
139 &mut self,
140 own_user_id: OwnedUserId,
141 own_profile: Option<Profile>,
142 date_divider_mode: DateDividerMode,
143 txn_id: OwnedTransactionId,
144 send_handle: Option<SendHandle>,
145 content: AnyMessageLikeEventContent,
146 ) {
147 let mut txn = self.transaction();
148
149 let mut date_divider_adjuster = DateDividerAdjuster::new(date_divider_mode);
150
151 let is_thread_focus = txn.focus.is_thread();
152 let (in_reply_to, thread_root) =
153 txn.meta.process_content_relations(&content, None, &txn.items, is_thread_focus);
154
155 let should_add_new_items = match &txn.focus {
157 TimelineFocusKind::Live { hide_threaded_events, .. } => {
158 thread_root.is_none() || !hide_threaded_events
159 }
160 TimelineFocusKind::Thread { root_event_id, .. } => {
161 thread_root.as_ref().is_some_and(|r| r == root_event_id)
162 }
163 TimelineFocusKind::Event { .. } | TimelineFocusKind::PinnedEvents { .. } => {
164 false
167 }
168 };
169
170 let ctx = TimelineEventContext {
171 sender: own_user_id,
172 sender_profile: own_profile,
173 forwarder: None,
174 forwarder_profile: None,
175 timestamp: MilliSecondsSinceUnixEpoch::now(),
176 read_receipts: Default::default(),
177 is_highlighted: false,
179 flow: Flow::Local { txn_id, send_handle },
180 should_add_new_items,
181 };
182
183 let timeline_action = TimelineAction::from_content(content, in_reply_to, thread_root, None);
184 TimelineEventHandler::new(&mut txn, &ctx)
185 .handle_event(&mut date_divider_adjuster, timeline_action, None)
186 .await;
187 txn.adjust_date_dividers(date_divider_adjuster);
188
189 txn.commit();
190 }
191
192 #[cfg(test)]
193 pub(super) fn handle_read_receipts(
194 &mut self,
195 receipt_event_content: ReceiptEventContent,
196 own_user_id: &ruma::UserId,
197 ) {
198 let mut txn = self.transaction();
199 txn.handle_explicit_read_receipts(receipt_event_content, own_user_id);
200 txn.commit();
201 }
202
203 pub(super) fn clear(&mut self) {
204 let mut txn = self.transaction();
205 txn.clear();
206 txn.commit();
207 }
208
209 pub(super) async fn replace_with_remote_events<Events>(
215 &mut self,
216 events: Events,
217 origin: RemoteEventOrigin,
218 room_data_provider: &P,
219 settings: &TimelineSettings,
220 ) where
221 Events: IntoIterator,
222 Events::Item: Into<TimelineEvent>,
223 {
224 let mut txn = self.transaction();
225 txn.clear();
226 txn.handle_remote_events_with_diffs(
227 vec![VectorDiff::Append { values: events.into_iter().map(Into::into).collect() }],
228 origin,
229 room_data_provider,
230 settings,
231 )
232 .await;
233 txn.commit();
234 }
235
236 pub(super) fn mark_all_events_as_encrypted(&mut self) {
237 let mut txn = self.transaction();
240 txn.mark_all_events_as_encrypted();
241 txn.commit();
242 }
243
244 pub(super) fn transaction(&mut self) -> TimelineStateTransaction<'_, P> {
245 TimelineStateTransaction::new(&mut self.items, &mut self.meta, &self.focus)
246 }
247}