diff --git a/lib/encryption/utils/key_verification.dart b/lib/encryption/utils/key_verification.dart index c98c3d58..e011e101 100644 --- a/lib/encryption/utils/key_verification.dart +++ b/lib/encryption/utils/key_verification.dart @@ -1205,8 +1205,7 @@ const knownHashes = ['sha256']; const knownHashesAuthentificationCodes = ['hkdf-hmac-sha256']; class _KeyVerificationMethodSas extends _KeyVerificationMethod { - _KeyVerificationMethodSas({required KeyVerification request}) - : super(request: request); + _KeyVerificationMethodSas({required super.request}); @override // ignore: overridden_fields diff --git a/lib/src/client.dart b/lib/src/client.dart index 2b03896a..8910a6ed 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -261,7 +261,7 @@ class Client extends MatrixApi { bool enableDehydratedDevices = false; - /// Wether read receipts are sent as public receipts by default or just as private receipts. + /// Whether read receipts are sent as public receipts by default or just as private receipts. bool receiptsPublicByDefault = true; /// Whether this client supports end-to-end encryption using olm. @@ -940,60 +940,76 @@ class Client extends MatrixApi { final leave = syncResp.rooms?.leave; if (leave != null) { for (final entry in leave.entries) { - final id = entry.key; - final room = entry.value; - final leftRoom = Room( - id: id, - membership: Membership.leave, - client: this, - roomAccountData: - room.accountData?.asMap().map((k, v) => MapEntry(v.type, v)) ?? - {}, - ); - - final timeline = Timeline( - room: leftRoom, - chunk: TimelineChunk( - events: room.timeline?.events?.reversed - .toList() // we display the event in the other sence - .map((e) => Event.fromMatrixEvent(e, leftRoom)) - .toList() ?? - [])); - - leftRoom.prev_batch = room.timeline?.prevBatch; - room.state?.forEach((event) { - leftRoom.setState(Event.fromMatrixEvent( - event, - leftRoom, - )); - }); - - room.timeline?.events?.forEach((event) { - leftRoom.setState(Event.fromMatrixEvent( - event, - leftRoom, - )); - }); - - for (var i = 0; i < timeline.events.length; i++) { - // Try to decrypt encrypted events but don't update the database. - if (leftRoom.encrypted && leftRoom.client.encryptionEnabled) { - if (timeline.events[i].type == EventTypes.Encrypted) { - timeline.events[i] = - await leftRoom.client.encryption!.decryptRoomEvent( - leftRoom.id, - timeline.events[i], - ); - } - } - } - - _archivedRooms.add(ArchivedRoom(room: leftRoom, timeline: timeline)); + await _storeArchivedRoom(entry.key, entry.value); } } return _archivedRooms; } + /// [_storeArchivedRoom] + /// @leftRoom we can pass a room which was left so that we don't loose states + Future _storeArchivedRoom( + String id, + LeftRoomUpdate update, { + Room? leftRoom, + }) async { + final roomUpdate = update; + final archivedRoom = leftRoom ?? + Room( + id: id, + membership: Membership.leave, + client: this, + roomAccountData: roomUpdate.accountData + ?.asMap() + .map((k, v) => MapEntry(v.type, v)) ?? + {}, + ); + // Set membership of room to leave, in the case we got a left room passed, otherwise + // the left room would have still membership join, which would be wrong for the setState later + archivedRoom.membership = Membership.leave; + final timeline = Timeline( + room: archivedRoom, + chunk: TimelineChunk( + events: roomUpdate.timeline?.events?.reversed + .toList() // we display the event in the other sence + .map((e) => Event.fromMatrixEvent(e, archivedRoom)) + .toList() ?? + [])); + + archivedRoom.prev_batch = update.timeline?.prevBatch; + update.state?.forEach((event) { + archivedRoom.setState(Event.fromMatrixEvent( + event, + archivedRoom, + )); + }); + + update.timeline?.events?.forEach((event) { + archivedRoom.setState(Event.fromMatrixEvent( + event, + archivedRoom, + )); + }); + + for (var i = 0; i < timeline.events.length; i++) { + // Try to decrypt encrypted events but don't update the database. + if (archivedRoom.encrypted && archivedRoom.client.encryptionEnabled) { + if (timeline.events[i].type == EventTypes.Encrypted) { + await archivedRoom.client.encryption! + .decryptRoomEvent( + archivedRoom.id, + timeline.events[i], + ) + .then( + (decrypted) => timeline.events[i] = decrypted, + ); + } + } + } + + _archivedRooms.add(ArchivedRoom(room: archivedRoom, timeline: timeline)); + } + /// Uploads a file and automatically caches it in the database, if it is small enough /// and returns the mxc url. @override @@ -1892,7 +1908,7 @@ class Client extends MatrixApi { final syncRoomUpdate = entry.value; await database?.storeRoomUpdate(id, syncRoomUpdate, this); - final room = _updateRoomsByRoomUpdate(id, syncRoomUpdate); + final room = await _updateRoomsByRoomUpdate(id, syncRoomUpdate); final timelineUpdateType = direction != null ? (direction == Direction.b @@ -2132,7 +2148,8 @@ class Client extends MatrixApi { } } - Room _updateRoomsByRoomUpdate(String roomId, SyncRoomUpdate chatUpdate) { + Future _updateRoomsByRoomUpdate( + String roomId, SyncRoomUpdate chatUpdate) async { // Update the chat list item. // Search the room in the rooms final roomIndex = rooms.indexWhere((r) => r.id == roomId); @@ -2175,8 +2192,13 @@ class Client extends MatrixApi { room.stopStaleCallsChecker(room.id); rooms.removeAt(roomIndex); + + // in order to keep the archive in sync, add left room to archive + if (chatUpdate is LeftRoomUpdate) { + await _storeArchivedRoom(room.id, chatUpdate, leftRoom: room); + } } - // Update notification, highlight count and/or additional informations + // Update notification, highlight count and/or additional information else if (found && chatUpdate is JoinedRoomUpdate && (rooms[roomIndex].membership != membership || @@ -2206,7 +2228,7 @@ class Client extends MatrixApi { requestHistoryOnLimitedTimeline) { Logs().v( 'Limited timeline for ${rooms[roomIndex].id} request history now'); - runInRoot(rooms[roomIndex].requestHistory); + unawaited(runInRoot(rooms[roomIndex].requestHistory)); } } return room; diff --git a/lib/src/event.dart b/lib/src/event.dart index b000e3f9..f352143b 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -79,10 +79,10 @@ class Event extends MatrixEvent { Event({ this.status = defaultStatus, - required Map content, - required String type, + required Map super.content, + required super.type, required String eventId, - required String senderId, + required super.senderId, required DateTime originServerTs, Map? unsigned, Map? prevContent, @@ -91,10 +91,7 @@ class Event extends MatrixEvent { MatrixEvent? originalSource, }) : _originalSource = originalSource, super( - content: content, - type: type, eventId: eventId, - senderId: senderId, originServerTs: originServerTs, roomId: room.id, ) { diff --git a/lib/src/room.dart b/lib/src/room.dart index 861f41d2..d65d2690 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -1187,6 +1187,12 @@ class Room { Future forget() async { await client.database?.forgetRoom(id); await client.forgetRoom(id); + // Update archived rooms, otherwise an archived room may still be in the + // list after a forget room call + final roomIndex = client.archivedRooms.indexWhere((r) => r.room.id == id); + if (roomIndex != -1) { + client.archivedRooms.removeAt(roomIndex); + } return; } @@ -1375,7 +1381,7 @@ class Room { ); final events = [ - if (resp.eventsAfter != null) ...resp.eventsAfter!.reversed.toList(), + if (resp.eventsAfter != null) ...resp.eventsAfter!.reversed, if (resp.event != null) resp.event!, if (resp.eventsBefore != null) ...resp.eventsBefore! ].map((e) => Event.fromMatrixEvent(e, this)).toList(); diff --git a/lib/src/user.dart b/lib/src/user.dart index e32e3b46..5842cfe7 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -43,25 +43,17 @@ class User extends Event { } User.fromState({ - Map? prevContent, - required String stateKey, - Map content = const {}, + super.prevContent, + required String super.stateKey, + super.content = const {}, required String typeKey, - required String eventId, - required String senderId, - required DateTime originServerTs, - dynamic unsigned, - required Room room, + required super.eventId, + required super.senderId, + required super.originServerTs, + super.unsigned, + required super.room, }) : super( - stateKey: stateKey, - prevContent: prevContent, - content: content, type: typeKey, - eventId: eventId, - senderId: senderId, - originServerTs: originServerTs, - unsigned: unsigned, - room: room, ); /// The full qualified Matrix ID in the format @username:server.abc. diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 44456a26..7f9e1d5d 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -135,7 +135,8 @@ class SimpleSignableKey extends MatrixSignableKey { @override String? identifier; - SimpleSignableKey.fromJson(Map json) : super.fromJson(json); + SimpleSignableKey.fromJson(Map super.json) + : super.fromJson(); } abstract class SignableKey extends MatrixSignableKey { @@ -166,8 +167,8 @@ abstract class SignableKey extends MatrixSignableKey { bool get crossVerified => hasValidSignatureChain(); bool get signed => hasValidSignatureChain(verifiedOnly: false); - SignableKey.fromJson(Map json, this.client) - : super.fromJson(json) { + SignableKey.fromJson(Map super.json, this.client) + : super.fromJson() { _verified = false; _blocked = false; } diff --git a/lib/src/utils/http_timeout.dart b/lib/src/utils/http_timeout.dart index de0d745f..3646d8de 100644 --- a/lib/src/utils/http_timeout.dart +++ b/lib/src/utils/http_timeout.dart @@ -50,7 +50,7 @@ abstract class TimeoutHttpClient extends http.BaseClient { } class FixedTimeoutHttpClient extends TimeoutHttpClient { - FixedTimeoutHttpClient(http.Client inner, this.timeout) : super(inner); + FixedTimeoutHttpClient(super.inner, this.timeout); @override Duration timeout; } diff --git a/lib/src/utils/matrix_file.dart b/lib/src/utils/matrix_file.dart index ba102ea6..f9af3c99 100644 --- a/lib/src/utils/matrix_file.dart +++ b/lib/src/utils/matrix_file.dart @@ -17,6 +17,7 @@ */ /// Workaround until [File] in dart:io and dart:html is unified +library; import 'dart:async'; import 'dart:typed_data'; @@ -91,15 +92,14 @@ class MatrixFile { class MatrixImageFile extends MatrixFile { MatrixImageFile({ - required Uint8List bytes, - required String name, - String? mimeType, + required super.bytes, + required super.name, + super.mimeType, int? width, int? height, this.blurhash, }) : _width = width, - _height = height, - super(bytes: bytes, name: name, mimeType: mimeType); + _height = height; /// Creates a new image file and calculates the width, height and blurhash. static Future create({ @@ -355,13 +355,12 @@ class MatrixVideoFile extends MatrixFile { final int? duration; MatrixVideoFile( - {required Uint8List bytes, - required String name, - String? mimeType, + {required super.bytes, + required super.name, + super.mimeType, this.width, this.height, - this.duration}) - : super(bytes: bytes, name: name, mimeType: mimeType); + this.duration}); @override String get msgType => 'm.video'; @@ -379,11 +378,10 @@ class MatrixAudioFile extends MatrixFile { final int? duration; MatrixAudioFile( - {required Uint8List bytes, - required String name, - String? mimeType, - this.duration}) - : super(bytes: bytes, name: name, mimeType: mimeType); + {required super.bytes, + required super.name, + super.mimeType, + this.duration}); @override String get msgType => 'm.audio'; diff --git a/lib/src/utils/to_device_event.dart b/lib/src/utils/to_device_event.dart index 09a8c550..acc936d1 100644 --- a/lib/src/utils/to_device_event.dart +++ b/lib/src/utils/to_device_event.dart @@ -26,10 +26,10 @@ class ToDeviceEvent extends BasicEventWithSender { ToDeviceEvent({ required String sender, - required String type, - required Map content, + required super.type, + required Map super.content, this.encryptedContent, - }) : super(senderId: sender, type: type, content: content); + }) : super(senderId: sender); factory ToDeviceEvent.fromJson(Map json) { final event = BasicEventWithSender.fromJson(json); diff --git a/pubspec.yaml b/pubspec.yaml index f8d71f0f..c0510568 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,9 +33,9 @@ dependencies: dev_dependencies: coverage: ">=0.15.0 <2.0.0" - file: ^6.1.1 + file: ">=6.1.1 <8.0.0" import_sorter: ^4.6.0 - lints: ^2.0.0 + lints: ^3.0.0 test: ^1.15.7 #flutter_test: {sdk: flutter} #dependency_overrides: diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index 1217840d..4cb25c41 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -36,7 +36,7 @@ Uint8List secureRandomBytes(int len) { } class MockSSSS extends SSSS { - MockSSSS(Encryption encryption) : super(encryption); + MockSSSS(super.encryption); bool requestedSecrets = false; @override