matrix_sdk/event_cache/caches/
mod.rs1use std::{collections::HashMap, ops::Deref, 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, automatic_pagination::AutomaticPagination, 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 automatic_pagination: Option<AutomaticPagination>,
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 automatic_pagination,
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 {
296 let mut updates = updates.clone();
297 updates.timeline = aggregator::aggregate_timeline_for_room(updates.timeline);
298
299 room.handle_joined_room_update(updates).await?;
300 }
301
302 {
304 let mut updates = updates.clone();
305 updates.account_data.clear();
306 updates.ambiguity_changes.clear();
307
308 let timeline_for_threads = aggregator::aggregate_timeline_for_threads(
309 &updates.timeline,
310 threads.read().await.deref(),
311 room.state().read().await?,
312 &internals.room_version_rules.redaction,
313 )
314 .await?;
315
316 for (thread_id, timeline) in timeline_for_threads {
317 let mut updates = updates.clone();
318 updates.timeline = timeline;
319
320 let thread = self.thread(thread_id).await?;
321 thread.handle_joined_room_update(updates).await?;
322
323 let new_thread_summary =
324 thread.state().read().await?.compute_thread_summary().await?;
325
326 room.update_thread_summary(thread.thread_id(), new_thread_summary).await?;
327 }
328 }
329
330 if let Some(pinned_events) = pinned_events.get() {
332 let mut updates = updates.clone();
333 updates.timeline = aggregator::aggregate_timeline_for_pinned_events(
334 &updates.timeline,
335 &pinned_events.state().read().await?.current_event_ids(),
336 &internals.room_version_rules.redaction,
337 );
338
339 pinned_events.handle_joined_room_update(updates).await?;
340 }
341
342 {
344 let _ = event_focused;
347 }
348
349 Ok(())
350 }
351
352 pub(super) async fn handle_left_room_update(&self, updates: LeftRoomUpdate) -> Result<()> {
354 let Self { room, threads, pinned_events, event_focused, internals } = &self;
355
356 {
358 let mut updates = updates.clone();
359 updates.timeline = aggregator::aggregate_timeline_for_room(updates.timeline);
360
361 room.handle_left_room_update(updates).await?;
362 }
363
364 {
366 let mut updates = updates.clone();
367 updates.account_data.clear();
368 updates.ambiguity_changes.clear();
369
370 let timeline_for_threads = aggregator::aggregate_timeline_for_threads(
371 &updates.timeline,
372 threads.read().await.deref(),
373 room.state().read().await?,
374 &internals.room_version_rules.redaction,
375 )
376 .await?;
377
378 for (thread_id, timeline) in timeline_for_threads {
379 let mut updates = updates.clone();
380 updates.timeline = timeline;
381
382 let thread = self.thread(thread_id).await?;
383 thread.handle_left_room_update(updates).await?;
384
385 let new_thread_summary =
386 thread.state().read().await?.compute_thread_summary().await?;
387
388 room.update_thread_summary(thread.thread_id(), new_thread_summary).await?;
389 }
390 }
391
392 if let Some(pinned_events) = pinned_events.get() {
394 let mut updates = updates.clone();
395 updates.timeline = aggregator::aggregate_timeline_for_pinned_events(
396 &updates.timeline,
397 &pinned_events.state().read().await?.current_event_ids(),
398 &internals.room_version_rules.redaction,
399 );
400
401 pinned_events.handle_left_room_update(updates).await?;
402 }
403
404 {
406 let _ = event_focused;
409 }
410
411 Ok(())
412 }
413
414 #[cfg(feature = "e2e-encryption")]
419 pub async fn all_in_memory_events(&self) -> Result<impl Iterator<Item = Event>> {
420 let mut events = self.room.events().await?;
425
426 {
428 let event_focused = self.event_focused.read().await;
429
430 for event_focused in event_focused.values() {
431 events.extend(event_focused.events().await?);
432 }
433 }
434
435 Ok(events.into_iter())
436 }
437
438 #[cfg(feature = "e2e-encryption")]
447 pub async fn all_events_of_type(
448 &self,
449 event_type: Option<&str>,
450 session_id: Option<&str>,
451 ) -> Result<impl Iterator<Item = Event>> {
452 let mut events = {
455 let state = self.internals.state.read().await?;
456
457 state.store.get_room_events(self.room.room_id(), event_type, session_id).await?
458 };
459
460 {
463 let event_focused = self.event_focused.read().await;
464
465 for event_focused in event_focused.values() {
466 events.extend(
467 event_focused
468 .events()
469 .await?
470 .into_iter()
471 .filter(|event| event_type == event.kind.event_type().as_deref())
472 .filter(|event| session_id == event.kind.session_id()),
473 );
474 }
475 }
476
477 Ok(events.into_iter())
478 }
479}
480
481#[derive(Clone, Debug)]
483pub struct TimelineVectorDiffs {
484 pub diffs: Vec<VectorDiff<Event>>,
486 pub origin: EventsOrigin,
488}
489
490#[derive(Debug)]
492pub(super) enum EventLocation {
493 Memory(Position),
495
496 Store,
498}