diff --git a/crates/matrix-sdk/src/event_cache/pagination.rs b/crates/matrix-sdk/src/event_cache/pagination.rs index 32e83052a..b196e2d36 100644 --- a/crates/matrix-sdk/src/event_cache/pagination.rs +++ b/crates/matrix-sdk/src/event_cache/pagination.rs @@ -126,7 +126,51 @@ impl RoomPagination { } } + /// Paginate from either the storage or the network, and let pagination + /// status observers know about updates. async fn run_backwards_impl(&self, batch_size: u16) -> Result> { + // There is at least one gap that must be resolved; reach the network. + // First, ensure there's no other ongoing back-pagination. + let status_observable = &self.inner.pagination_status; + + let prev_status = status_observable.set(RoomPaginationStatus::Paginating); + if !matches!(prev_status, RoomPaginationStatus::Idle { .. }) { + return Err(EventCacheError::AlreadyBackpaginating); + } + + let reset_status_on_drop_guard = ResetStatusOnDrop { + prev_status: Some(prev_status), + pagination_status: status_observable.clone(), + }; + + match self.paginate_backwards_impl(batch_size).await? { + Some(outcome) => { + // Back-pagination's over and successful, don't reset the status to the previous + // value. + reset_status_on_drop_guard.disarm(); + + // Notify subscribers that pagination ended. + status_observable + .set(RoomPaginationStatus::Idle { hit_timeline_start: outcome.reached_start }); + Ok(Some(outcome)) + } + + None => { + // We keep the previous status value, because we haven't obtained more + // information about the pagination. + Ok(None) + } + } + } + + /// Paginate from either the storage or the network. + /// + /// This method isn't concerned with setting the pagination status; only the + /// caller is. + async fn paginate_backwards_impl( + &self, + batch_size: u16, + ) -> Result> { // A linked chunk might not be entirely loaded (if it's been lazy-loaded). Try // to load from storage first, then from network if storage indicated // there's no previous events chunk to load. @@ -165,25 +209,18 @@ impl RoomPagination { self.paginate_backwards_with_network(batch_size).await } + /// Run a single pagination request (/messages) to the server. + /// + /// If there's no previous-batch token, it will wait for one for a short + /// while to get one, or if it's already done so or seen a + /// previous-batch token before, it will immediately indicate + /// it's reached the end of the timeline. async fn paginate_backwards_with_network( &self, batch_size: u16, ) -> Result> { const DEFAULT_WAIT_FOR_TOKEN_DURATION: Duration = Duration::from_secs(3); - // There is at least one gap that must be resolved; reach the network. - // First, ensure there's no other ongoing back-pagination. - let prev_status = self.inner.pagination_status.set(RoomPaginationStatus::Paginating); - - if !matches!(prev_status, RoomPaginationStatus::Idle { .. }) { - return Err(EventCacheError::AlreadyBackpaginating); - } - - let reset_status_on_drop_guard = ResetStatusOnDrop { - prev_status: Some(prev_status), - pagination_status: self.inner.pagination_status.clone(), - }; - let prev_token = self.get_or_wait_for_token(Some(DEFAULT_WAIT_FOR_TOKEN_DURATION)).await; let prev_token = match prev_token { @@ -222,11 +259,11 @@ impl RoomPagination { matches!(chunk.content(), ChunkContent::Gap(Gap { ref prev_token }) if *prev_token == token) }); - // We got a previous-batch token from the linked chunk *before* running the - // request, which is missing from the linked chunk *after* - // completing the request. It may be a sign the linked chunk has - // been reset, and it's an error in any case. if gap_id.is_none() { + // We got a previous-batch token from the linked chunk *before* running the + // request, which is missing from the linked chunk *after* completing the + // request. It may be a sign the linked chunk has been reset, + // and it's an error in any case. return Ok(None); } @@ -355,14 +392,6 @@ impl RoomPagination { let backpagination_outcome = BackPaginationOutcome { events, reached_start }; - // Back-pagination's over; time to disarm the status guard. - reset_status_on_drop_guard.disarm(); - - // Notify subscribers that pagination ended. - self.inner - .pagination_status - .set(RoomPaginationStatus::Idle { hit_timeline_start: reached_start }); - if !sync_timeline_events_diffs.is_empty() { let _ = self.inner.sender.send(RoomEventCacheUpdate::UpdateTimelineEvents { diffs: sync_timeline_events_diffs, diff --git a/crates/matrix-sdk/src/event_cache/room/mod.rs b/crates/matrix-sdk/src/event_cache/room/mod.rs index 641239b6a..bcd7bf5b7 100644 --- a/crates/matrix-sdk/src/event_cache/room/mod.rs +++ b/crates/matrix-sdk/src/event_cache/room/mod.rs @@ -848,8 +848,6 @@ mod private { Ok(None) => { // No previous chunk: no events to insert. Better, it means we've reached // the start of the timeline! - self.pagination_status - .set(RoomPaginationStatus::Idle { hit_timeline_start: true }); return Ok(LoadMoreEventsBackwardsOutcome::StartOfTimeline); }