From 3e29240e3039b69a14cc5e9fad53de1c60626e66 Mon Sep 17 00:00:00 2001 From: Karthikeyan S Date: Tue, 6 May 2025 16:03:13 +0530 Subject: [PATCH] chore: prev_batch update after a limited timeline follow-up --- lib/src/client.dart | 11 ++- test/timeline_test.dart | 158 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 10 deletions(-) diff --git a/lib/src/client.dart b/lib/src/client.dart index fdbc4374..47f4296d 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -2989,12 +2989,11 @@ class Client extends MatrixApi { (chatUpdate.unreadNotifications?.highlightCount ?? 0) || chatUpdate.summary != null || chatUpdate.timeline?.prevBatch != null)) { - /// [InvitedRoomUpdate] doesn't have prev_batch, so we want to set it in case - /// the room first appeared in sync update when membership was invite. - if ((rooms[roomIndex].membership == Membership.invite || - chatUpdate.timeline?.limited == true || - rooms[roomIndex].prev_batch == null) && - chatUpdate.timeline?.prevBatch != null) { + /// 1. [InvitedRoomUpdate] doesn't have prev_batch, so we want to set it in case + /// the room first appeared in sync update when membership was invite. + /// 2. We also reset the prev_batch if the timeline is limited. + if (rooms[roomIndex].membership == Membership.invite || + chatUpdate.timeline?.limited == true) { rooms[roomIndex].prev_batch = chatUpdate.timeline?.prevBatch; } rooms[roomIndex].membership = membership; diff --git a/test/timeline_test.dart b/test/timeline_test.dart index 1643b5f4..60a445f7 100644 --- a/test/timeline_test.dart +++ b/test/timeline_test.dart @@ -78,8 +78,8 @@ void main() { room = Room( id: roomID, client: client, - prev_batch: 'room_preset_1234', roomAccountData: {}, + prev_batch: 'room_preset_1234', ); timeline = Timeline( room: room, @@ -1204,8 +1204,14 @@ void main() { ); }); - test('make sure a limited timeline resets the prev_batch', () async { + test('make sure timeline with null prev_batch is not reset incorrectly', + () async { timeline.events.clear(); + timeline.room.prev_batch = null; + + Timeline t = await room.getTimeline(); + expect(t.events.length, 0); + expect(t.room.prev_batch, null); await client.handleSync( SyncUpdate( @@ -1214,7 +1220,146 @@ void main() { join: { timeline.room.id: JoinedRoomUpdate( timeline: TimelineUpdate( - prevBatch: 'null', + prevBatch: 'room_preset_1234', + events: [ + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase 3'}, + 'event_id': '33', + 'sender': '@alice:example.com', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }), + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase 4'}, + 'event_id': '44', + 'sender': '@alice:example.com', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }), + ], + ), + ), + }, + ), + ), + ); + + t = await room.getTimeline(); + expect(t.events.length, 2); + expect( + t.room.prev_batch, + null, + reason: + 'The prev_batch is null, which means that no earlier events are available. Having a new prev_batch is sync shouldn\'t reset it.', + ); + }); + + test('make sure room prev_batch is set correctly when invited', () async { + final newRoomId1 = '!newroom:example.com'; + final newRoomId2 = '!newroom2:example.com'; + + await client.handleSync( + SyncUpdate( + nextBatch: 'something', + rooms: RoomsUpdate( + invite: { + newRoomId1: InvitedRoomUpdate( + inviteState: [ + StrippedStateEvent( + type: EventTypes.RoomMember, + senderId: '@bob:example.com', + stateKey: client.userID, + content: { + 'membership': 'invite', + }, + ), + ], + ), + newRoomId2: InvitedRoomUpdate( + inviteState: [ + StrippedStateEvent( + type: EventTypes.RoomMember, + senderId: '@bob:example.com', + stateKey: client.userID, + content: { + 'membership': 'invite', + }, + ), + ], + ), + }, + ), + ), + ); + + Room? newRoom1 = client.getRoomById(newRoomId1); + expect(newRoom1?.membership, Membership.invite); + expect(newRoom1?.prev_batch, null); + + Room? newRoom2 = client.getRoomById(newRoomId2); + expect(newRoom2?.membership, Membership.invite); + expect(newRoom2?.prev_batch, null); + + await client.handleSync( + SyncUpdate( + nextBatch: 'something', + rooms: RoomsUpdate( + join: { + newRoomId1: JoinedRoomUpdate( + timeline: TimelineUpdate( + prevBatch: + null, // this means that no earlier events are available + events: [ + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'event_id': '33', + 'sender': '@alice:example.com', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }), + ], + ), + ), + newRoomId2: JoinedRoomUpdate( + timeline: TimelineUpdate( + prevBatch: 'actual_prev_batch', + events: [ + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'event_id': '33', + 'sender': '@alice:example.com', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }), + ], + ), + ), + }, + ), + ), + ); + + newRoom1 = client.getRoomById(newRoomId1); + expect(newRoom1?.membership, Membership.join); + expect(newRoom1?.prev_batch, null); + + newRoom2 = client.getRoomById(newRoomId2); + expect(newRoom2?.membership, Membership.join); + expect(newRoom2?.prev_batch, 'actual_prev_batch'); + }); + + test('make sure a limited timeline resets the prev_batch', () async { + timeline.events.clear(); + expect(timeline.room.prev_batch, 'room_preset_1234'); + + await client.handleSync( + SyncUpdate( + nextBatch: 'something', + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + prevBatch: 'room_preset_5678', events: [ MatrixEvent.fromJson({ 'type': 'm.room.message', @@ -1241,7 +1386,12 @@ void main() { Timeline t = await room.getTimeline(); expect(t.events.length, 2); - expect(t.room.prev_batch, 'room_preset_1234'); + expect( + t.room.prev_batch, + 'room_preset_1234', + reason: + 'The prev_batch should only be set the first time and be updated when requesting history. It shouldn\'t be updated every sync incorrectly.', + ); await client.handleSync( SyncUpdate(