matrix_sdk/event_cache/caches/room/updates.rs
1// Copyright 2026 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16
17use matrix_sdk_base::{
18 deserialized_responses::AmbiguityChange,
19 event_cache::{Event, Gap},
20 linked_chunk::{self, OwnedLinkedChunkId},
21};
22use ruma::{
23 OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId, events::AnySyncEphemeralRoomEvent,
24 serde::Raw,
25};
26use tokio::sync::broadcast::{Receiver, Sender};
27
28use super::super::TimelineVectorDiffs;
29
30/// An update related to events happened in a room.
31#[derive(Debug, Clone)]
32pub enum RoomEventCacheUpdate {
33 /// The fully read marker has moved to a different event.
34 MoveReadMarkerTo {
35 /// Event at which the read marker is now pointing.
36 event_id: OwnedEventId,
37 },
38
39 /// The members have changed.
40 UpdateMembers {
41 /// Collection of ambiguity changes that room member events trigger.
42 ///
43 /// This is a map of event ID of the `m.room.member` event to the
44 /// details of the ambiguity change.
45 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
46
47 /// Collection of avatar changes that room member events trigger.
48 avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
49 },
50
51 /// The room has received updates for the timeline as _diffs_.
52 UpdateTimelineEvents(TimelineVectorDiffs),
53
54 /// The room has received new ephemeral events.
55 AddEphemeralEvents {
56 /// XXX: this is temporary, until read receipts are handled in the event
57 /// cache
58 events: Vec<Raw<AnySyncEphemeralRoomEvent>>,
59 },
60}
61
62/// Represents a timeline update of a room. It hides the details of
63/// [`RoomEventCacheUpdate`] by being more generic.
64///
65/// This is used by [`EventCache::subscribe_to_room_generic_updates`][0]. Please
66/// read it to learn more about the motivation behind this type.
67///
68/// [0]: super::super::super::EventCache::subscribe_to_room_generic_updates
69#[derive(Clone, Debug)]
70pub struct RoomEventCacheGenericUpdate {
71 /// The room ID owning the timeline.
72 pub room_id: OwnedRoomId,
73}
74
75/// An update being triggered when events change in the persisted event cache
76/// for any room.
77#[derive(Clone, Debug)]
78pub struct RoomEventCacheLinkedChunkUpdate {
79 /// The linked chunk affected by the update.
80 pub linked_chunk_id: OwnedLinkedChunkId,
81
82 /// A vector of all the linked chunk updates that happened during this event
83 /// cache update.
84 pub updates: Vec<linked_chunk::Update<Event, Gap>>,
85}
86
87impl RoomEventCacheLinkedChunkUpdate {
88 /// Return all the new events propagated by this update, in topological
89 /// order.
90 pub fn events(self) -> impl DoubleEndedIterator<Item = Event> {
91 use itertools::Either;
92 self.updates.into_iter().flat_map(|update| match update {
93 linked_chunk::Update::PushItems { items, .. } => {
94 Either::Left(Either::Left(items.into_iter()))
95 }
96 linked_chunk::Update::ReplaceItem { item, .. } => {
97 Either::Left(Either::Right(std::iter::once(item)))
98 }
99 linked_chunk::Update::RemoveItem { .. }
100 | linked_chunk::Update::DetachLastItems { .. }
101 | linked_chunk::Update::StartReattachItems
102 | linked_chunk::Update::EndReattachItems
103 | linked_chunk::Update::NewItemsChunk { .. }
104 | linked_chunk::Update::NewGapChunk { .. }
105 | linked_chunk::Update::RemoveChunk(..)
106 | linked_chunk::Update::Clear => {
107 // All these updates don't contain any new event.
108 Either::Right(std::iter::empty())
109 }
110 })
111 }
112}
113
114/// A small type to send updates in all channels.
115#[derive(Clone)]
116pub struct RoomEventCacheUpdateSender {
117 room_sender: Sender<RoomEventCacheUpdate>,
118 generic_sender: Sender<RoomEventCacheGenericUpdate>,
119}
120
121impl RoomEventCacheUpdateSender {
122 /// Create a new [`RoomEventCacheUpdateSender`].
123 pub fn new(generic_sender: Sender<RoomEventCacheGenericUpdate>) -> Self {
124 Self { room_sender: Sender::new(32), generic_sender }
125 }
126
127 /// Send a [`RoomEventCacheUpdate`] and an optional
128 /// [`RoomEventCacheGenericUpdate`].
129 pub fn send(
130 &self,
131 room_update: RoomEventCacheUpdate,
132 generic_update: Option<RoomEventCacheGenericUpdate>,
133 ) {
134 let _ = self.room_sender.send(room_update);
135
136 if let Some(generic_update) = generic_update {
137 let _ = self.generic_sender.send(generic_update);
138 }
139 }
140
141 /// Create a new [`Receiver`] of [`RoomEventCacheUpdate`].
142 pub(super) fn new_room_receiver(&self) -> Receiver<RoomEventCacheUpdate> {
143 self.room_sender.subscribe()
144 }
145}