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<P>>,
49}
50
51impl<P: RoomDataProvider> TimelineState<P> {
52 pub(super) fn new(
53 focus: Arc<TimelineFocusKind<P>>,
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 ) -> Self {
60 Self {
61 items: ObservableItems::new(),
62 meta: TimelineMetadata::new(
63 own_user_id,
64 room_version_rules,
65 internal_id_prefix,
66 unable_to_decrypt_hook,
67 is_room_encrypted,
68 ),
69 focus,
70 }
71 }
72
73 pub(super) async fn handle_remote_events_with_diffs(
75 &mut self,
76 diffs: Vec<VectorDiff<TimelineEvent>>,
77 origin: RemoteEventOrigin,
78 room_data: &P,
79 settings: &TimelineSettings,
80 ) {
81 if diffs.is_empty() {
82 return;
83 }
84
85 let mut transaction = self.transaction();
86 transaction.handle_remote_events_with_diffs(diffs, origin, room_data, settings).await;
87 transaction.commit();
88 }
89
90 pub(super) async fn handle_remote_aggregations(
92 &mut self,
93 diffs: Vec<VectorDiff<TimelineEvent>>,
94 origin: RemoteEventOrigin,
95 room_data: &P,
96 settings: &TimelineSettings,
97 ) {
98 if diffs.is_empty() {
99 return;
100 }
101
102 let mut transaction = self.transaction();
103 transaction.handle_remote_aggregations(diffs, origin, room_data, settings).await;
104 transaction.commit();
105 }
106
107 pub(super) fn handle_fully_read_marker(&mut self, fully_read_event_id: OwnedEventId) {
110 let mut txn = self.transaction();
111 txn.set_fully_read_event(fully_read_event_id);
112 txn.commit();
113 }
114
115 #[instrument(skip_all)]
116 pub(super) async fn handle_ephemeral_events(
117 &mut self,
118 events: Vec<Raw<AnySyncEphemeralRoomEvent>>,
119 room_data_provider: &P,
120 ) {
121 if events.is_empty() {
122 return;
123 }
124
125 let mut txn = self.transaction();
126
127 trace!("Handling ephemeral room events");
128 let own_user_id = room_data_provider.own_user_id();
129 for raw_event in events {
130 match raw_event.deserialize() {
131 Ok(AnySyncEphemeralRoomEvent::Receipt(ev)) => {
132 txn.handle_explicit_read_receipts(ev.content, own_user_id);
133 }
134 Ok(_) => {}
135 Err(e) => {
136 let event_type = raw_event.get_field::<String>("type").ok().flatten();
137 warn!(event_type, "Failed to deserialize ephemeral event: {e}");
138 }
139 }
140 }
141
142 txn.commit();
143 }
144
145 #[allow(clippy::too_many_arguments)]
147 #[instrument(skip_all)]
148 pub(super) async fn handle_local_event(
149 &mut self,
150 own_user_id: OwnedUserId,
151 own_profile: Option<Profile>,
152 date_divider_mode: DateDividerMode,
153 txn_id: OwnedTransactionId,
154 send_handle: Option<SendHandle>,
155 content: AnyMessageLikeEventContent,
156 ) {
157 let mut txn = self.transaction();
158
159 let mut date_divider_adjuster = DateDividerAdjuster::new(date_divider_mode);
160
161 let is_thread_focus = txn.focus.is_thread();
162 let (in_reply_to, thread_root) =
163 txn.meta.process_content_relations(&content, None, &txn.items, is_thread_focus);
164
165 let should_add_new_items = match &txn.focus {
167 TimelineFocusKind::Live { hide_threaded_events } => {
168 thread_root.is_none() || !hide_threaded_events
169 }
170 TimelineFocusKind::Thread { root_event_id, .. } => {
171 thread_root.as_ref().is_some_and(|r| r == root_event_id)
172 }
173 TimelineFocusKind::Event { .. } | TimelineFocusKind::PinnedEvents { .. } => {
174 false
177 }
178 };
179
180 let ctx = TimelineEventContext {
181 sender: own_user_id,
182 sender_profile: own_profile,
183 timestamp: MilliSecondsSinceUnixEpoch::now(),
184 read_receipts: Default::default(),
185 is_highlighted: false,
187 flow: Flow::Local { txn_id, send_handle },
188 should_add_new_items,
189 };
190
191 let timeline_action = TimelineAction::from_content(content, in_reply_to, thread_root, None);
192 TimelineEventHandler::new(&mut txn, ctx)
193 .handle_event(&mut date_divider_adjuster, timeline_action)
194 .await;
195 txn.adjust_date_dividers(date_divider_adjuster);
196
197 txn.commit();
198 }
199
200 #[cfg(test)]
201 pub(super) fn handle_read_receipts(
202 &mut self,
203 receipt_event_content: ReceiptEventContent,
204 own_user_id: &ruma::UserId,
205 ) {
206 let mut txn = self.transaction();
207 txn.handle_explicit_read_receipts(receipt_event_content, own_user_id);
208 txn.commit();
209 }
210
211 pub(super) fn clear(&mut self) {
212 let mut txn = self.transaction();
213 txn.clear();
214 txn.commit();
215 }
216
217 pub(super) async fn replace_with_remote_events<Events>(
223 &mut self,
224 events: Events,
225 origin: RemoteEventOrigin,
226 room_data_provider: &P,
227 settings: &TimelineSettings,
228 ) where
229 Events: IntoIterator,
230 Events::Item: Into<TimelineEvent>,
231 {
232 let mut txn = self.transaction();
233 txn.clear();
234 txn.handle_remote_events_with_diffs(
235 vec![VectorDiff::Append { values: events.into_iter().map(Into::into).collect() }],
236 origin,
237 room_data_provider,
238 settings,
239 )
240 .await;
241 txn.commit();
242 }
243
244 pub(super) fn mark_all_events_as_encrypted(&mut self) {
245 let mut txn = self.transaction();
248 txn.mark_all_events_as_encrypted();
249 txn.commit();
250 }
251
252 pub(super) fn transaction(&mut self) -> TimelineStateTransaction<'_, P> {
253 TimelineStateTransaction::new(&mut self.items, &mut self.meta, &*self.focus)
254 }
255}