@@ -435,7 +435,7 @@ impl RoomEventCacheInner {
435
435
} else {
436
436
// Add the previous back-pagination token (if present), followed by the timeline
437
437
// events themselves.
438
- let ( _ , sync_timeline_events_diffs) = state
438
+ let ( ( ) , mut sync_timeline_events_diffs) = state
439
439
. with_events_mut ( |room_events| {
440
440
// If we only received duplicated events, we don't need to store the gap: if
441
441
// there was a gap, we'd have received an unknown event at the tail of
@@ -461,8 +461,21 @@ impl RoomEventCacheInner {
461
461
} )
462
462
. await ?;
463
463
464
+ if prev_batch. is_some ( ) && !all_duplicates {
464
465
// If there was a previous batch token, and there's at least one non-duplicated
466
+ // new event, unload the chunks so it only contains the last
467
+ // one; otherwise, there might be a valid gap in between, and
468
+ // observers may not render it (yet). In this case, extend the
469
+ // updates with those from the unload; the new updates include a `clear` (as of
470
+ // 2025-02-24), so they will remove all the previous ones first.
471
+ //
465
472
// We must do this *after* the above call to `.with_events_mut`, so the new
473
+ // events and gaps are properly persisted to storage.
474
+ if let Some ( new_events_diffs) = state. shrink_to_last_chunk ( ) . await ? {
475
+ sync_timeline_events_diffs. extend ( new_events_diffs) ;
476
+ }
477
+ }
478
+
466
479
{
467
480
// Fill the AllEventsCache.
468
481
let mut all_events = self . all_events . write ( ) . await ;
@@ -1690,6 +1703,8 @@ mod tests {
1690
1703
#[ cfg( not( target_arch = "wasm32" ) ) ] // This uses the cross-process lock, so needs time support.
1691
1704
#[ async_test]
1692
1705
async fn test_no_useless_gaps ( ) {
1706
+ use crate :: event_cache:: room:: LoadMoreEventsBackwardsOutcome ;
1707
+
1693
1708
let room_id = room_id ! ( "!galette:saucisse.bzh" ) ;
1694
1709
1695
1710
let client = MockClientBuilder :: new ( "http://localhost" . to_owned ( ) ) . build ( ) . await ;
@@ -1725,7 +1740,7 @@ mod tests {
1725
1740
. unwrap ( ) ;
1726
1741
1727
1742
{
1728
- let state = room_event_cache. inner . state . read ( ) . await ;
1743
+ let mut state = room_event_cache. inner . state . write ( ) . await ;
1729
1744
1730
1745
let mut num_gaps = 0 ;
1731
1746
let mut num_events = 0 ;
@@ -1737,6 +1752,26 @@ mod tests {
1737
1752
}
1738
1753
}
1739
1754
1755
+ // The limited sync unloads the chunk, so it will appear as if there are only
1756
+ // the events.
1757
+ assert_eq ! ( num_gaps, 0 ) ;
1758
+ assert_eq ! ( num_events, 1 ) ;
1759
+
1760
+ // But if I manually reload more of the chunk, the gap will be present.
1761
+ assert_matches ! (
1762
+ state. load_more_events_backwards( ) . await . unwrap( ) ,
1763
+ LoadMoreEventsBackwardsOutcome :: Gap
1764
+ ) ;
1765
+
1766
+ num_gaps = 0 ;
1767
+ num_events = 0 ;
1768
+ for c in state. events ( ) . chunks ( ) {
1769
+ match c. content ( ) {
1770
+ ChunkContent :: Items ( items) => num_events += items. len ( ) ,
1771
+ ChunkContent :: Gap ( _) => num_gaps += 1 ,
1772
+ }
1773
+ }
1774
+
1740
1775
// The gap must have been stored.
1741
1776
assert_eq ! ( num_gaps, 1 ) ;
1742
1777
assert_eq ! ( num_events, 1 ) ;
0 commit comments