Skip to content

Commit dc90c77

Browse files
committed
feat(sdk): Introduce the LatestEvents API.
This patch is the first part of the new `LatestEvents` API. It contains the “framework”, i.e. the structure, tasks, logic to make it work, but no latest events are computed for the moment. The Latest Events API provides a lazy, reactive and efficient way to compute the latest event for a room or a thread. The latest event represents the last displayable and relevant event a room or a thread has been received. It is usually displayed in a _summary_, e.g. below the room title in a room list. The entry point is `LatestEvents`. It is preferable to get a reference to it from `Client::latest_events`, which already plugs everything to build it. `LatestEvents` is using the `EventCache` and the `SendQueue` to respectively get known remote events (i.e. synced from the server), or local events (i.e. ones being sent). \## Laziness `LatestEvents` is lazy, it means that, despites `LatestEvents` is listening to all `EventCache` or `SendQueue` updates, it will only do something if one is expected to get the latest event for a particular room or a particular thread. Concretely, it means that until `LatestEvents::listen_to_room` is called for a particular room, no latest event will ever be computed for that room (and similarly with `LatestEvents::listen_to_thread`). If one is no longer interested to get the latest event for a particular room or thread, the `LatestEvents::forget_room` and `LatestEvents::forget_thread` methods must be used. \## Reactive `LatestEvents` is designed to be reactive. Use `LatestEvents::listen_and_subscribe_to_room` (same for thread) to get a `Subscriber`.
1 parent 6c9038e commit dc90c77

File tree

6 files changed

+960
-1
lines changed

6 files changed

+960
-1
lines changed

crates/matrix-sdk/src/client/builder/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,8 @@ impl ClientBuilder {
574574
};
575575

576576
let event_cache = OnceCell::new();
577+
let latest_events = OnceCell::new();
578+
577579
let inner = ClientInner::new(
578580
auth_ctx,
579581
server,
@@ -585,6 +587,7 @@ impl ClientBuilder {
585587
self.respect_login_well_known,
586588
event_cache,
587589
send_queue,
590+
latest_events,
588591
#[cfg(feature = "e2e-encryption")]
589592
self.encryption_settings,
590593
#[cfg(feature = "e2e-encryption")]

crates/matrix-sdk/src/client/mod.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ use crate::{
9191
EventHandlerStore, ObservableEventHandler, SyncEvent,
9292
},
9393
http_client::HttpClient,
94+
latest_events::LatestEvents,
9495
media::MediaError,
9596
notification_settings::NotificationSettings,
9697
room::RoomMember,
9798
room_preview::RoomPreview,
98-
send_queue::SendQueueData,
99+
send_queue::{SendQueue, SendQueueData},
99100
sliding_sync::Version as SlidingSyncVersion,
100101
sync::{RoomUpdate, SyncResponse},
101102
Account, AuthApi, AuthSession, Error, HttpError, Media, Pusher, RefreshTokenError, Result,
@@ -344,6 +345,11 @@ pub(crate) struct ClientInner {
344345
/// The `max_upload_size` value of the homeserver, it contains the max
345346
/// request size you can send.
346347
pub(crate) server_max_upload_size: Mutex<OnceCell<UInt>>,
348+
349+
/// The entry point to get the [`LatestEvent`] of rooms and threads.
350+
///
351+
/// [`LatestEvent`]: crate::latest_event::LatestEvent
352+
latest_events: OnceCell<LatestEvents>,
347353
}
348354

349355
impl ClientInner {
@@ -364,6 +370,7 @@ impl ClientInner {
364370
respect_login_well_known: bool,
365371
event_cache: OnceCell<EventCache>,
366372
send_queue: Arc<SendQueueData>,
373+
latest_events: OnceCell<LatestEvents>,
367374
#[cfg(feature = "e2e-encryption")] encryption_settings: EncryptionSettings,
368375
#[cfg(feature = "e2e-encryption")] enable_share_history_on_invite: bool,
369376
cross_process_store_locks_holder_name: String,
@@ -394,6 +401,7 @@ impl ClientInner {
394401
sync_beat: event_listener::Event::new(),
395402
event_cache,
396403
send_queue_data: send_queue,
404+
latest_events,
397405
#[cfg(feature = "e2e-encryption")]
398406
e2ee: EncryptionData::new(encryption_settings),
399407
#[cfg(feature = "e2e-encryption")]
@@ -2638,6 +2646,7 @@ impl Client {
26382646
self.inner.respect_login_well_known,
26392647
self.inner.event_cache.clone(),
26402648
self.inner.send_queue_data.clone(),
2649+
self.inner.latest_events.clone(),
26412650
#[cfg(feature = "e2e-encryption")]
26422651
self.inner.e2ee.encryption_settings,
26432652
#[cfg(feature = "e2e-encryption")]
@@ -2656,6 +2665,16 @@ impl Client {
26562665
self.inner.event_cache.get().unwrap()
26572666
}
26582667

2668+
/// The [`LatestEvents`] instance for this [`Client`].
2669+
pub async fn latest_events(&self) -> &LatestEvents {
2670+
self.inner
2671+
.latest_events
2672+
.get_or_init(|| async {
2673+
LatestEvents::new(self.event_cache().clone(), SendQueue::new(self.clone()))
2674+
})
2675+
.await
2676+
}
2677+
26592678
/// Waits until an at least partially synced room is received, and returns
26602679
/// it.
26612680
///

crates/matrix-sdk/src/event_cache/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,15 @@ pub enum RoomEventCacheGenericUpdate {
637637
},
638638
}
639639

640+
impl RoomEventCacheGenericUpdate {
641+
/// Get the room ID that has triggered this generic update.
642+
pub fn room_id(&self) -> &RoomId {
643+
match self {
644+
Self::TimelineUpdated { room_id } => room_id,
645+
}
646+
}
647+
}
648+
640649
/// An update related to events happened in a room.
641650
#[derive(Debug, Clone)]
642651
pub enum RoomEventCacheUpdate {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 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+
15+
use eyeball::{AsyncLock, SharedObservable, Subscriber};
16+
use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId};
17+
18+
/// The latest event of a room or a thread.
19+
///
20+
/// Use [`LatestEvent::subscribe`] to get a stream of updates.
21+
#[derive(Debug)]
22+
pub(super) struct LatestEvent {
23+
/// The room owning this latest event.
24+
_room_id: OwnedRoomId,
25+
/// The thread (if any) owning this latest event.
26+
_thread_id: Option<OwnedEventId>,
27+
/// The latest event value.
28+
value: SharedObservable<LatestEventValue, AsyncLock>,
29+
}
30+
31+
impl LatestEvent {
32+
pub(super) fn new(room_id: &RoomId, thread_id: Option<&EventId>) -> Option<Self> {
33+
Some(Self {
34+
_room_id: room_id.to_owned(),
35+
_thread_id: thread_id.map(ToOwned::to_owned),
36+
value: SharedObservable::new_async(LatestEventValue::None),
37+
})
38+
}
39+
40+
/// Return a [`Subscriber`] to new values.
41+
pub async fn subscribe(&self) -> Subscriber<LatestEventValue, AsyncLock> {
42+
self.value.subscribe().await
43+
}
44+
}
45+
46+
/// A latest event value!
47+
#[derive(Debug, Clone)]
48+
pub enum LatestEventValue {
49+
/// No value has been computed yet, or no candidate value was found.
50+
None,
51+
}

0 commit comments

Comments
 (0)