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