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 room_state = state
119 .try_insert_once_with(
120 states::selectors::RoomStateSelector::new(room_id.to_owned()),
121 |store_guard| {
122 room::RoomEventCacheState::new(
123 own_user_id.clone(),
124 room_id.to_owned(),
125 weak_room.clone(),
126 room_version_rules.clone(),
127 enabled_thread_support,
128 update_sender.clone(),
129 linked_chunk_update_sender.clone(),
130 store_guard,
131 pagination_status.clone(),
132 back_pagination_queue,
133 )
134 },
135 )
136 .await?;
137
138 let timeline_is_not_empty =
139 room_state.read().await?.room_linked_chunk().revents().next().is_some();
140
141 let room_event_cache = room::RoomEventCache::new(
142 room_id.to_owned(),
143 weak_room,
144 own_user_id,
145 room_state,
146 pagination_status,
147 auto_shrink_sender.clone(),
148 update_sender,
149 );
150
151 if timeline_is_not_empty {
154 let _ = generic_update_sender
155 .send(room::RoomEventCacheGenericUpdate { room_id: room_id.to_owned() });
156 }
157
158 Ok(Self {
159 room: room_event_cache,
160 threads: Arc::new(RwLock::new(HashMap::new())),
161 pinned_events: OnceCell::new(),
162 event_focused: Arc::new(RwLock::new(HashMap::new())),
163 internals: CachesInternals {
164 state: state.clone(),
165 auto_shrink_sender,
166 linked_chunk_update_sender,
167 room_version_rules,
168 },
169 })
170 }
171
172 pub fn room(&self) -> &room::RoomEventCache {
176 &self.room
177 }
178
179 pub async fn thread(
187 &self,
188 thread_id: OwnedEventId,
189 ) -> Result<
190 OwnedRwLockReadGuard<
191 HashMap<OwnedEventId, thread::ThreadEventCache>,
192 thread::ThreadEventCache,
193 >,
194 > {
195 Ok(
196 match OwnedRwLockWriteGuard::try_downgrade_map(
197 self.threads.clone().write_owned().await,
198 |threads| threads.get(&thread_id),
199 ) {
200 Ok(locked_cache) => locked_cache,
202 Err(mut threads) => {
204 let room = &self.room;
205 let cache = thread::ThreadEventCache::new(
206 room.room_id().to_owned(),
207 thread_id.clone(),
208 room.own_user_id().to_owned(),
209 self.internals.room_version_rules.clone(),
210 room.weak_room().to_owned(),
211 &self.internals.state,
212 self.internals.auto_shrink_sender.clone(),
213 room.update_sender().generic_update_sender().clone(),
214 self.internals.linked_chunk_update_sender.clone(),
215 )
216 .await?;
217
218 threads.insert(thread_id.clone(), cache);
219
220 OwnedRwLockWriteGuard::downgrade_map(threads, |threads| {
221 threads.get(&thread_id).unwrap()
222 })
223 }
224 },
225 )
226 }
227
228 pub async fn pinned_events(&self) -> Result<&pinned_events::PinnedEventsCache> {
232 self.pinned_events
233 .get_or_try_init(|| {
234 pinned_events::PinnedEventsCache::new(
235 self.room.weak_room(),
236 self.room.own_user_id().clone(),
237 self.internals.room_version_rules.clone(),
238 self.internals.linked_chunk_update_sender.clone(),
239 &self.internals.state,
240 )
241 })
242 .await
243 }
244
245 pub async fn event_focused(
249 &self,
250 event_id: OwnedEventId,
251 thread_mode: event_focused::EventFocusThreadMode,
252 number_of_initial_events: u16,
253 ) -> Result<
254 OwnedRwLockReadGuard<
255 HashMap<event_focused::EventFocusedCacheKey, event_focused::EventFocusedCache>,
256 event_focused::EventFocusedCache,
257 >,
258 > {
259 let key = event_focused::EventFocusedCacheKey { focused_event_id: event_id, thread_mode };
260
261 Ok(
262 match OwnedRwLockWriteGuard::try_downgrade_map(
263 self.event_focused.clone().write_owned().await,
264 |event_focused_caches| event_focused_caches.get(&key),
265 ) {
266 Ok(locked_cache) => locked_cache,
268 Err(mut event_focused_caches) => {
270 let cache = event_focused::EventFocusedCache::new(
271 self.room.weak_room().clone(),
272 key.clone(),
273 &self.internals.state,
274 self.internals.linked_chunk_update_sender.clone(),
275 )
276 .await?;
277 cache.start_from(number_of_initial_events, thread_mode).await?;
278
279 event_focused_caches.insert(key.clone(), cache);
280
281 OwnedRwLockWriteGuard::downgrade_map(
282 event_focused_caches,
283 |event_focused_caches| event_focused_caches.get(&key).unwrap(),
284 )
285 }
286 },
287 )
288 }
289
290 pub(super) async fn handle_joined_room_update(&self, updates: JoinedRoomUpdate) -> Result<()> {
292 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
293
294 let JoinedRoomUpdate {
298 unread_notifications: _,
301 state: _,
303
304 timeline: original_timeline,
307 ephemeral: original_ephemeral,
308
309 account_data,
311 ambiguity_changes,
312 avatar_changes,
313 } = updates;
314
315 let original_ephemeral = original_ephemeral
317 .into_iter()
318 .filter_map(|ephemeral_event| ephemeral_event.deserialize().ok())
319 .collect::<Vec<_>>();
320
321 {
323 let (timeline, read_receipts) =
324 aggregator::aggregate_timeline_and_read_receipts_for_room(
325 &original_timeline,
326 &original_ephemeral,
327 );
328
329 room.handle_joined_room_update(
330 timeline,
331 read_receipts,
332 account_data,
333 ambiguity_changes,
334 avatar_changes,
335 )
336 .await?;
337 }
338
339 {
341 let timeline_and_read_receipts_for_threads = {
342 let all_states_lock = states::CacheStateLock::new(
346 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
347 self.internals.state.clone(),
348 );
349 let all_states = all_states_lock.read().await?;
350
351 aggregator::aggregate_timeline_and_read_receipts_for_threads(
352 &original_timeline,
353 &original_ephemeral,
354 all_states.threads(),
355 all_states.room(),
356 &internals.room_version_rules.redaction,
357 )
358 .await?
359 };
360
361 for (thread_id, (timeline, read_receipts)) in timeline_and_read_receipts_for_threads {
362 let update_thread_summary = timeline.events.is_empty().not();
364
365 let thread = self.thread(thread_id).await?;
366 thread.handle_joined_room_update(timeline, read_receipts).await?;
367
368 if update_thread_summary {
369 let new_thread_summary =
370 thread.state().read().await?.compute_thread_summary().await?;
371
372 room.update_thread_summary(thread.thread_id(), new_thread_summary).await?;
373 }
374 }
375 }
376
377 if let Some(pinned_events) = pinned_events.get() {
379 let timeline = aggregator::aggregate_timeline_for_pinned_events(
380 &original_timeline,
381 &pinned_events.state().read().await?.current_event_ids(),
382 &internals.room_version_rules.redaction,
383 );
384
385 pinned_events.handle_joined_room_update(timeline).await?;
386 }
387
388 {
390 let _ = event_focused;
393 }
394
395 Ok(())
396 }
397
398 pub(super) async fn handle_left_room_update(&self, updates: LeftRoomUpdate) -> Result<()> {
400 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
401
402 let LeftRoomUpdate {
406 state: _,
408 account_data: _,
410
411 timeline: original_timeline,
413
414 ambiguity_changes,
416 } = updates;
417
418 {
420 let (timeline, _read_receipts) =
421 aggregator::aggregate_timeline_and_read_receipts_for_room(&original_timeline, &[]);
422
423 room.handle_left_room_update(timeline, ambiguity_changes).await?;
424 }
425
426 {
428 let timeline_and_read_receipts_for_threads = {
429 let all_caches_states_lock = states::CacheStateLock::new(
433 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
434 self.internals.state.clone(),
435 );
436 let all_caches_states = all_caches_states_lock.read().await?;
437
438 aggregator::aggregate_timeline_and_read_receipts_for_threads(
439 &original_timeline,
440 &[],
441 all_caches_states.threads(),
442 all_caches_states.room(),
443 &internals.room_version_rules.redaction,
444 )
445 .await?
446 };
447
448 for (thread_id, (timeline, _read_receipts)) in timeline_and_read_receipts_for_threads {
449 let thread = self.thread(thread_id).await?;
450 thread.handle_left_room_update(timeline).await?;
451 }
452 }
453
454 if let Some(pinned_events) = pinned_events.get() {
456 let timeline = aggregator::aggregate_timeline_for_pinned_events(
457 &original_timeline,
458 &pinned_events.state().read().await?.current_event_ids(),
459 &internals.room_version_rules.redaction,
460 );
461
462 pinned_events.handle_left_room_update(timeline).await?;
463 }
464
465 {
467 let _ = event_focused;
470 }
471
472 Ok(())
473 }
474
475 #[cfg(feature = "e2e-encryption")]
480 pub async fn all_in_memory_events(&self) -> Result<impl Iterator<Item = Event>> {
481 let mut events = self.room.events().await?;
486
487 {
489 let event_focused = self.event_focused.read().await;
490
491 for event_focused in event_focused.values() {
492 events.extend(event_focused.events().await?);
493 }
494 }
495
496 Ok(events.into_iter())
497 }
498
499 #[cfg(feature = "e2e-encryption")]
508 pub async fn all_events_of_type(
509 &self,
510 event_type: Option<&str>,
511 session_id: Option<&str>,
512 ) -> Result<impl Iterator<Item = Event>> {
513 let mut events = {
516 let state = self.internals.state.read().await?;
517
518 state.store.get_room_events(self.room.room_id(), event_type, session_id).await?
519 };
520
521 {
524 let event_focused = self.event_focused.read().await;
525
526 for event_focused in event_focused.values() {
527 events.extend(
528 event_focused
529 .events()
530 .await?
531 .into_iter()
532 .filter(|event| event_type == event.kind.event_type().as_deref())
533 .filter(|event| session_id == event.kind.session_id()),
534 );
535 }
536 }
537
538 Ok(events.into_iter())
539 }
540}
541
542#[derive(Clone, Debug)]
544pub struct TimelineVectorDiffs {
545 pub diffs: Vec<VectorDiff<Event>>,
547 pub origin: EventsOrigin,
549}
550
551#[derive(Debug)]
553pub(super) enum EventLocation {
554 Memory(Position),
556
557 Store,
559}