1use std::{collections::HashMap, ops::Not, sync::Arc};
16
17use eyeball::SharedObservable;
18use eyeball_im::VectorDiff;
19use matrix_sdk_base::{
20 ThreadingSupport,
21 event_cache::Event,
22 linked_chunk::Position,
23 sync::{JoinedRoomUpdate, LeftRoomUpdate},
24};
25use ruma::{OwnedEventId, RoomId, room_version_rules::RoomVersionRules};
26use tokio::sync::{
27 OnceCell, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock, broadcast::Sender, mpsc,
28};
29
30use self::subscriber::AutoShrinkMessage;
31use super::{
32 EventCacheError, EventsOrigin, Result, back_pagination_queue::BackPaginationQueue, states,
33};
34use crate::{client::WeakClient, room::WeakRoom};
35
36mod aggregator;
37pub mod event_focused;
38pub mod event_linked_chunk;
39pub mod pagination;
40pub mod pinned_events;
41mod read_receipts;
42pub mod room;
43pub mod subscriber;
44pub mod thread;
45
46#[derive(Debug)]
48pub(super) struct Caches {
49 pub room: room::RoomEventCache,
53
54 pub threads: Arc<RwLock<HashMap<OwnedEventId, thread::ThreadEventCache>>>,
59
60 pub pinned_events: OnceCell<pinned_events::PinnedEventsCache>,
64
65 pub event_focused:
70 Arc<RwLock<HashMap<event_focused::EventFocusedCacheKey, event_focused::EventFocusedCache>>>,
71
72 internals: CachesInternals,
74}
75
76#[derive(Debug)]
77struct CachesInternals {
78 state: states::StateLock,
79 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
80 linked_chunk_update_sender: Sender<room::RoomEventCacheLinkedChunkUpdate>,
81 room_version_rules: RoomVersionRules,
82}
83
84impl Caches {
85 pub async fn new(
87 weak_client: &WeakClient,
88 room_id: &RoomId,
89 generic_update_sender: Sender<room::RoomEventCacheGenericUpdate>,
90 linked_chunk_update_sender: Sender<room::RoomEventCacheLinkedChunkUpdate>,
91 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
92 state: &states::StateLock,
93 back_pagination_queue: Option<BackPaginationQueue>,
94 ) -> Result<Self> {
95 let Some(client) = weak_client.get() else {
96 return Err(EventCacheError::ClientDropped);
97 };
98
99 let weak_room = WeakRoom::new(weak_client.clone(), room_id.to_owned());
100
101 let room = client
102 .get_room(room_id)
103 .ok_or_else(|| EventCacheError::RoomNotFound { room_id: room_id.to_owned() })?;
104 let room_version_rules = room.clone_info().room_version_rules_or_default();
105
106 let pagination_status = SharedObservable::new(pagination::SharedPaginationStatus::Idle {
107 hit_timeline_start: false,
108 });
109
110 let enabled_thread_support =
111 matches!(client.base_client().threading_support, ThreadingSupport::Enabled { .. });
112
113 let update_sender = room::RoomEventCacheUpdateSender::new(generic_update_sender.clone());
114
115 let own_user_id =
116 client.user_id().expect("the user must be logged in, at this point").to_owned();
117
118 let mut timeline_is_not_empty = false;
123
124 let room_state = state
125 .try_insert_once_with(
126 states::selectors::RoomStateSelector::new(room_id.to_owned()),
127 async |store_guard| {
128 let room_state = room::RoomEventCacheState::new(
129 own_user_id.clone(),
130 room_id.to_owned(),
131 weak_room.clone(),
132 room_version_rules.clone(),
133 enabled_thread_support,
134 update_sender.clone(),
135 linked_chunk_update_sender.clone(),
136 store_guard,
137 pagination_status.clone(),
138 back_pagination_queue,
139 )
140 .await?;
141
142 timeline_is_not_empty =
143 room_state.room_linked_chunk().revents().next().is_some();
144
145 Ok(room_state)
146 },
147 )
148 .await?;
149
150 let room_event_cache = room::RoomEventCache::new(
151 room_id.to_owned(),
152 weak_room,
153 own_user_id,
154 room_state,
155 pagination_status,
156 auto_shrink_sender.clone(),
157 update_sender,
158 );
159
160 if timeline_is_not_empty {
163 let _ = generic_update_sender
164 .send(room::RoomEventCacheGenericUpdate { room_id: room_id.to_owned() });
165 }
166
167 Ok(Self {
168 room: room_event_cache,
169 threads: Arc::new(RwLock::new(HashMap::new())),
170 pinned_events: OnceCell::new(),
171 event_focused: Arc::new(RwLock::new(HashMap::new())),
172 internals: CachesInternals {
173 state: state.clone(),
174 auto_shrink_sender,
175 linked_chunk_update_sender,
176 room_version_rules,
177 },
178 })
179 }
180
181 pub fn room(&self) -> &room::RoomEventCache {
185 &self.room
186 }
187
188 pub async fn thread(
196 &self,
197 thread_id: OwnedEventId,
198 ) -> Result<
199 OwnedRwLockReadGuard<
200 HashMap<OwnedEventId, thread::ThreadEventCache>,
201 thread::ThreadEventCache,
202 >,
203 > {
204 Ok(
205 match OwnedRwLockWriteGuard::try_downgrade_map(
206 self.threads.clone().write_owned().await,
207 |threads| threads.get(&thread_id),
208 ) {
209 Ok(locked_cache) => locked_cache,
211 Err(mut threads) => {
213 let room = &self.room;
214 let cache = thread::ThreadEventCache::new(
215 room.room_id().to_owned(),
216 thread_id.clone(),
217 room.own_user_id().to_owned(),
218 self.internals.room_version_rules.clone(),
219 room.weak_room().to_owned(),
220 &self.internals.state,
221 self.internals.auto_shrink_sender.clone(),
222 room.update_sender().generic_update_sender().clone(),
223 self.internals.linked_chunk_update_sender.clone(),
224 )
225 .await?;
226
227 threads.insert(thread_id.clone(), cache);
228
229 OwnedRwLockWriteGuard::downgrade_map(threads, |threads| {
230 threads.get(&thread_id).unwrap()
231 })
232 }
233 },
234 )
235 }
236
237 pub async fn pinned_events(&self) -> Result<&pinned_events::PinnedEventsCache> {
241 self.pinned_events
242 .get_or_try_init(|| {
243 pinned_events::PinnedEventsCache::new(
244 self.room.weak_room(),
245 self.room.own_user_id().clone(),
246 self.internals.room_version_rules.clone(),
247 self.internals.linked_chunk_update_sender.clone(),
248 &self.internals.state,
249 )
250 })
251 .await
252 }
253
254 pub async fn event_focused(
258 &self,
259 event_id: OwnedEventId,
260 thread_mode: event_focused::EventFocusThreadMode,
261 number_of_initial_events: u16,
262 ) -> Result<
263 OwnedRwLockReadGuard<
264 HashMap<event_focused::EventFocusedCacheKey, event_focused::EventFocusedCache>,
265 event_focused::EventFocusedCache,
266 >,
267 > {
268 let key = event_focused::EventFocusedCacheKey { focused_event_id: event_id, thread_mode };
269
270 Ok(
271 match OwnedRwLockWriteGuard::try_downgrade_map(
272 self.event_focused.clone().write_owned().await,
273 |event_focused_caches| event_focused_caches.get(&key),
274 ) {
275 Ok(locked_cache) => locked_cache,
277 Err(mut event_focused_caches) => {
279 let cache = event_focused::EventFocusedCache::new(
280 self.room.weak_room().clone(),
281 key.clone(),
282 &self.internals.state,
283 self.internals.linked_chunk_update_sender.clone(),
284 )
285 .await?;
286 cache.start_from(number_of_initial_events, thread_mode).await?;
287
288 event_focused_caches.insert(key.clone(), cache);
289
290 OwnedRwLockWriteGuard::downgrade_map(
291 event_focused_caches,
292 |event_focused_caches| event_focused_caches.get(&key).unwrap(),
293 )
294 }
295 },
296 )
297 }
298
299 pub(super) async fn handle_joined_room_update(&self, updates: JoinedRoomUpdate) -> Result<()> {
301 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
302
303 let JoinedRoomUpdate {
307 unread_notifications: _,
310 state: _,
312
313 timeline: original_timeline,
317 ephemeral: original_ephemeral,
318
319 account_data,
321 ambiguity_changes,
322 avatar_changes,
323 } = updates;
324
325 let original_ephemeral = original_ephemeral
327 .into_iter()
328 .filter_map(|ephemeral_event| ephemeral_event.deserialize().ok())
329 .collect::<Vec<_>>();
330
331 {
333 let (timeline, read_receipts) =
334 aggregator::aggregate_timeline_and_read_receipts_for_room(
335 &original_timeline,
336 &original_ephemeral,
337 );
338
339 room.handle_joined_room_update(
340 timeline,
341 read_receipts,
342 account_data,
343 ambiguity_changes,
344 avatar_changes,
345 )
346 .await?;
347 }
348
349 {
351 let timeline_and_read_receipts_for_threads = {
352 let all_states_lock = states::CacheStateLock::new(
357 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
358 self.internals.state.clone(),
359 );
360 let all_states = all_states_lock.read().await?;
361
362 aggregator::aggregate_timeline_and_read_receipts_for_threads(
363 &original_timeline,
364 &original_ephemeral,
365 all_states.threads(),
366 all_states.room(),
367 &internals.room_version_rules.redaction,
368 )
369 .await?
370 };
371
372 for (thread_id, (timeline, read_receipts)) in timeline_and_read_receipts_for_threads {
373 let update_thread_summary = timeline.events.is_empty().not();
376
377 let thread = self.thread(thread_id).await?;
378 thread.handle_joined_room_update(timeline, read_receipts).await?;
379
380 if update_thread_summary {
381 let new_thread_summary = thread.update_thread_summary().await?;
382
383 room.update_thread_summary(thread.thread_id(), new_thread_summary)?;
384 }
385 }
386 }
387
388 if let Some(pinned_events) = pinned_events.get() {
390 let timeline = aggregator::aggregate_timeline_for_pinned_events(
391 &original_timeline,
392 &pinned_events.state().read().await?.current_event_ids(),
393 &internals.room_version_rules.redaction,
394 );
395
396 pinned_events.handle_joined_room_update(timeline).await?;
397 }
398
399 {
401 let _ = event_focused;
404 }
405
406 Ok(())
407 }
408
409 pub(super) async fn handle_left_room_update(&self, updates: LeftRoomUpdate) -> Result<()> {
411 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
412
413 let LeftRoomUpdate {
417 state: _,
419 account_data: _,
421
422 timeline: original_timeline,
424
425 ambiguity_changes,
427 } = updates;
428
429 {
431 let (timeline, _read_receipts) =
432 aggregator::aggregate_timeline_and_read_receipts_for_room(&original_timeline, &[]);
433
434 room.handle_left_room_update(timeline, ambiguity_changes).await?;
435 }
436
437 {
439 let timeline_and_read_receipts_for_threads = {
440 let all_caches_states_lock = states::CacheStateLock::new(
445 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
446 self.internals.state.clone(),
447 );
448 let all_caches_states = all_caches_states_lock.read().await?;
449
450 aggregator::aggregate_timeline_and_read_receipts_for_threads(
451 &original_timeline,
452 &[],
453 all_caches_states.threads(),
454 all_caches_states.room(),
455 &internals.room_version_rules.redaction,
456 )
457 .await?
458 };
459
460 for (thread_id, (timeline, _read_receipts)) in timeline_and_read_receipts_for_threads {
461 let thread = self.thread(thread_id).await?;
462 thread.handle_left_room_update(timeline).await?;
463 }
464 }
465
466 if let Some(pinned_events) = pinned_events.get() {
468 let timeline = aggregator::aggregate_timeline_for_pinned_events(
469 &original_timeline,
470 &pinned_events.state().read().await?.current_event_ids(),
471 &internals.room_version_rules.redaction,
472 );
473
474 pinned_events.handle_left_room_update(timeline).await?;
475 }
476
477 {
479 let _ = event_focused;
482 }
483
484 Ok(())
485 }
486
487 #[cfg(feature = "e2e-encryption")]
492 pub async fn all_in_memory_events(&self) -> Result<impl Iterator<Item = Event>> {
493 let mut events = self.room.events().await?;
498
499 {
501 let event_focused = self.event_focused.read().await;
502
503 for event_focused in event_focused.values() {
504 events.extend(event_focused.events().await?);
505 }
506 }
507
508 Ok(events.into_iter())
509 }
510
511 #[cfg(feature = "experimental-search")]
513 pub async fn stored_events(&self) -> Result<Vec<Event>> {
514 let state = self.internals.state.read().await?;
515 Ok(state.store.get_room_events(self.room.room_id(), None, None).await?)
516 }
517
518 #[cfg(feature = "e2e-encryption")]
527 pub async fn all_events_of_type(
528 &self,
529 event_type: Option<&str>,
530 session_id: Option<&str>,
531 ) -> Result<impl Iterator<Item = Event>> {
532 let mut events = {
535 let state = self.internals.state.read().await?;
536
537 state.store.get_room_events(self.room.room_id(), event_type, session_id).await?
538 };
539
540 {
543 let event_focused = self.event_focused.read().await;
544
545 for event_focused in event_focused.values() {
546 events.extend(
547 event_focused
548 .events()
549 .await?
550 .into_iter()
551 .filter(|event| event_type == event.kind.event_type().as_deref())
552 .filter(|event| session_id == event.kind.session_id()),
553 );
554 }
555 }
556
557 Ok(events.into_iter())
558 }
559}
560
561#[derive(Clone, Debug)]
563pub struct TimelineVectorDiffs {
564 pub diffs: Vec<VectorDiff<Event>>,
566 pub origin: EventsOrigin,
568}
569
570#[derive(Debug)]
572pub(super) enum EventLocation {
573 Memory(Position),
575
576 Store,
578}