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,
308 ephemeral: original_ephemeral,
309
310 account_data,
312 ambiguity_changes,
313 avatar_changes,
314 } = updates;
315
316 let original_ephemeral = original_ephemeral
318 .into_iter()
319 .filter_map(|ephemeral_event| ephemeral_event.deserialize().ok())
320 .collect::<Vec<_>>();
321
322 {
324 let (timeline, read_receipts) =
325 aggregator::aggregate_timeline_and_read_receipts_for_room(
326 &original_timeline,
327 &original_ephemeral,
328 );
329
330 room.handle_joined_room_update(
331 timeline,
332 read_receipts,
333 account_data,
334 ambiguity_changes,
335 avatar_changes,
336 )
337 .await?;
338 }
339
340 {
342 let timeline_and_read_receipts_for_threads = {
343 let all_states_lock = states::CacheStateLock::new(
348 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
349 self.internals.state.clone(),
350 );
351 let all_states = all_states_lock.read().await?;
352
353 aggregator::aggregate_timeline_and_read_receipts_for_threads(
354 &original_timeline,
355 &original_ephemeral,
356 all_states.threads(),
357 all_states.room(),
358 &internals.room_version_rules.redaction,
359 )
360 .await?
361 };
362
363 for (thread_id, (timeline, read_receipts)) in timeline_and_read_receipts_for_threads {
364 let update_thread_summary = timeline.events.is_empty().not();
367
368 let thread = self.thread(thread_id).await?;
369 thread.handle_joined_room_update(timeline, read_receipts).await?;
370
371 if update_thread_summary {
372 let new_thread_summary = thread.update_thread_summary().await?;
373
374 room.update_thread_summary(thread.thread_id(), new_thread_summary)?;
375 }
376 }
377 }
378
379 if let Some(pinned_events) = pinned_events.get() {
381 let timeline = aggregator::aggregate_timeline_for_pinned_events(
382 &original_timeline,
383 &pinned_events.state().read().await?.current_event_ids(),
384 &internals.room_version_rules.redaction,
385 );
386
387 pinned_events.handle_joined_room_update(timeline).await?;
388 }
389
390 {
392 let _ = event_focused;
395 }
396
397 Ok(())
398 }
399
400 pub(super) async fn handle_left_room_update(&self, updates: LeftRoomUpdate) -> Result<()> {
402 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
403
404 let LeftRoomUpdate {
408 state: _,
410 account_data: _,
412
413 timeline: original_timeline,
415
416 ambiguity_changes,
418 } = updates;
419
420 {
422 let (timeline, _read_receipts) =
423 aggregator::aggregate_timeline_and_read_receipts_for_room(&original_timeline, &[]);
424
425 room.handle_left_room_update(timeline, ambiguity_changes).await?;
426 }
427
428 {
430 let timeline_and_read_receipts_for_threads = {
431 let all_caches_states_lock = states::CacheStateLock::new(
436 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
437 self.internals.state.clone(),
438 );
439 let all_caches_states = all_caches_states_lock.read().await?;
440
441 aggregator::aggregate_timeline_and_read_receipts_for_threads(
442 &original_timeline,
443 &[],
444 all_caches_states.threads(),
445 all_caches_states.room(),
446 &internals.room_version_rules.redaction,
447 )
448 .await?
449 };
450
451 for (thread_id, (timeline, _read_receipts)) in timeline_and_read_receipts_for_threads {
452 let thread = self.thread(thread_id).await?;
453 thread.handle_left_room_update(timeline).await?;
454 }
455 }
456
457 if let Some(pinned_events) = pinned_events.get() {
459 let timeline = aggregator::aggregate_timeline_for_pinned_events(
460 &original_timeline,
461 &pinned_events.state().read().await?.current_event_ids(),
462 &internals.room_version_rules.redaction,
463 );
464
465 pinned_events.handle_left_room_update(timeline).await?;
466 }
467
468 {
470 let _ = event_focused;
473 }
474
475 Ok(())
476 }
477
478 #[cfg(feature = "e2e-encryption")]
483 pub async fn all_in_memory_events(&self) -> Result<impl Iterator<Item = Event>> {
484 let mut events = self.room.events().await?;
489
490 {
492 let event_focused = self.event_focused.read().await;
493
494 for event_focused in event_focused.values() {
495 events.extend(event_focused.events().await?);
496 }
497 }
498
499 Ok(events.into_iter())
500 }
501
502 #[cfg(feature = "e2e-encryption")]
511 pub async fn all_events_of_type(
512 &self,
513 event_type: Option<&str>,
514 session_id: Option<&str>,
515 ) -> Result<impl Iterator<Item = Event>> {
516 let mut events = {
519 let state = self.internals.state.read().await?;
520
521 state.store.get_room_events(self.room.room_id(), event_type, session_id).await?
522 };
523
524 {
527 let event_focused = self.event_focused.read().await;
528
529 for event_focused in event_focused.values() {
530 events.extend(
531 event_focused
532 .events()
533 .await?
534 .into_iter()
535 .filter(|event| event_type == event.kind.event_type().as_deref())
536 .filter(|event| session_id == event.kind.session_id()),
537 );
538 }
539 }
540
541 Ok(events.into_iter())
542 }
543}
544
545#[derive(Clone, Debug)]
547pub struct TimelineVectorDiffs {
548 pub diffs: Vec<VectorDiff<Event>>,
550 pub origin: EventsOrigin,
552}
553
554#[derive(Debug)]
556pub(super) enum EventLocation {
557 Memory(Position),
559
560 Store,
562}