fix(sdk): Events from the Send Queue are inserted in the Event Cache if and only if it is no empty.

This patch fixes a bug where inserting an event from the Send Queue in
an empty Event Cache will break the back-pagination logic. Indeed, the
detection of the start of the timeline during the back-pagination is
conditioned to the emptiness of the cache:

- if there is no gap, and if the cache is not empty, then we consider
  the start of the timeline has been reached.

However, if an event from the Send Queue has been inserted, with no
previous batch token (because none can be computed at this step), no gap
is present and the cache won't be empty, so… this is wrongly assumed to
be the start of the timeline.

The solution to this problem is to insert the event from the Send Queue
if the cache is not empty.
This commit is contained in:
Ivan Enderlin
2026-04-02 11:35:11 +02:00
parent db4c1f8dbd
commit 1afebd6d4e
@@ -381,13 +381,7 @@ impl RoomEventCache {
/// Handle a single event from the `SendQueue`.
pub(crate) async fn insert_sent_event_from_send_queue(&self, event: Event) -> Result<()> {
self.inner
.handle_timeline(
Timeline { limited: false, prev_batch: None, events: vec![event] },
Vec::new(),
BTreeMap::new(),
)
.await
self.inner.insert_sent_event_from_send_queue(event).await
}
/// Save some events in the event cache, for further retrieval with
@@ -514,6 +508,22 @@ impl RoomEventCacheInner {
timeline: Timeline,
ephemeral_events: Vec<Raw<AnySyncEphemeralRoomEvent>>,
ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
) -> Result<()> {
self.handle_timeline_inner(
self.state.write().await?,
timeline,
ephemeral_events,
ambiguity_changes,
)
.await
}
async fn handle_timeline_inner(
&self,
mut state: RoomEventCacheStateLockWriteGuard<'_>,
timeline: Timeline,
ephemeral_events: Vec<Raw<AnySyncEphemeralRoomEvent>>,
ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
) -> Result<()> {
if timeline.events.is_empty()
&& timeline.prev_batch.is_none()
@@ -527,7 +537,9 @@ impl RoomEventCacheInner {
trace!("adding new events");
let (stored_prev_batch_token, timeline_event_diffs) =
self.state.write().await?.handle_sync(timeline, &ephemeral_events).await?;
state.handle_sync(timeline, &ephemeral_events).await?;
drop(state);
// Now that all events have been added, we can trigger the
// `pagination_token_notifier`.
@@ -559,6 +571,29 @@ impl RoomEventCacheInner {
Ok(())
}
/// Handle a single event from the `SendQueue`.
///
/// The event is inserted if and only if the cache is not empty.
async fn insert_sent_event_from_send_queue(&self, event: Event) -> Result<()> {
let state = self.state.write().await?;
// Insert the event if the room is not empty, otherwise it can break the
// pagination logic when detecting the start of the timeline because no gap can
// be inserted properly: it is impossible to compute a `prev_batch` token here.
if state.room_linked_chunk().events().next().is_some() {
return self
.handle_timeline_inner(
state,
Timeline { limited: false, prev_batch: None, events: vec![event] },
Vec::new(),
BTreeMap::new(),
)
.await;
}
Ok(())
}
}
#[derive(Clone, Copy)]