From f1842ba5d07a45c7115dd2da849de489191faae1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 17 Dec 2024 17:30:44 +0100 Subject: [PATCH] refactor(ui): `Timeline` receives pagination events as `VectorDiff`s! This patch allows the paginated events of a `Timeline` to be received via `RoomEventCacheUpdate::UpdateTimelineEvents` as `VectorDiff`s. --- .../src/timeline/controller/mod.rs | 2 +- .../matrix-sdk-ui/src/timeline/pagination.rs | 16 +++++++----- .../matrix-sdk/src/event_cache/pagination.rs | 11 +++++++- .../tests/integration/event_cache.rs | 25 ++++++++++++++++++- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/crates/matrix-sdk-ui/src/timeline/controller/mod.rs b/crates/matrix-sdk-ui/src/timeline/controller/mod.rs index 529930741..36409f662 100644 --- a/crates/matrix-sdk-ui/src/timeline/controller/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/controller/mod.rs @@ -124,7 +124,7 @@ pub(super) struct TimelineController { pub(crate) room_data_provider: P, /// Settings applied to this timeline. - settings: TimelineSettings, + pub(super) settings: TimelineSettings, } #[derive(Clone)] diff --git a/crates/matrix-sdk-ui/src/timeline/pagination.rs b/crates/matrix-sdk-ui/src/timeline/pagination.rs index abe49acac..175ffe46a 100644 --- a/crates/matrix-sdk-ui/src/timeline/pagination.rs +++ b/crates/matrix-sdk-ui/src/timeline/pagination.rs @@ -77,12 +77,16 @@ impl super::Timeline { let num_events = events.len(); trace!("Back-pagination succeeded with {num_events} events"); - // TODO(hywan): Remove, and let spread events via - // `matrix_sdk::event_cache::RoomEventCacheUpdate` from - // `matrix_sdk::event_cache::RoomPagination::run_backwards`. - self.controller - .add_events_at(events.into_iter(), TimelineNewItemPosition::Start { origin: RemoteEventOrigin::Pagination }) - .await; + // If `TimelineSettings::vectordiffs_as_inputs` is enabled, + // we don't need to add events manually: everything we need + // is to let the `EventCache` receive the events from this + // pagination, and emit its updates as `VectorDiff`s, which + // will be handled by the `Timeline` naturally. + if !self.controller.settings.vectordiffs_as_inputs { + self.controller + .add_events_at(events.into_iter(), TimelineNewItemPosition::Start { origin: RemoteEventOrigin::Pagination }) + .await; + } if num_events == 0 && !reached_start { // As an exceptional contract: if there were no events in the response, diff --git a/crates/matrix-sdk/src/event_cache/pagination.rs b/crates/matrix-sdk/src/event_cache/pagination.rs index 180a03c12..3c14f3dd3 100644 --- a/crates/matrix-sdk/src/event_cache/pagination.rs +++ b/crates/matrix-sdk/src/event_cache/pagination.rs @@ -28,7 +28,7 @@ use super::{ events::{Gap, RoomEvents}, RoomEventCacheInner, }, - BackPaginationOutcome, Result, + BackPaginationOutcome, EventsOrigin, Result, RoomEventCacheUpdate, }; /// An API object to run pagination queries on a [`super::RoomEventCache`]. @@ -227,6 +227,15 @@ impl RoomPagination { debug!("not storing previous batch token, because we deduplicated all new back-paginated events"); } + let sync_timeline_events_diffs = room_events.updates_as_vector_diffs(); + + if !sync_timeline_events_diffs.is_empty() { + let _ = self.inner.sender.send(RoomEventCacheUpdate::UpdateTimelineEvents { + diffs: sync_timeline_events_diffs, + origin: EventsOrigin::Sync, + }); + } + BackPaginationOutcome { events, reached_start } }) .await?; diff --git a/crates/matrix-sdk/tests/integration/event_cache.rs b/crates/matrix-sdk/tests/integration/event_cache.rs index 98ee29db2..5f9f76782 100644 --- a/crates/matrix-sdk/tests/integration/event_cache.rs +++ b/crates/matrix-sdk/tests/integration/event_cache.rs @@ -281,6 +281,9 @@ async fn test_backpaginate_once() { assert_event_matches_msg(&events[1], "hello"); assert_eq!(events.len(), 2); + let next = room_stream.recv().now_or_never(); + assert_matches!(next, Some(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }))); + let next = room_stream.recv().now_or_never(); assert_matches!(next, None); } @@ -400,6 +403,14 @@ async fn test_backpaginate_many_times_with_many_iterations() { assert_event_matches_msg(&events[3], "heyo"); assert_eq!(events.len(), 4); + // First iteration. + let next = room_stream.recv().now_or_never(); + assert_matches!(next, Some(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }))); + + // Second iteration. + let next = room_stream.recv().now_or_never(); + assert_matches!(next, Some(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }))); + assert!(room_stream.is_empty()); } @@ -523,6 +534,14 @@ async fn test_backpaginate_many_times_with_one_iteration() { assert_event_matches_msg(&events[3], "heyo"); assert_eq!(events.len(), 4); + // First pagination. + let next = room_stream.recv().now_or_never(); + assert_matches!(next, Some(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }))); + + // Second pagination. + let next = room_stream.recv().now_or_never(); + assert_matches!(next, Some(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }))); + assert!(room_stream.is_empty()); } @@ -680,7 +699,7 @@ async fn test_backpaginating_without_token() { let room = server.sync_joined_room(&client, room_id).await; let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap(); - let (events, room_stream) = room_event_cache.subscribe().await.unwrap(); + let (events, mut room_stream) = room_event_cache.subscribe().await.unwrap(); assert!(events.is_empty()); assert!(room_stream.is_empty()); @@ -712,6 +731,9 @@ async fn test_backpaginating_without_token() { assert_event_matches_msg(&events[0], "hi"); assert_eq!(events.len(), 1); + let next = room_stream.recv().now_or_never(); + assert_matches!(next, Some(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }))); + assert!(room_stream.is_empty()); } @@ -772,6 +794,7 @@ async fn test_limited_timeline_resets_pagination() { server.sync_room(&client, JoinedRoomBuilder::new(room_id).set_timeline_limited()).await; // We receive an update about the limited timeline. + assert_let_timeout!(Ok(RoomEventCacheUpdate::UpdateTimelineEvents { .. }) = room_stream.recv()); assert_let_timeout!(Ok(RoomEventCacheUpdate::Clear) = room_stream.recv()); // The paginator state is reset: status set to Initial, hasn't hit the timeline