diff --git a/lib/fake_matrix_api.dart b/lib/fake_matrix_api.dart index f0b011a7..6bbbc67a 100644 --- a/lib/fake_matrix_api.dart +++ b/lib/fake_matrix_api.dart @@ -331,6 +331,31 @@ class FakeMatrixApi extends BaseClient { }; } + // since direction is b, the start and end are reversed + static const Map emptyHistoryResponse = { + 'start': 'simpleHistoryResponse', // next_batch + 'end': null, // prev_batch + 'chunk': [], + 'state': [], + }; + static const Map simpleHistoryResponse = { + 'start': '1', // next_batch + 'end': 'emptyHistoryResponse', // prev_batch + 'chunk': [ + { + 'content': {'body': '0'}, + 'type': 'm.room.message', + 'event_id': '0', + 'room_id': 'new_room_id', + 'sender': '@example:example.org', + 'origin_server_ts': 1432735824653, + 'unsigned': {'age': 1234}, + 'state_key': '', + }, + ], + 'state': [], + }; + static const Map messagesResponsePast = { 'start': 't47429-4392820_219380_26003_2265', 'end': 't47409-4357353_219380_26003_2265', @@ -1724,6 +1749,10 @@ class FakeMatrixApi extends BaseClient { 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, }, + '/client/v3/rooms/new_room_id/messages?from=emptyHistoryResponse&dir=b&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D': + (var req) => emptyHistoryResponse, + '/client/v3/rooms/new_room_id/messages?from=1&dir=b&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D': + (var req) => simpleHistoryResponse, '/client/v3/rooms/!localpart%3Aserver.abc/messages?from=1234&dir=b&to=1234&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D': (var req) => messagesResponsePast, '/client/v3/rooms/!localpart%3Aserver.abc/messages?from=&dir=b&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D': diff --git a/lib/src/client.dart b/lib/src/client.dart index c593cebe..c420b69f 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -2984,14 +2984,18 @@ 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?.prevBatch != null) { + rooms[roomIndex].prev_batch = chatUpdate.timeline?.prevBatch; + } rooms[roomIndex].membership = membership; rooms[roomIndex].notificationCount = chatUpdate.unreadNotifications?.notificationCount ?? 0; rooms[roomIndex].highlightCount = chatUpdate.unreadNotifications?.highlightCount ?? 0; - if (chatUpdate.timeline?.prevBatch != null) { - rooms[roomIndex].prev_batch = chatUpdate.timeline?.prevBatch; - } + final summary = chatUpdate.summary; if (summary != null) { final roomSummaryJson = rooms[roomIndex].summary.toJson() diff --git a/test/client_test.dart b/test/client_test.dart index 081b2a5d..f23844f4 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -761,21 +761,59 @@ void main() { expect(room.lastEvent!.content['body'], '* foxies'); }); - test('set prev_batch if in sync', () async { - final roomId = '!726s6s6q:example.com'; - final room = matrix.getRoomById(roomId)!; - - expect(room.prev_batch, 't44-23535_0_0'); - // put an important state event in-memory + test('set prev_batch when invite then join', () async { await matrix.handleSync( SyncUpdate.fromJson({ - 'next_batch': 'fakesync', + 'next_batch': '1', + 'rooms': { + 'invite': { + 'new_room_id': { + 'invite_state': { + 'events': [ + MatrixEvent( + eventId: '0', + type: EventTypes.RoomCreate, + content: {'body': '0'}, + senderId: '@alice:example.com', + stateKey: '@alice:example.com', + originServerTs: DateTime.now(), + ).toJson(), + ], + }, + }, + }, + }, + }), + ); + expect( + matrix.rooms.singleWhereOrNull( + (element) => element.id == 'new_room_id', + ), + isNotNull, + ); + + final newRoom = matrix.getRoomById('new_room_id')!; + + expect(newRoom.prev_batch, null); + + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': '3', 'rooms': { 'join': { - roomId: { + 'new_room_id': { 'timeline': { - 'events': [], - 'prev_batch': 'prev_batch_1', + 'events': [ + MatrixEvent( + eventId: '2', + type: EventTypes.RoomCreate, + content: {'body': '2'}, + senderId: '@alice:example.com', + stateKey: '@alice:example.com', + originServerTs: DateTime.now(), + ).toJson(), + ], + 'prev_batch': '1', }, }, }, @@ -783,7 +821,56 @@ void main() { }), ); - expect(room.prev_batch, 'prev_batch_1'); + final timeline = await newRoom.getTimeline(); + expect(newRoom.prev_batch, '1'); + expect(timeline.events.length, 1); + expect(timeline.events.first.eventId, '2'); + + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': '4', + 'rooms': { + 'join': { + 'new_room_id': { + 'timeline': { + 'events': [ + MatrixEvent( + eventId: '3', + type: EventTypes.Message, + content: {'body': '3'}, + senderId: '@alice2:example.com', + stateKey: '@alice2:example.com', + originServerTs: DateTime.now(), + ).toJson(), + ], + 'prev_batch': '2', + }, + }, + }, + }, + }), + ); + + expect(newRoom.prev_batch, '1'); + expect(timeline.events.length, 2); + expect(timeline.events.last.eventId, '2'); + expect(timeline.events.first.eventId, '3'); + + if (timeline.canRequestHistory) { + await timeline.requestHistory(); + } + expect(newRoom.prev_batch, 'emptyHistoryResponse'); + expect(timeline.events.length, 3); + expect(timeline.events.last.eventId, '0'); + expect(timeline.events.first.eventId, '3'); + + while (timeline.canRequestHistory) { + await timeline.requestHistory(); + } + expect(newRoom.prev_batch, null); + expect(timeline.events.length, 3); + expect(timeline.events.last.eventId, '0'); + expect(timeline.events.first.eventId, '3'); }); test('getProfileFromUserId', () async {