From e313426dd909598cba53c78bd065f3c1a8dee733 Mon Sep 17 00:00:00 2001 From: Krille Fear Date: Tue, 9 Nov 2021 09:22:13 +0100 Subject: [PATCH] refactor: Move setreadmarker functionality to timeline Apps had a hard time to just set the marker for the last event. The lastEvent in the Room may not be the actual last event because we ignore several event types there. Therefore it makes sense to refactor the setUnread method. Now the timeline class has an easy method to set the read marker to the last synced event, which can only be known by the timeline if we want to avoid another DB access. --- lib/src/room.dart | 19 ++++++++----------- lib/src/timeline.dart | 10 ++++++++++ test/fake_matrix_api.dart | 1 + test/room_test.dart | 8 ++++---- test/timeline_test.dart | 18 ++++++++++++++++++ 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/src/room.dart b/lib/src/room.dart index 538d591c..b69cf0b1 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -506,10 +506,14 @@ class Room { /// Returns true if this room is unread bool get isUnread => notificationCount > 0 || markedUnread; - /// Sets an unread flag manually for this room. Similar to the setUnreadMarker - /// this changes the local account data model before syncing it to make sure - /// this works if there is no connection to the homeserver. - Future setUnread(bool unread) async { + @Deprecated('Use [markUnread] instead') + Future setUnread(bool unread) => markUnread(unread); + + /// Sets an unread flag manually for this room. This changes the local account + /// data model before syncing it to make sure + /// this works if there is no connection to the homeserver. This does **not** + /// set a read marker! + Future markUnread(bool unread) async { final content = MarkedUnread(unread).toJson(); await _handleFakeSync(SyncUpdate(nextBatch: '') ..rooms = (RoomsUpdate() @@ -527,13 +531,6 @@ class Room { EventType.markedUnread, content, ); - final lastEvent = this.lastEvent; - if (!unread && lastEvent != null) { - await setReadMarker( - lastEvent.eventId, - mRead: lastEvent.eventId, - ); - } } /// Returns true if this room has a m.favourite tag. diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index b4cc82b7..0cfb0bdf 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -18,6 +18,8 @@ import 'dart:async'; +import 'package:collection/src/iterable_extensions.dart'; + import '../matrix.dart'; import 'event.dart'; import 'event_status.dart'; @@ -172,6 +174,14 @@ class Timeline { } } + /// Set the read marker to the last synced event in this timeline. + Future setReadMarker([String? eventId]) async { + eventId ??= + events.firstWhereOrNull((event) => event.status.isSynced)?.eventId; + if (eventId == null) return; + return room.setReadMarker(eventId, mRead: eventId); + } + int _findEvent({String? event_id, String? unsigned_txid}) { // we want to find any existing event where either the passed event_id or the passed unsigned_txid // matches either the event_id or transaction_id of the existing event. diff --git a/test/fake_matrix_api.dart b/test/fake_matrix_api.dart index bdb023e2..e8564e21 100644 --- a/test/fake_matrix_api.dart +++ b/test/fake_matrix_api.dart @@ -1779,6 +1779,7 @@ class FakeMatrixApi extends MockClient { '/client/r0/rooms/!localpart%3Aexample.com/receipt/m.read/%241234%3Aexample.com': (var req) => {}, '/client/r0/rooms/!localpart%3Aexample.com/read_markers': (var req) => {}, + '/client/r0/rooms/!1234%3Aexample.com/read_markers': (var req) => {}, '/client/r0/user/${Uri.encodeComponent('@othertest:fakeServer.notExisting')}/filter': (var req) => {'filter_id': '1234'}, '/client/r0/user/${Uri.encodeComponent('@test:fakeServer.notExisting')}/filter': diff --git a/test/room_test.dart b/test/room_test.dart index 494748eb..b68f08de 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -743,15 +743,15 @@ void main() { }); test('Test marked unread room', () async { - await room.setUnread(true); - await room.setUnread(false); - expect(room.isUnread, false); + await room.markUnread(true); + await room.markUnread(false); + expect(room.markedUnread, false); room.roomAccountData['com.famedly.marked_unread'] = BasicRoomEvent.fromJson({ 'content': {'unread': true}, 'type': 'com.famedly.marked_unread' }); - expect(room.isUnread, true); + expect(room.markedUnread, true); }); test('joinRules', () async { diff --git a/test/timeline_test.dart b/test/timeline_test.dart index 6e2be8b8..1deb85bf 100644 --- a/test/timeline_test.dart +++ b/test/timeline_test.dart @@ -372,6 +372,24 @@ void main() { expect(timeline.events[0].status, EventStatus.error); expect(timeline.events.length, 1); }); + test('setReadMarker', () async { + client.onEvent.add(EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': 'will-work', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + )); + await Future.delayed(Duration(milliseconds: 50)); + room.notificationCount = 1; + await timeline.setReadMarker(); + expect(room.notificationCount, 0); + }); test('sending an event and the http request finishes first, 0 -> 1 -> 2', () async { timeline.events.clear();