From 9c32193dffb017ab63d05119cb917efa7d5e2332 Mon Sep 17 00:00:00 2001 From: Mohammad Reza Moradi Date: Thu, 7 Nov 2024 16:13:37 +0100 Subject: [PATCH] chore: add require trailing comma lint --- analysis_options.yaml | 1 + lib/encryption/cross_signing.dart | 46 +- lib/encryption/encryption.dart | 52 +- lib/encryption/key_manager.dart | 305 +- lib/encryption/key_verification_manager.dart | 10 +- lib/encryption/olm_manager.dart | 240 +- lib/encryption/ssss.dart | 116 +- lib/encryption/utils/bootstrap.dart | 41 +- lib/encryption/utils/key_verification.dart | 162 +- .../utils/outbound_group_session.dart | 14 +- lib/encryption/utils/session_key.dart | 24 +- lib/fake_matrix_api.dart | 682 ++-- lib/matrix_api_lite/generated/api.dart | 2257 ++++++++----- lib/matrix_api_lite/generated/model.dart | 351 +- lib/matrix_api_lite/matrix_api.dart | 18 +- .../model/auth/authentication_password.dart | 11 +- ...authentication_third_party_identifier.dart | 7 +- .../auth/authentication_three_pid_creds.dart | 18 +- .../model/basic_event_with_sender.dart | 7 +- lib/matrix_api_lite/model/children_state.dart | 3 +- .../events/forwarded_room_key_content.dart | 4 +- .../model/events/image_pack_content.dart | 82 +- .../model/events/room_encrypted_content.dart | 11 +- .../model/events/room_key_content.dart | 11 +- .../events/room_key_request_content.dart | 11 +- .../events/secret_storage_key_content.dart | 11 +- lib/matrix_api_lite/model/matrix_event.dart | 3 +- .../model/matrix_exception.dart | 6 +- .../model/presence_content.dart | 5 +- lib/matrix_api_lite/model/room_keys_keys.dart | 19 +- .../model/stripped_state_event.dart | 11 +- lib/matrix_api_lite/model/sync_update.dart | 36 +- .../utils/filter_map_extension.dart | 3 +- .../utils/try_get_map_extension.dart | 19 +- .../extension_recent_emoji/recent_emoji.dart | 5 +- .../msc_1236_widgets/src/widget.dart | 14 +- .../msc_2835_uia_login.dart | 30 +- .../msc_3814_dehydrated_devices/api.dart | 25 +- .../msc_3814_dehydrated_devices.dart | 21 +- .../msc_3935_cute_events.dart | 4 +- ...blished_custom_refresh_token_lifetime.dart | 12 +- lib/src/client.dart | 616 ++-- lib/src/database/database_api.dart | 16 +- .../database/hive_collections_database.dart | 431 ++- lib/src/database/hive_database.dart | 664 ++-- lib/src/database/indexeddb_box.dart | 24 +- lib/src/database/matrix_sdk_database.dart | 419 ++- lib/src/database/sqflite_box.dart | 3 +- .../sqflite_encryption_helper/io.dart | 9 +- lib/src/event.dart | 222 +- lib/src/event_status.dart | 2 +- lib/src/models/receipts.dart | 12 +- lib/src/models/timeline_chunk.dart | 7 +- lib/src/presence.dart | 34 +- lib/src/room.dart | 404 ++- lib/src/timeline.dart | 99 +- lib/src/user.dart | 34 +- lib/src/utils/commands_extension.dart | 21 +- lib/src/utils/compute_callback.dart | 17 +- lib/src/utils/crypto/ffi.dart | 117 +- lib/src/utils/crypto/js.dart | 27 +- lib/src/utils/crypto/native.dart | 19 +- lib/src/utils/crypto/subtle.dart | 54 +- lib/src/utils/device_keys_list.dart | 55 +- lib/src/utils/event_localizations.dart | 132 +- lib/src/utils/event_update.dart | 19 +- lib/src/utils/html_to_text.dart | 34 +- lib/src/utils/http_timeout.dart | 4 +- lib/src/utils/image_pack_extension.dart | 40 +- lib/src/utils/markdown.dart | 12 +- .../utils/matrix_default_localizations.dart | 8 +- lib/src/utils/matrix_file.dart | 77 +- lib/src/utils/matrix_id_string_extension.dart | 27 +- lib/src/utils/matrix_localizations.dart | 8 +- lib/src/utils/native_implementations.dart | 7 +- lib/src/utils/pushrule_evaluator.dart | 33 +- lib/src/utils/sync_update_item_count.dart | 30 +- lib/src/utils/to_device_event.dart | 5 +- lib/src/utils/uia_request.dart | 4 +- lib/src/utils/uri_extension.dart | 27 +- .../native_implementations_web_worker.dart | 13 +- ...ative_implementations_web_worker_stub.dart | 3 +- lib/src/utils/web_worker/web_worker.dart | 12 +- lib/src/voip/backend/call_backend_model.dart | 3 +- lib/src/voip/backend/livekit_backend.dart | 73 +- lib/src/voip/backend/mesh_backend.dart | 230 +- lib/src/voip/call_session.dart | 356 +- lib/src/voip/group_call_session.dart | 40 +- lib/src/voip/models/call_events.dart | 16 +- lib/src/voip/models/key_provider.dart | 17 +- lib/src/voip/models/webrtc_delegate.dart | 5 +- lib/src/voip/utils/conn_tester.dart | 15 +- .../voip/utils/famedly_call_extension.dart | 29 +- lib/src/voip/utils/stream_helper.dart | 4 +- .../voip/utils/user_media_constraints.dart | 2 +- lib/src/voip/utils/voip_constants.dart | 3 +- lib/src/voip/voip.dart | 158 +- test/calls_test.dart | 948 +++--- test/canonical_json_test.dart | 12 +- test/client_test.dart | 856 +++-- test/commands_test.dart | 290 +- test/database_api_test.dart | 93 +- test/device_keys_list_test.dart | 154 +- test/encryption/bootstrap_test.dart | 334 +- test/encryption/cross_signing_test.dart | 76 +- .../encrypt_decrypt_to_device_test.dart | 42 +- test/encryption/key_manager_test.dart | 533 +-- test/encryption/key_request_test.dart | 472 +-- test/encryption/key_verification_test.dart | 456 ++- test/encryption/olm_manager_test.dart | 144 +- test/encryption/online_key_backup_test.dart | 20 +- .../encryption/qr_verification_self_test.dart | 190 +- test/encryption/ssss_test.dart | 136 +- test/encryption/utils_test.dart | 30 +- test/event_test.dart | 2922 ++++++++++------- test/fake_client.dart | 12 +- test/fake_database.dart | 6 +- test/image_pack_test.dart | 300 +- test/markdown_test.dart | 180 +- .../try_get_map_extension_test.dart | 18 +- test/matrix_exception_test.dart | 6 +- test/matrix_file_test.dart | 7 +- test/matrix_localizations_test.dart | 73 +- .../msc_3814_dehydrated_devices_test.dart | 21 +- test/mxc_uri_extension_test.dart | 115 +- test/push_notification.dart | 6 +- test/pushevaluator_test.dart | 409 ++- test/room_archived_test.dart | 128 +- test/room_test.dart | 823 +++-- test/sync_filter_test.dart | 2 +- test/timeline_context_test.dart | 739 +++-- test/timeline_test.dart | 1203 ++++--- test/user_test.dart | 52 +- test/webrtc_stub.dart | 69 +- test_driver/matrixsdk_test.dart | 834 ++--- 135 files changed, 13207 insertions(+), 8964 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 9ea324932..55408e809 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -36,6 +36,7 @@ linter: prefer_single_quotes: true sort_child_properties_last: true sort_pub_dependencies: true + require_trailing_commas: true # Be nice to our users and allow them to configure what gets logged. avoid_print: true diff --git a/lib/encryption/cross_signing.dart b/lib/encryption/cross_signing.dart index 499b147be..70c89a6d4 100644 --- a/lib/encryption/cross_signing.dart +++ b/lib/encryption/cross_signing.dart @@ -72,11 +72,12 @@ class CrossSigning { null; } - Future selfSign( - {String? passphrase, - String? recoveryKey, - String? keyOrPassphrase, - OpenSSSS? openSsss}) async { + Future selfSign({ + String? passphrase, + String? recoveryKey, + String? keyOrPassphrase, + OpenSSSS? openSsss, + }) async { var handle = openSsss; if (handle == null) { handle = encryption.ssss.open(EventTypes.CrossSigningMasterKey); @@ -89,7 +90,8 @@ class CrossSigning { await handle.maybeCacheAll(); } final masterPrivateKey = base64decodeUnpadded( - await handle.getStored(EventTypes.CrossSigningMasterKey)); + await handle.getStored(EventTypes.CrossSigningMasterKey), + ); final keyObj = olm.PkSigning(); String? masterPubkey; try { @@ -117,11 +119,13 @@ class CrossSigning { ]); } - bool signable(List keys) => keys.any((key) => - key is CrossSigningKey && key.usage.contains('master') || - key is DeviceKeys && - key.userId == client.userID && - key.identifier != client.deviceID); + bool signable(List keys) => keys.any( + (key) => + key is CrossSigningKey && key.usage.contains('master') || + key is DeviceKeys && + key.userId == client.userID && + key.identifier != client.deviceID, + ); Future sign(List keys) async { final signedKeys = []; @@ -133,7 +137,10 @@ class CrossSigning { } void addSignature( - SignableKey key, SignableKey signedWith, String signature) { + SignableKey key, + SignableKey signedWith, + String signature, + ) { final signedKey = key.cloneForSigning(); ((signedKey.signatures ??= >{})[signedWith.userId] ??= @@ -154,9 +161,11 @@ class CrossSigning { // we don't care about signing other cross-signing keys } else { // okay, we'll sign a device key with our self signing key - selfSigningKey ??= base64decodeUnpadded(await encryption.ssss - .getCached(EventTypes.CrossSigningSelfSigning) ?? - ''); + selfSigningKey ??= base64decodeUnpadded( + await encryption.ssss + .getCached(EventTypes.CrossSigningSelfSigning) ?? + '', + ); if (selfSigningKey.isNotEmpty) { final signature = _sign(key.signingContent, selfSigningKey); addSignature(key, userKeys.selfSigningKey!, signature); @@ -164,9 +173,10 @@ class CrossSigning { } } else if (key is CrossSigningKey && key.usage.contains('master')) { // we are signing someone elses master key - userSigningKey ??= base64decodeUnpadded(await encryption.ssss - .getCached(EventTypes.CrossSigningUserSigning) ?? - ''); + userSigningKey ??= base64decodeUnpadded( + await encryption.ssss.getCached(EventTypes.CrossSigningUserSigning) ?? + '', + ); if (userSigningKey.isNotEmpty) { final signature = _sign(key.signingContent, userSigningKey); addSignature(key, userKeys.userSigningKey!, signature); diff --git a/lib/encryption/encryption.dart b/lib/encryption/encryption.dart index 81f799b9b..5a4ec7e31 100644 --- a/lib/encryption/encryption.dart +++ b/lib/encryption/encryption.dart @@ -105,9 +105,15 @@ class Encryption { ); void handleDeviceOneTimeKeysCount( - Map? countJson, List? unusedFallbackKeyTypes) { - runInRoot(() async => olmManager.handleDeviceOneTimeKeysCount( - countJson, unusedFallbackKeyTypes)); + Map? countJson, + List? unusedFallbackKeyTypes, + ) { + runInRoot( + () async => olmManager.handleDeviceOneTimeKeysCount( + countJson, + unusedFallbackKeyTypes, + ), + ); } void onSync() { @@ -173,9 +179,10 @@ class Encryption { return await olmManager.decryptToDeviceEvent(event); } catch (e, s) { Logs().w( - '[LibOlm] Could not decrypt to device event from ${event.sender} with content: ${event.content}', - e, - s); + '[LibOlm] Could not decrypt to device event from ${event.sender} with content: ${event.content}', + e, + s, + ); client.onEncryptionError.add( SdkError( exception: e is Exception ? e : Exception(e), @@ -241,7 +248,10 @@ class Encryption { client.database // ignore: discarded_futures ?.updateInboundGroupSessionIndexes( - json.encode(inboundGroupSession.indexes), roomId, sessionId) + json.encode(inboundGroupSession.indexes), + roomId, + sessionId, + ) // ignore: discarded_futures .onError((e, _) => Logs().e('Ignoring error for updating indexes')); } @@ -255,8 +265,10 @@ class Encryption { ?.session_id() ?? '') == content.sessionId) { - runInRoot(() async => - keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true)); + runInRoot( + () async => + keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true), + ); } if (canRequestSession) { decryptedPayload = { @@ -295,9 +307,12 @@ class Encryption { ); } - Future decryptRoomEvent(String roomId, Event event, - {bool store = false, - EventUpdateType updateType = EventUpdateType.timeline}) async { + Future decryptRoomEvent( + String roomId, + Event event, { + bool store = false, + EventUpdateType updateType = EventUpdateType.timeline, + }) async { if (event.type != EventTypes.Encrypted || event.redacted) { return event; } @@ -351,8 +366,10 @@ class Encryption { /// Encrypts the given json payload and creates a send-ready m.room.encrypted /// payload. This will create a new outgoingGroupSession if necessary. Future> encryptGroupMessagePayload( - String roomId, Map payload, - {String type = EventTypes.Message}) async { + String roomId, + Map payload, { + String type = EventTypes.Message, + }) async { payload = copyMap(payload); final Map? mRelatesTo = payload.remove('m.relates_to'); @@ -403,9 +420,10 @@ class Encryption { } Future>>> encryptToDeviceMessage( - List deviceKeys, - String type, - Map payload) async { + List deviceKeys, + String type, + Map payload, + ) async { return await olmManager.encryptToDeviceMessage(deviceKeys, type, payload); } diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index 5386b7636..fa9371c64 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -252,19 +252,23 @@ class KeyManager { // do e2ee recovery _requestedSessionIds.add(requestIdent); - runInRoot(() async => request( - room, - sessionId, - senderKey, - tryOnlineBackup: tryOnlineBackup, - onlineKeyBackupOnly: onlineKeyBackupOnly, - )); + runInRoot( + () async => request( + room, + sessionId, + senderKey, + tryOnlineBackup: tryOnlineBackup, + onlineKeyBackupOnly: onlineKeyBackupOnly, + ), + ); } } /// Loads an inbound group session Future loadInboundGroupSession( - String roomId, String sessionId) async { + String roomId, + String sessionId, + ) async { final sess = _inboundGroupSessions[roomId]?[sessionId]; if (sess != null) { if (sess.sessionId != sessionId && sess.sessionId.isNotEmpty) { @@ -290,7 +294,8 @@ class KeyManager { } Map> _getDeviceKeyIdMap( - List deviceKeys) { + List deviceKeys, + ) { final deviceKeyIds = >{}; for (final device in deviceKeys) { final deviceId = device.deviceId; @@ -312,8 +317,11 @@ class KeyManager { /// Clears the existing outboundGroupSession but first checks if the participating /// devices have been changed. Returns false if the session has not been cleared because /// it wasn't necessary. Otherwise returns true. - Future clearOrUseOutboundGroupSession(String roomId, - {bool wipe = false, bool use = true}) async { + Future clearOrUseOutboundGroupSession( + String roomId, { + bool wipe = false, + bool use = true, + }) async { final room = client.getRoomById(roomId); final sess = getOutboundGroupSession(roomId); if (room == null || sess == null || sess.outboundGroupSession == null) { @@ -336,7 +344,9 @@ class KeyManager { } final inboundSess = await loadInboundGroupSession( - room.id, sess.outboundGroupSession!.session_id()); + room.id, + sess.outboundGroupSession!.session_id(), + ); if (inboundSess == null) { wipe = true; } @@ -365,15 +375,19 @@ class KeyManager { // we also know that all the old user IDs appear in the old one, else we have already wiped the session for (final userId in oldUserIds) { final oldBlockedDevices = sess.devices.containsKey(userId) - ? Set.from(sess.devices[userId]!.entries - .where((e) => e.value) - .map((e) => e.key)) + ? Set.from( + sess.devices[userId]!.entries + .where((e) => e.value) + .map((e) => e.key), + ) : {}; final newBlockedDevices = newDeviceKeyIds.containsKey(userId) - ? Set.from(newDeviceKeyIds[userId]! - .entries - .where((e) => e.value) - .map((e) => e.key)) + ? Set.from( + newDeviceKeyIds[userId]! + .entries + .where((e) => e.value) + .map((e) => e.key), + ) : {}; // we don't really care about old devices that got dropped (deleted), we only care if new ones got added and if new ones got blocked // check if new devices got blocked @@ -383,15 +397,19 @@ class KeyManager { } // and now add all the new devices! final oldDeviceIds = sess.devices.containsKey(userId) - ? Set.from(sess.devices[userId]!.entries - .where((e) => !e.value) - .map((e) => e.key)) + ? Set.from( + sess.devices[userId]!.entries + .where((e) => !e.value) + .map((e) => e.key), + ) : {}; final newDeviceIds = newDeviceKeyIds.containsKey(userId) - ? Set.from(newDeviceKeyIds[userId]! - .entries - .where((e) => !e.value) - .map((e) => e.key)) + ? Set.from( + newDeviceKeyIds[userId]! + .entries + .where((e) => !e.value) + .map((e) => e.key), + ) : {}; // check if a device got removed @@ -403,8 +421,11 @@ class KeyManager { // check if any new devices need keys final newDevices = newDeviceIds.difference(oldDeviceIds); if (newDeviceIds.isNotEmpty) { - devicesToReceive.addAll(newDeviceKeys.where( - (d) => d.userId == userId && newDevices.contains(d.deviceId))); + devicesToReceive.addAll( + newDeviceKeys.where( + (d) => d.userId == userId && newDevices.contains(d.deviceId), + ), + ); } } } @@ -438,18 +459,23 @@ class KeyManager { } } await client.database?.updateInboundGroupSessionAllowedAtIndex( - json.encode(inboundSess!.allowedAtIndex), - room.id, - sess.outboundGroupSession!.session_id()); + json.encode(inboundSess!.allowedAtIndex), + room.id, + sess.outboundGroupSession!.session_id(), + ); // send out the key await client.sendToDeviceEncryptedChunked( - devicesToReceive, EventTypes.RoomKey, rawSession); + devicesToReceive, + EventTypes.RoomKey, + rawSession, + ); } } catch (e, s) { Logs().e( - '[LibOlm] Unable to re-send the session key at later index to new devices', - e, - s); + '[LibOlm] Unable to re-send the session key at later index to new devices', + e, + s, + ); } return false; } @@ -462,14 +488,17 @@ class KeyManager { /// Store an outbound group session in the database Future storeOutboundGroupSession( - String roomId, OutboundGroupSession sess) async { + String roomId, + OutboundGroupSession sess, + ) async { final userID = client.userID; if (userID == null) return; await client.database?.storeOutboundGroupSession( - roomId, - sess.outboundGroupSession!.pickle(userID), - json.encode(sess.devices), - sess.creationTime.millisecondsSinceEpoch); + roomId, + sess.outboundGroupSession!.pickle(userID), + json.encode(sess.devices), + sess.creationTime.millisecondsSinceEpoch, + ); } final Map> @@ -507,18 +536,21 @@ class KeyManager { } Future _createOutboundGroupSession( - String roomId) async { + String roomId, + ) async { await clearOrUseOutboundGroupSession(roomId, wipe: true); await client.firstSyncReceived; final room = client.getRoomById(roomId); if (room == null) { throw Exception( - 'Tried to create a megolm session in a non-existing room ($roomId)!'); + 'Tried to create a megolm session in a non-existing room ($roomId)!', + ); } final userID = client.userID; if (userID == null) { throw Exception( - 'Tried to create a megolm session without being logged in!'); + 'Tried to create a megolm session without being logged in!', + ); } final deviceKeys = await room.getUserDeviceKeys(); @@ -549,8 +581,12 @@ class KeyManager { outboundGroupSession.message_index(); } await setInboundGroupSession( - roomId, rawSession['session_id'], encryption.identityKey!, rawSession, - allowedAtIndex: allowedAtIndex); + roomId, + rawSession['session_id'], + encryption.identityKey!, + rawSession, + allowedAtIndex: allowedAtIndex, + ); final sess = OutboundGroupSession( devices: deviceKeyIds, creationTime: DateTime.now(), @@ -559,14 +595,18 @@ class KeyManager { ); try { await client.sendToDeviceEncryptedChunked( - deviceKeys, EventTypes.RoomKey, rawSession); + deviceKeys, + EventTypes.RoomKey, + rawSession, + ); await storeOutboundGroupSession(roomId, sess); _outboundGroupSessions[roomId] = sess; } catch (e, s) { Logs().e( - '[LibOlm] Unable to send the session key to the participating devices', - e, - s); + '[LibOlm] Unable to send the session key to the participating devices', + e, + s, + ); sess.dispose(); rethrow; } @@ -611,8 +651,9 @@ class KeyManager { GetRoomKeysVersionCurrentResponse? _roomKeysVersionCache; DateTime? _roomKeysVersionCacheDate; - Future getRoomKeysBackupInfo( - [bool useCache = true]) async { + Future getRoomKeysBackupInfo([ + bool useCache = true, + ]) async { if (_roomKeysVersionCache != null && _roomKeysVersionCacheDate != null && useCache && @@ -650,10 +691,13 @@ class KeyManager { final sessionData = session.sessionData; Map? decrypted; try { - decrypted = json.decode(decryption.decrypt( + decrypted = json.decode( + decryption.decrypt( sessionData['ephemeral'] as String, sessionData['mac'] as String, - sessionData['ciphertext'] as String)); + sessionData['ciphertext'] as String, + ), + ); } catch (e, s) { Logs().e('[LibOlm] Error decrypting room key', e, s); } @@ -662,12 +706,16 @@ class KeyManager { decrypted['session_id'] = sessionId; decrypted['room_id'] = roomId; await setInboundGroupSession( - roomId, sessionId, senderKey, decrypted, - forwarded: true, - senderClaimedKeys: decrypted - .tryGetMap('sender_claimed_keys') ?? - {}, - uploaded: true); + roomId, + sessionId, + senderKey, + decrypted, + forwarded: true, + senderClaimedKeys: + decrypted.tryGetMap('sender_claimed_keys') ?? + {}, + uploaded: true, + ); } } } @@ -733,10 +781,14 @@ class KeyManager { } catch (err, stacktrace) { if (err is MatrixException && err.errcode == 'M_NOT_FOUND') { Logs().i( - '[KeyManager] Key not in online key backup, requesting it from other devices...'); + '[KeyManager] Key not in online key backup, requesting it from other devices...', + ); } else { - Logs().e('[KeyManager] Failed to access online key backup', err, - stacktrace); + Logs().e( + '[KeyManager] Failed to access online key backup', + err, + stacktrace, + ); } } // TODO: also don't request from others if we have an index of 0 now @@ -785,15 +837,17 @@ class KeyManager { void startAutoUploadKeys() { _uploadKeysOnSync = encryption.client.onSync.stream.listen( - (_) async => uploadInboundGroupSessions(skipIfInProgress: true)); + (_) async => uploadInboundGroupSessions(skipIfInProgress: true), + ); } /// This task should be performed after sync processing but should not block /// the sync. To make sure that it never gets executed multiple times, it is /// skipped when an upload task is already in progress. Set `skipIfInProgress` /// to `false` to await the pending upload task instead. - Future uploadInboundGroupSessions( - {bool skipIfInProgress = false}) async { + Future uploadInboundGroupSessions({ + bool skipIfInProgress = false, + }) async { final database = client.database; final userID = client.userID; if (database == null || userID == null) { @@ -849,10 +903,12 @@ class KeyManager { for (final dbSession in dbSessions) { final device = client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey); - args.dbSessions.add(DbInboundGroupSessionBundle( - dbSession: dbSession, - verified: device?.verified ?? false, - )); + args.dbSessions.add( + DbInboundGroupSessionBundle( + dbSession: dbSession, + verified: device?.verified ?? false, + ), + ); i++; if (i > 10) { await Future.delayed(Duration(milliseconds: 1)); @@ -868,7 +924,9 @@ class KeyManager { // no need to optimze this, as we only run it so seldomly and almost never with many keys at once for (final dbSession in dbSessions) { await database.markInboundGroupSessionAsUploaded( - dbSession.roomId, dbSession.sessionId); + dbSession.roomId, + dbSession.sessionId, + ); } } finally { decryption.free(); @@ -895,7 +953,8 @@ class KeyManager { if (event.content['action'] == 'request') { // we are *receiving* a request Logs().i( - '[KeyManager] Received key sharing request from ${event.sender}:${event.content['requesting_device_id']}...'); + '[KeyManager] Received key sharing request from ${event.sender}:${event.content['requesting_device_id']}...', + ); if (!event.content.containsKey('body')) { Logs().w('[KeyManager] No body, doing nothing'); return; // no body @@ -908,7 +967,8 @@ class KeyManager { final roomId = body.tryGet('room_id'); if (roomId == null) { Logs().w( - '[KeyManager] Wrong type for room_id or no room_id, doing nothing'); + '[KeyManager] Wrong type for room_id or no room_id, doing nothing', + ); return; // wrong type for roomId or no roomId found } final device = client.userDeviceKeys[event.sender] @@ -930,7 +990,8 @@ class KeyManager { final sessionId = body.tryGet('session_id'); if (sessionId == null) { Logs().w( - '[KeyManager] Wrong type for session_id or no session_id, doing nothing'); + '[KeyManager] Wrong type for session_id or no session_id, doing nothing', + ); return; // wrong type for session_id } // okay, let's see if we have this session at all @@ -941,7 +1002,8 @@ class KeyManager { } if (event.content['request_id'] is! String) { Logs().w( - '[KeyManager] Wrong type for request_id or no request_id, doing nothing'); + '[KeyManager] Wrong type for request_id or no request_id, doing nothing', + ); return; // wrong type for request_id } final request = KeyManagerKeyShareRequest( @@ -974,7 +1036,8 @@ class KeyManager { final index = session.allowedAtIndex[device.userId]![device.curve25519Key]!; Logs().i( - '[KeyManager] Valid foreign request, forwarding key at index $index...'); + '[KeyManager] Valid foreign request, forwarding key at index $index...', + ); await roomKeyRequest.forwardKey(index); } else { Logs() @@ -1002,15 +1065,19 @@ class KeyManager { ); return; } - final request = outgoingShareRequests.values.firstWhereOrNull((r) => - r.room.id == event.content['room_id'] && - r.sessionId == event.content['session_id']); + final request = outgoingShareRequests.values.firstWhereOrNull( + (r) => + r.room.id == event.content['room_id'] && + r.sessionId == event.content['session_id'], + ); if (request == null || request.canceled) { return; // no associated request found or it got canceled } - final device = request.devices.firstWhereOrNull((d) => - d.userId == event.sender && - d.curve25519Key == encryptedContent['sender_key']); + final device = request.devices.firstWhereOrNull( + (d) => + d.userId == event.sender && + d.curve25519Key == encryptedContent['sender_key'], + ); if (device == null) { return; // someone we didn't send our request to replied....better ignore this } @@ -1026,14 +1093,19 @@ class KeyManager { } // TODO: verify that the keys work to decrypt a message // alright, all checks out, let's go ahead and store this session - await setInboundGroupSession(request.room.id, request.sessionId, - device.curve25519Key!, event.content, - forwarded: true, - senderClaimedKeys: { - 'ed25519': event.content['sender_claimed_ed25519_key'] as String, - }); + await setInboundGroupSession( + request.room.id, + request.sessionId, + device.curve25519Key!, + event.content, + forwarded: true, + senderClaimedKeys: { + 'ed25519': event.content['sender_claimed_ed25519_key'] as String, + }, + ); request.devices.removeWhere( - (k) => k.userId == device.userId && k.deviceId == device.deviceId); + (k) => k.userId == device.userId && k.deviceId == device.deviceId, + ); outgoingShareRequests.remove(request.requestId); // send cancel to all other devices if (request.devices.isEmpty) { @@ -1057,7 +1129,8 @@ class KeyManager { ); } else if (event.type == EventTypes.RoomKey) { Logs().v( - '[KeyManager] Received room key with session ${event.content['session_id']}'); + '[KeyManager] Received room key with session ${event.content['session_id']}', + ); final encryptedContent = event.encryptedContent; if (encryptedContent == null) { Logs().v('[KeyManager] not encrypted, ignoring...'); @@ -1067,7 +1140,8 @@ class KeyManager { final sessionId = event.content.tryGet('session_id'); if (roomId == null || sessionId == null) { Logs().w( - 'Either room_id or session_id are not the expected type or missing'); + 'Either room_id or session_id are not the expected type or missing', + ); return; } final sender_ed25519 = client.userDeviceKeys[event.sender] @@ -1077,8 +1151,12 @@ class KeyManager { } Logs().v('[KeyManager] Keeping room key'); await setInboundGroupSession( - roomId, sessionId, encryptedContent['sender_key'], event.content, - forwarded: false); + roomId, + sessionId, + encryptedContent['sender_key'], + event.content, + forwarded: false, + ); } } @@ -1105,13 +1183,13 @@ class KeyManagerKeyShareRequest { final String sessionId; bool canceled; - KeyManagerKeyShareRequest( - {required this.requestId, - List? devices, - required this.room, - required this.sessionId, - this.canceled = false}) - : devices = devices ?? []; + KeyManagerKeyShareRequest({ + required this.requestId, + List? devices, + required this.room, + required this.sessionId, + this.canceled = false, + }) : devices = devices ?? []; } class RoomKeyRequest extends ToDeviceEvent { @@ -1119,11 +1197,14 @@ class RoomKeyRequest extends ToDeviceEvent { KeyManagerKeyShareRequest request; RoomKeyRequest.fromToDeviceEvent( - ToDeviceEvent toDeviceEvent, this.keyManager, this.request) - : super( - sender: toDeviceEvent.sender, - content: toDeviceEvent.content, - type: toDeviceEvent.type); + ToDeviceEvent toDeviceEvent, + this.keyManager, + this.request, + ) : super( + sender: toDeviceEvent.sender, + content: toDeviceEvent.content, + type: toDeviceEvent.type, + ); Room get room => request.room; @@ -1155,7 +1236,8 @@ class RoomKeyRequest extends ToDeviceEvent { ? keyManager.encryption.fingerprintKey : null); message['session_key'] = session.inboundGroupSession!.export_session( - index ?? session.inboundGroupSession!.first_known_index()); + index ?? session.inboundGroupSession!.first_known_index(), + ); // send the actual reply of the key back to the requester await keyManager.client.sendToDeviceEncrypted( [requestingDevice], @@ -1217,8 +1299,10 @@ RoomKeys generateUploadKeysImplementation(GenerateUploadKeysArgs args) { } class DbInboundGroupSessionBundle { - DbInboundGroupSessionBundle( - {required this.dbSession, required this.verified}); + DbInboundGroupSessionBundle({ + required this.dbSession, + required this.verified, + }); factory DbInboundGroupSessionBundle.fromJson(Map json) => DbInboundGroupSessionBundle( @@ -1236,8 +1320,11 @@ class DbInboundGroupSessionBundle { } class GenerateUploadKeysArgs { - GenerateUploadKeysArgs( - {required this.pubkey, required this.dbSessions, required this.userId}); + GenerateUploadKeysArgs({ + required this.pubkey, + required this.dbSessions, + required this.userId, + }); factory GenerateUploadKeysArgs.fromJson(Map json) => GenerateUploadKeysArgs( diff --git a/lib/encryption/key_verification_manager.dart b/lib/encryption/key_verification_manager.dart index f636dd77d..517da6c3a 100644 --- a/lib/encryption/key_verification_manager.dart +++ b/lib/encryption/key_verification_manager.dart @@ -126,9 +126,15 @@ class KeyVerificationManager { final room = client.getRoomById(update.roomID) ?? Room(id: update.roomID, client: client); final newKeyRequest = KeyVerification( - encryption: encryption, userId: event['sender'], room: room); + encryption: encryption, + userId: event['sender'], + room: room, + ); await newKeyRequest.handlePayload( - type, event['content'], event['event_id']); + type, + event['content'], + event['event_id'], + ); if (newKeyRequest.state != KeyVerificationState.askAccept) { // something went wrong, let's just dispose the request newKeyRequest.dispose(); diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index 37bce3320..bc9e2f09b 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -193,7 +193,7 @@ class OlmManager { 'device_id': ourDeviceId, 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': {}, }; @@ -247,7 +247,8 @@ class OlmManager { if (dehydratedDeviceAlgorithm == null || dehydratedDevicePickleKey == null) { throw Exception( - 'You need to provide both the pickle key and the algorithm to use dehydrated devices!'); + 'You need to provide both the pickle key and the algorithm to use dehydrated devices!', + ); } await client.uploadDehydratedDevice( @@ -265,13 +266,14 @@ class OlmManager { ); return true; } - final currentUpload = - this.currentUpload = CancelableOperation.fromFuture(client.uploadKeys( - deviceKeys: - uploadDeviceKeys ? MatrixDeviceKeys.fromJson(deviceKeys) : null, - oneTimeKeys: signedOneTimeKeys, - fallbackKeys: signedFallbackKeys, - )); + final currentUpload = this.currentUpload = CancelableOperation.fromFuture( + client.uploadKeys( + deviceKeys: + uploadDeviceKeys ? MatrixDeviceKeys.fromJson(deviceKeys) : null, + oneTimeKeys: signedOneTimeKeys, + fallbackKeys: signedFallbackKeys, + ), + ); final response = await currentUpload.valueOrCancellation(); if (response == null) { _uploadKeysLock = false; @@ -314,11 +316,12 @@ class OlmManager { } await uploadKeys( - uploadDeviceKeys: uploadDeviceKeys, - oldKeyCount: oldKeyCount, - updateDatabase: updateDatabase, - unusedFallbackKey: unusedFallbackKey, - retry: retry - 1); + uploadDeviceKeys: uploadDeviceKeys, + oldKeyCount: oldKeyCount, + updateDatabase: updateDatabase, + unusedFallbackKey: unusedFallbackKey, + retry: retry - 1, + ); } } finally { _uploadKeysLock = false; @@ -330,47 +333,50 @@ class OlmManager { final _otkUpdateDedup = AsyncCache.ephemeral(); Future handleDeviceOneTimeKeysCount( - Map? countJson, List? unusedFallbackKeyTypes) async { + Map? countJson, + List? unusedFallbackKeyTypes, + ) async { if (!enabled) { return; } - await _otkUpdateDedup.fetch(() => - runBenchmarked('handleOtkUpdate', () async { - final haveFallbackKeys = encryption.isMinOlmVersion(3, 2, 0); - // Check if there are at least half of max_number_of_one_time_keys left on the server - // and generate and upload more if not. + await _otkUpdateDedup.fetch( + () => runBenchmarked('handleOtkUpdate', () async { + final haveFallbackKeys = encryption.isMinOlmVersion(3, 2, 0); + // Check if there are at least half of max_number_of_one_time_keys left on the server + // and generate and upload more if not. - // If the server did not send us a count, assume it is 0 - final keyCount = countJson?.tryGet('signed_curve25519') ?? 0; + // If the server did not send us a count, assume it is 0 + final keyCount = countJson?.tryGet('signed_curve25519') ?? 0; - // If the server does not support fallback keys, it will not tell us about them. - // If the server supports them but has no key, upload a new one. - var unusedFallbackKey = true; - if (unusedFallbackKeyTypes?.contains('signed_curve25519') == false) { - unusedFallbackKey = false; - } + // If the server does not support fallback keys, it will not tell us about them. + // If the server supports them but has no key, upload a new one. + var unusedFallbackKey = true; + if (unusedFallbackKeyTypes?.contains('signed_curve25519') == false) { + unusedFallbackKey = false; + } - // fixup accidental too many uploads. We delete only one of them so that the server has time to update the counts and because we will get rate limited anyway. - if (keyCount > _olmAccount!.max_number_of_one_time_keys()) { - final requestingKeysFrom = { - client.userID!: {ourDeviceId!: 'signed_curve25519'} - }; - await client.claimKeys(requestingKeysFrom, timeout: 10000); - } + // fixup accidental too many uploads. We delete only one of them so that the server has time to update the counts and because we will get rate limited anyway. + if (keyCount > _olmAccount!.max_number_of_one_time_keys()) { + final requestingKeysFrom = { + client.userID!: {ourDeviceId!: 'signed_curve25519'}, + }; + await client.claimKeys(requestingKeysFrom, timeout: 10000); + } - // Only upload keys if they are less than half of the max or we have no unused fallback key - if (keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) || - !unusedFallbackKey) { - await uploadKeys( - oldKeyCount: - keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) - ? keyCount - : null, - unusedFallbackKey: haveFallbackKeys ? unusedFallbackKey : null, - ); - } - })); + // Only upload keys if they are less than half of the max or we have no unused fallback key + if (keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) || + !unusedFallbackKey) { + await uploadKeys( + oldKeyCount: + keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) + ? keyCount + : null, + unusedFallbackKey: haveFallbackKeys ? unusedFallbackKey : null, + ); + } + }), + ); } Future storeOlmSession(OlmSession session) async { @@ -389,11 +395,12 @@ class OlmManager { _olmSessions[session.identityKey]![ix] = session; } await encryption.olmDatabase?.storeOlmSession( - session.identityKey, - session.sessionId!, - session.pickledSession!, - session.lastReceived?.millisecondsSinceEpoch ?? - DateTime.now().millisecondsSinceEpoch); + session.identityKey, + session.sessionId!, + session.pickledSession!, + session.lastReceived?.millisecondsSinceEpoch ?? + DateTime.now().millisecondsSinceEpoch, + ); } Future _decryptToDeviceEvent(ToDeviceEvent event) async { @@ -427,9 +434,10 @@ class OlmManager { if (device != null) { device.lastActive = DateTime.now(); await encryption.olmDatabase?.setLastActiveUserDeviceKey( - device.lastActive.millisecondsSinceEpoch, - device.userId, - device.deviceId!); + device.lastActive.millisecondsSinceEpoch, + device.userId, + device.deviceId!, + ); } } catch (e, s) { Logs().e('Error while updating olm session timestamp', e, s); @@ -447,7 +455,9 @@ class OlmManager { } catch (e) { // The message was encrypted during this session, but is unable to decrypt throw DecryptException( - DecryptException.decryptionFailed, e.toString()); + DecryptException.decryptionFailed, + e.toString(), + ); } await updateSessionUsage(session); break; @@ -475,13 +485,15 @@ class OlmManager { plaintext = newSession.decrypt(type, body); - await storeOlmSession(OlmSession( - key: client.userID!, - identityKey: senderKey, - sessionId: newSession.session_id(), - session: newSession, - lastReceived: DateTime.now(), - )); + await storeOlmSession( + OlmSession( + key: client.userID!, + identityKey: senderKey, + sessionId: newSession.session_id(), + session: newSession, + lastReceived: DateTime.now(), + ), + ); await updateSessionUsage(); } catch (e) { newSession.free(); @@ -515,7 +527,8 @@ class OlmManager { } Future getOlmSessionsForDevicesFromDatabase( - List senderKeys) async { + List senderKeys, + ) async { final rows = await encryption.olmDatabase?.getOlmSessionsForDevices( senderKeys, client.userID!, @@ -532,8 +545,10 @@ class OlmManager { } } - Future> getOlmSessions(String senderKey, - {bool getFromDb = true}) async { + Future> getOlmSessions( + String senderKey, { + bool getFromDb = true, + }) async { var sess = olmSessions[senderKey]; if ((getFromDb) && (sess == null || sess.isEmpty)) { final sessions = await getOlmSessionsFromDatabase(senderKey); @@ -545,10 +560,12 @@ class OlmManager { if (sess == null) { return []; } - sess.sort((a, b) => a.lastReceived == b.lastReceived - ? (a.sessionId ?? '').compareTo(b.sessionId ?? '') - : (b.lastReceived ?? DateTime(0)) - .compareTo(a.lastReceived ?? DateTime(0))); + sess.sort( + (a, b) => a.lastReceived == b.lastReceived + ? (a.sessionId ?? '').compareTo(b.sessionId ?? '') + : (b.lastReceived ?? DateTime(0)) + .compareTo(a.lastReceived ?? DateTime(0)), + ); return sess; } @@ -570,7 +587,8 @@ class OlmManager { .subtract(Duration(hours: 1)) .isBefore(_restoredOlmSessionsTime[mapKey]!)) { Logs().w( - '[OlmManager] Skipping restore session, one was restored in the past hour'); + '[OlmManager] Skipping restore session, one was restored in the past hour', + ); return; } _restoredOlmSessionsTime[mapKey] = DateTime.now(); @@ -608,7 +626,8 @@ class OlmManager { Future startOutgoingOlmSessions(List deviceKeys) async { Logs().v( - '[OlmManager] Starting session with ${deviceKeys.length} devices...'); + '[OlmManager] Starting session with ${deviceKeys.length} devices...', + ); final requestingKeysFrom = >{}; for (final device in deviceKeys) { if (requestingKeysFrom[device.userId] == null) { @@ -644,15 +663,20 @@ class OlmManager { final session = olm.Session(); try { session.create_outbound( - _olmAccount!, identityKey, deviceKey.tryGet('key')!); - await storeOlmSession(OlmSession( - key: client.userID!, - identityKey: identityKey, - sessionId: session.session_id(), - session: session, - lastReceived: - DateTime.now(), // we want to use a newly created session - )); + _olmAccount!, + identityKey, + deviceKey.tryGet('key')!, + ); + await storeOlmSession( + OlmSession( + key: client.userID!, + identityKey: identityKey, + sessionId: session.session_id(), + session: session, + lastReceived: + DateTime.now(), // we want to use a newly created session + ), + ); } catch (e, s) { session.free(); Logs() @@ -668,8 +692,11 @@ class OlmManager { /// Throws `NoOlmSessionFoundException` if there is no olm session with this /// device and none could be created. Future> encryptToDeviceMessagePayload( - DeviceKeys device, String type, Map payload, - {bool getFromDb = true}) async { + DeviceKeys device, + String type, + Map payload, { + bool getFromDb = true, + }) async { final sess = await getOlmSessions(device.curve25519Key!, getFromDb: getFromDb); if (sess.isEmpty) { @@ -688,12 +715,13 @@ class OlmManager { if (encryption.olmDatabase != null) { try { await encryption.olmDatabase?.setLastSentMessageUserDeviceKey( - json.encode({ - 'type': type, - 'content': payload, - }), - device.userId, - device.deviceId!); + json.encode({ + 'type': type, + 'content': payload, + }), + device.userId, + device.deviceId!, + ); } catch (e, s) { // we can ignore this error, since it would just make us use a different olm session possibly Logs().w('Error while updating olm usage timestamp', e, s); @@ -712,18 +740,22 @@ class OlmManager { } Future>>> encryptToDeviceMessage( - List deviceKeys, - String type, - Map payload) async { + List deviceKeys, + String type, + Map payload, + ) async { final data = >>{}; // first check if any of our sessions we want to encrypt for are in the database if (encryption.olmDatabase != null) { await getOlmSessionsForDevicesFromDatabase( - deviceKeys.map((d) => d.curve25519Key!).toList()); + deviceKeys.map((d) => d.curve25519Key!).toList(), + ); } final deviceKeysWithoutSession = List.from(deviceKeys); - deviceKeysWithoutSession.removeWhere((DeviceKeys deviceKeys) => - olmSessions[deviceKeys.curve25519Key]?.isNotEmpty ?? false); + deviceKeysWithoutSession.removeWhere( + (DeviceKeys deviceKeys) => + olmSessions[deviceKeys.curve25519Key]?.isNotEmpty ?? false, + ); if (deviceKeysWithoutSession.isNotEmpty) { await startOutgoingOlmSessions(deviceKeysWithoutSession); } @@ -731,8 +763,11 @@ class OlmManager { final userData = data[device.userId] ??= {}; try { userData[device.deviceId!] = await encryptToDeviceMessagePayload( - device, type, payload, - getFromDb: false); + device, + type, + payload, + getFromDb: false, + ); } on NoOlmSessionFoundException catch (e) { Logs().d('[LibOlm] Error encrypting to-device event', e); continue; @@ -753,12 +788,14 @@ class OlmManager { return; } final device = client.getUserDeviceKeysByCurve25519Key( - encryptedContent.tryGet('sender_key') ?? ''); + encryptedContent.tryGet('sender_key') ?? '', + ); if (device == null) { return; // device not found } Logs().v( - '[OlmManager] Device ${device.userId}:${device.deviceId} generated a new olm session, replaying last sent message...'); + '[OlmManager] Device ${device.userId}:${device.deviceId} generated a new olm session, replaying last sent message...', + ); final lastSentMessageRes = await encryption.olmDatabase ?.getLastSentMessageUserDeviceKey(device.userId, device.deviceId!); if (lastSentMessageRes == null || @@ -773,7 +810,10 @@ class OlmManager { if (lastSentMessage['type'] != EventTypes.Dummy) { // okay, time to send the message! await client.sendToDeviceEncrypted( - [device], lastSentMessage['type'], lastSentMessage['content']); + [device], + lastSentMessage['type'], + lastSentMessage['content'], + ); } } } diff --git a/lib/encryption/ssss.dart b/lib/encryption/ssss.dart index 198dd98b0..e95669ddd 100644 --- a/lib/encryption/ssss.dart +++ b/lib/encryption/ssss.dart @@ -82,13 +82,17 @@ class SSSS { final hmacKey = Hmac(sha256, prk.bytes).convert(aesKey.bytes + utf8.encode(name) + b); return DerivedKeys( - aesKey: Uint8List.fromList(aesKey.bytes), - hmacKey: Uint8List.fromList(hmacKey.bytes)); + aesKey: Uint8List.fromList(aesKey.bytes), + hmacKey: Uint8List.fromList(hmacKey.bytes), + ); } static Future encryptAes( - String data, Uint8List key, String name, - [String? ivStr]) async { + String data, + Uint8List key, + String name, [ + String? ivStr, + ]) async { Uint8List iv; if (ivStr != null) { iv = base64decodeUnpadded(ivStr); @@ -106,13 +110,17 @@ class SSSS { final hmac = Hmac(sha256, keys.hmacKey).convert(ciphertext); return EncryptedContent( - iv: base64.encode(iv), - ciphertext: base64.encode(ciphertext), - mac: base64.encode(hmac.bytes)); + iv: base64.encode(iv), + ciphertext: base64.encode(ciphertext), + mac: base64.encode(hmac.bytes), + ); } static Future decryptAes( - EncryptedContent data, Uint8List key, String name) async { + EncryptedContent data, + Uint8List key, + String name, + ) async { final keys = deriveKeys(key, name); final cipher = base64decodeUnpadded(data.ciphertext); final hmac = base64 @@ -144,8 +152,12 @@ class SSSS { throw InvalidPassphraseException('Incorrect length'); } - return Uint8List.fromList(result.sublist(olmRecoveryKeyPrefix.length, - olmRecoveryKeyPrefix.length + ssssKeyLength)); + return Uint8List.fromList( + result.sublist( + olmRecoveryKeyPrefix.length, + olmRecoveryKeyPrefix.length + ssssKeyLength, + ), + ); } static String encodeRecoveryKey(Uint8List recoveryKey) { @@ -160,7 +172,9 @@ class SSSS { } static Future keyFromPassphrase( - String passphrase, PassphraseInfo info) async { + String passphrase, + PassphraseInfo info, + ) async { if (info.algorithm != AlgorithmTypes.pbkdf2) { throw InvalidPassphraseException('Unknown algorithm'); } @@ -171,11 +185,12 @@ class SSSS { throw InvalidPassphraseException('Passphrase info without salt'); } return await uc.pbkdf2( - Uint8List.fromList(utf8.encode(passphrase)), - Uint8List.fromList(utf8.encode(info.salt!)), - uc.sha512, - info.iterations!, - info.bits ?? 256); + Uint8List.fromList(utf8.encode(passphrase)), + Uint8List.fromList(utf8.encode(info.salt!)), + uc.sha512, + info.iterations!, + info.bits ?? 256, + ); } void setValidator(String type, FutureOr Function(String) validator) { @@ -252,7 +267,10 @@ class SSSS { // noooow we set the account data await client.setAccountData( - client.userID!, accountDataTypeKeyId, content.toJson()); + client.userID!, + accountDataTypeKeyId, + content.toJson(), + ); while (!client.accountData.containsKey(accountDataTypeKeyId)) { Logs().v('Waiting accountData to have $accountDataTypeKeyId'); @@ -354,8 +372,13 @@ class SSSS { return decrypted; } - Future store(String type, String secret, String keyId, Uint8List key, - {bool add = false}) async { + Future store( + String type, + String secret, + String keyId, + Uint8List key, { + bool add = false, + }) async { final encrypted = await encryptAes(secret, key, type); Map? content; if (add && client.accountData[type] != null) { @@ -386,7 +409,11 @@ class SSSS { } Future validateAndStripOtherKeys( - String type, String secret, String keyId, Uint8List key) async { + String type, + String secret, + String keyId, + Uint8List key, + ) async { if (await getStored(type, keyId, key) != secret) { throw Exception('Secrets do not match up!'); } @@ -457,11 +484,13 @@ class SSSS { devices = client.userDeviceKeys[client.userID]!.deviceKeys.values.toList(); } - devices.removeWhere((DeviceKeys d) => - d.userId != client.userID || - !d.verified || - d.blocked || - d.deviceId == client.deviceID); + devices.removeWhere( + (DeviceKeys d) => + d.userId != client.userID || + !d.verified || + d.blocked || + d.deviceId == client.deviceID, + ); if (devices.isEmpty) { Logs().w('[SSSS] No devices'); return; @@ -555,9 +584,11 @@ class SSSS { } final request = pendingShareRequests[event.content['request_id']]!; // alright, as we received a known request id, let's check if the sender is valid - final device = request.devices.firstWhereOrNull((d) => - d.userId == event.sender && - d.curve25519Key == encryptedContent['sender_key']); + final device = request.devices.firstWhereOrNull( + (d) => + d.userId == event.sender && + d.curve25519Key == encryptedContent['sender_key'], + ); if (device == null) { Logs().i('[SSSS] Someone else replied?'); return; // someone replied whom we didn't send the share request to @@ -645,9 +676,11 @@ class _ShareRequest { final List devices; final DateTime start; - _ShareRequest( - {required this.requestId, required this.type, required this.devices}) - : start = DateTime.now(); + _ShareRequest({ + required this.requestId, + required this.type, + required this.devices, + }) : start = DateTime.now(); } class EncryptedContent { @@ -655,8 +688,11 @@ class EncryptedContent { final String ciphertext; final String mac; - EncryptedContent( - {required this.iv, required this.ciphertext, required this.mac}); + EncryptedContent({ + required this.iv, + required this.ciphertext, + required this.mac, + }); } class DerivedKeys { @@ -682,11 +718,12 @@ class OpenSSSS { String? get recoveryKey => isUnlocked ? SSSS.encodeRecoveryKey(privateKey!) : null; - Future unlock( - {String? passphrase, - String? recoveryKey, - String? keyOrPassphrase, - bool postUnlock = true}) async { + Future unlock({ + String? passphrase, + String? recoveryKey, + String? keyOrPassphrase, + bool postUnlock = true, + }) async { if (keyOrPassphrase != null) { try { await unlock(recoveryKey: keyOrPassphrase, postUnlock: postUnlock); @@ -701,7 +738,8 @@ class OpenSSSS { } else if (passphrase != null) { if (!hasPassphrase) { throw InvalidPassphraseException( - 'Tried to unlock with passphrase while key does not have a passphrase'); + 'Tried to unlock with passphrase while key does not have a passphrase', + ); } privateKey = await Future.value( ssss.client.nativeImplementations.keyFromPassphrase( diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index 6c6f5fe78..163921b1d 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -164,7 +164,8 @@ class Bootstrap { Set allNeededKeys() { final secrets = analyzeSecrets(); secrets.removeWhere( - (k, v) => v.isEmpty); // we don't care about the failed secrets here + (k, v) => v.isEmpty, + ); // we don't care about the failed secrets here final keys = {}; final defaultKeyId = encryption.ssss.defaultKeyId; int removeKey(String key) { @@ -297,7 +298,8 @@ class Bootstrap { await encryption.ssss.setDefaultKeyId(newSsssKey!.keyId); while (encryption.ssss.defaultKeyId != newSsssKey!.keyId) { Logs().v( - 'Waiting accountData to have the correct m.secret_storage.default_key'); + 'Waiting accountData to have the correct m.secret_storage.default_key', + ); await client.oneShotSync(); } if (oldSsssKeys != null) { @@ -354,10 +356,11 @@ class Bootstrap { } } - Future askSetupCrossSigning( - {bool setupMasterKey = false, - bool setupSelfSigningKey = false, - bool setupUserSigningKey = false}) async { + Future askSetupCrossSigning({ + bool setupMasterKey = false, + bool setupSelfSigningKey = false, + bool setupUserSigningKey = false, + }) async { if (state != BootstrapState.askSetupCrossSigning) { throw BootstrapBadStateException(); } @@ -395,8 +398,8 @@ class Bootstrap { } else { Logs().v('Get stored key...'); masterSigningKey = base64decodeUnpadded( - await newSsssKey?.getStored(EventTypes.CrossSigningMasterKey) ?? - ''); + await newSsssKey?.getStored(EventTypes.CrossSigningMasterKey) ?? '', + ); if (masterSigningKey.isEmpty) { // no master signing key :( throw BootstrapBadStateException('No master key'); @@ -473,12 +476,13 @@ class Bootstrap { state = BootstrapState.loading; Logs().v('Upload device signing keys.'); await client.uiaRequestBackground( - (AuthenticationData? auth) => client.uploadCrossSigningKeys( - masterKey: masterKey, - selfSigningKey: selfSigningKey, - userSigningKey: userSigningKey, - auth: auth, - )); + (AuthenticationData? auth) => client.uploadCrossSigningKeys( + masterKey: masterKey, + selfSigningKey: selfSigningKey, + userSigningKey: userSigningKey, + auth: auth, + ), + ); Logs().v('Device signing keys have been uploaded.'); // aaaand set the SSSS secrets if (masterKey != null) { @@ -503,7 +507,8 @@ class Bootstrap { if (client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key != masterKey.publicKey) { throw BootstrapBadStateException( - 'ERROR: New master key does not match up!'); + 'ERROR: New master key does not match up!', + ); } Logs().v('Set own master key to verified...'); await client.userDeviceKeys[client.userID]!.masterKey! @@ -512,7 +517,8 @@ class Bootstrap { } if (selfSigningKey != null) { keysToSign.add( - client.userDeviceKeys[client.userID]!.deviceKeys[client.deviceID]!); + client.userDeviceKeys[client.userID]!.deviceKeys[client.deviceID]!, + ); } Logs().v('Sign ourself...'); await encryption.crossSigning.sign(keysToSign); @@ -575,7 +581,8 @@ class Bootstrap { await newSsssKey?.store(megolmKey, base64.encode(privKey)); Logs().v( - 'And finally set all megolm keys as needing to be uploaded again...'); + 'And finally set all megolm keys as needing to be uploaded again...', + ); await client.database?.markInboundGroupSessionsAsNeedingUpload(); Logs().v('And uploading keys...'); await client.encryption?.keyManager.uploadInboundGroupSessions(); diff --git a/lib/encryption/utils/key_verification.dart b/lib/encryption/utils/key_verification.dart index a088e949d..e836d8085 100644 --- a/lib/encryption/utils/key_verification.dart +++ b/lib/encryption/utils/key_verification.dart @@ -145,7 +145,9 @@ List _intersect(List? a, List? b) => (b == null || a == null) ? [] : a.where(b.contains).toList(); List _calculatePossibleMethods( - List knownMethods, List payloadMethods) { + List knownMethods, + List payloadMethods, +) { final output = []; final copyKnownMethods = List.from(knownMethods); final copyPayloadMethods = List.from(payloadMethods); @@ -193,7 +195,9 @@ List _bytesToInt(Uint8List bytes, int totalBits) { } _KeyVerificationMethod _makeVerificationMethod( - String type, KeyVerification request) { + String type, + KeyVerification request, +) { if (type == EventTypes.Sas) { return _KeyVerificationMethodSas(request: request); } @@ -237,13 +241,13 @@ class KeyVerification { QRCode? qrCode; String? randomSharedSecretForQRCode; SignableKey? keyToVerify; - KeyVerification( - {required this.encryption, - this.room, - required this.userId, - String? deviceId, - this.onUpdate}) - : _deviceId = deviceId, + KeyVerification({ + required this.encryption, + this.room, + required this.userId, + String? deviceId, + this.onUpdate, + }) : _deviceId = deviceId, lastActivity = DateTime.now(); void dispose() { @@ -287,8 +291,10 @@ class KeyVerification { /// Once you get a ready event, i.e both sides are in a `askChoice` state, /// send either `m.reciprocate.v1` or `m.sas.v1` here. If you continue with /// qr, send the qrData you just scanned - Future continueVerification(String type, - {Uint8List? qrDataRawBytes}) async { + Future continueVerification( + String type, { + Uint8List? qrDataRawBytes, + }) async { bool qrChecksOut = false; if (possibleMethods.contains(type)) { if (qrDataRawBytes != null) { @@ -307,7 +313,8 @@ class KeyVerification { } } else { Logs().e( - '[KeyVerification] tried to continue verification with a unknown method'); + '[KeyVerification] tried to continue verification with a unknown method', + ); await cancel('m.unknown_method'); } } @@ -356,8 +363,11 @@ class KeyVerification { return mode; } - Future handlePayload(String type, Map payload, - [String? eventId]) async { + Future handlePayload( + String type, + Map payload, [ + String? eventId, + ]) async { if (isDone) { return; // no need to do anything with already canceled requests } @@ -380,8 +390,10 @@ class KeyVerification { now.add(Duration(minutes: 5)).isBefore(verifyTime)) { // if the request is more than 20min in the past we just silently fail it // to not generate too many cancels - await cancel('m.timeout', - now.subtract(Duration(minutes: 20)).isAfter(verifyTime)); + await cancel( + 'm.timeout', + now.subtract(Duration(minutes: 20)).isAfter(verifyTime), + ); return; } @@ -397,7 +409,9 @@ class KeyVerification { oppositePossibleMethods = List.from(payload['methods']); // verify it has a method we can use possibleMethods = _calculatePossibleMethods( - knownVerificationMethods, payload['methods']); + knownVerificationMethods, + payload['methods'], + ); if (possibleMethods.isEmpty) { // reject it outright await cancel('m.unknown_method'); @@ -412,17 +426,22 @@ class KeyVerification { transactionId ??= eventId ?? payload['transaction_id']; // and broadcast the cancel to the other devices final devices = List.from( - client.userDeviceKeys[userId]?.deviceKeys.values ?? - Iterable.empty()); + client.userDeviceKeys[userId]?.deviceKeys.values ?? + Iterable.empty(), + ); devices.removeWhere( - (d) => {deviceId, client.deviceID}.contains(d.deviceId)); + (d) => {deviceId, client.deviceID}.contains(d.deviceId), + ); final cancelPayload = { 'reason': 'Another device accepted the request', 'code': 'm.accepted', }; makePayload(cancelPayload); await client.sendToDeviceEncrypted( - devices, EventTypes.KeyVerificationCancel, cancelPayload); + devices, + EventTypes.KeyVerificationCancel, + cancelPayload, + ); } _deviceId ??= payload['from_device']; @@ -437,7 +456,9 @@ class KeyVerification { oppositePossibleMethods = List.from(payload['methods']); possibleMethods = _calculatePossibleMethods( - knownVerificationMethods, payload['methods']); + knownVerificationMethods, + payload['methods'], + ); if (possibleMethods.isEmpty) { // reject it outright await cancel('m.unknown_method'); @@ -577,11 +598,12 @@ class KeyVerification { setState(KeyVerificationState.error); } - Future openSSSS( - {String? passphrase, - String? recoveryKey, - String? keyOrPassphrase, - bool skip = false}) async { + Future openSSSS({ + String? passphrase, + String? recoveryKey, + String? keyOrPassphrase, + bool skip = false, + }) async { Future next() async { if (_nextAction == 'request') { await sendRequest(); @@ -600,9 +622,10 @@ class KeyVerification { } final handle = encryption.ssss.open(EventTypes.CrossSigningUserSigning); await handle.unlock( - passphrase: passphrase, - recoveryKey: recoveryKey, - keyOrPassphrase: keyOrPassphrase); + passphrase: passphrase, + recoveryKey: recoveryKey, + keyOrPassphrase: keyOrPassphrase, + ); await handle.maybeCacheAll(); await next(); } @@ -611,7 +634,7 @@ class KeyVerification { Future acceptVerification() async { if (!(await verifyLastStep([ EventTypes.KeyVerificationRequest, - EventTypes.KeyVerificationStart + EventTypes.KeyVerificationStart, ]))) { return; } @@ -633,7 +656,9 @@ class KeyVerification { // we are removing stuff only using the old possibleMethods should be ok here. final copyPossibleMethods = List.from(possibleMethods); possibleMethods = _calculatePossibleMethods( - copyKnownVerificationMethods, copyPossibleMethods); + copyKnownVerificationMethods, + copyPossibleMethods, + ); } } // we need to send a ready event @@ -657,7 +682,7 @@ class KeyVerification { } if (!(await verifyLastStep([ EventTypes.KeyVerificationRequest, - EventTypes.KeyVerificationStart + EventTypes.KeyVerificationStart, ]))) { return; } @@ -728,12 +753,16 @@ class KeyVerification { if (requestInterval.length <= i) { return; } - Timer(Duration(seconds: requestInterval[i]), - () => maybeRequestSSSSSecrets(i + 1)); + Timer( + Duration(seconds: requestInterval[i]), + () => maybeRequestSSSSSecrets(i + 1), + ); } - Future verifyKeysSAS(Map keys, - Future Function(String, SignableKey) verifier) async { + Future verifyKeysSAS( + Map keys, + Future Function(String, SignableKey) verifier, + ) async { _verifiedDevices = []; final userDeviceKey = client.userDeviceKeys[userId]; @@ -759,7 +788,9 @@ class KeyVerification { final wasUnknownSession = client.isUnknownSession; for (final key in _verifiedDevices) { await key.setVerified( - true, false); // we don't want to sign the keys juuuust yet + true, + false, + ); // we don't want to sign the keys juuuust yet if (key is CrossSigningKey && key.usage.contains('master')) { verifiedMasterKey = true; } @@ -859,7 +890,8 @@ class KeyVerification { return true; } Logs().e( - '[KeyVerificaton] lastStep mismatch cancelling, expected from ${checkLastStep.toString()} was ${lastStep.toString()}'); + '[KeyVerificaton] lastStep mismatch cancelling, expected from ${checkLastStep.toString()} was ${lastStep.toString()}', + ); await cancel('m.unexpected_message'); return false; } @@ -917,9 +949,12 @@ class KeyVerification { EventTypes.KeyVerificationRequest, EventTypes.KeyVerificationCancel, }.contains(type)) { - final deviceKeys = client.userDeviceKeys[userId]?.deviceKeys.values - .where((deviceKey) => deviceKey.hasValidSignatureChain( - verifiedByTheirMasterKey: true)); + final deviceKeys = + client.userDeviceKeys[userId]?.deviceKeys.values.where( + (deviceKey) => deviceKey.hasValidSignatureChain( + verifiedByTheirMasterKey: true, + ), + ); if (deviceKeys != null) { await client.sendToDeviceEncrypted( @@ -930,14 +965,16 @@ class KeyVerification { } } else { Logs().e( - '[Key Verification] Tried to broadcast and un-broadcastable type: $type'); + '[Key Verification] Tried to broadcast and un-broadcastable type: $type', + ); } } else { if (client.userDeviceKeys[userId]?.deviceKeys[deviceId] != null) { await client.sendToDeviceEncrypted( - [client.userDeviceKeys[userId]!.deviceKeys[deviceId]!], - type, - payload); + [client.userDeviceKeys[userId]!.deviceKeys[deviceId]!], + type, + payload, + ); } else { Logs().e('[Key Verification] Unknown device'); } @@ -982,7 +1019,8 @@ class KeyVerification { final otherUserMasterKey = otherUserKeys?.masterKey; final secondKey = encodeBase64Unpadded( - data.sublist(10 + encodedTxnLen + 32, 10 + encodedTxnLen + 32 + 32)); + data.sublist(10 + encodedTxnLen + 32, 10 + encodedTxnLen + 32 + 32), + ); final randomSharedSecret = encodeBase64Unpadded(data.sublist(10 + encodedTxnLen + 32 + 32)); @@ -991,7 +1029,8 @@ class KeyVerification { .contains(remoteQrMode)) { if (!(ownMasterKey?.verified ?? false)) { Logs().e( - '[KeyVerification] verifyQrData because you were in mode 0/2 and had untrusted msk'); + '[KeyVerification] verifyQrData because you were in mode 0/2 and had untrusted msk', + ); return false; } } @@ -1244,7 +1283,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { if (!(await request.verifyLastStep([ EventTypes.KeyVerificationReady, EventTypes.KeyVerificationRequest, - EventTypes.KeyVerificationStart + EventTypes.KeyVerificationStart, ]))) { return; // abort } @@ -1257,7 +1296,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { case EventTypes.KeyVerificationAccept: if (!(await request.verifyLastStep([ EventTypes.KeyVerificationReady, - EventTypes.KeyVerificationRequest + EventTypes.KeyVerificationRequest, ]))) { return; } @@ -1270,7 +1309,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { case 'm.key.verification.key': if (!(await request.verifyLastStep([ EventTypes.KeyVerificationAccept, - EventTypes.KeyVerificationStart + EventTypes.KeyVerificationStart, ]))) { return; } @@ -1338,7 +1377,9 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { return false; } final possibleKeyAgreementProtocols = _intersect( - knownKeyAgreementProtocols, payload['key_agreement_protocols']); + knownKeyAgreementProtocols, + payload['key_agreement_protocols'], + ); if (possibleKeyAgreementProtocols.isEmpty) { return false; } @@ -1349,14 +1390,17 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } hash = possibleHashes.first; final possibleMessageAuthenticationCodes = _intersect( - knownHashesAuthentificationCodes, - payload['message_authentication_codes']); + knownHashesAuthentificationCodes, + payload['message_authentication_codes'], + ); if (possibleMessageAuthenticationCodes.isEmpty) { return false; } messageAuthenticationCode = possibleMessageAuthenticationCodes.first; final possibleAuthenticationTypes = _intersect( - knownAuthentificationTypes, payload['short_authentication_string']); + knownAuthentificationTypes, + payload['short_authentication_string'], + ); if (possibleAuthenticationTypes.isEmpty) { return false; } @@ -1394,7 +1438,9 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } messageAuthenticationCode = payload['message_authentication_code']; final possibleAuthenticationTypes = _intersect( - knownAuthentificationTypes, payload['short_authentication_string']); + knownAuthentificationTypes, + payload['short_authentication_string'], + ); if (possibleAuthenticationTypes.isEmpty) { return false; } @@ -1497,7 +1543,9 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { await request.verifyKeysSAS(mac, (String mac, SignableKey key) async { return mac == _calculateMac( - key.ed25519Key!, '${baseInfo}ed25519:${key.identifier!}'); + key.ed25519Key!, + '${baseInfo}ed25519:${key.identifier!}', + ); }); } diff --git a/lib/encryption/utils/outbound_group_session.dart b/lib/encryption/utils/outbound_group_session.dart index ef6bb52d6..f04a5998b 100644 --- a/lib/encryption/utils/outbound_group_session.dart +++ b/lib/encryption/utils/outbound_group_session.dart @@ -35,11 +35,12 @@ class OutboundGroupSession { bool get isValid => outboundGroupSession != null; final String key; - OutboundGroupSession( - {required this.devices, - required this.creationTime, - required this.outboundGroupSession, - required this.key}); + OutboundGroupSession({ + required this.devices, + required this.creationTime, + required this.outboundGroupSession, + required this.key, + }); OutboundGroupSession.fromJson(Map dbEntry, this.key) { try { @@ -49,7 +50,8 @@ class OutboundGroupSession { } catch (e) { // devices is bad (old data), so just not use this session Logs().i( - '[OutboundGroupSession] Session in database is old, not using it. $e'); + '[OutboundGroupSession] Session in database is old, not using it. $e', + ); return; } outboundGroupSession = olm.OutboundGroupSession(); diff --git a/lib/encryption/utils/session_key.dart b/lib/encryption/utils/session_key.dart index 31cec3076..d2da4a156 100644 --- a/lib/encryption/utils/session_key.dart +++ b/lib/encryption/utils/session_key.dart @@ -60,17 +60,17 @@ class SessionKey { /// Id of this session String sessionId; - SessionKey( - {required this.content, - required this.inboundGroupSession, - required this.key, - Map? indexes, - Map>? allowedAtIndex, - required this.roomId, - required this.sessionId, - required this.senderKey, - required this.senderClaimedKeys}) - : indexes = indexes ?? {}, + SessionKey({ + required this.content, + required this.inboundGroupSession, + required this.key, + Map? indexes, + Map>? allowedAtIndex, + required this.roomId, + required this.sessionId, + required this.senderKey, + required this.senderClaimedKeys, + }) : indexes = indexes ?? {}, allowedAtIndex = allowedAtIndex ?? >{}; SessionKey.fromDb(StoredInboundGroupSession dbEntry, this.key) @@ -94,7 +94,7 @@ class SessionKey { ?.catchMap((k, v) => MapEntry(k, v)) ?? (content['sender_claimed_ed25519_key'] is String ? { - 'ed25519': content['sender_claimed_ed25519_key'] + 'ed25519': content['sender_claimed_ed25519_key'], } : {})); diff --git a/lib/fake_matrix_api.dart b/lib/fake_matrix_api.dart index b0b8f0aba..ff9e2f472 100644 --- a/lib/fake_matrix_api.dart +++ b/lib/fake_matrix_api.dart @@ -94,7 +94,7 @@ class FakeMatrixApi extends BaseClient { Set servers = { 'https://fakeserver.notexisting', 'https://fakeserver.notexisting:1337', - 'https://fakeserverpriortoauthmedia.notexisting' + 'https://fakeserverpriortoauthmedia.notexisting', }; FutureOr mockIntercept(Request request) async { @@ -129,8 +129,9 @@ class FakeMatrixApi extends BaseClient { if (!servers.contains(request.url.origin)) { return Response( - 'Not found ${request.url.origin}...', - 404); + 'Not found ${request.url.origin}...', + 404, + ); } if (!{ @@ -191,11 +192,13 @@ class FakeMatrixApi extends BaseClient { res = {'displayname': '', 'membership': 'ban'}; } else if (method == 'PUT' && action.contains( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/')) { + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/', + )) { res = {'event_id': '\$event${_eventCounter++}'}; } else if (method == 'PUT' && action.contains( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/state/')) { + '/client/v3/rooms/!1234%3AfakeServer.notExisting/state/', + )) { res = {'event_id': '\$event${_eventCounter++}'}; } else if (action.contains('/client/v3/sync')) { // Sync requests with timeout @@ -209,7 +212,7 @@ class FakeMatrixApi extends BaseClient { // ensure we don't generate new keys for no reason 'device_one_time_keys_count': { 'curve25519': 10, - 'signed_curve25519': 100 + 'signed_curve25519': 100, }, }; } else if (method == 'PUT' && @@ -240,10 +243,15 @@ class FakeMatrixApi extends BaseClient { nextBatch: '', rooms: RoomsUpdate( join: { - roomId: JoinedRoomUpdate(accountData: [ - sdk.BasicRoomEvent( - content: decodeJson(data), type: type, roomId: roomId) - ]) + roomId: JoinedRoomUpdate( + accountData: [ + sdk.BasicRoomEvent( + content: decodeJson(data), + type: type, + roomId: roomId, + ), + ], + ), }, ), ); @@ -258,15 +266,17 @@ class FakeMatrixApi extends BaseClient { } else { res = { 'errcode': 'M_UNRECOGNIZED', - 'error': 'Unrecognized request: $action' + 'error': 'Unrecognized request: $action', }; statusCode = 405; } } - unawaited(Future.delayed(Duration(milliseconds: 1)).then((_) async { - _apiCallStream.add(action); - })); + unawaited( + Future.delayed(Duration(milliseconds: 1)).then((_) async { + _apiCallStream.add(action); + }), + ); return Response.bytes(utf8.encode(json.encode(res)), statusCode); } @@ -303,7 +313,7 @@ class FakeMatrixApi extends BaseClient { for (final keyType in { 'master_key', 'self_signing_key', - 'user_signing_key' + 'user_signing_key', }) { if (jsonBody[keyType] != null) { final key = @@ -331,14 +341,14 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '3143273582443PhrSn:example.org', 'room_id': '!1234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, { 'content': {'name': 'The room name'}, @@ -348,7 +358,7 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, { 'content': { @@ -360,22 +370,22 @@ class FakeMatrixApi extends BaseClient { 'mimetype': 'image/jpeg', 'size': 46144, 'w': 300, - 'h': 300 + 'h': 300, }, 'w': 480, 'h': 320, 'duration': 2140786, 'size': 1563685, - 'mimetype': 'video/mp4' + 'mimetype': 'video/mp4', }, - 'msgtype': 'm.video' + 'msgtype': 'm.video', }, 'type': 'm.room.message', 'event_id': '1143273582443PhrSn:example.org', 'room_id': '!1234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } ], 'state': [], @@ -394,22 +404,22 @@ class FakeMatrixApi extends BaseClient { 'mimetype': 'image/jpeg', 'size': 46144, 'w': 300, - 'h': 300 + 'h': 300, }, 'w': 480, 'h': 320, 'duration': 2140786, 'size': 1563685, - 'mimetype': 'video/mp4' + 'mimetype': 'video/mp4', }, - 'msgtype': 'm.video' + 'msgtype': 'm.video', }, 'type': 'm.room.message', 'event_id': '1143273582443PhrSn:example.org', 'room_id': '!1234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, { 'content': {'name': 'The room name'}, @@ -419,21 +429,21 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, { 'content': { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '3143273582443PhrSn:example.org', 'room_id': '!1234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } ], 'state': [], @@ -454,14 +464,14 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '3143273582443PhrSn:example.org', 'room_id': '!5345234234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, { 'content': {'name': 'The room name'}, @@ -471,7 +481,7 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, { 'content': { @@ -483,22 +493,22 @@ class FakeMatrixApi extends BaseClient { 'mimetype': 'image/jpeg', 'size': 46144, 'w': 300, - 'h': 300 + 'h': 300, }, 'w': 480, 'h': 320, 'duration': 2140786, 'size': 1563685, - 'mimetype': 'video/mp4' + 'mimetype': 'video/mp4', }, - 'msgtype': 'm.video' + 'msgtype': 'm.video', }, 'type': 'm.room.message', 'event_id': '1143273582466PhrSn:example.org', 'room_id': '!5345234234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824654, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } ], 'state': [], @@ -512,7 +522,7 @@ class FakeMatrixApi extends BaseClient { 'summary': { 'm.heroes': ['@alice:example.com'], 'm.joined_member_count': 1, - 'm.invited_member_count': 0 + 'm.invited_member_count': 0, }, 'unread_notifications': { 'highlight_count': 2, @@ -530,17 +540,17 @@ class FakeMatrixApi extends BaseClient { 'displayname': 'Alice Margatroid', }, 'origin_server_ts': 1417731086795, - 'event_id': '66697273743031:example.com' + 'event_id': '66697273743031:example.com', }, { 'sender': '@alice:example.com', 'type': 'm.room.canonical_alias', 'content': { - 'alias': '#famedlyContactDiscovery:fakeServer.notExisting' + 'alias': '#famedlyContactDiscovery:fakeServer.notExisting', }, 'state_key': '', 'origin_server_ts': 1417731086796, - 'event_id': '66697273743032:example.com' + 'event_id': '66697273743032:example.com', }, { 'sender': '@alice:example.com', @@ -548,11 +558,11 @@ class FakeMatrixApi extends BaseClient { 'state_key': '', 'content': {'algorithm': AlgorithmTypes.megolmV1AesSha2}, 'origin_server_ts': 1417731086795, - 'event_id': '666972737430353:example.com' + 'event_id': '666972737430353:example.com', }, { 'content': { - 'pinned': ['1234:bla'] + 'pinned': ['1234:bla'], }, 'type': 'm.room.pinned_events', 'event_id': '21432735824443PhrSn:example.org', @@ -560,9 +570,9 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, - ] + ], }, 'timeline': { 'events': [ @@ -574,39 +584,39 @@ class FakeMatrixApi extends BaseClient { 'prev_content': {'membership': 'invite'}, 'origin_server_ts': 1417731086795, 'event_id': '\$7365636s6r6432:example.com', - 'unsigned': {'foo': 'bar'} + 'unsigned': {'foo': 'bar'}, }, { 'sender': '@alice:example.com', 'type': 'm.room.message', 'content': {'body': 'I am a fish', 'msgtype': 'm.text'}, 'origin_server_ts': 1417731086797, - 'event_id': '74686972643033:example.com' + 'event_id': '74686972643033:example.com', } ], 'limited': true, - 'prev_batch': 't34-23535_0_0' + 'prev_batch': 't34-23535_0_0', }, 'ephemeral': { 'events': [ { 'type': 'm.typing', 'content': { - 'user_ids': ['@alice:example.com'] - } + 'user_ids': ['@alice:example.com'], + }, }, { 'content': { '\$7365636s6r6432:example.com': { 'm.read': { - '@alice:example.com': {'ts': 1436451550453} - } - } + '@alice:example.com': {'ts': 1436451550453}, + }, + }, }, 'room_id': '!726s6s6q:example.com', - 'type': 'm.receipt' + 'type': 'm.receipt', } - ] + ], }, 'account_data': { 'events': [ @@ -614,16 +624,16 @@ class FakeMatrixApi extends BaseClient { 'type': 'm.tag', 'content': { 'tags': { - 'work': {'order': 1} - } - } + 'work': {'order': 1}, + }, + }, }, { 'type': 'org.example.custom.room.config', - 'content': {'custom_config_key': 'custom_config_value'} + 'content': {'custom_config_key': 'custom_config_value'}, } - ] - } + ], + }, }, '!calls:example.com': { 'state': { @@ -638,7 +648,7 @@ class FakeMatrixApi extends BaseClient { 'displayname': 'Test User', }, 'origin_server_ts': 1417731086795, - 'event_id': 'calls_1:example.com' + 'event_id': 'calls_1:example.com', }, { 'sender': '@alice:example.com', @@ -650,9 +660,9 @@ class FakeMatrixApi extends BaseClient { 'displayname': 'Alice Margatroid', }, 'origin_server_ts': 1417731086795, - 'event_id': 'calls_2:example.com' + 'event_id': 'calls_2:example.com', }, - ] + ], }, }, }, @@ -664,17 +674,17 @@ class FakeMatrixApi extends BaseClient { 'sender': '@alice:example.com', 'type': 'm.room.name', 'state_key': '', - 'content': {'name': 'My Room Name'} + 'content': {'name': 'My Room Name'}, }, { 'sender': '@alice:example.com', 'type': 'm.room.member', 'state_key': '@bob:example.com', - 'content': {'membership': 'invite'} + 'content': {'membership': 'invite'}, } - ] - } - } + ], + }, + }, }, 'leave': { '!726s6s6f:example.com': { @@ -686,9 +696,9 @@ class FakeMatrixApi extends BaseClient { 'state_key': '', 'content': {'name': 'left room'}, 'origin_server_ts': 1417731086795, - 'event_id': '66697273743031:example.com' + 'event_id': '66697273743031:example.com', }, - ] + ], }, 'timeline': { 'events': [ @@ -698,11 +708,11 @@ class FakeMatrixApi extends BaseClient { 'content': {'text': 'Hallo'}, 'origin_server_ts': 1417731086795, 'event_id': '7365636s6r64300:example.com', - 'unsigned': {'foo': 'bar'} + 'unsigned': {'foo': 'bar'}, }, ], 'limited': true, - 'prev_batch': 't34-23535_0_0' + 'prev_batch': 't34-23535_0_0', }, 'account_data': { 'events': [ @@ -710,17 +720,17 @@ class FakeMatrixApi extends BaseClient { 'type': 'm.tag', 'content': { 'tags': { - 'work': {'order': 1} - } - } + 'work': {'order': 1}, + }, + }, }, { 'type': 'org.example.custom.room.config', - 'content': {'custom_config_key': 'custom_config_value'} + 'content': {'custom_config_key': 'custom_config_value'}, } - ] - } - } + ], + }, + }, }, }, 'presence': { @@ -728,9 +738,9 @@ class FakeMatrixApi extends BaseClient { { 'sender': '@alice:example.com', 'type': 'm.presence', - 'content': {'presence': 'online'} + 'content': {'presence': 'online'}, } - ] + ], }, 'account_data': { 'events': [ @@ -742,12 +752,12 @@ class FakeMatrixApi extends BaseClient { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight'} + {'set_tweak': 'highlight'}, ], 'default': true, 'enabled': true, 'pattern': 'alice', - 'rule_id': '.m.rule.contains_user_name' + 'rule_id': '.m.rule.contains_user_name', } ], 'override': [ @@ -756,7 +766,7 @@ class FakeMatrixApi extends BaseClient { 'conditions': [], 'default': true, 'enabled': false, - 'rule_id': '.m.rule.master' + 'rule_id': '.m.rule.master', }, { 'actions': ['dont_notify'], @@ -764,12 +774,12 @@ class FakeMatrixApi extends BaseClient { { 'key': 'content.msgtype', 'kind': 'event_match', - 'pattern': 'm.notice' + 'pattern': 'm.notice', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.suppress_notices' + 'rule_id': '.m.rule.suppress_notices', } ], 'room': [ @@ -784,7 +794,7 @@ class FakeMatrixApi extends BaseClient { ], 'default': true, 'enabled': true, - 'rule_id': '!localpart:server.abc' + 'rule_id': '!localpart:server.abc', } ], 'sender': [], @@ -793,130 +803,130 @@ class FakeMatrixApi extends BaseClient { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'ring'}, - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.call.invite' + 'pattern': 'm.call.invite', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.call' + 'rule_id': '.m.rule.call', }, { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight'} + {'set_tweak': 'highlight'}, ], 'conditions': [ - {'kind': 'contains_display_name'} + {'kind': 'contains_display_name'}, ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.contains_display_name' + 'rule_id': '.m.rule.contains_display_name', }, { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ {'is': '2', 'kind': 'room_member_count'}, { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.message' + 'pattern': 'm.room.message', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.room_one_to_one' + 'rule_id': '.m.rule.room_one_to_one', }, { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.member' + 'pattern': 'm.room.member', }, { 'key': 'content.membership', 'kind': 'event_match', - 'pattern': 'invite' + 'pattern': 'invite', }, { 'key': 'state_key', 'kind': 'event_match', - 'pattern': '@alice:example.com' + 'pattern': '@alice:example.com', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.invite_for_me' + 'rule_id': '.m.rule.invite_for_me', }, { 'actions': [ 'notify', - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.member' + 'pattern': 'm.room.member', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.member_event' + 'rule_id': '.m.rule.member_event', }, { 'actions': [ 'notify', - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.message' + 'pattern': 'm.room.message', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.message' + 'rule_id': '.m.rule.message', } - ] - } + ], + }, }, - 'type': 'm.push_rules' + 'type': 'm.push_rules', }, { 'type': 'org.example.custom.config', - 'content': {'custom_config_key': 'custom_config_value'} + 'content': {'custom_config_key': 'custom_config_value'}, }, { 'content': { '@bob:example.com': [ '!726s6s6q:example.com', - '!hgfedcba:example.com' - ] + '!hgfedcba:example.com', + ], }, - 'type': 'm.direct' + 'type': 'm.direct', }, { 'type': EventTypes.SecretStorageDefaultKey, - 'content': {'key': '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'} + 'content': {'key': '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'}, }, { 'type': 'm.secret_storage.key.0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3', @@ -925,11 +935,11 @@ class FakeMatrixApi extends BaseClient { 'passphrase': { 'algorithm': AlgorithmTypes.pbkdf2, 'iterations': 500000, - 'salt': 'F4jJ80mr0Fc8mRwU9JgA3lQDyjPuZXQL' + 'salt': 'F4jJ80mr0Fc8mRwU9JgA3lQDyjPuZXQL', }, 'iv': 'HjbTgIoQH2pI7jQo19NUzA==', - 'mac': 'QbJjQzDnAggU0cM4RBnDxw2XyarRGjdahcKukP9xVlk=' - } + 'mac': 'QbJjQzDnAggU0cM4RBnDxw2XyarRGjdahcKukP9xVlk=', + }, }, { 'type': 'm.cross_signing.master', @@ -939,10 +949,10 @@ class FakeMatrixApi extends BaseClient { 'iv': 'eIb2IITxtmcq+1TrT8D5eQ==', 'ciphertext': 'lWRTPo5qxf4LAVwVPzGHOyMcP181n7bb9/B0lvkLDC2Oy4DvAL0eLx2x3bY=', - 'mac': 'Ynx89tIxPkx0o6ljMgxszww17JOgB4tg4etmNnMC9XI=' - } - } - } + 'mac': 'Ynx89tIxPkx0o6ljMgxszww17JOgB4tg4etmNnMC9XI=', + }, + }, + }, }, { 'type': EventTypes.CrossSigningSelfSigning, @@ -952,10 +962,10 @@ class FakeMatrixApi extends BaseClient { 'iv': 'YqU2XIjYulYZl+bkZtGgVw==', 'ciphertext': 'kM2TSoy/jR/4d357ZoRPbpPypxQl6XRLo3FsEXz+f7vIOp82GeRp28RYb3k=', - 'mac': 'F+DZa5tAFmWsYSryw5EuEpzTmmABRab4GETkM85bGGo=' - } - } - } + 'mac': 'F+DZa5tAFmWsYSryw5EuEpzTmmABRab4GETkM85bGGo=', + }, + }, + }, }, { 'type': EventTypes.CrossSigningUserSigning, @@ -965,10 +975,10 @@ class FakeMatrixApi extends BaseClient { 'iv': 'D7AM3LXFu7ZlyGOkR+OeqQ==', 'ciphertext': 'bYA2+OMgsO6QB1E31aY+ESAWrT0fUBTXqajy4qmL7bVDSZY4Uj64EXNbHuA=', - 'mac': 'j2UtyPo/UBSoiaQCWfzCiRZXp3IRt0ZZujuXgUMjnw4=' - } - } - } + 'mac': 'j2UtyPo/UBSoiaQCWfzCiRZXp3IRt0ZZujuXgUMjnw4=', + }, + }, + }, }, { 'type': EventTypes.MegolmBackup, @@ -978,10 +988,10 @@ class FakeMatrixApi extends BaseClient { 'iv': 'cL/0MJZaiEd3fNU+I9oJrw==', 'ciphertext': 'WL73Pzdk5wZdaaSpaeRH0uZYKcxkuV8IS6Qa2FEfA1+vMeRLuHcWlXbMX0w=', - 'mac': '+xozp909S6oDX8KRV8D8ZFVRyh7eEYQpPP76f+DOsnw=' - } - } - } + 'mac': '+xozp909S6oDX8KRV8D8ZFVRyh7eEYQpPP76f+DOsnw=', + }, + }, + }, }, { 'type': 'io.element.recent_emoji', @@ -991,11 +1001,11 @@ class FakeMatrixApi extends BaseClient { ['🖇️', 0], ['🙃', 'error'], [null, null], - [1, ''] - ] - } + [1, ''], + ], + }, } - ] + ], }, 'to_device': { 'events': [ @@ -1004,8 +1014,8 @@ class FakeMatrixApi extends BaseClient { 'type': 'm.new_device', 'content': { 'device_id': 'XYZABCDE', - 'rooms': ['!726s6s6q:example.com'] - } + 'rooms': ['!726s6s6q:example.com'], + }, }, // { // 'sender': '@othertest:fakeServer.notExisting', @@ -1034,7 +1044,7 @@ class FakeMatrixApi extends BaseClient { }, 'type': 'm.room.encrypted', }, - ] + ], }, 'device_lists': { 'changed': [ @@ -1065,14 +1075,14 @@ class FakeMatrixApi extends BaseClient { 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', 'formatted_body': - 'This is a second text example message' + 'This is a second text example message', }, 'type': 'm.room.message', 'event_id': '143274597446PhrSn:example.org', 'room_id': '!5345234234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824654, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, { 'content': { @@ -1080,17 +1090,17 @@ class FakeMatrixApi extends BaseClient { 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', 'formatted_body': - 'This is a first text example message' + 'This is a first text example message', }, 'type': 'm.room.message', 'event_id': '143274597443PhrSn:example.org', 'room_id': '!5345234234:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } ], - 'prev_batch': 't_1234a' + 'prev_batch': 't_1234a', }, 'state': { 'events': [ @@ -1102,9 +1112,9 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, - ] + ], }, 'account_data': { 'events': [ @@ -1127,14 +1137,14 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, - ] + ], }, - 'prev_batch': 't_1234b' + 'prev_batch': 't_1234b', }, }, - } + }, }; Map> api = { @@ -1150,7 +1160,7 @@ class FakeMatrixApi extends BaseClient { 'og:image:type': 'image/png', 'og:image:height': 48, 'og:image:width': 48, - 'matrix:image:size': 102400 + 'matrix:image:size': 102400, }, '/client/v1/media/preview_url?url=https%3A%2F%2Fmatrix.org&ts=10': (var req) => { @@ -1161,7 +1171,7 @@ class FakeMatrixApi extends BaseClient { 'og:image:type': 'image/png', 'og:image:height': 48, 'og:image:width': 48, - 'matrix:image:size': 102400 + 'matrix:image:size': 102400, }, '/media/v3/config': (var req) => {'m.upload.size': 50000000}, '/client/v1/media/config': (var req) => {'m.upload.size': 50000000}, @@ -1169,16 +1179,16 @@ class FakeMatrixApi extends BaseClient { 'm.homeserver': {'base_url': 'https://fakeserver.notexisting'}, 'm.identity_server': {'base_url': 'https://identity.example.com'}, 'org.example.custom.property': { - 'app_url': 'https://custom.app.example.org' - } + 'app_url': 'https://custom.app.example.org', + }, }, '/client/v3/user/%40alice%3Aexample.com/rooms/!localpart%3Aexample.com/tags': (var req) => { 'tags': { 'm.favourite': {'order': 0.1}, 'u.Work': {'order': 0.7}, - 'u.Customers': {} - } + 'u.Customers': {}, + }, }, '/client/v3/events?from=1234&timeout=10&roomId=%211234': (var req) => { 'start': 's3456_9_0', @@ -1189,43 +1199,43 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '\$143273582443PhrSn:example.org', 'room_id': '!somewhere:over.the.rainbow', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } - ] + ], }, '/client/v3/thirdparty/location?alias=1234': (var req) => [ { 'alias': '#freenode_#matrix:matrix.org', 'protocol': 'irc', - 'fields': {'network': 'freenode', 'channel': '#matrix'} + 'fields': {'network': 'freenode', 'channel': '#matrix'}, } ], '/client/v3/thirdparty/location/irc': (var req) => [ { 'alias': '#freenode_#matrix:matrix.org', 'protocol': 'irc', - 'fields': {'network': 'freenode', 'channel': '#matrix'} + 'fields': {'network': 'freenode', 'channel': '#matrix'}, } ], '/client/v3/thirdparty/user/irc': (var req) => [ { 'userid': '@_gitter_jim:matrix.org', 'protocol': 'gitter', - 'fields': {'user': 'jim'} + 'fields': {'user': 'jim'}, } ], '/client/v3/thirdparty/user?userid=1234': (var req) => [ { 'userid': '@_gitter_jim:matrix.org', 'protocol': 'gitter', - 'fields': {'user': 'jim'} + 'fields': {'user': 'jim'}, } ], '/client/v3/thirdparty/protocol/irc': (var req) => { @@ -1235,19 +1245,19 @@ class FakeMatrixApi extends BaseClient { 'field_types': { 'network': { 'regexp': '([a-z0-9]+\\.)*[a-z0-9]+', - 'placeholder': 'irc.example.org' + 'placeholder': 'irc.example.org', }, 'nickname': {'regexp': '[^\\s#]+', 'placeholder': 'username'}, - 'channel': {'regexp': '#[^\\s]+', 'placeholder': '#foobar'} + 'channel': {'regexp': '#[^\\s]+', 'placeholder': '#foobar'}, }, 'instances': [ { 'desc': 'Freenode', 'icon': 'mxc://example.org/JkLmNoPq', 'fields': {'network': 'freenode'}, - 'network_id': 'freenode' + 'network_id': 'freenode', } - ] + ], }, '/client/v3/thirdparty/protocols': (var req) => { 'irc': { @@ -1257,19 +1267,19 @@ class FakeMatrixApi extends BaseClient { 'field_types': { 'network': { 'regexp': '([a-z0-9]+\\.)*[a-z0-9]+', - 'placeholder': 'irc.example.org' + 'placeholder': 'irc.example.org', }, 'nickname': {'regexp': '[^\\s]+', 'placeholder': 'username'}, - 'channel': {'regexp': '#[^\\s]+', 'placeholder': '#foobar'} + 'channel': {'regexp': '#[^\\s]+', 'placeholder': '#foobar'}, }, 'instances': [ { 'network_id': 'freenode', 'desc': 'Freenode', 'icon': 'mxc://example.org/JkLmNoPq', - 'fields': {'network': 'freenode.net'} + 'fields': {'network': 'freenode.net'}, } - ] + ], }, 'gitter': { 'user_fields': ['username'], @@ -1279,18 +1289,18 @@ class FakeMatrixApi extends BaseClient { 'username': {'regexp': '@[^\\s]+', 'placeholder': '@username'}, 'room': { 'regexp': '[^\\s]+\\/[^\\s]+', - 'placeholder': 'matrix-org/matrix-doc' - } + 'placeholder': 'matrix-org/matrix-doc', + }, }, 'instances': [ { 'network_id': 'gitter', 'desc': 'Gitter', 'icon': 'mxc://example.org/zXyWvUt', - 'fields': {} + 'fields': {}, } - ] - } + ], + }, }, '/client/v3/account/whoami': (var req) => {'user_id': 'alice@example.com'}, @@ -1303,11 +1313,11 @@ class FakeMatrixApi extends BaseClient { '1': 'stable', '2': 'stable', '3': 'unstable', - 'test-version': 'unstable' - } + 'test-version': 'unstable', + }, }, - 'com.example.custom.ratelimit': {'max_requests_per_hour': 600} - } + 'com.example.custom.ratelimit': {'max_requests_per_hour': 600}, + }, }, '/client/v3/rooms/1234/context/1234?filter=%7B%7D&limit=10': (var req) => { @@ -1318,14 +1328,14 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '\$143273582443PhrSn:example.org', 'room_id': '!636q39766251:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } ], 'event': { @@ -1335,17 +1345,17 @@ class FakeMatrixApi extends BaseClient { 'h': 398, 'w': 394, 'mimetype': 'image/jpeg', - 'size': 31037 + 'size': 31037, }, 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE', - 'msgtype': 'm.image' + 'msgtype': 'm.image', }, 'type': 'm.room.message', 'event_id': '\$f3h4d129462ha:example.com', 'room_id': '!636q39766251:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, 'events_before': [ { @@ -1354,14 +1364,14 @@ class FakeMatrixApi extends BaseClient { 'filename': 'something-important.doc', 'info': {'mimetype': 'application/msword', 'size': 46144}, 'msgtype': 'm.file', - 'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe' + 'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', }, 'type': 'm.room.message', 'event_id': '\$143273582443PhrSn:example.org', 'room_id': '!636q39766251:example.com', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, } ], 'start': 't27-54_2_0_2', @@ -1373,8 +1383,8 @@ class FakeMatrixApi extends BaseClient { 'm.federate': true, 'predecessor': { 'event_id': '\$something:example.org', - 'room_id': '!oldroom:example.org' - } + 'room_id': '!oldroom:example.org', + }, }, 'type': 'm.room.create', 'event_id': '\$143273582443PhrSn:example.org', @@ -1382,13 +1392,13 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, { 'content': { 'membership': 'join', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid' + 'displayname': 'Alice Margatroid', }, 'type': 'm.room.member', 'event_id': '\$143273582443PhrSn:example.org', @@ -1396,9 +1406,9 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '@alice:example.org' + 'state_key': '@alice:example.org', } - ] + ], }, '/client/v3/admin/whois/%40alice%3Aexample.com': (var req) => { 'user_id': '@peter:rabbit.rocks', @@ -1410,19 +1420,19 @@ class FakeMatrixApi extends BaseClient { { 'ip': '127.0.0.1', 'last_seen': 1411996332123, - 'user_agent': 'curl/7.31.0-DEV' + 'user_agent': 'curl/7.31.0-DEV', }, { 'ip': '10.0.0.2', 'last_seen': 1411996332123, 'user_agent': - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36' + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36', } - ] + ], } - ] - } - } + ], + }, + }, }, '/client/v3/user/%40alice%3Aexample.com/account_data/test.account.data': (var req) => {'foo': 'bar'}, @@ -1430,7 +1440,7 @@ class FakeMatrixApi extends BaseClient { (var req) => {'foo': 'bar'}, '/client/v3/directory/room/%23testalias%3Aexample.com': (var reqI) => { 'room_id': '!abnjk1jdasj98:capuchins.com', - 'servers': ['capuchins.com', 'matrix.org', 'another.com'] + 'servers': ['capuchins.com', 'matrix.org', 'another.com'], }, '/client/v3/account/3pid': (var req) => { 'threepids': [ @@ -1438,9 +1448,9 @@ class FakeMatrixApi extends BaseClient { 'medium': 'email', 'address': 'monkey@banana.island', 'validated_at': 1535176800000, - 'added_at': 1535336848756 + 'added_at': 1535336848756, } - ] + ], }, '/client/v3/devices': (var req) => { 'devices': [ @@ -1448,9 +1458,9 @@ class FakeMatrixApi extends BaseClient { 'device_id': 'QBUAZIFURK', 'display_name': 'android', 'last_seen_ip': '1.2.3.4', - 'last_seen_ts': 1474491775024 + 'last_seen_ts': 1474491775024, } - ] + ], }, '/client/v3/notifications?from=1234&limit=10&only=1234': (var req) => { 'next_token': 'abcdef', @@ -1466,23 +1476,23 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '\$143273582443PhrSn:example.org', 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} - } + 'unsigned': {'age': 1234}, + }, } - ] + ], }, '/client/v3/devices/QBUAZIFURK': (var req) => { 'device_id': 'QBUAZIFURK', 'display_name': 'android', 'last_seen_ip': '1.2.3.4', - 'last_seen_ts': 1474491775024 + 'last_seen_ts': 1474491775024, }, '/client/v3/profile/%40test%3AfakeServer.notExisting': (var reqI) => {'displayname': 'Some First Name Some Last Name'}, @@ -1500,20 +1510,20 @@ class FakeMatrixApi extends BaseClient { 'uris': [ 'turn:turn.example.com:3478?transport=udp', 'turn:10.20.30.40:3478?transport=tcp', - 'turns:10.20.30.40:443?transport=tcp' + 'turns:10.20.30.40:443?transport=tcp', ], - 'ttl': 86400 + 'ttl': 86400, }, '/client/v3/presence/${Uri.encodeComponent('@alice:example.com')}/status': (var req) => { 'presence': 'unavailable', 'last_active_ago': 420845, 'status_msg': 'test', - 'currently_active': false + 'currently_active': false, }, '/client/v3/keys/changes?from=1234&to=1234': (var req) => { 'changed': ['@alice:example.com', '@bob:example.org'], - 'left': ['@clara:example.com', '@doug:example.org'] + 'left': ['@clara:example.com', '@doug:example.org'], }, '/client/v3/pushers': (var req) => { 'pushers': [ @@ -1528,9 +1538,9 @@ class FakeMatrixApi extends BaseClient { 'data': { 'url': 'https://example.com/_matrix/push/v1/notify', 'format': 'event_id_only', - } + }, } - ] + ], }, '/client/v3/publicRooms?limit=10&since=1234&server=example.com': (var req) => { @@ -1544,22 +1554,22 @@ class FakeMatrixApi extends BaseClient { 'num_joined_members': 37, 'room_id': '!ol19s:bleecker.street', 'topic': 'Tasty tasty cheese', - 'world_readable': true + 'world_readable': true, } ], 'next_batch': 'p190q', 'prev_batch': 'p1902', - 'total_room_count_estimate': 115 + 'total_room_count_estimate': 115, }, '/client/v3/room/!localpart%3Aexample.com/aliases': (var req) => { 'aliases': [ '#somewhere:example.com', '#another:example.com', - '#hat_trick:example.com' - ] + '#hat_trick:example.com', + ], }, '/client/v3/joined_rooms': (var req) => { - 'joined_rooms': ['!foo:example.com'] + 'joined_rooms': ['!foo:example.com'], }, '/client/v3/directory/list/room/!localpart%3Aexample.com': (var req) => {'visibility': 'public'}, @@ -1596,13 +1606,13 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, { 'content': { 'membership': 'join', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid' + 'displayname': 'Alice Margatroid', }, 'type': 'm.room.member', 'event_id': '\$143273582443PhrSn:example.org', @@ -1610,7 +1620,7 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '@alice:example.org' + 'state_key': '@alice:example.org', }, { 'content': { @@ -1619,8 +1629,8 @@ class FakeMatrixApi extends BaseClient { 'm.federate': true, 'predecessor': { 'event_id': '\$something:example.org', - 'room_id': '!oldroom:example.org' - } + 'room_id': '!oldroom:example.org', + }, }, 'type': 'm.room.create', 'event_id': '\$143273582443PhrSn:example.org', @@ -1628,7 +1638,7 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', }, { 'content': { @@ -1641,7 +1651,7 @@ class FakeMatrixApi extends BaseClient { 'state_default': 50, 'users': {'@example:localhost': 100}, 'users_default': 0, - 'notifications': {'room': 20} + 'notifications': {'room': 20}, }, 'type': 'm.room.power_levels', 'event_id': '\$143273582443PhrSn:example.org', @@ -1649,7 +1659,7 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '' + 'state_key': '', } ], '/client/v3/rooms/!localpart:server.abc/event/1234': (var req) => { @@ -1657,28 +1667,28 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '143273582443PhrSn:example.org', 'room_id': '!localpart:server.abc', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, '/client/v3/rooms/!localpart%3Aserver.abc/event/1234': (var req) => { 'content': { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '143273582443PhrSn:example.org', 'room_id': '!localpart:server.abc', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, '/client/v3/rooms/!1234%3Aexample.com/event/not_found': (var req) => { 'errcode': 'M_NOT_FOUND', @@ -1690,14 +1700,14 @@ class FakeMatrixApi extends BaseClient { 'body': 'This is an example text message', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' + 'formatted_body': 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '143273582443PhrSn:example.org', 'room_id': '!localpart:server.abc', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, '/client/v3/rooms/!1234%3Aexample.com/event/encrypted_event': (var req) => { @@ -1706,14 +1716,14 @@ class FakeMatrixApi extends BaseClient { 'ciphertext': 'invalid', 'device_id': 'SOME_DEVICE', 'sender_key': 'invalid', - 'session_id': 'not_found' + 'session_id': 'not_found', }, 'type': 'm.room.encrypted', 'event_id': '143273582443PhrSn:example.org', 'room_id': '!localpart:server.abc', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} + 'unsigned': {'age': 1234}, }, '/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, @@ -1737,16 +1747,16 @@ class FakeMatrixApi extends BaseClient { }, '/client/v3/login': (var req) => { 'flows': [ - {'type': 'm.login.password'} - ] + {'type': 'm.login.password'}, + ], }, '/client/v3/rooms/!localpart%3Aserver.abc/joined_members': (var req) => { 'joined': { '@bar:example.com': { 'display_name': 'Bar', - 'avatar_url': 'mxc://riot.ovh/printErCATzZijQsSDWorRaK' - } - } + 'avatar_url': 'mxc://riot.ovh/printErCATzZijQsSDWorRaK', + }, + }, }, '/client/v3/rooms/!localpart%3Aserver.abc/members?at=1234&membership=join¬_membership=leave': (var req) => { @@ -1755,7 +1765,7 @@ class FakeMatrixApi extends BaseClient { 'content': { 'membership': 'join', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid' + 'displayname': 'Alice Margatroid', }, 'type': 'm.room.member', 'event_id': '§143273582443PhrSn:example.org', @@ -1763,9 +1773,9 @@ class FakeMatrixApi extends BaseClient { 'sender': '@alice:example.com', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '@alice:example.com' + 'state_key': '@alice:example.com', } - ] + ], }, '/client/v3/rooms/!696r7674:example.com/members': (var req) => { 'chunk': [ @@ -1773,7 +1783,7 @@ class FakeMatrixApi extends BaseClient { 'content': { 'membership': 'join', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid' + 'displayname': 'Alice Margatroid', }, 'type': 'm.room.member', 'event_id': '§143273582443PhrSn:example.org', @@ -1781,9 +1791,9 @@ class FakeMatrixApi extends BaseClient { 'sender': '@alice:example.com', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '@alice:example.com' + 'state_key': '@alice:example.com', } - ] + ], }, '/client/v3/rooms/!726s6s6q%3Aexample.com/members': (var req) => { 'chunk': [ @@ -1791,7 +1801,7 @@ class FakeMatrixApi extends BaseClient { 'content': { 'membership': 'join', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid' + 'displayname': 'Alice Margatroid', }, 'type': 'm.room.member', 'event_id': '§143273582443PhrSn:example.org', @@ -1799,9 +1809,9 @@ class FakeMatrixApi extends BaseClient { 'sender': '@alice:example.com', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '@alice:example.com' + 'state_key': '@alice:example.com', } - ] + ], }, '/client/v3/rooms/!localpart%3Aserver.abc/members': (var req) => { 'chunk': [ @@ -1809,7 +1819,7 @@ class FakeMatrixApi extends BaseClient { 'content': { 'membership': 'join', 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid' + 'displayname': 'Alice Margatroid', }, 'type': 'm.room.member', 'event_id': '§143273582443PhrSn:example.org', @@ -1817,22 +1827,22 @@ class FakeMatrixApi extends BaseClient { 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': '@alice:example.org' + 'state_key': '@alice:example.org', } - ] + ], }, '/client/v3/pushrules/global/content/nocake': (var req) => { 'actions': ['dont_notify'], 'pattern': 'cake*lie', 'rule_id': 'nocake', 'enabled': true, - 'default': false + 'default': false, }, '/client/v3/pushrules/global/content/nocake/enabled': (var req) => { 'enabled': true, }, '/client/v3/pushrules/global/content/nocake/actions': (var req) => { - 'actions': ['notify'] + 'actions': ['notify'], }, '/client/v3/pushrules': (var req) => { 'global': { @@ -1841,12 +1851,12 @@ class FakeMatrixApi extends BaseClient { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight'} + {'set_tweak': 'highlight'}, ], 'default': true, 'enabled': true, 'pattern': 'alice', - 'rule_id': '.m.rule.contains_user_name' + 'rule_id': '.m.rule.contains_user_name', } ], 'override': [ @@ -1855,7 +1865,7 @@ class FakeMatrixApi extends BaseClient { 'conditions': [], 'default': true, 'enabled': false, - 'rule_id': '.m.rule.master' + 'rule_id': '.m.rule.master', }, { 'actions': ['dont_notify'], @@ -1863,12 +1873,12 @@ class FakeMatrixApi extends BaseClient { { 'key': 'content.msgtype', 'kind': 'event_match', - 'pattern': 'm.notice' + 'pattern': 'm.notice', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.suppress_notices' + 'rule_id': '.m.rule.suppress_notices', } ], 'room': [], @@ -1878,106 +1888,106 @@ class FakeMatrixApi extends BaseClient { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'ring'}, - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.call.invite' + 'pattern': 'm.call.invite', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.call' + 'rule_id': '.m.rule.call', }, { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight'} + {'set_tweak': 'highlight'}, ], 'conditions': [ - {'kind': 'contains_display_name'} + {'kind': 'contains_display_name'}, ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.contains_display_name' + 'rule_id': '.m.rule.contains_display_name', }, { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ - {'is': '2', 'kind': 'room_member_count'} + {'is': '2', 'kind': 'room_member_count'}, ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.room_one_to_one' + 'rule_id': '.m.rule.room_one_to_one', }, { 'actions': [ 'notify', {'set_tweak': 'sound', 'value': 'default'}, - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.member' + 'pattern': 'm.room.member', }, { 'key': 'content.membership', 'kind': 'event_match', - 'pattern': 'invite' + 'pattern': 'invite', }, { 'key': 'state_key', 'kind': 'event_match', - 'pattern': '@alice:example.com' + 'pattern': '@alice:example.com', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.invite_for_me' + 'rule_id': '.m.rule.invite_for_me', }, { 'actions': [ 'notify', - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.member' + 'pattern': 'm.room.member', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.member_event' + 'rule_id': '.m.rule.member_event', }, { 'actions': [ 'notify', - {'set_tweak': 'highlight', 'value': false} + {'set_tweak': 'highlight', 'value': false}, ], 'conditions': [ { 'key': 'type', 'kind': 'event_match', - 'pattern': 'm.room.message' + 'pattern': 'm.room.message', } ], 'default': true, 'enabled': true, - 'rule_id': '.m.rule.message' + 'rule_id': '.m.rule.message', } - ] - } + ], + }, }, '/client/v3/sync?filter=%7B%22room%22%3A%7B%22include_leave%22%3Atrue%2C%22state%22%3A%7B%22lazy_load_members%22%3Atrue%7D%2C%22timeline%22%3A%7B%22limit%22%3A10%7D%7D%7D&timeout=0': (var req) => archiveSyncResponse, @@ -1991,31 +2001,31 @@ class FakeMatrixApi extends BaseClient { 'room': { 'state': { 'types': ['m.room.*'], - 'not_rooms': ['!726s6s6q:example.com'] + 'not_rooms': ['!726s6s6q:example.com'], }, 'timeline': { 'limit': 10, 'types': ['m.room.message'], 'not_rooms': ['!726s6s6q:example.com'], - 'not_senders': ['@spam:example.com'] + 'not_senders': ['@spam:example.com'], }, 'ephemeral': { 'types': ['m.receipt', 'm.typing'], 'not_rooms': ['!726s6s6q:example.com'], - 'not_senders': ['@spam:example.com'] + 'not_senders': ['@spam:example.com'], }, 'account_data': { 'types': ['m.receipt', 'm.typing'], 'not_rooms': ['!726s6s6q:example.com'], - 'not_senders': ['@spam:example.com'] - } + 'not_senders': ['@spam:example.com'], + }, }, 'presence': { 'types': ['m.presence'], - 'not_senders': ['@alice:example.com'] + 'not_senders': ['@alice:example.com'], }, 'event_format': 'client', - 'event_fields': ['type', 'content', 'sender'] + 'event_fields': ['type', 'content', 'sender'], }, '/client/v3/room_keys/version': (var req) => { 'algorithm': AlgorithmTypes.megolmBackupV1Curve25519AesSha2, @@ -2085,7 +2095,7 @@ class FakeMatrixApi extends BaseClient { '/client/v3/refresh': (var req) => { 'access_token': 'a_new_token', 'expires_in_ms': 1000 * 60 * 5, - 'refresh_token': 'another_new_token' + 'refresh_token': 'another_new_token', }, '/client/v3/delete_devices': (var req) => {}, '/client/v3/account/3pid/add': (var req) => {}, @@ -2104,9 +2114,9 @@ class FakeMatrixApi extends BaseClient { '!qPewotXpIctQySfjSy:localhost': { 'order': 1, 'next_batch': 'BdgFsdfHSf-dsFD', - 'results': ['\$144429830826TWwbB:localhost'] - } - } + 'results': ['\$144429830826TWwbB:localhost'], + }, + }, }, 'highlights': ['martians', 'men'], 'next_batch': '5FdgFsd234dfgsdfFD', @@ -2120,19 +2130,19 @@ class FakeMatrixApi extends BaseClient { 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', 'formatted_body': - 'This is an example text message' + 'This is an example text message', }, 'type': 'm.room.message', 'event_id': '\$144429830826TWwbB:localhost', 'room_id': '!qPewotXpIctQySfjSy:localhost', 'sender': '@example:example.org', 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} - } + 'unsigned': {'age': 1234}, + }, } - ] - } - } + ], + }, + }, }, '/client/v3/account/deactivate': (var req) => {'id_server_unbind_result': 'success'}, @@ -2141,34 +2151,34 @@ class FakeMatrixApi extends BaseClient { { 'user_id': '@foo:bar.com', 'display_name': 'Foo', - 'avatar_url': 'mxc://bar.com/foo' + 'avatar_url': 'mxc://bar.com/foo', } ], - 'limited': false + 'limited': false, }, '/client/v3/register/email/requestToken': (var req) => { 'sid': '123abc', - 'submit_url': 'https://example.org/path/to/submitToken' + 'submit_url': 'https://example.org/path/to/submitToken', }, '/client/v3/register/msisdn/requestToken': (var req) => { 'sid': '123abc', - 'submit_url': 'https://example.org/path/to/submitToken' + 'submit_url': 'https://example.org/path/to/submitToken', }, '/client/v3/account/password/email/requestToken': (var req) => { 'sid': '123abc', - 'submit_url': 'https://example.org/path/to/submitToken' + 'submit_url': 'https://example.org/path/to/submitToken', }, '/client/v3/account/password/msisdn/requestToken': (var req) => { 'sid': '123abc', - 'submit_url': 'https://example.org/path/to/submitToken' + 'submit_url': 'https://example.org/path/to/submitToken', }, '/client/v3/account/3pid/email/requestToken': (var req) => { 'sid': '123abc', - 'submit_url': 'https://example.org/path/to/submitToken' + 'submit_url': 'https://example.org/path/to/submitToken', }, '/client/v3/account/3pid/msisdn/requestToken': (var req) => { 'sid': '123abc', - 'submit_url': 'https://example.org/path/to/submitToken' + 'submit_url': 'https://example.org/path/to/submitToken', }, '/client/v3/rooms/!localpart%3Aexample.com/receipt/m.read/%241234%3Aexample.com': (var req) => {}, @@ -2189,12 +2199,12 @@ class FakeMatrixApi extends BaseClient { 'num_joined_members': 37, 'room_id': '!ol19s:bleecker.street', 'topic': 'Tasty tasty cheese', - 'world_readable': true + 'world_readable': true, } ], 'next_batch': 'p190q', 'prev_batch': 'p1902', - 'total_room_count_estimate': 115 + 'total_room_count_estimate': 115, }, '/client/v3/keys/claim': (var req) => { 'failures': {}, @@ -2208,11 +2218,11 @@ class FakeMatrixApi extends BaseClient { 'signatures': { '@alice:example.com': { 'ed25519:JLAFKJWSCS': - 'XdboCa0Ljoh0Y0i/IVnmMqy/+T1hJyu8BA/nRYniJMQ7QWh/pGS5AsWswdARD+MAX+r4u98Qzk0y27HUddZXDA' - } - } - } - } + 'XdboCa0Ljoh0Y0i/IVnmMqy/+T1hJyu8BA/nRYniJMQ7QWh/pGS5AsWswdARD+MAX+r4u98Qzk0y27HUddZXDA', + }, + }, + }, + }, }, if (decodeJson(req)['one_time_keys'] ['@test:fakeServer.notExisting'] != @@ -2230,7 +2240,7 @@ class FakeMatrixApi extends BaseClient { }, }, }, - } + }, }, '/client/v3/rooms/!localpart%3Aexample.com/invite': (var req) => {}, '/client/v3/rooms/!1234%3AfakeServer.notExisting/invite': (var req) => {}, @@ -2259,7 +2269,7 @@ class FakeMatrixApi extends BaseClient { ?.keys .length ?? 0, - } + }, }, '/client/v3/keys/query': (var req) => { 'failures': {}, @@ -2270,40 +2280,40 @@ class FakeMatrixApi extends BaseClient { 'device_id': 'JLAFKJWSCS', 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': { 'curve25519:JLAFKJWSCS': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', 'ed25519:JLAFKJWSCS': - 'rUFJftIWpFF/jqqz3bexGGYiG8UobKhzkeabqw1v0zM' + 'rUFJftIWpFF/jqqz3bexGGYiG8UobKhzkeabqw1v0zM', }, 'signatures': { '@alice:example.com': { 'ed25519:JLAFKJWSCS': - 'go3mi5o3Ile+Ik+lCEpHmBmyJmKWfnRDCBBvfaVlKsMyha5IORuYcxwEUrAeLyAeeeHvkWDFX+No5eY1jYeKBw' - } + 'go3mi5o3Ile+Ik+lCEpHmBmyJmKWfnRDCBBvfaVlKsMyha5IORuYcxwEUrAeLyAeeeHvkWDFX+No5eY1jYeKBw', + }, }, - 'unsigned': {'device_display_name': 'Alices mobile phone'} + 'unsigned': {'device_display_name': 'Alices mobile phone'}, }, 'OTHERDEVICE': { 'user_id': '@alice:example.com', 'device_id': 'OTHERDEVICE', 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': { 'curve25519:OTHERDEVICE': 'wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE', 'ed25519:OTHERDEVICE': - '2Lyaj5NB7HPqKZMjZpA/pECXuQ+9wi8AGFdw33y3DuQ' + '2Lyaj5NB7HPqKZMjZpA/pECXuQ+9wi8AGFdw33y3DuQ', }, 'signatures': { '@alice:example.com': { 'ed25519:OTHERDEVICE': 'bwHd6ylISP13AICdDPd0HQd4V6dvvd4vno8/OwUNdm9UAprr3YjkDqVw425I74u2UQAarq9bytBqVqFyD6trAw', - } + }, }, }, }, @@ -2313,13 +2323,13 @@ class FakeMatrixApi extends BaseClient { 'device_id': 'GHTYAJCE', 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': { 'curve25519:GHTYAJCE': '7rvl3jORJkBiK4XX1e5TnGnqz068XfYJ0W++Ml63rgk', 'ed25519:GHTYAJCE': - 'gjL//fyaFHADt9KBADGag8g7F8Up78B/K1zXeiEPLJo' + 'gjL//fyaFHADt9KBADGag8g7F8Up78B/K1zXeiEPLJo', }, 'signatures': { '@test:fakeServer.notExisting': { @@ -2335,13 +2345,13 @@ class FakeMatrixApi extends BaseClient { 'device_id': 'OTHERDEVICE', 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': { 'curve25519:OTHERDEVICE': 'R96BA0qE1+QAWLp7E1jyWSTJ1VXMLpEdiM2SZHlKMXM', 'ed25519:OTHERDEVICE': - 'EQo9eYbSygIbOR+tVJziqAY1NI6Gga+JQOVIqJe4mr4' + 'EQo9eYbSygIbOR+tVJziqAY1NI6Gga+JQOVIqJe4mr4', }, 'signatures': { '@test:fakeServer.notExisting': { @@ -2359,7 +2369,7 @@ class FakeMatrixApi extends BaseClient { 'device_id': 'FOXDEVICE', 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': { 'curve25519:FOXDEVICE': @@ -2459,14 +2469,14 @@ class FakeMatrixApi extends BaseClient { 'access_token': 'SomeT0kenHere', 'token_type': 'Bearer', 'matrix_server_name': 'example.com', - 'expires_in': 3600.0 + 'expires_in': 3600.0, }, '/client/v3/user/@test:fakeServer.notExisting/openid/request_token': (var req) => { 'access_token': 'SomeT0kenHere', 'token_type': 'Bearer', 'matrix_server_name': 'example.com', - 'expires_in': 3600 + 'expires_in': 3600, }, '/client/v3/login': (var req) => { 'user_id': '@test:fakeServer.notExisting', @@ -2475,8 +2485,8 @@ class FakeMatrixApi extends BaseClient { 'device_id': 'GHTYAJCE', 'well_known': { 'm.homeserver': {'base_url': 'https://example.org'}, - 'm.identity_server': {'base_url': 'https://id.example.org'} - } + 'm.identity_server': {'base_url': 'https://id.example.org'}, + }, }, '/media/v3/upload?filename=file.jpeg': (var req) => {'content_uri': 'mxc://example.com/AQwafuaFswefuhsfAFAgsw'}, @@ -2654,7 +2664,7 @@ class FakeMatrixApi extends BaseClient { }, '/client/unstable/org.matrix.msc3814.v1/dehydrated_device': (var _) => { 'device_id': 'DEHYDDEV', - } + }, }, 'DELETE': { '/unknown/token': (var req) => {'errcode': 'M_UNKNOWN_TOKEN'}, diff --git a/lib/matrix_api_lite/generated/api.dart b/lib/matrix_api_lite/generated/api.dart index 9b7ded4e1..ce47e3720 100644 --- a/lib/matrix_api_lite/generated/api.dart +++ b/lib/matrix_api_lite/generated/api.dart @@ -88,17 +88,22 @@ class Api { /// The duration in milliseconds that the /// [`/_matrix/app/v1/ping`](#post_matrixappv1ping) /// request took from the homeserver's point of view. - Future pingAppservice(String appserviceId, - {String? transactionId}) async { + Future pingAppservice( + String appserviceId, { + String? transactionId, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/appservice/${Uri.encodeComponent(appserviceId)}/ping'); + path: + '_matrix/client/v1/appservice/${Uri.encodeComponent(appserviceId)}/ping', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (transactionId != null) 'transaction_id': transactionId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (transactionId != null) 'transaction_id': transactionId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -138,15 +143,18 @@ class Api { /// of 1 request per minute. /// /// [auth] Additional authentication information for the user-interactive authentication API. - Future generateLoginToken( - {AuthenticationData? auth}) async { + Future generateLoginToken({ + AuthenticationData? auth, + }) async { final requestUri = Uri(path: '_matrix/client/v1/login/get_token'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -201,21 +209,27 @@ class Api { /// repository SHOULD impose a maximum value for this parameter. The /// content repository MAY respond before the timeout. /// - Future getContentAuthed(String serverName, String mediaId, - {int? timeoutMs}) async { + Future getContentAuthed( + String serverName, + String mediaId, { + int? timeoutMs, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/media/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', - queryParameters: { - if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), - }); + path: + '_matrix/client/v1/media/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', + queryParameters: { + if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); return FileResponse( - contentType: response.headers['content-type'], data: responseBody); + contentType: response.headers['content-type'], + data: responseBody, + ); } /// This will download content from the content repository (same as @@ -247,21 +261,27 @@ class Api { /// content repository MAY respond before the timeout. /// Future getContentOverrideNameAuthed( - String serverName, String mediaId, String fileName, - {int? timeoutMs}) async { + String serverName, + String mediaId, + String fileName, { + int? timeoutMs, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/media/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}/${Uri.encodeComponent(fileName)}', - queryParameters: { - if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), - }); + path: + '_matrix/client/v1/media/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}/${Uri.encodeComponent(fileName)}', + queryParameters: { + if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); return FileResponse( - contentType: response.headers['content-type'], data: responseBody); + contentType: response.headers['content-type'], + data: responseBody, + ); } /// Get information about a URL for the client. Typically this is called when a @@ -280,11 +300,13 @@ class Api { /// return a newer version if it does not have the requested version /// available. Future getUrlPreviewAuthed(Uri url, {int? ts}) async { - final requestUri = - Uri(path: '_matrix/client/v1/media/preview_url', queryParameters: { - 'url': url.toString(), - if (ts != null) 'ts': ts.toString(), - }); + final requestUri = Uri( + path: '_matrix/client/v1/media/preview_url', + queryParameters: { + 'url': url.toString(), + if (ts != null) 'ts': ts.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -346,25 +368,34 @@ class Api { /// server SHOULD behave as though `animated` is `false`. /// Future getContentThumbnailAuthed( - String serverName, String mediaId, int width, int height, - {Method? method, int? timeoutMs, bool? animated}) async { + String serverName, + String mediaId, + int width, + int height, { + Method? method, + int? timeoutMs, + bool? animated, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/media/thumbnail/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', - queryParameters: { - 'width': width.toString(), - 'height': height.toString(), - if (method != null) 'method': method.name, - if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), - if (animated != null) 'animated': animated.toString(), - }); + path: + '_matrix/client/v1/media/thumbnail/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', + queryParameters: { + 'width': width.toString(), + 'height': height.toString(), + if (method != null) 'method': method.name, + if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), + if (animated != null) 'animated': animated.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); return FileResponse( - contentType: response.headers['content-type'], data: responseBody); + contentType: response.headers['content-type'], + data: responseBody, + ); } /// Queries the server to determine if a given registration token is still @@ -382,10 +413,11 @@ class Api { /// the server. Future registrationTokenValidity(String token) async { final requestUri = Uri( - path: '_matrix/client/v1/register/m.login.registration_token/validity', - queryParameters: { - 'token': token, - }); + path: '_matrix/client/v1/register/m.login.registration_token/validity', + queryParameters: { + 'token': token, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); @@ -422,17 +454,22 @@ class Api { /// /// [from] A pagination token from a previous result. If specified, `max_depth` and `suggested_only` cannot /// be changed from the first request. - Future getSpaceHierarchy(String roomId, - {bool? suggestedOnly, int? limit, int? maxDepth, String? from}) async { + Future getSpaceHierarchy( + String roomId, { + bool? suggestedOnly, + int? limit, + int? maxDepth, + String? from, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/hierarchy', - queryParameters: { - if (suggestedOnly != null) 'suggested_only': suggestedOnly.toString(), - if (limit != null) 'limit': limit.toString(), - if (maxDepth != null) 'max_depth': maxDepth.toString(), - if (from != null) 'from': from, - }); + path: '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/hierarchy', + queryParameters: { + if (suggestedOnly != null) 'suggested_only': suggestedOnly.toString(), + if (limit != null) 'limit': limit.toString(), + if (maxDepth != null) 'max_depth': maxDepth.toString(), + if (from != null) 'from': from, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -493,22 +530,25 @@ class Api { /// /// The default value is `false`. Future getRelatingEvents( - String roomId, String eventId, - {String? from, - String? to, - int? limit, - Direction? dir, - bool? recurse}) async { + String roomId, + String eventId, { + String? from, + String? to, + int? limit, + Direction? dir, + bool? recurse, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/relations/${Uri.encodeComponent(eventId)}', - queryParameters: { - if (from != null) 'from': from, - if (to != null) 'to': to, - if (limit != null) 'limit': limit.toString(), - if (dir != null) 'dir': dir.name, - if (recurse != null) 'recurse': recurse.toString(), - }); + path: + '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/relations/${Uri.encodeComponent(eventId)}', + queryParameters: { + if (from != null) 'from': from, + if (to != null) 'to': to, + if (limit != null) 'limit': limit.toString(), + if (dir != null) 'dir': dir.name, + if (recurse != null) 'recurse': recurse.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -572,22 +612,26 @@ class Api { /// /// The default value is `false`. Future getRelatingEventsWithRelType( - String roomId, String eventId, String relType, - {String? from, - String? to, - int? limit, - Direction? dir, - bool? recurse}) async { + String roomId, + String eventId, + String relType, { + String? from, + String? to, + int? limit, + Direction? dir, + bool? recurse, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/relations/${Uri.encodeComponent(eventId)}/${Uri.encodeComponent(relType)}', - queryParameters: { - if (from != null) 'from': from, - if (to != null) 'to': to, - if (limit != null) 'limit': limit.toString(), - if (dir != null) 'dir': dir.name, - if (recurse != null) 'recurse': recurse.toString(), - }); + path: + '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/relations/${Uri.encodeComponent(eventId)}/${Uri.encodeComponent(relType)}', + queryParameters: { + if (from != null) 'from': from, + if (to != null) 'to': to, + if (limit != null) 'limit': limit.toString(), + if (dir != null) 'dir': dir.name, + if (recurse != null) 'recurse': recurse.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -596,7 +640,8 @@ class Api { final responseString = utf8.decode(responseBody); final json = jsonDecode(responseString); return GetRelatingEventsWithRelTypeResponse.fromJson( - json as Map); + json as Map, + ); } /// Retrieve all of the child events for a given parent event which relate to the parent @@ -658,22 +703,27 @@ class Api { /// The default value is `false`. Future getRelatingEventsWithRelTypeAndEventType( - String roomId, String eventId, String relType, String eventType, - {String? from, - String? to, - int? limit, - Direction? dir, - bool? recurse}) async { + String roomId, + String eventId, + String relType, + String eventType, { + String? from, + String? to, + int? limit, + Direction? dir, + bool? recurse, + }) async { final requestUri = Uri( - path: - '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/relations/${Uri.encodeComponent(eventId)}/${Uri.encodeComponent(relType)}/${Uri.encodeComponent(eventType)}', - queryParameters: { - if (from != null) 'from': from, - if (to != null) 'to': to, - if (limit != null) 'limit': limit.toString(), - if (dir != null) 'dir': dir.name, - if (recurse != null) 'recurse': recurse.toString(), - }); + path: + '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/relations/${Uri.encodeComponent(eventId)}/${Uri.encodeComponent(relType)}/${Uri.encodeComponent(eventType)}', + queryParameters: { + if (from != null) 'from': from, + if (to != null) 'to': to, + if (limit != null) 'limit': limit.toString(), + if (dir != null) 'dir': dir.name, + if (recurse != null) 'recurse': recurse.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -682,7 +732,8 @@ class Api { final responseString = utf8.decode(responseBody); final json = jsonDecode(responseString); return GetRelatingEventsWithRelTypeAndEventTypeResponse.fromJson( - json as Map); + json as Map, + ); } /// This API is used to paginate through the list of the thread roots in a given room. @@ -704,15 +755,20 @@ class Api { /// /// [from] A pagination token from a previous result. When not provided, the server starts paginating from /// the most recent event visible to the user (as per history visibility rules; topologically). - Future getThreadRoots(String roomId, - {Include? include, int? limit, String? from}) async { + Future getThreadRoots( + String roomId, { + Include? include, + int? limit, + String? from, + }) async { final requestUri = Uri( - path: '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/threads', - queryParameters: { - if (include != null) 'include': include.name, - if (limit != null) 'limit': limit.toString(), - if (from != null) 'from': from, - }); + path: '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/threads', + queryParameters: { + if (include != null) 'include': include.name, + if (limit != null) 'limit': limit.toString(), + if (from != null) 'from': from, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -754,14 +810,18 @@ class Api { /// /// [dir] The direction in which to search. `f` for forwards, `b` for backwards. Future getEventByTimestamp( - String roomId, int ts, Direction dir) async { + String roomId, + int ts, + Direction dir, + ) async { final requestUri = Uri( - path: - '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/timestamp_to_event', - queryParameters: { - 'ts': ts.toString(), - 'dir': dir.name, - }); + path: + '_matrix/client/v1/rooms/${Uri.encodeComponent(roomId)}/timestamp_to_event', + queryParameters: { + 'ts': ts.toString(), + 'dir': dir.name, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -795,7 +855,8 @@ class Api { return ((v) => v != null ? (v as List) .map( - (v) => ThirdPartyIdentifier.fromJson(v as Map)) + (v) => ThirdPartyIdentifier.fromJson(v as Map), + ) .toList() : null)(json['threepids']); } @@ -832,9 +893,11 @@ class Api { final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'three_pid_creds': threePidCreds.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'three_pid_creds': threePidCreds.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -858,17 +921,22 @@ class Api { /// [clientSecret] The client secret used in the session with the homeserver. /// /// [sid] The session identifier given by the homeserver. - Future add3PID(String clientSecret, String sid, - {AuthenticationData? auth}) async { + Future add3PID( + String clientSecret, + String sid, { + AuthenticationData? auth, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/3pid/add'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - 'client_secret': clientSecret, - 'sid': sid, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + 'client_secret': clientSecret, + 'sid': sid, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -892,18 +960,24 @@ class Api { /// [idServer] The identity server to use. /// /// [sid] The session identifier given by the identity server. - Future bind3PID(String clientSecret, String idAccessToken, - String idServer, String sid) async { + Future bind3PID( + String clientSecret, + String idAccessToken, + String idServer, + String sid, + ) async { final requestUri = Uri(path: '_matrix/client/v3/account/3pid/bind'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'id_access_token': idAccessToken, - 'id_server': idServer, - 'sid': sid, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'id_access_token': idAccessToken, + 'id_server': idServer, + 'sid': sid, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -936,17 +1010,21 @@ class Api { /// or the homeserver was not able to determine an identity server to /// unbind from. Future delete3pidFromAccount( - String address, ThirdPartyIdentifierMedium medium, - {String? idServer}) async { + String address, + ThirdPartyIdentifierMedium medium, { + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/3pid/delete'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'address': address, - if (idServer != null) 'id_server': idServer, - 'medium': medium.name, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'address': address, + if (idServer != null) 'id_server': idServer, + 'medium': medium.name, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -999,20 +1077,27 @@ class Api { /// This parameter is deprecated with a plan to be removed in a future specification /// version for `/account/password` and `/register` requests. Future requestTokenTo3PIDEmail( - String clientSecret, String email, int sendAttempt, - {String? nextLink, String? idAccessToken, String? idServer}) async { + String clientSecret, + String email, + int sendAttempt, { + String? nextLink, + String? idAccessToken, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/3pid/email/requestToken'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'email': email, - if (nextLink != null) 'next_link': nextLink, - 'send_attempt': sendAttempt, - if (idAccessToken != null) 'id_access_token': idAccessToken, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'email': email, + if (nextLink != null) 'next_link': nextLink, + 'send_attempt': sendAttempt, + if (idAccessToken != null) 'id_access_token': idAccessToken, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1066,21 +1151,29 @@ class Api { /// This parameter is deprecated with a plan to be removed in a future specification /// version for `/account/password` and `/register` requests. Future requestTokenTo3PIDMSISDN( - String clientSecret, String country, String phoneNumber, int sendAttempt, - {String? nextLink, String? idAccessToken, String? idServer}) async { + String clientSecret, + String country, + String phoneNumber, + int sendAttempt, { + String? nextLink, + String? idAccessToken, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/3pid/msisdn/requestToken'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'country': country, - if (nextLink != null) 'next_link': nextLink, - 'phone_number': phoneNumber, - 'send_attempt': sendAttempt, - if (idAccessToken != null) 'id_access_token': idAccessToken, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'country': country, + if (nextLink != null) 'next_link': nextLink, + 'phone_number': phoneNumber, + 'send_attempt': sendAttempt, + if (idAccessToken != null) 'id_access_token': idAccessToken, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1112,17 +1205,21 @@ class Api { /// refuses to support the request or the homeserver was not able to determine /// an identity server to unbind from. Future unbind3pidFromAccount( - String address, ThirdPartyIdentifierMedium medium, - {String? idServer}) async { + String address, + ThirdPartyIdentifierMedium medium, { + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/3pid/unbind'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'address': address, - if (idServer != null) 'id_server': idServer, - 'medium': medium.name, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'address': address, + if (idServer != null) 'id_server': idServer, + 'medium': medium.name, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1182,19 +1279,24 @@ class Api { /// being unable to determine an identity server to unbind from. This /// must be `success` if the homeserver has no identifiers to unbind /// for the user. - Future deactivateAccount( - {AuthenticationData? auth, bool? erase, String? idServer}) async { + Future deactivateAccount({ + AuthenticationData? auth, + bool? erase, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/deactivate'); final request = Request('POST', baseUri!.resolveUri(requestUri)); if (bearerToken != null) { request.headers['authorization'] = 'Bearer ${bearerToken!}'; } request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - if (erase != null) 'erase': erase, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + if (erase != null) 'erase': erase, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1227,19 +1329,24 @@ class Api { /// for the user's remaining devices. /// /// [newPassword] The new password for the account. - Future changePassword(String newPassword, - {AuthenticationData? auth, bool? logoutDevices}) async { + Future changePassword( + String newPassword, { + AuthenticationData? auth, + bool? logoutDevices, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/password'); final request = Request('POST', baseUri!.resolveUri(requestUri)); if (bearerToken != null) { request.headers['authorization'] = 'Bearer ${bearerToken!}'; } request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - if (logoutDevices != null) 'logout_devices': logoutDevices, - 'new_password': newPassword, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + if (logoutDevices != null) 'logout_devices': logoutDevices, + 'new_password': newPassword, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1298,20 +1405,27 @@ class Api { /// This parameter is deprecated with a plan to be removed in a future specification /// version for `/account/password` and `/register` requests. Future requestTokenToResetPasswordEmail( - String clientSecret, String email, int sendAttempt, - {String? nextLink, String? idAccessToken, String? idServer}) async { + String clientSecret, + String email, + int sendAttempt, { + String? nextLink, + String? idAccessToken, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/password/email/requestToken'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'email': email, - if (nextLink != null) 'next_link': nextLink, - 'send_attempt': sendAttempt, - if (idAccessToken != null) 'id_access_token': idAccessToken, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'email': email, + if (nextLink != null) 'next_link': nextLink, + 'send_attempt': sendAttempt, + if (idAccessToken != null) 'id_access_token': idAccessToken, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1372,21 +1486,29 @@ class Api { /// This parameter is deprecated with a plan to be removed in a future specification /// version for `/account/password` and `/register` requests. Future requestTokenToResetPasswordMSISDN( - String clientSecret, String country, String phoneNumber, int sendAttempt, - {String? nextLink, String? idAccessToken, String? idServer}) async { + String clientSecret, + String country, + String phoneNumber, + int sendAttempt, { + String? nextLink, + String? idAccessToken, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/account/password/msisdn/requestToken'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'country': country, - if (nextLink != null) 'next_link': nextLink, - 'phone_number': phoneNumber, - 'send_attempt': sendAttempt, - if (idAccessToken != null) 'id_access_token': idAccessToken, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'country': country, + if (nextLink != null) 'next_link': nextLink, + 'phone_number': phoneNumber, + 'send_attempt': sendAttempt, + if (idAccessToken != null) 'id_access_token': idAccessToken, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1424,7 +1546,8 @@ class Api { /// [userId] The user to look up. Future getWhoIs(String userId) async { final requestUri = Uri( - path: '_matrix/client/v3/admin/whois/${Uri.encodeComponent(userId)}'); + path: '_matrix/client/v3/admin/whois/${Uri.encodeComponent(userId)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -1565,40 +1688,43 @@ class Api { /// /// returns `room_id`: /// The created room's ID. - Future createRoom( - {Map? creationContent, - List? initialState, - List? invite, - List? invite3pid, - bool? isDirect, - String? name, - Map? powerLevelContentOverride, - CreateRoomPreset? preset, - String? roomAliasName, - String? roomVersion, - String? topic, - Visibility? visibility}) async { + Future createRoom({ + Map? creationContent, + List? initialState, + List? invite, + List? invite3pid, + bool? isDirect, + String? name, + Map? powerLevelContentOverride, + CreateRoomPreset? preset, + String? roomAliasName, + String? roomVersion, + String? topic, + Visibility? visibility, + }) async { final requestUri = Uri(path: '_matrix/client/v3/createRoom'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (creationContent != null) 'creation_content': creationContent, - if (initialState != null) - 'initial_state': initialState.map((v) => v.toJson()).toList(), - if (invite != null) 'invite': invite.map((v) => v).toList(), - if (invite3pid != null) - 'invite_3pid': invite3pid.map((v) => v.toJson()).toList(), - if (isDirect != null) 'is_direct': isDirect, - if (name != null) 'name': name, - if (powerLevelContentOverride != null) - 'power_level_content_override': powerLevelContentOverride, - if (preset != null) 'preset': preset.name, - if (roomAliasName != null) 'room_alias_name': roomAliasName, - if (roomVersion != null) 'room_version': roomVersion, - if (topic != null) 'topic': topic, - if (visibility != null) 'visibility': visibility.name, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (creationContent != null) 'creation_content': creationContent, + if (initialState != null) + 'initial_state': initialState.map((v) => v.toJson()).toList(), + if (invite != null) 'invite': invite.map((v) => v).toList(), + if (invite3pid != null) + 'invite_3pid': invite3pid.map((v) => v.toJson()).toList(), + if (isDirect != null) 'is_direct': isDirect, + if (name != null) 'name': name, + if (powerLevelContentOverride != null) + 'power_level_content_override': powerLevelContentOverride, + if (preset != null) 'preset': preset.name, + if (roomAliasName != null) 'room_alias_name': roomAliasName, + if (roomVersion != null) 'room_version': roomVersion, + if (topic != null) 'topic': topic, + if (visibility != null) 'visibility': visibility.name, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1615,16 +1741,20 @@ class Api { /// user-interactive authentication API. /// /// [devices] The list of device IDs to delete. - Future deleteDevices(List devices, - {AuthenticationData? auth}) async { + Future deleteDevices( + List devices, { + AuthenticationData? auth, + }) async { final requestUri = Uri(path: '_matrix/client/v3/delete_devices'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - 'devices': devices.map((v) => v).toList(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + 'devices': devices.map((v) => v).toList(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1667,9 +1797,11 @@ class Api { final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1706,9 +1838,11 @@ class Api { final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (displayName != null) 'display_name': displayName, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (displayName != null) 'display_name': displayName, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1736,16 +1870,22 @@ class Api { /// [visibility] Whether the room should be visible (public) in the directory /// or not (private). Future> updateAppserviceRoomDirectoryVisibility( - String networkId, String roomId, Visibility visibility) async { + String networkId, + String roomId, + Visibility visibility, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/directory/list/appservice/${Uri.encodeComponent(networkId)}/${Uri.encodeComponent(roomId)}'); + path: + '_matrix/client/v3/directory/list/appservice/${Uri.encodeComponent(networkId)}/${Uri.encodeComponent(roomId)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'visibility': visibility.name, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'visibility': visibility.name, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1762,8 +1902,9 @@ class Api { /// The visibility of the room in the directory. Future getRoomVisibilityOnDirectory(String roomId) async { final requestUri = Uri( - path: - '_matrix/client/v3/directory/list/room/${Uri.encodeComponent(roomId)}'); + path: + '_matrix/client/v3/directory/list/room/${Uri.encodeComponent(roomId)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); @@ -1786,17 +1927,22 @@ class Api { /// /// [visibility] The new visibility setting for the room. /// Defaults to 'public'. - Future setRoomVisibilityOnDirectory(String roomId, - {Visibility? visibility}) async { + Future setRoomVisibilityOnDirectory( + String roomId, { + Visibility? visibility, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/directory/list/room/${Uri.encodeComponent(roomId)}'); + path: + '_matrix/client/v3/directory/list/room/${Uri.encodeComponent(roomId)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (visibility != null) 'visibility': visibility.name, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (visibility != null) 'visibility': visibility.name, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1822,8 +1968,9 @@ class Api { /// Future deleteRoomAlias(String roomAlias) async { final requestUri = Uri( - path: - '_matrix/client/v3/directory/room/${Uri.encodeComponent(roomAlias)}'); + path: + '_matrix/client/v3/directory/room/${Uri.encodeComponent(roomAlias)}', + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -1845,8 +1992,9 @@ class Api { /// Future getRoomIdByAlias(String roomAlias) async { final requestUri = Uri( - path: - '_matrix/client/v3/directory/room/${Uri.encodeComponent(roomAlias)}'); + path: + '_matrix/client/v3/directory/room/${Uri.encodeComponent(roomAlias)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); @@ -1865,14 +2013,17 @@ class Api { /// [roomId] The room ID to set. Future setRoomAlias(String roomAlias, String roomId) async { final requestUri = Uri( - path: - '_matrix/client/v3/directory/room/${Uri.encodeComponent(roomAlias)}'); + path: + '_matrix/client/v3/directory/room/${Uri.encodeComponent(roomAlias)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'room_id': roomId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'room_id': roomId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -1895,10 +2046,13 @@ class Api { /// [timeout] The maximum time in milliseconds to wait for an event. @deprecated Future getEvents({String? from, int? timeout}) async { - final requestUri = Uri(path: '_matrix/client/v3/events', queryParameters: { - if (from != null) 'from': from, - if (timeout != null) 'timeout': timeout.toString(), - }); + final requestUri = Uri( + path: '_matrix/client/v3/events', + queryParameters: { + if (from != null) 'from': from, + if (timeout != null) 'timeout': timeout.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -1926,13 +2080,19 @@ class Api { /// [timeout] The maximum time in milliseconds to wait for an event. /// /// [roomId] The room ID for which events should be returned. - Future peekEvents( - {String? from, int? timeout, String? roomId}) async { - final requestUri = Uri(path: '_matrix/client/v3/events', queryParameters: { - if (from != null) 'from': from, - if (timeout != null) 'timeout': timeout.toString(), - if (roomId != null) 'room_id': roomId, - }); + Future peekEvents({ + String? from, + int? timeout, + String? roomId, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/events', + queryParameters: { + if (from != null) 'from': from, + if (timeout != null) 'timeout': timeout.toString(), + if (roomId != null) 'room_id': roomId, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -1994,25 +2154,30 @@ class Api { /// /// returns `room_id`: /// The joined room ID. - Future joinRoom(String roomIdOrAlias, - {List? serverName, - List? via, - String? reason, - ThirdPartySigned? thirdPartySigned}) async { + Future joinRoom( + String roomIdOrAlias, { + List? serverName, + List? via, + String? reason, + ThirdPartySigned? thirdPartySigned, + }) async { final requestUri = Uri( - path: '_matrix/client/v3/join/${Uri.encodeComponent(roomIdOrAlias)}', - queryParameters: { - if (serverName != null) 'server_name': serverName, - if (via != null) 'via': via, - }); + path: '_matrix/client/v3/join/${Uri.encodeComponent(roomIdOrAlias)}', + queryParameters: { + if (serverName != null) 'server_name': serverName, + if (via != null) 'via': via, + }, + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - if (thirdPartySigned != null) - 'third_party_signed': thirdPartySigned.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + if (thirdPartySigned != null) + 'third_party_signed': thirdPartySigned.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2058,11 +2223,13 @@ class Api { /// such call. This may be used by the server as a hint to check its /// caches are up to date. Future getKeysChanges(String from, String to) async { - final requestUri = - Uri(path: '_matrix/client/v3/keys/changes', queryParameters: { - 'from': from, - 'to': to, - }); + final requestUri = Uri( + path: '_matrix/client/v3/keys/changes', + queryParameters: { + 'from': from, + 'to': to, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2081,17 +2248,20 @@ class Api { /// [timeout] The time (in milliseconds) to wait when downloading keys from /// remote servers. 10 seconds is the recommended default. Future claimKeys( - Map> oneTimeKeys, - {int? timeout}) async { + Map> oneTimeKeys, { + int? timeout, + }) async { final requestUri = Uri(path: '_matrix/client/v3/keys/claim'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'one_time_keys': oneTimeKeys - .map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v)))), - if (timeout != null) 'timeout': timeout, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'one_time_keys': oneTimeKeys + .map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v)))), + if (timeout != null) 'timeout': timeout, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2131,22 +2301,25 @@ class Api { /// the accompanying master key, or by the user\'s most recently /// uploaded master key if no master key is included in the /// request. - Future uploadCrossSigningKeys( - {AuthenticationData? auth, - MatrixCrossSigningKey? masterKey, - MatrixCrossSigningKey? selfSigningKey, - MatrixCrossSigningKey? userSigningKey}) async { + Future uploadCrossSigningKeys({ + AuthenticationData? auth, + MatrixCrossSigningKey? masterKey, + MatrixCrossSigningKey? selfSigningKey, + MatrixCrossSigningKey? userSigningKey, + }) async { final requestUri = Uri(path: '_matrix/client/v3/keys/device_signing/upload'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - if (masterKey != null) 'master_key': masterKey.toJson(), - if (selfSigningKey != null) 'self_signing_key': selfSigningKey.toJson(), - if (userSigningKey != null) 'user_signing_key': userSigningKey.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + if (masterKey != null) 'master_key': masterKey.toJson(), + if (selfSigningKey != null) 'self_signing_key': selfSigningKey.toJson(), + if (userSigningKey != null) 'user_signing_key': userSigningKey.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2163,17 +2336,21 @@ class Api { /// /// [timeout] The time (in milliseconds) to wait when downloading keys from /// remote servers. 10 seconds is the recommended default. - Future queryKeys(Map> deviceKeys, - {int? timeout}) async { + Future queryKeys( + Map> deviceKeys, { + int? timeout, + }) async { final requestUri = Uri(path: '_matrix/client/v3/keys/query'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'device_keys': - deviceKeys.map((k, v) => MapEntry(k, v.map((v) => v).toList())), - if (timeout != null) 'timeout': timeout, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'device_keys': + deviceKeys.map((k, v) => MapEntry(k, v.map((v) => v).toList())), + if (timeout != null) 'timeout': timeout, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2197,23 +2374,30 @@ class Api { /// be set to `M_INVALID_SIGNATURE`. Future>>?> uploadCrossSigningSignatures( - Map>> body) async { + Map>> body, + ) async { final requestUri = Uri(path: '_matrix/client/v3/keys/signatures/upload'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode( - body.map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v)))))); + request.bodyBytes = utf8.encode( + jsonEncode( + body.map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v)))), + ), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); final responseString = utf8.decode(responseBody); final json = jsonDecode(responseString); return ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, - (v as Map) - .map((k, v) => MapEntry(k, v as Map)))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + (v as Map) + .map((k, v) => MapEntry(k, v as Map)), + ), + ) : null)(json['failures']); } @@ -2248,19 +2432,22 @@ class Api { /// of that type currently held on the server for this device. /// If an algorithm is not listed, the count for that algorithm /// is to be assumed zero. - Future> uploadKeys( - {MatrixDeviceKeys? deviceKeys, - Map? fallbackKeys, - Map? oneTimeKeys}) async { + Future> uploadKeys({ + MatrixDeviceKeys? deviceKeys, + Map? fallbackKeys, + Map? oneTimeKeys, + }) async { final requestUri = Uri(path: '_matrix/client/v3/keys/upload'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (deviceKeys != null) 'device_keys': deviceKeys.toJson(), - if (fallbackKeys != null) 'fallback_keys': fallbackKeys, - if (oneTimeKeys != null) 'one_time_keys': oneTimeKeys, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (deviceKeys != null) 'device_keys': deviceKeys.toJson(), + if (fallbackKeys != null) 'fallback_keys': fallbackKeys, + if (oneTimeKeys != null) 'one_time_keys': oneTimeKeys, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2299,20 +2486,27 @@ class Api { /// /// returns `room_id`: /// The knocked room ID. - Future knockRoom(String roomIdOrAlias, - {List? serverName, List? via, String? reason}) async { + Future knockRoom( + String roomIdOrAlias, { + List? serverName, + List? via, + String? reason, + }) async { final requestUri = Uri( - path: '_matrix/client/v3/knock/${Uri.encodeComponent(roomIdOrAlias)}', - queryParameters: { - if (serverName != null) 'server_name': serverName, - if (via != null) 'via': via, - }); + path: '_matrix/client/v3/knock/${Uri.encodeComponent(roomIdOrAlias)}', + queryParameters: { + if (serverName != null) 'server_name': serverName, + if (via != null) 'via': via, + }, + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2382,32 +2576,36 @@ class Api { /// endpoint, like `m.login.password` or `m.login.token`. /// /// [user] The fully qualified user ID or just local part of the user ID, to log in. Deprecated in favour of `identifier`. - Future login(String type, - {String? address, - String? deviceId, - AuthenticationIdentifier? identifier, - String? initialDeviceDisplayName, - String? medium, - String? password, - bool? refreshToken, - String? token, - String? user}) async { + Future login( + String type, { + String? address, + String? deviceId, + AuthenticationIdentifier? identifier, + String? initialDeviceDisplayName, + String? medium, + String? password, + bool? refreshToken, + String? token, + String? user, + }) async { final requestUri = Uri(path: '_matrix/client/v3/login'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (address != null) 'address': address, - if (deviceId != null) 'device_id': deviceId, - if (identifier != null) 'identifier': identifier.toJson(), - if (initialDeviceDisplayName != null) - 'initial_device_display_name': initialDeviceDisplayName, - if (medium != null) 'medium': medium, - if (password != null) 'password': password, - if (refreshToken != null) 'refresh_token': refreshToken, - if (token != null) 'token': token, - 'type': type, - if (user != null) 'user': user, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (address != null) 'address': address, + if (deviceId != null) 'device_id': deviceId, + if (identifier != null) 'identifier': identifier.toJson(), + if (initialDeviceDisplayName != null) + 'initial_device_display_name': initialDeviceDisplayName, + if (medium != null) 'medium': medium, + if (password != null) 'password': password, + if (refreshToken != null) 'refresh_token': refreshToken, + if (token != null) 'token': token, + 'type': type, + if (user != null) 'user': user, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2465,14 +2663,19 @@ class Api { /// [only] Allows basic filtering of events returned. Supply `highlight` /// to return only events where the notification had the highlight /// tweak set. - Future getNotifications( - {String? from, int? limit, String? only}) async { - final requestUri = - Uri(path: '_matrix/client/v3/notifications', queryParameters: { - if (from != null) 'from': from, - if (limit != null) 'limit': limit.toString(), - if (only != null) 'only': only, - }); + Future getNotifications({ + String? from, + int? limit, + String? only, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/notifications', + queryParameters: { + if (from != null) 'from': from, + if (limit != null) 'limit': limit.toString(), + if (only != null) 'only': only, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2488,8 +2691,8 @@ class Api { /// [userId] The user whose presence state to get. Future getPresence(String userId) async { final requestUri = Uri( - path: - '_matrix/client/v3/presence/${Uri.encodeComponent(userId)}/status'); + path: '_matrix/client/v3/presence/${Uri.encodeComponent(userId)}/status', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2510,18 +2713,23 @@ class Api { /// [presence] The new presence state. /// /// [statusMsg] The status message to attach to this state. - Future setPresence(String userId, PresenceType presence, - {String? statusMsg}) async { + Future setPresence( + String userId, + PresenceType presence, { + String? statusMsg, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/presence/${Uri.encodeComponent(userId)}/status'); + path: '_matrix/client/v3/presence/${Uri.encodeComponent(userId)}/status', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'presence': presence.name, - if (statusMsg != null) 'status_msg': statusMsg, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'presence': presence.name, + if (statusMsg != null) 'status_msg': statusMsg, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2560,8 +2768,9 @@ class Api { /// The user's avatar URL if they have set one, otherwise not present. Future getAvatarUrl(String userId) async { final requestUri = Uri( - path: - '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/avatar_url'); + path: + '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/avatar_url', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); if (bearerToken != null) { request.headers['authorization'] = 'Bearer ${bearerToken!}'; @@ -2583,14 +2792,17 @@ class Api { /// [avatarUrl] The new avatar URL for this user. Future setAvatarUrl(String userId, Uri? avatarUrl) async { final requestUri = Uri( - path: - '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/avatar_url'); + path: + '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/avatar_url', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (avatarUrl != null) 'avatar_url': avatarUrl.toString(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (avatarUrl != null) 'avatar_url': avatarUrl.toString(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2609,8 +2821,9 @@ class Api { /// The user's display name if they have set one, otherwise not present. Future getDisplayName(String userId) async { final requestUri = Uri( - path: - '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/displayname'); + path: + '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/displayname', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); if (bearerToken != null) { request.headers['authorization'] = 'Bearer ${bearerToken!}'; @@ -2631,14 +2844,17 @@ class Api { /// [displayname] The new display name for this user. Future setDisplayName(String userId, String? displayname) async { final requestUri = Uri( - path: - '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/displayname'); + path: + '_matrix/client/v3/profile/${Uri.encodeComponent(userId)}/displayname', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (displayname != null) 'displayname': displayname, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (displayname != null) 'displayname': displayname, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2661,14 +2877,19 @@ class Api { /// /// [server] The server to fetch the public room lists from. Defaults to the /// local server. Case sensitive. - Future getPublicRooms( - {int? limit, String? since, String? server}) async { - final requestUri = - Uri(path: '_matrix/client/v3/publicRooms', queryParameters: { - if (limit != null) 'limit': limit.toString(), - if (since != null) 'since': since, - if (server != null) 'server': server, - }); + Future getPublicRooms({ + int? limit, + String? since, + String? server, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/publicRooms', + queryParameters: { + if (limit != null) 'limit': limit.toString(), + if (since != null) 'since': since, + if (server != null) 'server': server, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); @@ -2700,29 +2921,34 @@ class Api { /// /// [thirdPartyInstanceId] The specific third-party network/protocol to request from the /// homeserver. Can only be used if `include_all_networks` is false. - Future queryPublicRooms( - {String? server, - PublicRoomQueryFilter? filter, - bool? includeAllNetworks, - int? limit, - String? since, - String? thirdPartyInstanceId}) async { - final requestUri = - Uri(path: '_matrix/client/v3/publicRooms', queryParameters: { - if (server != null) 'server': server, - }); + Future queryPublicRooms({ + String? server, + PublicRoomQueryFilter? filter, + bool? includeAllNetworks, + int? limit, + String? since, + String? thirdPartyInstanceId, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/publicRooms', + queryParameters: { + if (server != null) 'server': server, + }, + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (filter != null) 'filter': filter.toJson(), - if (includeAllNetworks != null) - 'include_all_networks': includeAllNetworks, - if (limit != null) 'limit': limit, - if (since != null) 'since': since, - if (thirdPartyInstanceId != null) - 'third_party_instance_id': thirdPartyInstanceId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (filter != null) 'filter': filter.toJson(), + if (includeAllNetworks != null) + 'include_all_networks': includeAllNetworks, + if (limit != null) 'limit': limit, + if (since != null) 'since': since, + if (thirdPartyInstanceId != null) + 'third_party_instance_id': thirdPartyInstanceId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2790,8 +3016,9 @@ class Api { /// Future deletePushRule(PushRuleKind kind, String ruleId) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}'); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}', + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2811,8 +3038,9 @@ class Api { /// Future getPushRule(PushRuleKind kind, String ruleId) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}'); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2864,27 +3092,33 @@ class Api { /// /// [pattern] Only applicable to `content` rules. The glob-style pattern to match against. Future setPushRule( - PushRuleKind kind, String ruleId, List actions, - {String? before, - String? after, - List? conditions, - String? pattern}) async { + PushRuleKind kind, + String ruleId, + List actions, { + String? before, + String? after, + List? conditions, + String? pattern, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}', - queryParameters: { - if (before != null) 'before': before, - if (after != null) 'after': after, - }); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}', + queryParameters: { + if (before != null) 'before': before, + if (after != null) 'after': after, + }, + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'actions': actions.map((v) => v).toList(), - if (conditions != null) - 'conditions': conditions.map((v) => v.toJson()).toList(), - if (pattern != null) 'pattern': pattern, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'actions': actions.map((v) => v).toList(), + if (conditions != null) + 'conditions': conditions.map((v) => v.toJson()).toList(), + if (pattern != null) 'pattern': pattern, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2904,10 +3138,13 @@ class Api { /// returns `actions`: /// The action(s) to perform for this rule. Future> getPushRuleActions( - PushRuleKind kind, String ruleId) async { + PushRuleKind kind, + String ruleId, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/actions'); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/actions', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2929,16 +3166,22 @@ class Api { /// /// [actions] The action(s) to perform for this rule. Future setPushRuleActions( - PushRuleKind kind, String ruleId, List actions) async { + PushRuleKind kind, + String ruleId, + List actions, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/actions'); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/actions', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'actions': actions.map((v) => v).toList(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'actions': actions.map((v) => v).toList(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -2959,8 +3202,9 @@ class Api { /// Whether the push rule is enabled or not. Future isPushRuleEnabled(PushRuleKind kind, String ruleId) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/enabled'); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/enabled', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -2981,16 +3225,22 @@ class Api { /// /// [enabled] Whether the push rule is enabled or not. Future setPushRuleEnabled( - PushRuleKind kind, String ruleId, bool enabled) async { + PushRuleKind kind, + String ruleId, + bool enabled, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/enabled'); + path: + '_matrix/client/v3/pushrules/global/${Uri.encodeComponent(kind.name)}/${Uri.encodeComponent(ruleId)}/enabled', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'enabled': enabled, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'enabled': enabled, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3022,9 +3272,11 @@ class Api { final requestUri = Uri(path: '_matrix/client/v3/refresh'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'refresh_token': refreshToken, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'refresh_token': refreshToken, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3098,31 +3350,36 @@ class Api { /// /// [username] The basis for the localpart of the desired Matrix ID. If omitted, /// the homeserver MUST generate a Matrix ID local part. - Future register( - {AccountKind? kind, - AuthenticationData? auth, - String? deviceId, - bool? inhibitLogin, - String? initialDeviceDisplayName, - String? password, - bool? refreshToken, - String? username}) async { - final requestUri = - Uri(path: '_matrix/client/v3/register', queryParameters: { - if (kind != null) 'kind': kind.name, - }); + Future register({ + AccountKind? kind, + AuthenticationData? auth, + String? deviceId, + bool? inhibitLogin, + String? initialDeviceDisplayName, + String? password, + bool? refreshToken, + String? username, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/register', + queryParameters: { + if (kind != null) 'kind': kind.name, + }, + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (auth != null) 'auth': auth.toJson(), - if (deviceId != null) 'device_id': deviceId, - if (inhibitLogin != null) 'inhibit_login': inhibitLogin, - if (initialDeviceDisplayName != null) - 'initial_device_display_name': initialDeviceDisplayName, - if (password != null) 'password': password, - if (refreshToken != null) 'refresh_token': refreshToken, - if (username != null) 'username': username, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (auth != null) 'auth': auth.toJson(), + if (deviceId != null) 'device_id': deviceId, + if (inhibitLogin != null) 'inhibit_login': inhibitLogin, + if (initialDeviceDisplayName != null) + 'initial_device_display_name': initialDeviceDisplayName, + if (password != null) 'password': password, + if (refreshToken != null) 'refresh_token': refreshToken, + if (username != null) 'username': username, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3150,10 +3407,12 @@ class Api { /// A flag to indicate that the username is available. This should always /// be `true` when the server replies with 200 OK. Future checkUsernameAvailability(String username) async { - final requestUri = - Uri(path: '_matrix/client/v3/register/available', queryParameters: { - 'username': username, - }); + final requestUri = Uri( + path: '_matrix/client/v3/register/available', + queryParameters: { + 'username': username, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); @@ -3202,20 +3461,27 @@ class Api { /// This parameter is deprecated with a plan to be removed in a future specification /// version for `/account/password` and `/register` requests. Future requestTokenToRegisterEmail( - String clientSecret, String email, int sendAttempt, - {String? nextLink, String? idAccessToken, String? idServer}) async { + String clientSecret, + String email, + int sendAttempt, { + String? nextLink, + String? idAccessToken, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/register/email/requestToken'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'email': email, - if (nextLink != null) 'next_link': nextLink, - 'send_attempt': sendAttempt, - if (idAccessToken != null) 'id_access_token': idAccessToken, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'email': email, + if (nextLink != null) 'next_link': nextLink, + 'send_attempt': sendAttempt, + if (idAccessToken != null) 'id_access_token': idAccessToken, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3265,21 +3531,29 @@ class Api { /// This parameter is deprecated with a plan to be removed in a future specification /// version for `/account/password` and `/register` requests. Future requestTokenToRegisterMSISDN( - String clientSecret, String country, String phoneNumber, int sendAttempt, - {String? nextLink, String? idAccessToken, String? idServer}) async { + String clientSecret, + String country, + String phoneNumber, + int sendAttempt, { + String? nextLink, + String? idAccessToken, + String? idServer, + }) async { final requestUri = Uri(path: '_matrix/client/v3/register/msisdn/requestToken'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'client_secret': clientSecret, - 'country': country, - if (nextLink != null) 'next_link': nextLink, - 'phone_number': phoneNumber, - 'send_attempt': sendAttempt, - if (idAccessToken != null) 'id_access_token': idAccessToken, - if (idServer != null) 'id_server': idServer, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'client_secret': clientSecret, + 'country': country, + if (nextLink != null) 'next_link': nextLink, + 'phone_number': phoneNumber, + 'send_attempt': sendAttempt, + if (idAccessToken != null) 'id_access_token': idAccessToken, + if (idServer != null) 'id_server': idServer, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3292,10 +3566,12 @@ class Api { /// /// [version] The backup from which to delete the key Future deleteRoomKeys(String version) async { - final requestUri = - Uri(path: '_matrix/client/v3/room_keys/keys', queryParameters: { - 'version': version, - }); + final requestUri = Uri( + path: '_matrix/client/v3/room_keys/keys', + queryParameters: { + 'version': version, + }, + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3310,10 +3586,12 @@ class Api { /// /// [version] The backup from which to retrieve the keys. Future getRoomKeys(String version) async { - final requestUri = - Uri(path: '_matrix/client/v3/room_keys/keys', queryParameters: { - 'version': version, - }); + final requestUri = Uri( + path: '_matrix/client/v3/room_keys/keys', + queryParameters: { + 'version': version, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3330,11 +3608,15 @@ class Api { /// /// [body] The backup data. Future putRoomKeys( - String version, RoomKeys body) async { - final requestUri = - Uri(path: '_matrix/client/v3/room_keys/keys', queryParameters: { - 'version': version, - }); + String version, + RoomKeys body, + ) async { + final requestUri = Uri( + path: '_matrix/client/v3/room_keys/keys', + queryParameters: { + 'version': version, + }, + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -3353,12 +3635,15 @@ class Api { /// /// [version] The backup from which to delete the key. Future deleteRoomKeysByRoomId( - String roomId, String version) async { + String roomId, + String version, + ) async { final requestUri = Uri( - path: '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}', - queryParameters: { - 'version': version, - }); + path: '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}', + queryParameters: { + 'version': version, + }, + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3375,12 +3660,15 @@ class Api { /// /// [version] The backup from which to retrieve the key. Future getRoomKeysByRoomId( - String roomId, String version) async { + String roomId, + String version, + ) async { final requestUri = Uri( - path: '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}', - queryParameters: { - 'version': version, - }); + path: '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}', + queryParameters: { + 'version': version, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3399,12 +3687,16 @@ class Api { /// /// [body] The backup data Future putRoomKeysByRoomId( - String roomId, String version, RoomKeyBackup body) async { + String roomId, + String version, + RoomKeyBackup body, + ) async { final requestUri = Uri( - path: '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}', - queryParameters: { - 'version': version, - }); + path: '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}', + queryParameters: { + 'version': version, + }, + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -3425,13 +3717,17 @@ class Api { /// /// [version] The backup from which to delete the key Future deleteRoomKeyBySessionId( - String roomId, String sessionId, String version) async { + String roomId, + String sessionId, + String version, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}', - queryParameters: { - 'version': version, - }); + path: + '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}', + queryParameters: { + 'version': version, + }, + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3450,13 +3746,17 @@ class Api { /// /// [version] The backup from which to retrieve the key. Future getRoomKeyBySessionId( - String roomId, String sessionId, String version) async { + String roomId, + String sessionId, + String version, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}', - queryParameters: { - 'version': version, - }); + path: + '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}', + queryParameters: { + 'version': version, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3476,14 +3776,19 @@ class Api { /// [version] The backup in which to store the key. Must be the current backup. /// /// [body] The key data. - Future putRoomKeyBySessionId(String roomId, - String sessionId, String version, KeyBackupData body) async { + Future putRoomKeyBySessionId( + String roomId, + String sessionId, + String version, + KeyBackupData body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}', - queryParameters: { - 'version': version, - }); + path: + '_matrix/client/v3/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}', + queryParameters: { + 'version': version, + }, + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -3507,7 +3812,8 @@ class Api { final responseString = utf8.decode(responseBody); final json = jsonDecode(responseString); return GetRoomKeysVersionCurrentResponse.fromJson( - json as Map); + json as Map, + ); } /// Creates a new backup. @@ -3521,15 +3827,19 @@ class Api { /// returns `version`: /// The backup version. This is an opaque string. Future postRoomKeysVersion( - BackupAlgorithm algorithm, Map authData) async { + BackupAlgorithm algorithm, + Map authData, + ) async { final requestUri = Uri(path: '_matrix/client/v3/room_keys/version'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'algorithm': algorithm.name, - 'auth_data': authData, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'algorithm': algorithm.name, + 'auth_data': authData, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3547,8 +3857,9 @@ class Api { /// or [`GET /_matrix/client/v3/room_keys/version/{version}`](https://spec.matrix.org/unstable/client-server-api/#get_matrixclientv3room_keysversionversion). Future deleteRoomKeysVersion(String version) async { final requestUri = Uri( - path: - '_matrix/client/v3/room_keys/version/${Uri.encodeComponent(version)}'); + path: + '_matrix/client/v3/room_keys/version/${Uri.encodeComponent(version)}', + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3567,8 +3878,9 @@ class Api { /// or this endpoint. Future getRoomKeysVersion(String version) async { final requestUri = Uri( - path: - '_matrix/client/v3/room_keys/version/${Uri.encodeComponent(version)}'); + path: + '_matrix/client/v3/room_keys/version/${Uri.encodeComponent(version)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3592,18 +3904,24 @@ class Api { /// [authData] Algorithm-dependent data. See the documentation for the backup /// algorithms in [Server-side key backups](https://spec.matrix.org/unstable/client-server-api/#server-side-key-backups) for more information on the /// expected format of the data. - Future> putRoomKeysVersion(String version, - BackupAlgorithm algorithm, Map authData) async { + Future> putRoomKeysVersion( + String version, + BackupAlgorithm algorithm, + Map authData, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/room_keys/version/${Uri.encodeComponent(version)}'); + path: + '_matrix/client/v3/room_keys/version/${Uri.encodeComponent(version)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'algorithm': algorithm.name, - 'auth_data': authData, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'algorithm': algorithm.name, + 'auth_data': authData, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3635,7 +3953,8 @@ class Api { /// The server's local aliases on the room. Can be empty. Future> getLocalAliases(String roomId) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/aliases'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/aliases', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3663,10 +3982,12 @@ class Api { final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - 'user_id': userId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + 'user_id': userId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3698,15 +4019,20 @@ class Api { /// homeserver prefers. /// /// See [Filtering](https://spec.matrix.org/unstable/client-server-api/#filtering) for more information. - Future getEventContext(String roomId, String eventId, - {int? limit, String? filter}) async { + Future getEventContext( + String roomId, + String eventId, { + int? limit, + String? filter, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/context/${Uri.encodeComponent(eventId)}', - queryParameters: { - if (limit != null) 'limit': limit.toString(), - if (filter != null) 'filter': filter, - }); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/context/${Uri.encodeComponent(eventId)}', + queryParameters: { + if (limit != null) 'limit': limit.toString(), + if (filter != null) 'filter': filter, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3725,8 +4051,9 @@ class Api { /// [eventId] The event ID to get. Future getOneRoomEvent(String roomId, String eventId) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/event/${Uri.encodeComponent(eventId)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/event/${Uri.encodeComponent(eventId)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3750,7 +4077,8 @@ class Api { /// [roomId] The room identifier to forget. Future forgetRoom(String roomId) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/forget'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/forget', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3814,19 +4142,27 @@ class Api { /// /// [medium] The kind of address being passed in the address field, for example /// `email` (see [the list of recognised values](https://spec.matrix.org/unstable/appendices/#3pid-types)). - Future inviteBy3PID(String roomId, String address, String idAccessToken, - String idServer, String medium) async { + Future inviteBy3PID( + String roomId, + String address, + String idAccessToken, + String idServer, + String medium, + ) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/invite'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/invite', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'address': address, - 'id_access_token': idAccessToken, - 'id_server': idServer, - 'medium': medium, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'address': address, + 'id_access_token': idAccessToken, + 'id_server': idServer, + 'medium': medium, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3856,17 +4192,23 @@ class Api { /// membership event. /// /// [userId] The fully qualified user ID of the invitee. - Future inviteUser(String roomId, String userId, - {String? reason}) async { + Future inviteUser( + String roomId, + String userId, { + String? reason, + }) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/invite'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/invite', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - 'user_id': userId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + 'user_id': userId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3898,18 +4240,24 @@ class Api { /// /// returns `room_id`: /// The joined room ID. - Future joinRoomById(String roomId, - {String? reason, ThirdPartySigned? thirdPartySigned}) async { + Future joinRoomById( + String roomId, { + String? reason, + ThirdPartySigned? thirdPartySigned, + }) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/join'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/join', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - if (thirdPartySigned != null) - 'third_party_signed': thirdPartySigned.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + if (thirdPartySigned != null) + 'third_party_signed': thirdPartySigned.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3926,8 +4274,9 @@ class Api { /// A map from user ID to a RoomMember object. Future?> getJoinedMembersByRoom(String roomId) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/joined_members'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/joined_members', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -3936,8 +4285,10 @@ class Api { final responseString = utf8.decode(responseBody); final json = jsonDecode(responseString); return ((v) => v != null - ? (v as Map).map((k, v) => - MapEntry(k, RoomMember.fromJson(v as Map))) + ? (v as Map).map( + (k, v) => + MapEntry(k, RoomMember.fromJson(v as Map)), + ) : null)(json['joined']); } @@ -3957,14 +4308,17 @@ class Api { /// [userId] The fully qualified user ID of the user being kicked. Future kick(String roomId, String userId, {String? reason}) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/kick'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/kick', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - 'user_id': userId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + 'user_id': userId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -3991,13 +4345,16 @@ class Api { /// membership event. Future leaveRoom(String roomId, {String? reason}) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/leave'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/leave', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4025,15 +4382,20 @@ class Api { /// /// returns `chunk`: /// - Future?> getMembersByRoom(String roomId, - {String? at, Membership? membership, Membership? notMembership}) async { + Future?> getMembersByRoom( + String roomId, { + String? at, + Membership? membership, + Membership? notMembership, + }) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/members', - queryParameters: { - if (at != null) 'at': at, - if (membership != null) 'membership': membership.name, - if (notMembership != null) 'not_membership': notMembership.name, - }); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/members', + queryParameters: { + if (at != null) 'at': at, + if (membership != null) 'membership': membership.name, + if (notMembership != null) 'not_membership': notMembership.name, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4080,17 +4442,24 @@ class Api { /// [limit] The maximum number of events to return. Default: 10. /// /// [filter] A JSON RoomEventFilter to filter returned events with. - Future getRoomEvents(String roomId, Direction dir, - {String? from, String? to, int? limit, String? filter}) async { + Future getRoomEvents( + String roomId, + Direction dir, { + String? from, + String? to, + int? limit, + String? filter, + }) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/messages', - queryParameters: { - if (from != null) 'from': from, - if (to != null) 'to': to, - 'dir': dir.name, - if (limit != null) 'limit': limit.toString(), - if (filter != null) 'filter': filter, - }); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/messages', + queryParameters: { + if (from != null) 'from': from, + if (to != null) 'to': to, + 'dir': dir.name, + if (limit != null) 'limit': limit.toString(), + if (filter != null) 'filter': filter, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4116,19 +4485,26 @@ class Api { /// [mReadPrivate] The event ID to set the *private* read receipt location at. This /// equivalent to calling `/receipt/m.read.private/$elsewhere:example.org` /// and is provided here to save that extra call. - Future setReadMarker(String roomId, - {String? mFullyRead, String? mRead, String? mReadPrivate}) async { + Future setReadMarker( + String roomId, { + String? mFullyRead, + String? mRead, + String? mReadPrivate, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/read_markers'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/read_markers', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (mFullyRead != null) 'm.fully_read': mFullyRead, - if (mRead != null) 'm.read': mRead, - if (mReadPrivate != null) 'm.read.private': mReadPrivate, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (mFullyRead != null) 'm.fully_read': mFullyRead, + if (mRead != null) 'm.read': mRead, + if (mReadPrivate != null) 'm.read.private': mReadPrivate, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4156,17 +4532,23 @@ class Api { /// not specified, the read receipt is *unthreaded* /// (default). Future postReceipt( - String roomId, ReceiptType receiptType, String eventId, - {String? threadId}) async { + String roomId, + ReceiptType receiptType, + String eventId, { + String? threadId, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/receipt/${Uri.encodeComponent(receiptType.name)}/${Uri.encodeComponent(eventId)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/receipt/${Uri.encodeComponent(receiptType.name)}/${Uri.encodeComponent(eventId)}', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (threadId != null) 'thread_id': threadId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (threadId != null) 'thread_id': threadId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4198,17 +4580,24 @@ class Api { /// /// returns `event_id`: /// A unique identifier for the event. - Future redactEvent(String roomId, String eventId, String txnId, - {String? reason}) async { + Future redactEvent( + String roomId, + String eventId, + String txnId, { + String? reason, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/redact/${Uri.encodeComponent(eventId)}/${Uri.encodeComponent(txnId)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/redact/${Uri.encodeComponent(eventId)}/${Uri.encodeComponent(txnId)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4235,18 +4624,25 @@ class Api { /// /// [score] The score to rate this content as where -100 is most offensive /// and 0 is inoffensive. - Future reportContent(String roomId, String eventId, - {String? reason, int? score}) async { + Future reportContent( + String roomId, + String eventId, { + String? reason, + int? score, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/report/${Uri.encodeComponent(eventId)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/report/${Uri.encodeComponent(eventId)}', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - if (score != null) 'score': score, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + if (score != null) 'score': score, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4275,11 +4671,16 @@ class Api { /// /// returns `event_id`: /// A unique identifier for the event. - Future sendMessage(String roomId, String eventType, String txnId, - Map body) async { + Future sendMessage( + String roomId, + String eventType, + String txnId, + Map body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/send/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/send/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -4301,7 +4702,8 @@ class Api { /// [roomId] The room to look up the state for. Future> getRoomState(String roomId) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/state'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/state', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4326,10 +4728,14 @@ class Api { /// [stateKey] The key of the state to look up. Defaults to an empty string. When /// an empty string, the trailing slash on this endpoint is optional. Future> getRoomStateWithKey( - String roomId, String eventType, String stateKey) async { + String roomId, + String eventType, + String stateKey, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/state/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(stateKey)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/state/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(stateKey)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4370,11 +4776,16 @@ class Api { /// /// returns `event_id`: /// A unique identifier for the event. - Future setRoomStateWithKey(String roomId, String eventType, - String stateKey, Map body) async { + Future setRoomStateWithKey( + String roomId, + String eventType, + String stateKey, + Map body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/state/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(stateKey)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/state/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(stateKey)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -4400,18 +4811,25 @@ class Api { /// /// [typing] Whether the user is typing or not. If `false`, the `timeout` /// key can be omitted. - Future setTyping(String userId, String roomId, bool typing, - {int? timeout}) async { + Future setTyping( + String userId, + String roomId, + bool typing, { + int? timeout, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/typing/${Uri.encodeComponent(userId)}'); + path: + '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/typing/${Uri.encodeComponent(userId)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (timeout != null) 'timeout': timeout, - 'typing': typing, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (timeout != null) 'timeout': timeout, + 'typing': typing, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4433,14 +4851,17 @@ class Api { /// [userId] The fully qualified user ID of the user being unbanned. Future unban(String roomId, String userId, {String? reason}) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/unban'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/unban', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (reason != null) 'reason': reason, - 'user_id': userId, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (reason != null) 'reason': reason, + 'user_id': userId, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4459,13 +4880,16 @@ class Api { /// The ID of the new room. Future upgradeRoom(String roomId, String newVersion) async { final requestUri = Uri( - path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/upgrade'); + path: '_matrix/client/v3/rooms/${Uri.encodeComponent(roomId)}/upgrade', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'new_version': newVersion, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'new_version': newVersion, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4480,17 +4904,24 @@ class Api { /// `next_batch` result from a previous call to this endpoint. /// /// [searchCategories] Describes which categories to search in and their criteria. - Future search(Categories searchCategories, - {String? nextBatch}) async { - final requestUri = Uri(path: '_matrix/client/v3/search', queryParameters: { - if (nextBatch != null) 'next_batch': nextBatch, - }); + Future search( + Categories searchCategories, { + String? nextBatch, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/search', + queryParameters: { + if (nextBatch != null) 'next_batch': nextBatch, + }, + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'search_categories': searchCategories.toJson(), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'search_categories': searchCategories.toJson(), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4511,18 +4942,24 @@ class Api { /// [messages] The messages to send. A map from user ID, to a map from /// device ID to message body. The device ID may also be `*`, /// meaning all known devices for the user. - Future sendToDevice(String eventType, String txnId, - Map>> messages) async { + Future sendToDevice( + String eventType, + String txnId, + Map>> messages, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/sendToDevice/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}'); + path: + '_matrix/client/v3/sendToDevice/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'messages': - messages.map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v)))), - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'messages': messages + .map((k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v)))), + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -4593,19 +5030,23 @@ class Api { /// /// By default, this is `0`, so the server will return immediately /// even if the response is empty. - Future sync( - {String? filter, - String? since, - bool? fullState, - PresenceType? setPresence, - int? timeout}) async { - final requestUri = Uri(path: '_matrix/client/v3/sync', queryParameters: { - if (filter != null) 'filter': filter, - if (since != null) 'since': since, - if (fullState != null) 'full_state': fullState.toString(), - if (setPresence != null) 'set_presence': setPresence.name, - if (timeout != null) 'timeout': timeout.toString(), - }); + Future sync({ + String? filter, + String? since, + bool? fullState, + PresenceType? setPresence, + int? timeout, + }) async { + final requestUri = Uri( + path: '_matrix/client/v3/sync', + queryParameters: { + if (filter != null) 'filter': filter, + if (since != null) 'since': since, + if (fullState != null) 'full_state': fullState.toString(), + if (setPresence != null) 'set_presence': setPresence.name, + if (timeout != null) 'timeout': timeout.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4621,10 +5062,12 @@ class Api { /// /// [alias] The Matrix room alias to look up. Future> queryLocationByAlias(String alias) async { - final requestUri = - Uri(path: '_matrix/client/v3/thirdparty/location', queryParameters: { - 'alias': alias, - }); + final requestUri = Uri( + path: '_matrix/client/v3/thirdparty/location', + queryParameters: { + 'alias': alias, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4650,14 +5093,17 @@ class Api { /// /// [fields] One or more custom fields to help identify the third-party /// location. - Future> queryLocationByProtocol(String protocol, - {Map? fields}) async { + Future> queryLocationByProtocol( + String protocol, { + Map? fields, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/thirdparty/location/${Uri.encodeComponent(protocol)}', - queryParameters: { - if (fields != null) ...fields, - }); + path: + '_matrix/client/v3/thirdparty/location/${Uri.encodeComponent(protocol)}', + queryParameters: { + if (fields != null) ...fields, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4675,8 +5121,9 @@ class Api { /// [protocol] The name of the protocol. Future getProtocolMetadata(String protocol) async { final requestUri = Uri( - path: - '_matrix/client/v3/thirdparty/protocol/${Uri.encodeComponent(protocol)}'); + path: + '_matrix/client/v3/thirdparty/protocol/${Uri.encodeComponent(protocol)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4700,17 +5147,20 @@ class Api { final responseString = utf8.decode(responseBody); final json = jsonDecode(responseString); return (json as Map).map( - (k, v) => MapEntry(k, Protocol.fromJson(v as Map))); + (k, v) => MapEntry(k, Protocol.fromJson(v as Map)), + ); } /// Retrieve an array of third-party users from a Matrix User ID. /// /// [userid] The Matrix User ID to look up. Future> queryUserByID(String userid) async { - final requestUri = - Uri(path: '_matrix/client/v3/thirdparty/user', queryParameters: { - 'userid': userid, - }); + final requestUri = Uri( + path: '_matrix/client/v3/thirdparty/user', + queryParameters: { + 'userid': userid, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4729,14 +5179,17 @@ class Api { /// [protocol] The name of the protocol. /// /// [fields] One or more custom fields that are passed to the AS to help identify the user. - Future> queryUserByProtocol(String protocol, - {Map? fields}) async { + Future> queryUserByProtocol( + String protocol, { + Map? fields, + }) async { final requestUri = Uri( - path: - '_matrix/client/v3/thirdparty/user/${Uri.encodeComponent(protocol)}', - queryParameters: { - if (fields != null) ...fields, - }); + path: + '_matrix/client/v3/thirdparty/user/${Uri.encodeComponent(protocol)}', + queryParameters: { + if (fields != null) ...fields, + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4758,10 +5211,13 @@ class Api { /// [type] The event type of the account data to get. Custom types should be /// namespaced to avoid clashes. Future> getAccountData( - String userId, String type) async { + String userId, + String type, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/account_data/${Uri.encodeComponent(type)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/account_data/${Uri.encodeComponent(type)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4785,10 +5241,14 @@ class Api { /// /// [body] The content of the account data. Future setAccountData( - String userId, String type, Map body) async { + String userId, + String type, + Map body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/account_data/${Uri.encodeComponent(type)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/account_data/${Uri.encodeComponent(type)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -4816,7 +5276,8 @@ class Api { /// declared filter by homeservers on some APIs. Future defineFilter(String userId, Filter body) async { final requestUri = Uri( - path: '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/filter'); + path: '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/filter', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -4836,8 +5297,9 @@ class Api { /// [filterId] The filter ID to download. Future getFilter(String userId, String filterId) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/filter/${Uri.encodeComponent(filterId)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/filter/${Uri.encodeComponent(filterId)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4862,10 +5324,13 @@ class Api { /// /// [body] An empty object. Reserved for future expansion. Future requestOpenIdToken( - String userId, Map body) async { + String userId, + Map body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/openid/request_token'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/openid/request_token', + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -4889,10 +5354,14 @@ class Api { /// [type] The event type of the account data to get. Custom types should be /// namespaced to avoid clashes. Future> getAccountDataPerRoom( - String userId, String roomId, String type) async { + String userId, + String roomId, + String type, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/account_data/${Uri.encodeComponent(type)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/account_data/${Uri.encodeComponent(type)}', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4916,11 +5385,16 @@ class Api { /// namespaced to avoid clashes. /// /// [body] The content of the account data. - Future setAccountDataPerRoom(String userId, String roomId, String type, - Map body) async { + Future setAccountDataPerRoom( + String userId, + String roomId, + String type, + Map body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/account_data/${Uri.encodeComponent(type)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/account_data/${Uri.encodeComponent(type)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -4944,8 +5418,9 @@ class Api { /// Future?> getRoomTags(String userId, String roomId) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/tags'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/tags', + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4969,8 +5444,9 @@ class Api { /// [tag] The tag to remove. Future deleteRoomTag(String userId, String roomId, String tag) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/tags/${Uri.encodeComponent(tag)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/tags/${Uri.encodeComponent(tag)}', + ); final request = Request('DELETE', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -4992,10 +5468,15 @@ class Api { /// /// [body] Extra data for the tag, e.g. ordering. Future setRoomTag( - String userId, String roomId, String tag, Tag body) async { + String userId, + String roomId, + String tag, + Tag body, + ) async { final requestUri = Uri( - path: - '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/tags/${Uri.encodeComponent(tag)}'); + path: + '_matrix/client/v3/user/${Uri.encodeComponent(userId)}/rooms/${Uri.encodeComponent(roomId)}/tags/${Uri.encodeComponent(tag)}', + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; @@ -5022,16 +5503,20 @@ class Api { /// [limit] The maximum number of results to return. Defaults to 10. /// /// [searchTerm] The term to search for - Future searchUserDirectory(String searchTerm, - {int? limit}) async { + Future searchUserDirectory( + String searchTerm, { + int? limit, + }) async { final requestUri = Uri(path: '_matrix/client/v3/user_directory/search'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (limit != null) 'limit': limit, - 'search_term': searchTerm, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (limit != null) 'limit': limit, + 'search_term': searchTerm, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); @@ -5184,22 +5669,30 @@ class Api { /// set to `true` the server must return the media content itself. /// @deprecated - Future getContent(String serverName, String mediaId, - {bool? allowRemote, int? timeoutMs, bool? allowRedirect}) async { + Future getContent( + String serverName, + String mediaId, { + bool? allowRemote, + int? timeoutMs, + bool? allowRedirect, + }) async { final requestUri = Uri( - path: - '_matrix/media/v3/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', - queryParameters: { - if (allowRemote != null) 'allow_remote': allowRemote.toString(), - if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), - if (allowRedirect != null) 'allow_redirect': allowRedirect.toString(), - }); + path: + '_matrix/media/v3/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', + queryParameters: { + if (allowRemote != null) 'allow_remote': allowRemote.toString(), + if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), + if (allowRedirect != null) 'allow_redirect': allowRedirect.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); return FileResponse( - contentType: response.headers['content-type'], data: responseBody); + contentType: response.headers['content-type'], + data: responseBody, + ); } /// {{% boxes/note %}} @@ -5245,22 +5738,30 @@ class Api { /// @deprecated Future getContentOverrideName( - String serverName, String mediaId, String fileName, - {bool? allowRemote, int? timeoutMs, bool? allowRedirect}) async { + String serverName, + String mediaId, + String fileName, { + bool? allowRemote, + int? timeoutMs, + bool? allowRedirect, + }) async { final requestUri = Uri( - path: - '_matrix/media/v3/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}/${Uri.encodeComponent(fileName)}', - queryParameters: { - if (allowRemote != null) 'allow_remote': allowRemote.toString(), - if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), - if (allowRedirect != null) 'allow_redirect': allowRedirect.toString(), - }); + path: + '_matrix/media/v3/download/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}/${Uri.encodeComponent(fileName)}', + queryParameters: { + if (allowRemote != null) 'allow_remote': allowRemote.toString(), + if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), + if (allowRedirect != null) 'allow_redirect': allowRedirect.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); return FileResponse( - contentType: response.headers['content-type'], data: responseBody); + contentType: response.headers['content-type'], + data: responseBody, + ); } /// {{% boxes/note %}} @@ -5283,11 +5784,13 @@ class Api { /// available. @deprecated Future getUrlPreview(Uri url, {int? ts}) async { - final requestUri = - Uri(path: '_matrix/media/v3/preview_url', queryParameters: { - 'url': url.toString(), - if (ts != null) 'ts': ts.toString(), - }); + final requestUri = Uri( + path: '_matrix/media/v3/preview_url', + queryParameters: { + 'url': url.toString(), + if (ts != null) 'ts': ts.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; final response = await httpClient.send(request); @@ -5363,30 +5866,37 @@ class Api { /// @deprecated Future getContentThumbnail( - String serverName, String mediaId, int width, int height, - {Method? method, - bool? allowRemote, - int? timeoutMs, - bool? allowRedirect, - bool? animated}) async { + String serverName, + String mediaId, + int width, + int height, { + Method? method, + bool? allowRemote, + int? timeoutMs, + bool? allowRedirect, + bool? animated, + }) async { final requestUri = Uri( - path: - '_matrix/media/v3/thumbnail/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', - queryParameters: { - 'width': width.toString(), - 'height': height.toString(), - if (method != null) 'method': method.name, - if (allowRemote != null) 'allow_remote': allowRemote.toString(), - if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), - if (allowRedirect != null) 'allow_redirect': allowRedirect.toString(), - if (animated != null) 'animated': animated.toString(), - }); + path: + '_matrix/media/v3/thumbnail/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', + queryParameters: { + 'width': width.toString(), + 'height': height.toString(), + if (method != null) 'method': method.name, + if (allowRemote != null) 'allow_remote': allowRemote.toString(), + if (timeoutMs != null) 'timeout_ms': timeoutMs.toString(), + if (allowRedirect != null) 'allow_redirect': allowRedirect.toString(), + if (animated != null) 'animated': animated.toString(), + }, + ); final request = Request('GET', baseUri!.resolveUri(requestUri)); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); return FileResponse( - contentType: response.headers['content-type'], data: responseBody); + contentType: response.headers['content-type'], + data: responseBody, + ); } /// @@ -5404,11 +5914,17 @@ class Api { /// /// returns `content_uri`: /// The [`mxc://` URI](https://spec.matrix.org/unstable/client-server-api/#matrix-content-mxc-uris) to the uploaded content. - Future uploadContent(Uint8List body, - {String? filename, String? contentType}) async { - final requestUri = Uri(path: '_matrix/media/v3/upload', queryParameters: { - if (filename != null) 'filename': filename, - }); + Future uploadContent( + Uint8List body, { + String? filename, + String? contentType, + }) async { + final requestUri = Uri( + path: '_matrix/media/v3/upload', + queryParameters: { + if (filename != null) 'filename': filename, + }, + ); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; if (contentType != null) request.headers['content-type'] = contentType; @@ -5443,14 +5959,19 @@ class Api { /// Defaults to `application/octet-stream` if it is not set. /// Future> uploadContentToMXC( - String serverName, String mediaId, Uint8List body, - {String? filename, String? contentType}) async { + String serverName, + String mediaId, + Uint8List body, { + String? filename, + String? contentType, + }) async { final requestUri = Uri( - path: - '_matrix/media/v3/upload/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', - queryParameters: { - if (filename != null) 'filename': filename, - }); + path: + '_matrix/media/v3/upload/${Uri.encodeComponent(serverName)}/${Uri.encodeComponent(mediaId)}', + queryParameters: { + if (filename != null) 'filename': filename, + }, + ); final request = Request('PUT', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer ${bearerToken!}'; if (contentType != null) request.headers['content-type'] = contentType; diff --git a/lib/matrix_api_lite/generated/model.dart b/lib/matrix_api_lite/generated/model.dart index 75996822c..8f9d2a619 100644 --- a/lib/matrix_api_lite/generated/model.dart +++ b/lib/matrix_api_lite/generated/model.dart @@ -80,14 +80,18 @@ class DiscoveryInformation { DiscoveryInformation.fromJson(Map json) : mHomeserver = HomeserverInformation.fromJson( - json['m.homeserver'] as Map), + json['m.homeserver'] as Map, + ), mIdentityServer = ((v) => v != null ? IdentityServerInformation.fromJson(v as Map) : null)(json['m.identity_server']), - additionalProperties = Map.fromEntries(json.entries - .where( - (e) => !['m.homeserver', 'm.identity_server'].contains(e.key)) - .map((e) => MapEntry(e.key, e.value))); + additionalProperties = Map.fromEntries( + json.entries + .where( + (e) => !['m.homeserver', 'm.identity_server'].contains(e.key), + ) + .map((e) => MapEntry(e.key, e.value)), + ); Map toJson() { final mIdentityServer = this.mIdentityServer; return { @@ -462,8 +466,18 @@ class PublicRoomsChunk { other.worldReadable == worldReadable); @dart.override - int get hashCode => Object.hash(avatarUrl, canonicalAlias, guestCanJoin, - joinRule, name, numJoinedMembers, roomId, roomType, topic, worldReadable); + int get hashCode => Object.hash( + avatarUrl, + canonicalAlias, + guestCanJoin, + joinRule, + name, + numJoinedMembers, + roomId, + roomType, + topic, + worldReadable, + ); } /// @@ -633,17 +647,18 @@ class SpaceRoomsChunk implements PublicRoomsChunk, SpaceHierarchyRoomsChunk { @dart.override int get hashCode => Object.hash( - avatarUrl, - canonicalAlias, - guestCanJoin, - joinRule, - name, - numJoinedMembers, - roomId, - roomType, - topic, - worldReadable, - childrenState); + avatarUrl, + canonicalAlias, + guestCanJoin, + joinRule, + name, + numJoinedMembers, + roomId, + roomType, + topic, + worldReadable, + childrenState, + ); } /// @@ -831,8 +846,8 @@ class GetRelatingEventsWithRelTypeAndEventTypeResponse { }); GetRelatingEventsWithRelTypeAndEventTypeResponse.fromJson( - Map json) - : chunk = (json['chunk'] as List) + Map json, + ) : chunk = (json['chunk'] as List) .map((v) => MatrixEvent.fromJson(v as Map)) .toList(), nextBatch = ((v) => v != null ? v as String : null)(json['next_batch']), @@ -1320,8 +1335,10 @@ class WhoIsInfo { WhoIsInfo.fromJson(Map json) : devices = ((v) => v != null - ? (v as Map).map((k, v) => - MapEntry(k, DeviceInfo.fromJson(v as Map))) + ? (v as Map).map( + (k, v) => + MapEntry(k, DeviceInfo.fromJson(v as Map)), + ) : null)(json['devices']), userId = ((v) => v != null ? v as String : null)(json['user_id']); Map toJson() { @@ -1398,8 +1415,10 @@ class RoomVersionsCapability { }); RoomVersionsCapability.fromJson(Map json) - : available = (json['available'] as Map).map((k, v) => - MapEntry(k, RoomVersionAvailable.values.fromString(v as String)!)), + : available = (json['available'] as Map).map( + (k, v) => + MapEntry(k, RoomVersionAvailable.values.fromString(v as String)!), + ), default$ = json['default'] as String; Map toJson() => { 'available': available.map((k, v) => MapEntry(k, v.name)), @@ -1456,16 +1475,20 @@ class Capabilities { mSetDisplayname = ((v) => v != null ? BooleanCapability.fromJson(v as Map) : null)(json['m.set_displayname']), - additionalProperties = Map.fromEntries(json.entries - .where((e) => ![ + additionalProperties = Map.fromEntries( + json.entries + .where( + (e) => ![ 'm.3pid_changes', 'm.change_password', 'm.get_login_token', 'm.room_versions', 'm.set_avatar_url', - 'm.set_displayname' - ].contains(e.key)) - .map((e) => MapEntry(e.key, e.value))); + 'm.set_displayname', + ].contains(e.key), + ) + .map((e) => MapEntry(e.key, e.value)), + ); Map toJson() { final m3pidChanges = this.m3pidChanges; final mChangePassword = this.mChangePassword; @@ -1519,8 +1542,14 @@ class Capabilities { other.mSetDisplayname == mSetDisplayname); @dart.override - int get hashCode => Object.hash(m3pidChanges, mChangePassword, mGetLoginToken, - mRoomVersions, mSetAvatarUrl, mSetDisplayname); + int get hashCode => Object.hash( + m3pidChanges, + mChangePassword, + mGetLoginToken, + mRoomVersions, + mSetAvatarUrl, + mSetDisplayname, + ); } /// @@ -1858,11 +1887,12 @@ class ThirdPartySigned { ThirdPartySigned.fromJson(Map json) : mxid = json['mxid'] as String, sender = json['sender'] as String, - signatures = (json['signatures'] as Map).map((k, v) => - MapEntry( - k, - (v as Map) - .map((k, v) => MapEntry(k, v as String)))), + signatures = (json['signatures'] as Map).map( + (k, v) => MapEntry( + k, + (v as Map).map((k, v) => MapEntry(k, v as String)), + ), + ), token = json['token'] as String; Map toJson() => { 'mxid': mxid, @@ -1957,10 +1987,12 @@ class ClaimKeysResponse { .map((k, v) => MapEntry(k, v as Map)) : null)(json['failures']), oneTimeKeys = (json['one_time_keys'] as Map).map( - (k, v) => MapEntry( - k, - (v as Map) - .map((k, v) => MapEntry(k, v as Map)))); + (k, v) => MapEntry( + k, + (v as Map) + .map((k, v) => MapEntry(k, v as Map)), + ), + ); Map toJson() { final failures = this.failures; return { @@ -2014,26 +2046,45 @@ class QueryKeysResponse { QueryKeysResponse.fromJson(Map json) : deviceKeys = ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, - (v as Map).map((k, v) => MapEntry( - k, MatrixDeviceKeys.fromJson(v as Map))))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + (v as Map).map( + (k, v) => MapEntry( + k, + MatrixDeviceKeys.fromJson(v as Map), + ), + ), + ), + ) : null)(json['device_keys']), failures = ((v) => v != null ? (v as Map) .map((k, v) => MapEntry(k, v as Map)) : null)(json['failures']), masterKeys = ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, MatrixCrossSigningKey.fromJson(v as Map))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + MatrixCrossSigningKey.fromJson(v as Map), + ), + ) : null)(json['master_keys']), selfSigningKeys = ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, MatrixCrossSigningKey.fromJson(v as Map))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + MatrixCrossSigningKey.fromJson(v as Map), + ), + ) : null)(json['self_signing_keys']), userSigningKeys = ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, MatrixCrossSigningKey.fromJson(v as Map))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + MatrixCrossSigningKey.fromJson(v as Map), + ), + ) : null)(json['user_signing_keys']); Map toJson() { final deviceKeys = this.deviceKeys; @@ -2044,7 +2095,8 @@ class QueryKeysResponse { return { if (deviceKeys != null) 'device_keys': deviceKeys.map( - (k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.toJson())))), + (k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.toJson()))), + ), if (failures != null) 'failures': failures.map((k, v) => MapEntry(k, v)), if (masterKeys != null) 'master_keys': masterKeys.map((k, v) => MapEntry(k, v.toJson())), @@ -2107,7 +2159,12 @@ class QueryKeysResponse { @dart.override int get hashCode => Object.hash( - deviceKeys, failures, masterKeys, selfSigningKeys, userSigningKeys); + deviceKeys, + failures, + masterKeys, + selfSigningKeys, + userSigningKeys, + ); } /// @@ -2123,9 +2180,11 @@ class LoginFlow { : getLoginToken = ((v) => v != null ? v as bool : null)(json['get_login_token']), type = json['type'] as String, - additionalProperties = Map.fromEntries(json.entries - .where((e) => !['get_login_token', 'type'].contains(e.key)) - .map((e) => MapEntry(e.key, e.value))); + additionalProperties = Map.fromEntries( + json.entries + .where((e) => !['get_login_token', 'type'].contains(e.key)) + .map((e) => MapEntry(e.key, e.value)), + ); Map toJson() { final getLoginToken = this.getLoginToken; return { @@ -2255,8 +2314,15 @@ class LoginResponse { other.wellKnown == wellKnown); @dart.override - int get hashCode => Object.hash(accessToken, deviceId, expiresInMs, - homeServer, refreshToken, userId, wellKnown); + int get hashCode => Object.hash( + accessToken, + deviceId, + expiresInMs, + homeServer, + refreshToken, + userId, + wellKnown, + ); } /// @@ -2663,9 +2729,11 @@ class PusherData { PusherData.fromJson(Map json) : format = ((v) => v != null ? v as String : null)(json['format']), url = ((v) => v != null ? Uri.parse(v as String) : null)(json['url']), - additionalProperties = Map.fromEntries(json.entries - .where((e) => !['format', 'url'].contains(e.key)) - .map((e) => MapEntry(e.key, e.value))); + additionalProperties = Map.fromEntries( + json.entries + .where((e) => !['format', 'url'].contains(e.key)) + .map((e) => MapEntry(e.key, e.value)), + ); Map toJson() { final format = this.format; final url = this.url; @@ -2824,8 +2892,16 @@ class Pusher implements PusherId { other.profileTag == profileTag); @dart.override - int get hashCode => Object.hash(appId, pushkey, appDisplayName, data, - deviceDisplayName, kind, lang, profileTag); + int get hashCode => Object.hash( + appId, + pushkey, + appDisplayName, + data, + deviceDisplayName, + kind, + lang, + profileTag, + ); } /// @@ -3316,7 +3392,13 @@ class RegisterResponse { @dart.override int get hashCode => Object.hash( - accessToken, deviceId, expiresInMs, homeServer, refreshToken, userId); + accessToken, + deviceId, + expiresInMs, + homeServer, + refreshToken, + userId, + ); } /// @@ -3414,8 +3496,10 @@ class RoomKeyBackup { }); RoomKeyBackup.fromJson(Map json) - : sessions = (json['sessions'] as Map).map((k, v) => - MapEntry(k, KeyBackupData.fromJson(v as Map))); + : sessions = (json['sessions'] as Map).map( + (k, v) => + MapEntry(k, KeyBackupData.fromJson(v as Map)), + ); Map toJson() => { 'sessions': sessions.map((k, v) => MapEntry(k, v.toJson())), }; @@ -3442,8 +3526,10 @@ class RoomKeys { }); RoomKeys.fromJson(Map json) - : rooms = (json['rooms'] as Map).map((k, v) => - MapEntry(k, RoomKeyBackup.fromJson(v as Map))); + : rooms = (json['rooms'] as Map).map( + (k, v) => + MapEntry(k, RoomKeyBackup.fromJson(v as Map)), + ); Map toJson() => { 'rooms': rooms.map((k, v) => MapEntry(k, v.toJson())), }; @@ -4039,8 +4125,14 @@ class RoomEventFilter { other.unreadThreadNotifications == unreadThreadNotifications); @dart.override - int get hashCode => Object.hash(containsUrl, includeRedundantMembers, - lazyLoadMembers, notRooms, rooms, unreadThreadNotifications); + int get hashCode => Object.hash( + containsUrl, + includeRedundantMembers, + lazyLoadMembers, + notRooms, + rooms, + unreadThreadNotifications, + ); } /// @@ -4192,17 +4284,18 @@ class SearchFilter implements EventFilter, RoomEventFilter { @dart.override int get hashCode => Object.hash( - limit, - notSenders, - notTypes, - senders, - types, - containsUrl, - includeRedundantMembers, - lazyLoadMembers, - notRooms, - rooms, - unreadThreadNotifications); + limit, + notSenders, + notTypes, + senders, + types, + containsUrl, + includeRedundantMembers, + lazyLoadMembers, + notRooms, + rooms, + unreadThreadNotifications, + ); } /// @@ -4393,7 +4486,14 @@ class RoomEventsCriteria { @dart.override int get hashCode => Object.hash( - eventContext, filter, groupings, includeState, keys, orderBy, searchTerm); + eventContext, + filter, + groupings, + includeState, + keys, + orderBy, + searchTerm, + ); } /// @@ -4545,8 +4645,12 @@ class SearchResultsEventContext { .toList() : null)(json['events_before']), profileInfo = ((v) => v != null - ? (v as Map).map((k, v) => - MapEntry(k, UserProfile.fromJson(v as Map))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + UserProfile.fromJson(v as Map), + ), + ) : null)(json['profile_info']), start = ((v) => v != null ? v as String : null)(json['start']); Map toJson() { @@ -4667,10 +4771,17 @@ class ResultRoomEvents { ResultRoomEvents.fromJson(Map json) : count = ((v) => v != null ? v as int : null)(json['count']), groups = ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, - (v as Map).map((k, v) => MapEntry( - k, GroupValue.fromJson(v as Map))))) + ? (v as Map).map( + (k, v) => MapEntry( + k, + (v as Map).map( + (k, v) => MapEntry( + k, + GroupValue.fromJson(v as Map), + ), + ), + ), + ) : null)(json['groups']), highlights = ((v) => v != null ? (v as List).map((v) => v as String).toList() @@ -4682,11 +4793,16 @@ class ResultRoomEvents { .toList() : null)(json['results']), state = ((v) => v != null - ? (v as Map).map((k, v) => MapEntry( - k, - (v as List) - .map((v) => MatrixEvent.fromJson(v as Map)) - .toList())) + ? (v as Map).map( + (k, v) => MapEntry( + k, + (v as List) + .map( + (v) => MatrixEvent.fromJson(v as Map), + ) + .toList(), + ), + ) : null)(json['state']); Map toJson() { final count = this.count; @@ -4699,7 +4815,8 @@ class ResultRoomEvents { if (count != null) 'count': count, if (groups != null) 'groups': groups.map( - (k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.toJson())))), + (k, v) => MapEntry(k, v.map((k, v) => MapEntry(k, v.toJson()))), + ), if (highlights != null) 'highlights': highlights.map((v) => v).toList(), if (nextBatch != null) 'next_batch': nextBatch, if (results != null) 'results': results.map((v) => v.toJson()).toList(), @@ -4797,7 +4914,8 @@ class SearchResults { SearchResults.fromJson(Map json) : searchCategories = ResultCategories.fromJson( - json['search_categories'] as Map); + json['search_categories'] as Map, + ); Map toJson() => { 'search_categories': searchCategories.toJson(), }; @@ -4957,8 +5075,9 @@ class Protocol { }); Protocol.fromJson(Map json) - : fieldTypes = (json['field_types'] as Map).map((k, v) => - MapEntry(k, FieldType.fromJson(v as Map))), + : fieldTypes = (json['field_types'] as Map).map( + (k, v) => MapEntry(k, FieldType.fromJson(v as Map)), + ), icon = json['icon'] as String, instances = (json['instances'] as List) .map((v) => ProtocolInstance.fromJson(v as Map)) @@ -5218,17 +5337,18 @@ class StateFilter implements EventFilter, RoomEventFilter { @dart.override int get hashCode => Object.hash( - limit, - notSenders, - notTypes, - senders, - types, - containsUrl, - includeRedundantMembers, - lazyLoadMembers, - notRooms, - rooms, - unreadThreadNotifications); + limit, + notSenders, + notTypes, + senders, + types, + containsUrl, + includeRedundantMembers, + lazyLoadMembers, + notRooms, + rooms, + unreadThreadNotifications, + ); } /// @@ -5320,7 +5440,14 @@ class RoomFilter { @dart.override int get hashCode => Object.hash( - accountData, ephemeral, includeLeave, notRooms, rooms, state, timeline); + accountData, + ephemeral, + includeLeave, + notRooms, + rooms, + state, + timeline, + ); } /// @@ -5461,9 +5588,11 @@ class Tag { Tag.fromJson(Map json) : order = ((v) => v != null ? (v as num).toDouble() : null)(json['order']), - additionalProperties = Map.fromEntries(json.entries - .where((e) => !['order'].contains(e.key)) - .map((e) => MapEntry(e.key, e.value))); + additionalProperties = Map.fromEntries( + json.entries + .where((e) => !['order'].contains(e.key)) + .map((e) => MapEntry(e.key, e.value)), + ); Map toJson() { final order = this.order; return { diff --git a/lib/matrix_api_lite/matrix_api.dart b/lib/matrix_api_lite/matrix_api.dart index d6da5c875..81023e13c 100644 --- a/lib/matrix_api_lite/matrix_api.dart +++ b/lib/matrix_api_lite/matrix_api.dart @@ -202,19 +202,29 @@ class MatrixApi extends Api { @Deprecated('Use [deleteRoomKeyBySessionId] instead') Future deleteRoomKeysBySessionId( - String roomId, String sessionId, String version) async { + String roomId, + String sessionId, + String version, + ) async { return deleteRoomKeyBySessionId(roomId, sessionId, version); } @Deprecated('Use [deleteRoomKeyBySessionId] instead') - Future putRoomKeysBySessionId(String roomId, - String sessionId, String version, KeyBackupData data) async { + Future putRoomKeysBySessionId( + String roomId, + String sessionId, + String version, + KeyBackupData data, + ) async { return putRoomKeyBySessionId(roomId, sessionId, version, data); } @Deprecated('Use [getRoomKeyBySessionId] instead') Future getRoomKeysBySessionId( - String roomId, String sessionId, String version) async { + String roomId, + String sessionId, + String version, + ) async { return getRoomKeyBySessionId(roomId, sessionId, version); } } diff --git a/lib/matrix_api_lite/model/auth/authentication_password.dart b/lib/matrix_api_lite/model/auth/authentication_password.dart index f2d3bce01..f6f4636f8 100644 --- a/lib/matrix_api_lite/model/auth/authentication_password.dart +++ b/lib/matrix_api_lite/model/auth/authentication_password.dart @@ -33,16 +33,19 @@ class AuthenticationPassword extends AuthenticationData { /// Identifier classes extending AuthenticationIdentifier. AuthenticationIdentifier identifier; - AuthenticationPassword( - {super.session, required this.password, required this.identifier}) - : super( + AuthenticationPassword({ + super.session, + required this.password, + required this.identifier, + }) : super( type: AuthenticationTypes.password, ); AuthenticationPassword.fromJson(super.json) : password = json['password'] as String, identifier = AuthenticationIdentifier.subFromJson( - json['identifier'] as Map), + json['identifier'] as Map, + ), super.fromJson(); @override diff --git a/lib/matrix_api_lite/model/auth/authentication_third_party_identifier.dart b/lib/matrix_api_lite/model/auth/authentication_third_party_identifier.dart index b90b18449..975ea3842 100644 --- a/lib/matrix_api_lite/model/auth/authentication_third_party_identifier.dart +++ b/lib/matrix_api_lite/model/auth/authentication_third_party_identifier.dart @@ -28,9 +28,10 @@ class AuthenticationThirdPartyIdentifier extends AuthenticationIdentifier { String medium; String address; - AuthenticationThirdPartyIdentifier( - {required this.medium, required this.address}) - : super(type: AuthenticationIdentifierTypes.thirdParty); + AuthenticationThirdPartyIdentifier({ + required this.medium, + required this.address, + }) : super(type: AuthenticationIdentifierTypes.thirdParty); AuthenticationThirdPartyIdentifier.fromJson(super.json) : medium = json['medium'] as String, diff --git a/lib/matrix_api_lite/model/auth/authentication_three_pid_creds.dart b/lib/matrix_api_lite/model/auth/authentication_three_pid_creds.dart index 0076e6041..46b6ef509 100644 --- a/lib/matrix_api_lite/model/auth/authentication_three_pid_creds.dart +++ b/lib/matrix_api_lite/model/auth/authentication_three_pid_creds.dart @@ -30,8 +30,11 @@ import 'package:matrix/matrix_api_lite/model/auth/authentication_data.dart'; class AuthenticationThreePidCreds extends AuthenticationData { late ThreepidCreds threepidCreds; - AuthenticationThreePidCreds( - {super.session, required String super.type, required this.threepidCreds}); + AuthenticationThreePidCreds({ + super.session, + required String super.type, + required this.threepidCreds, + }); AuthenticationThreePidCreds.fromJson(Map json) : super.fromJson(json) { @@ -55,11 +58,12 @@ class ThreepidCreds { String? idServer; String? idAccessToken; - ThreepidCreds( - {required this.sid, - required this.clientSecret, - this.idServer, - this.idAccessToken}); + ThreepidCreds({ + required this.sid, + required this.clientSecret, + this.idServer, + this.idAccessToken, + }); ThreepidCreds.fromJson(Map json) : sid = json['sid'] as String, diff --git a/lib/matrix_api_lite/model/basic_event_with_sender.dart b/lib/matrix_api_lite/model/basic_event_with_sender.dart index 360228027..294e72b41 100644 --- a/lib/matrix_api_lite/model/basic_event_with_sender.dart +++ b/lib/matrix_api_lite/model/basic_event_with_sender.dart @@ -26,8 +26,11 @@ import 'package:matrix/matrix_api_lite/model/basic_event.dart'; class BasicEventWithSender extends BasicEvent { String senderId; - BasicEventWithSender( - {required super.type, required super.content, required this.senderId}); + BasicEventWithSender({ + required super.type, + required super.content, + required this.senderId, + }); BasicEventWithSender.fromJson(super.json) : senderId = json['sender'] as String, diff --git a/lib/matrix_api_lite/model/children_state.dart b/lib/matrix_api_lite/model/children_state.dart index 5d993baf2..a82bc5c97 100644 --- a/lib/matrix_api_lite/model/children_state.dart +++ b/lib/matrix_api_lite/model/children_state.dart @@ -36,7 +36,8 @@ class ChildrenState extends StrippedStateEvent { ChildrenState.fromJson(super.json) : originServerTs = DateTime.fromMillisecondsSinceEpoch( - json['origin_server_ts'] as int), + json['origin_server_ts'] as int, + ), super.fromJson(); @override diff --git a/lib/matrix_api_lite/model/events/forwarded_room_key_content.dart b/lib/matrix_api_lite/model/events/forwarded_room_key_content.dart index bd5bd17d0..3d2961c42 100644 --- a/lib/matrix_api_lite/model/events/forwarded_room_key_content.dart +++ b/lib/matrix_api_lite/model/events/forwarded_room_key_content.dart @@ -40,7 +40,9 @@ class ForwardedRoomKeyContent extends RoomKeyContent { senderClaimedEd25519Key = json.tryGet('sender_claimed_ed25519_key', TryGet.required) ?? '', forwardingCurve25519KeyChain = json.tryGetList( - 'forwarding_curve25519_key_chain', TryGet.required) ?? + 'forwarding_curve25519_key_chain', + TryGet.required, + ) ?? [], super.fromJson(); diff --git a/lib/matrix_api_lite/model/events/image_pack_content.dart b/lib/matrix_api_lite/model/events/image_pack_content.dart index 60be7c822..b4e5e30bc 100644 --- a/lib/matrix_api_lite/model/events/image_pack_content.dart +++ b/lib/matrix_api_lite/model/events/image_pack_content.dart @@ -36,21 +36,27 @@ enum ImagePackUsage { } List? imagePackUsageFromJson(List? json) => json - ?.map((v) => { - 'sticker': ImagePackUsage.sticker, - 'emoticon': ImagePackUsage.emoticon, - }[v]) + ?.map( + (v) => { + 'sticker': ImagePackUsage.sticker, + 'emoticon': ImagePackUsage.emoticon, + }[v], + ) .whereType() .toList(); List imagePackUsageToJson( - List? usage, List? prevUsage) { + List? usage, + List? prevUsage, +) { final knownUsages = {'sticker', 'emoticon'}; final usagesStr = usage - ?.map((v) => { - ImagePackUsage.sticker: 'sticker', - ImagePackUsage.emoticon: 'emoticon', - }[v]) + ?.map( + (v) => { + ImagePackUsage.sticker: 'sticker', + ImagePackUsage.emoticon: 'emoticon', + }[v], + ) .whereType() .toList() ?? []; @@ -74,30 +80,42 @@ class ImagePackContent { ImagePackContent({required this.images, required this.pack}) : _json = {}; ImagePackContent.fromJson(Map json) - : _json = Map.fromEntries(json.entries.where( - (e) => !['images', 'pack', 'emoticons', 'short'].contains(e.key))), + : _json = Map.fromEntries( + json.entries.where( + (e) => !['images', 'pack', 'emoticons', 'short'].contains(e.key), + ), + ), pack = ImagePackPackContent.fromJson( - json.tryGetMap('pack') ?? {}), - images = json.tryGetMap('images')?.catchMap((k, v) => - MapEntry( + json.tryGetMap('pack') ?? {}, + ), + images = json.tryGetMap('images')?.catchMap( + (k, v) => MapEntry( k, ImagePackImageContent.fromJson( - v as Map))) ?? + v as Map, + ), + ), + ) ?? // the "emoticons" key needs a small migration on the key, ":string:" --> "string" - json.tryGetMap('emoticons')?.catchMap((k, v) => - MapEntry( + json.tryGetMap('emoticons')?.catchMap( + (k, v) => MapEntry( k.startsWith(':') && k.endsWith(':') ? k.substring(1, k.length - 1) : k, ImagePackImageContent.fromJson( - v as Map))) ?? + v as Map, + ), + ), + ) ?? // the "short" key was still just a map from shortcode to mxc uri - json.tryGetMap('short')?.catchMap((k, v) => - MapEntry( + json.tryGetMap('short')?.catchMap( + (k, v) => MapEntry( k.startsWith(':') && k.endsWith(':') ? k.substring(1, k.length - 1) : k, - ImagePackImageContent(url: Uri.parse(v)))) ?? + ImagePackImageContent(url: Uri.parse(v)), + ), + ) ?? {}; Map toJson() => { @@ -120,8 +138,9 @@ class ImagePackImageContent { : _json = {}; ImagePackImageContent.fromJson(Map json) - : _json = Map.fromEntries(json.entries - .where((e) => !['url', 'body', 'info'].contains(e.key))), + : _json = Map.fromEntries( + json.entries.where((e) => !['url', 'body', 'info'].contains(e.key)), + ), url = Uri.parse(json['url'] as String), body = json.tryGet('body'), info = json.tryGetMap('info'), @@ -148,13 +167,20 @@ class ImagePackPackContent { List? usage; String? attribution; - ImagePackPackContent( - {this.displayName, this.avatarUrl, this.usage, this.attribution}) - : _json = {}; + ImagePackPackContent({ + this.displayName, + this.avatarUrl, + this.usage, + this.attribution, + }) : _json = {}; ImagePackPackContent.fromJson(Map json) - : _json = Map.fromEntries(json.entries.where((e) => - !['display_name', 'avatar_url', 'attribution'].contains(e.key))), + : _json = Map.fromEntries( + json.entries.where( + (e) => + !['display_name', 'avatar_url', 'attribution'].contains(e.key), + ), + ), displayName = json.tryGet('display_name'), // we default to an invalid uri avatarUrl = Uri.tryParse(json.tryGet('avatar_url') ?? '.::'), diff --git a/lib/matrix_api_lite/model/events/room_encrypted_content.dart b/lib/matrix_api_lite/model/events/room_encrypted_content.dart index 60ee27ed0..904767c6b 100644 --- a/lib/matrix_api_lite/model/events/room_encrypted_content.dart +++ b/lib/matrix_api_lite/model/events/room_encrypted_content.dart @@ -45,8 +45,12 @@ class RoomEncryptedContent { // filter out invalid/incomplete CiphertextInfos ciphertextOlm = json .tryGet>('ciphertext', TryGet.silent) - ?.catchMap((k, v) => MapEntry( - k, CiphertextInfo.fromJson(v as Map))); + ?.catchMap( + (k, v) => MapEntry( + k, + CiphertextInfo.fromJson(v as Map), + ), + ); Map toJson() { final data = {}; @@ -66,7 +70,8 @@ class RoomEncryptedContent { ciphertextOlm!.map((k, v) => MapEntry(k, v.toJson())); if (ciphertextMegolm != null) { Logs().wtf( - 'ciphertextOlm and ciphertextMegolm are both set, which should never happen!'); + 'ciphertextOlm and ciphertextMegolm are both set, which should never happen!', + ); } } return data; diff --git a/lib/matrix_api_lite/model/events/room_key_content.dart b/lib/matrix_api_lite/model/events/room_key_content.dart index 41f1d3140..3a893dfba 100644 --- a/lib/matrix_api_lite/model/events/room_key_content.dart +++ b/lib/matrix_api_lite/model/events/room_key_content.dart @@ -34,11 +34,12 @@ class RoomKeyContent { String sessionId; String sessionKey; - RoomKeyContent( - {required this.algorithm, - required this.roomId, - required this.sessionId, - required this.sessionKey}); + RoomKeyContent({ + required this.algorithm, + required this.roomId, + required this.sessionId, + required this.sessionKey, + }); RoomKeyContent.fromJson(Map json) : algorithm = json.tryGet('algorithm', TryGet.required) ?? '', diff --git a/lib/matrix_api_lite/model/events/room_key_request_content.dart b/lib/matrix_api_lite/model/events/room_key_request_content.dart index f9d3052bc..6f2ca80f2 100644 --- a/lib/matrix_api_lite/model/events/room_key_request_content.dart +++ b/lib/matrix_api_lite/model/events/room_key_request_content.dart @@ -60,11 +60,12 @@ class RequestedKeyInfo { String sessionId; String senderKey; - RequestedKeyInfo( - {required this.algorithm, - required this.roomId, - required this.sessionId, - required this.senderKey}); + RequestedKeyInfo({ + required this.algorithm, + required this.roomId, + required this.sessionId, + required this.senderKey, + }); RequestedKeyInfo.fromJson(Map json) : algorithm = json.tryGet('algorithm', TryGet.required) ?? '', diff --git a/lib/matrix_api_lite/model/events/secret_storage_key_content.dart b/lib/matrix_api_lite/model/events/secret_storage_key_content.dart index 21f8d8954..a948882a7 100644 --- a/lib/matrix_api_lite/model/events/secret_storage_key_content.dart +++ b/lib/matrix_api_lite/model/events/secret_storage_key_content.dart @@ -63,11 +63,12 @@ class PassphraseInfo { int? iterations; int? bits; - PassphraseInfo( - {required this.algorithm, - required this.salt, - required this.iterations, - this.bits}); + PassphraseInfo({ + required this.algorithm, + required this.salt, + required this.iterations, + this.bits, + }); PassphraseInfo.fromJson(Map json) : algorithm = json.tryGet('algorithm', TryGet.required), diff --git a/lib/matrix_api_lite/model/matrix_event.dart b/lib/matrix_api_lite/model/matrix_event.dart index d096d0a1f..ea6a555a8 100644 --- a/lib/matrix_api_lite/model/matrix_event.dart +++ b/lib/matrix_api_lite/model/matrix_event.dart @@ -49,7 +49,8 @@ class MatrixEvent extends StrippedStateEvent { : eventId = json['event_id'] as String, roomId = json['room_id'] as String?, originServerTs = DateTime.fromMillisecondsSinceEpoch( - json['origin_server_ts'] as int), + json['origin_server_ts'] as int, + ), unsigned = (json['unsigned'] as Map?)?.copy(), prevContent = (json['prev_content'] as Map?)?.copy(), redacts = json['redacts'] as String?, diff --git a/lib/matrix_api_lite/model/matrix_exception.dart b/lib/matrix_api_lite/model/matrix_exception.dart index 3a4ea02a5..e55fb652f 100644 --- a/lib/matrix_api_lite/model/matrix_exception.dart +++ b/lib/matrix_api_lite/model/matrix_exception.dart @@ -115,8 +115,10 @@ class MatrixException implements Exception { ?.whereType>() .map((flow) => flow['stages']) .whereType>() - .map((stages) => - AuthenticationFlow(List.from(stages.whereType()))) + .map( + (stages) => + AuthenticationFlow(List.from(stages.whereType())), + ) .toList(); /// This section contains any information that the client will need to know in order to use a given type diff --git a/lib/matrix_api_lite/model/presence_content.dart b/lib/matrix_api_lite/model/presence_content.dart index f5fb89cb3..ad56f1ff9 100644 --- a/lib/matrix_api_lite/model/presence_content.dart +++ b/lib/matrix_api_lite/model/presence_content.dart @@ -31,8 +31,9 @@ class PresenceContent { PresenceContent.fromJson(Map json) : presence = PresenceType.values.firstWhere( - (p) => p.toString().split('.').last == json['presence'], - orElse: () => PresenceType.offline), + (p) => p.toString().split('.').last == json['presence'], + orElse: () => PresenceType.offline, + ), lastActiveAgo = json.tryGet('last_active_ago'), statusMsg = json.tryGet('status_msg'), currentlyActive = json.tryGet('currently_active'); diff --git a/lib/matrix_api_lite/model/room_keys_keys.dart b/lib/matrix_api_lite/model/room_keys_keys.dart index f670eda6b..402bd7974 100644 --- a/lib/matrix_api_lite/model/room_keys_keys.dart +++ b/lib/matrix_api_lite/model/room_keys_keys.dart @@ -29,11 +29,12 @@ class RoomKeysSingleKey { bool isVerified; Map sessionData; - RoomKeysSingleKey( - {required this.firstMessageIndex, - required this.forwardedCount, - required this.isVerified, - required this.sessionData}); + RoomKeysSingleKey({ + required this.firstMessageIndex, + required this.forwardedCount, + required this.isVerified, + required this.sessionData, + }); RoomKeysSingleKey.fromJson(Map json) : firstMessageIndex = json['first_message_index'] as int, @@ -57,8 +58,12 @@ class RoomKeysRoom { RoomKeysRoom({required this.sessions}); RoomKeysRoom.fromJson(Map json) - : sessions = (json['sessions'] as Map).map((k, v) => - MapEntry(k, RoomKeysSingleKey.fromJson(v as Map))); + : sessions = (json['sessions'] as Map).map( + (k, v) => MapEntry( + k, + RoomKeysSingleKey.fromJson(v as Map), + ), + ); Map toJson() { final data = {}; diff --git a/lib/matrix_api_lite/model/stripped_state_event.dart b/lib/matrix_api_lite/model/stripped_state_event.dart index 5c0a991b5..27f832994 100644 --- a/lib/matrix_api_lite/model/stripped_state_event.dart +++ b/lib/matrix_api_lite/model/stripped_state_event.dart @@ -26,11 +26,12 @@ import 'package:matrix/matrix_api_lite.dart'; class StrippedStateEvent extends BasicEventWithSender { String? stateKey; - StrippedStateEvent( - {required super.type, - required super.content, - required super.senderId, - this.stateKey}); + StrippedStateEvent({ + required super.type, + required super.content, + required super.senderId, + this.stateKey, + }); StrippedStateEvent.fromJson(super.json) : stateKey = json.tryGet('state_key'), diff --git a/lib/matrix_api_lite/model/sync_update.dart b/lib/matrix_api_lite/model/sync_update.dart index 24a4b2e9b..99cecdc57 100644 --- a/lib/matrix_api_lite/model/sync_update.dart +++ b/lib/matrix_api_lite/model/sync_update.dart @@ -61,7 +61,8 @@ class SyncUpdate { toDevice = json .tryGetMap>('to_device')?['events'] ?.map( - (i) => BasicEventWithSender.fromJson(i as Map)) + (i) => BasicEventWithSender.fromJson(i as Map), + ) .toList(), deviceLists = (() { final temp = json.tryGetMap('device_lists'); @@ -72,7 +73,8 @@ class SyncUpdate { deviceUnusedFallbackKeyTypes = json.tryGetList('device_unused_fallback_key_types') ?? json.tryGetList( - 'org.matrix.msc2732.device_unused_fallback_key_types'); + 'org.matrix.msc2732.device_unused_fallback_key_types', + ); Map toJson() { final data = {}; @@ -124,14 +126,24 @@ class RoomsUpdate { }); RoomsUpdate.fromJson(Map json) { - join = json.tryGetMap('join')?.catchMap((k, v) => - MapEntry(k, JoinedRoomUpdate.fromJson(v as Map))); - invite = json.tryGetMap('invite')?.catchMap((k, v) => - MapEntry(k, InvitedRoomUpdate.fromJson(v as Map))); - leave = json.tryGetMap('leave')?.catchMap((k, v) => - MapEntry(k, LeftRoomUpdate.fromJson(v as Map))); - knock = json.tryGetMap('knock')?.catchMap((k, v) => - MapEntry(k, KnockRoomUpdate.fromJson(v as Map))); + join = json.tryGetMap('join')?.catchMap( + (k, v) => + MapEntry(k, JoinedRoomUpdate.fromJson(v as Map)), + ); + invite = json.tryGetMap('invite')?.catchMap( + (k, v) => MapEntry( + k, + InvitedRoomUpdate.fromJson(v as Map), + ), + ); + leave = json.tryGetMap('leave')?.catchMap( + (k, v) => + MapEntry(k, LeftRoomUpdate.fromJson(v as Map)), + ); + knock = json.tryGetMap('knock')?.catchMap( + (k, v) => + MapEntry(k, KnockRoomUpdate.fromJson(v as Map)), + ); } Map toJson() { @@ -187,7 +199,9 @@ class JoinedRoomUpdate extends SyncRoomUpdate { ?.map((i) => BasicRoomEvent.fromJson(i as Map)) .toList(), unreadNotifications = json.tryGetFromJson( - 'unread_notifications', UnreadNotificationCounts.fromJson); + 'unread_notifications', + UnreadNotificationCounts.fromJson, + ); Map toJson() { final data = {}; diff --git a/lib/matrix_api_lite/utils/filter_map_extension.dart b/lib/matrix_api_lite/utils/filter_map_extension.dart index 11621e761..ceaad3366 100644 --- a/lib/matrix_api_lite/utils/filter_map_extension.dart +++ b/lib/matrix_api_lite/utils/filter_map_extension.dart @@ -1,7 +1,8 @@ extension FilterMap on Map { Map filterMap(MapEntry? Function(K, V) f) => Map.fromEntries( - entries.map((e) => f(e.key, e.value)).whereType>()); + entries.map((e) => f(e.key, e.value)).whereType>(), + ); Map catchMap(MapEntry Function(K, V) f) => filterMap((k, v) { diff --git a/lib/matrix_api_lite/utils/try_get_map_extension.dart b/lib/matrix_api_lite/utils/try_get_map_extension.dart index 68aef66bc..877f41c59 100644 --- a/lib/matrix_api_lite/utils/try_get_map_extension.dart +++ b/lib/matrix_api_lite/utils/try_get_map_extension.dart @@ -39,7 +39,8 @@ class _RequiredLog implements TryGet { const _RequiredLog(); @override void call(String key, Type expected, Type actual) => Logs().w( - 'Expected required "$expected" in event content for the Key "$key" but got "$actual" at ${StackTrace.current.firstLine}'); + 'Expected required "$expected" in event content for the Key "$key" but got "$actual" at ${StackTrace.current.firstLine}', + ); } class _OptionalLog implements TryGet { @@ -48,7 +49,8 @@ class _OptionalLog implements TryGet { void call(String key, Type expected, Type actual) { if (actual != Null) { Logs().w( - 'Expected optional "$expected" in event content for the Key "$key" but got "$actual" at ${StackTrace.current.firstLine}'); + 'Expected optional "$expected" in event content for the Key "$key" but got "$actual" at ${StackTrace.current.firstLine}', + ); } } } @@ -80,7 +82,8 @@ extension TryGetMapExtension on Map { return value.cast().toList(); } catch (_) { Logs().v( - 'Unable to create "List<$T>" in event content for the key "$key" at ${StackTrace.current.firstLine}'); + 'Unable to create "List<$T>" in event content for the key "$key" at ${StackTrace.current.firstLine}', + ); return null; } } @@ -96,13 +99,17 @@ extension TryGetMapExtension on Map { return Map.from(value.cast()); } catch (_) { Logs().v( - 'Unable to create "Map<$A,$B>" in event content for the key "$key" at ${StackTrace.current.firstLine}'); + 'Unable to create "Map<$A,$B>" in event content for the key "$key" at ${StackTrace.current.firstLine}', + ); return null; } } - A? tryGetFromJson(String key, A Function(Map) fromJson, - [TryGet log = TryGet.optional]) { + A? tryGetFromJson( + String key, + A Function(Map) fromJson, [ + TryGet log = TryGet.optional, + ]) { final value = tryGetMap(key, log); return value != null ? fromJson(value) : null; diff --git a/lib/msc_extensions/extension_recent_emoji/recent_emoji.dart b/lib/msc_extensions/extension_recent_emoji/recent_emoji.dart index 4f8343e54..0cc441ad4 100644 --- a/lib/msc_extensions/extension_recent_emoji/recent_emoji.dart +++ b/lib/msc_extensions/extension_recent_emoji/recent_emoji.dart @@ -56,6 +56,9 @@ extension RecentEmojiExtension on Client { if (userID == null) return; final content = List.from(data.entries.map((e) => [e.key, e.value])); return setAccountData( - userID!, 'io.element.recent_emoji', {'recent_emoji': content}); + userID!, + 'io.element.recent_emoji', + {'recent_emoji': content}, + ); } } diff --git a/lib/msc_extensions/msc_1236_widgets/src/widget.dart b/lib/msc_extensions/msc_1236_widgets/src/widget.dart index 4f44657d2..3d15591cd 100644 --- a/lib/msc_extensions/msc_1236_widgets/src/widget.dart +++ b/lib/msc_extensions/msc_1236_widgets/src/widget.dart @@ -51,8 +51,12 @@ class MatrixWidget { ); /// creates an `m.jitsi` [MatrixWidget] - factory MatrixWidget.jitsi(Room room, String name, Uri url, - {bool isAudioOnly = false}) => + factory MatrixWidget.jitsi( + Room room, + String name, + Uri url, { + bool isAudioOnly = false, + }) => MatrixWidget( room: room, name: name, @@ -104,8 +108,10 @@ class MatrixWidget { // `[a-zA-Z0-9_-]` as well as non string values if (data != null) ...Map.from(data!) - ..removeWhere((key, value) => - !RegExp(r'^[\w-]+$').hasMatch(key) || !value is String) + ..removeWhere( + (key, value) => + !RegExp(r'^[\w-]+$').hasMatch(key) || !value is String, + ) ..map((key, value) => MapEntry('\$key', value)), }; diff --git a/lib/msc_extensions/msc_2835_uia_login/msc_2835_uia_login.dart b/lib/msc_extensions/msc_2835_uia_login/msc_2835_uia_login.dart index 2fefc458b..0a65d2b40 100644 --- a/lib/msc_extensions/msc_2835_uia_login/msc_2835_uia_login.dart +++ b/lib/msc_extensions/msc_2835_uia_login/msc_2835_uia_login.dart @@ -29,20 +29,22 @@ extension UiaLogin on Client { final requestUri = Uri(path: '_matrix/client/$pathVersion/login'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - if (address != null) 'address': address, - if (deviceId != null) 'device_id': deviceId, - if (identifier != null) 'identifier': identifier.toJson(), - if (initialDeviceDisplayName != null) - 'initial_device_display_name': initialDeviceDisplayName, - if (medium != null) 'medium': medium, - if (password != null) 'password': password, - if (token != null) 'token': token, - 'type': type, - if (user != null) 'user': user, - if (auth != null) 'auth': auth.toJson(), - if (refreshToken != null) 'refresh_token': refreshToken, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + if (address != null) 'address': address, + if (deviceId != null) 'device_id': deviceId, + if (identifier != null) 'identifier': identifier.toJson(), + if (initialDeviceDisplayName != null) + 'initial_device_display_name': initialDeviceDisplayName, + if (medium != null) 'medium': medium, + if (password != null) 'password': password, + if (token != null) 'token': token, + 'type': type, + if (user != null) 'user': user, + if (auth != null) 'auth': auth.toJson(), + if (refreshToken != null) 'refresh_token': refreshToken, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); diff --git a/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart b/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart index 4534b435e..e4cbea912 100644 --- a/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart +++ b/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart @@ -68,16 +68,21 @@ extension DehydratedDeviceMatrixApi on MatrixApi { /// fetch events sent to a dehydrated device. /// https://github.com/matrix-org/matrix-spec-proposals/pull/3814 - Future getDehydratedDeviceEvents(String deviceId, - {String? nextBatch, int limit = 100}) async { - final response = await request(RequestType.POST, - '/client/unstable/org.matrix.msc3814.v1/dehydrated_device/$deviceId/events', - query: { - 'limit': limit.toString(), - }, - data: { - if (nextBatch != null) 'next_batch': nextBatch, - }); + Future getDehydratedDeviceEvents( + String deviceId, { + String? nextBatch, + int limit = 100, + }) async { + final response = await request( + RequestType.POST, + '/client/unstable/org.matrix.msc3814.v1/dehydrated_device/$deviceId/events', + query: { + 'limit': limit.toString(), + }, + data: { + if (nextBatch != null) 'next_batch': nextBatch, + }, + ); return DehydratedDeviceEvents.fromJson(response); } } diff --git a/lib/msc_extensions/msc_3814_dehydrated_devices/msc_3814_dehydrated_devices.dart b/lib/msc_extensions/msc_3814_dehydrated_devices/msc_3814_dehydrated_devices.dart index 33a467187..cc102b802 100644 --- a/lib/msc_extensions/msc_3814_dehydrated_devices/msc_3814_dehydrated_devices.dart +++ b/lib/msc_extensions/msc_3814_dehydrated_devices/msc_3814_dehydrated_devices.dart @@ -62,7 +62,8 @@ extension DehydratedDeviceHandler on Client { if (dehydratedDeviceIdentity == null || !dehydratedDeviceIdentity.hasValidSignatureChain()) { Logs().w( - 'Dehydrated device ${device.deviceId} is unknown or unverified, replacing it'); + 'Dehydrated device ${device.deviceId} is unknown or unverified, replacing it', + ); await _uploadNewDevice(secureStorage); return; } @@ -101,8 +102,10 @@ extension DehydratedDeviceHandler on Client { DehydratedDeviceEvents? events; do { - events = await getDehydratedDeviceEvents(device.deviceId, - nextBatch: events?.nextBatch); + events = await getDehydratedDeviceEvents( + device.deviceId, + nextBatch: events?.nextBatch, + ); for (final e in events.events ?? []) { // We are only interested in roomkeys, which ALWAYS need to be encrypted. @@ -144,15 +147,21 @@ extension DehydratedDeviceHandler on Client { Logs().i('Dehydrated device key not found, creating new one.'); pickleDeviceKey = base64.encode(uc.secureRandomBytes(128)); await secureStorage.store( - _ssssSecretNameForDehydratedDevice, pickleDeviceKey); + _ssssSecretNameForDehydratedDevice, + pickleDeviceKey, + ); } const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; final rnd = Random(); - final deviceIdSuffix = String.fromCharCodes(Iterable.generate( - 10, (_) => chars.codeUnitAt(rnd.nextInt(chars.length)))); + final deviceIdSuffix = String.fromCharCodes( + Iterable.generate( + 10, + (_) => chars.codeUnitAt(rnd.nextInt(chars.length)), + ), + ); final String device = 'FAM$deviceIdSuffix'; // Generate a new olm account for the dehydrated device. diff --git a/lib/msc_extensions/msc_3935_cute_events/msc_3935_cute_events.dart b/lib/msc_extensions/msc_3935_cute_events/msc_3935_cute_events.dart index 2c1c1d4ae..ae7bb0214 100644 --- a/lib/msc_extensions/msc_3935_cute_events/msc_3935_cute_events.dart +++ b/lib/msc_extensions/msc_3935_cute_events/msc_3935_cute_events.dart @@ -6,12 +6,12 @@ abstract class CuteEventContent { static Map get googlyEyes => { 'msgtype': CuteEventContent.eventType, 'cute_type': 'googly_eyes', - 'body': '👀' + 'body': '👀', }; static Map get cuddle => { 'msgtype': CuteEventContent.eventType, 'cute_type': 'cuddle', - 'body': '😊' + 'body': '😊', }; static Map get hug => { 'msgtype': CuteEventContent.eventType, diff --git a/lib/msc_extensions/msc_unpublished_custom_refresh_token_lifetime/msc_unpublished_custom_refresh_token_lifetime.dart b/lib/msc_extensions/msc_unpublished_custom_refresh_token_lifetime/msc_unpublished_custom_refresh_token_lifetime.dart index 6a6b5fee9..fd6436ff7 100644 --- a/lib/msc_extensions/msc_unpublished_custom_refresh_token_lifetime/msc_unpublished_custom_refresh_token_lifetime.dart +++ b/lib/msc_extensions/msc_unpublished_custom_refresh_token_lifetime/msc_unpublished_custom_refresh_token_lifetime.dart @@ -41,11 +41,13 @@ extension MscUnpublishedCustomRefreshTokenLifetime on MatrixApi { final requestUri = Uri(path: '_matrix/client/v3/refresh'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; - request.bodyBytes = utf8.encode(jsonEncode({ - 'refresh_token': refreshToken, - if (refreshTokenLifetimeMs != null) - customFieldKey: refreshTokenLifetimeMs, - })); + request.bodyBytes = utf8.encode( + jsonEncode({ + 'refresh_token': refreshToken, + if (refreshTokenLifetimeMs != null) + customFieldKey: refreshTokenLifetimeMs, + }), + ); final response = await httpClient.send(request); final responseBody = await response.stream.toBytes(); if (response.statusCode != 200) unexpectedResponse(response, responseBody); diff --git a/lib/src/client.dart b/lib/src/client.dart index d6d4c7bd6..c74051212 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -111,7 +111,9 @@ class Client extends MatrixApi { @Deprecated('Use [nativeImplementations] instead') Future runInBackground( - FutureOr Function(U arg) function, U arg) async { + FutureOr Function(U arg) function, + U arg, + ) async { final compute = this.compute; if (compute != null) { return await compute(function, arg); @@ -143,7 +145,8 @@ class Client extends MatrixApi { } Future Function( - MatrixImageFileResizeArguments)? customImageResizer; + MatrixImageFileResizeArguments, + )? customImageResizer; /// Create a client /// [clientName] = unique identifier of this client @@ -245,8 +248,11 @@ class Client extends MatrixApi { ? NativeImplementationsIsolate(compute) : nativeImplementations, super( - httpClient: FixedTimeoutHttpClient( - httpClient ?? http.Client(), defaultNetworkRequestTimeout)) { + httpClient: FixedTimeoutHttpClient( + httpClient ?? http.Client(), + defaultNetworkRequestTimeout, + ), + ) { if (logLevel != null) Logs().level = logLevel; importantStateEvents.addAll([ EventTypes.RoomName, @@ -399,10 +405,11 @@ class Client extends MatrixApi { void _updatePushrules() { final ruleset = TryGetPushRule.tryFromJson( - _accountData[EventTypes.PushRules] - ?.content - .tryGetMap('global') ?? - {}); + _accountData[EventTypes.PushRules] + ?.content + .tryGetMap('global') ?? + {}, + ); _pushruleEvaluator = PushruleEvaluator.fromRuleset(ruleset); } @@ -476,8 +483,12 @@ class Client extends MatrixApi { String MatrixIdOrDomain, ) async { try { - final response = await httpClient.get(Uri.https( - MatrixIdOrDomain.domain ?? '', '/.well-known/matrix/client')); + final response = await httpClient.get( + Uri.https( + MatrixIdOrDomain.domain ?? '', + '/.well-known/matrix/client', + ), + ); var respBody = response.body; try { respBody = utf8.decode(response.bodyBytes); @@ -491,7 +502,8 @@ class Client extends MatrixApi { // provide a reasonable fallback. return DiscoveryInformation( mHomeserver: HomeserverInformation( - baseUrl: Uri.https(MatrixIdOrDomain.domain ?? '', '')), + baseUrl: Uri.https(MatrixIdOrDomain.domain ?? '', ''), + ), ); } } @@ -539,7 +551,9 @@ class Client extends MatrixApi { final loginTypes = await getLoginFlows() ?? []; if (!loginTypes.any((f) => supportedLoginTypes.contains(f.type))) { throw BadServerLoginTypesException( - loginTypes.map((f) => f.type).toSet(), supportedLoginTypes); + loginTypes.map((f) => f.type).toSet(), + supportedLoginTypes, + ); } return (wellKnown, versions, loginTypes); @@ -602,7 +616,8 @@ class Client extends MatrixApi { final homeserver = this.homeserver; if (accessToken == null || deviceId_ == null || homeserver == null) { throw Exception( - 'Registered but token, device ID, user ID or homeserver is null.'); + 'Registered but token, device ID, user ID or homeserver is null.', + ); } final expiresInMs = response.expiresInMs; final tokenExpiresAt = expiresInMs == null @@ -610,13 +625,14 @@ class Client extends MatrixApi { : DateTime.now().add(Duration(milliseconds: expiresInMs)); await init( - newToken: accessToken, - newTokenExpiresAt: tokenExpiresAt, - newRefreshToken: response.refreshToken, - newUserID: userId, - newHomeserver: homeserver, - newDeviceName: initialDeviceDisplayName ?? '', - newDeviceID: deviceId_); + newToken: accessToken, + newTokenExpiresAt: tokenExpiresAt, + newRefreshToken: response.refreshToken, + newUserID: userId, + newHomeserver: homeserver, + newDeviceName: initialDeviceDisplayName ?? '', + newDeviceID: deviceId_, + ); return response; } @@ -726,7 +742,8 @@ class Client extends MatrixApi { /// Run any request and react on user interactive authentication flows here. Future uiaRequestBackground( - Future Function(AuthenticationData? auth) request) { + Future Function(AuthenticationData? auth) request, + ) { final completer = Completer(); UiaRequest? uia; uia = UiaRequest( @@ -788,12 +805,14 @@ class Client extends MatrixApi { if (enableEncryption) { initialState ??= []; if (!initialState.any((s) => s.type == EventTypes.Encryption)) { - initialState.add(StateEvent( - content: { - 'algorithm': supportedGroupEncryptionAlgorithms.first, - }, - type: EventTypes.Encryption, - )); + initialState.add( + StateEvent( + content: { + 'algorithm': supportedGroupEncryptionAlgorithms.first, + }, + type: EventTypes.Encryption, + ), + ); } } @@ -840,23 +859,27 @@ class Client extends MatrixApi { if (enableEncryption) { initialState ??= []; if (!initialState.any((s) => s.type == EventTypes.Encryption)) { - initialState.add(StateEvent( - content: { - 'algorithm': supportedGroupEncryptionAlgorithms.first, - }, - type: EventTypes.Encryption, - )); + initialState.add( + StateEvent( + content: { + 'algorithm': supportedGroupEncryptionAlgorithms.first, + }, + type: EventTypes.Encryption, + ), + ); } } if (historyVisibility != null) { initialState ??= []; if (!initialState.any((s) => s.type == EventTypes.HistoryVisibility)) { - initialState.add(StateEvent( - content: { - 'history_visibility': historyVisibility.text, - }, - type: EventTypes.HistoryVisibility, - )); + initialState.add( + StateEvent( + content: { + 'history_visibility': historyVisibility.text, + }, + type: EventTypes.HistoryVisibility, + ), + ); } } if (groupCall) { @@ -888,8 +911,12 @@ class Client extends MatrixApi { /// Wait for the room to appear into the enabled section of the room sync. /// By default, the function will listen for room in invite, join and leave /// sections of the sync. - Future waitForRoomInSync(String roomId, - {bool join = false, bool invite = false, bool leave = false}) async { + Future waitForRoomInSync( + String roomId, { + bool join = false, + bool invite = false, + bool leave = false, + }) async { if (!join && !invite && !leave) { join = true; invite = true; @@ -897,10 +924,12 @@ class Client extends MatrixApi { } // Wait for the next sync where this room appears. - final syncUpdate = await onSync.stream.firstWhere((sync) => - invite && (sync.rooms?.invite?.containsKey(roomId) ?? false) || - join && (sync.rooms?.join?.containsKey(roomId) ?? false) || - leave && (sync.rooms?.leave?.containsKey(roomId) ?? false)); + final syncUpdate = await onSync.stream.firstWhere( + (sync) => + invite && (sync.rooms?.invite?.containsKey(roomId) ?? false) || + join && (sync.rooms?.join?.containsKey(roomId) ?? false) || + leave && (sync.rooms?.leave?.containsKey(roomId) ?? false), + ); // Wait for this sync to be completely processed. await onSyncStatus.stream.firstWhere( @@ -927,15 +956,16 @@ class Client extends MatrixApi { /// room as a space with `room.toSpace()`. /// /// https://github.com/matrix-org/matrix-doc/blob/matthew/msc1772/proposals/1772-groups-as-rooms.md - Future createSpace( - {String? name, - String? topic, - Visibility visibility = Visibility.public, - String? spaceAliasName, - List? invite, - List? invite3pid, - String? roomVersion, - bool waitForSync = false}) async { + Future createSpace({ + String? name, + String? topic, + Visibility visibility = Visibility.public, + String? spaceAliasName, + List? invite, + List? invite3pid, + String? roomVersion, + bool waitForSync = false, + }) async { final id = await createRoom( name: name, topic: topic, @@ -964,8 +994,9 @@ class Client extends MatrixApi { /// from a room where the user exists. Set `useServerCache` to true to get any /// prior value from this function @Deprecated('Use fetchOwnProfile() instead') - Future fetchOwnProfileFromServer( - {bool useServerCache = false}) async { + Future fetchOwnProfileFromServer({ + bool useServerCache = false, + }) async { try { return await getProfileFromUserId( userID!, @@ -974,7 +1005,8 @@ class Client extends MatrixApi { ); } catch (e) { Logs().w( - '[Matrix] getting profile from homeserver failed, falling back to first room with required profile'); + '[Matrix] getting profile from homeserver failed, falling back to first room with required profile', + ); return await getProfileFromUserId( userID!, getFromRooms: true, @@ -1111,13 +1143,15 @@ class Client extends MatrixApi { Future> loadArchiveWithTimeline() async { _archivedRooms.clear(); - final filter = jsonEncode(Filter( - room: RoomFilter( - state: StateFilter(lazyLoadMembers: true), - includeLeave: true, - timeline: StateFilter(limit: 10), - ), - ).toJson()); + final filter = jsonEncode( + Filter( + room: RoomFilter( + state: StateFilter(lazyLoadMembers: true), + includeLeave: true, + timeline: StateFilter(limit: 10), + ), + ).toJson(), + ); final syncResp = await sync( filter: filter, @@ -1138,9 +1172,10 @@ class Client extends MatrixApi { // best indicator we have to sort them. For archived rooms where we don't // have any, we move them to the bottom. final beginningOfTime = DateTime.fromMillisecondsSinceEpoch(0); - _archivedRooms.sort((b, a) => - (a.room.lastEvent?.originServerTs ?? beginningOfTime) - .compareTo(b.room.lastEvent?.originServerTs ?? beginningOfTime)); + _archivedRooms.sort( + (b, a) => (a.room.lastEvent?.originServerTs ?? beginningOfTime) + .compareTo(b.room.lastEvent?.originServerTs ?? beginningOfTime), + ); return _archivedRooms; } @@ -1167,27 +1202,36 @@ class Client extends MatrixApi { // 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() ?? - [])); + 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; final stateEvents = roomUpdate.state; if (stateEvents != null) { - await _handleRoomEvents(archivedRoom, stateEvents, EventUpdateType.state, - store: false); + await _handleRoomEvents( + archivedRoom, + stateEvents, + EventUpdateType.state, + store: false, + ); } final timelineEvents = roomUpdate.timeline?.events; if (timelineEvents != null) { - await _handleRoomEvents(archivedRoom, timelineEvents.reversed.toList(), - EventUpdateType.timeline, - store: false); + await _handleRoomEvents( + archivedRoom, + timelineEvents.reversed.toList(), + EventUpdateType.timeline, + store: false, + ); } for (var i = 0; i < timeline.events.length; i++) { @@ -1233,11 +1277,12 @@ class Client extends MatrixApi { /// repository APIs, for example, proxies may enforce a lower upload size limit /// than is advertised by the server on this endpoint. @override - Future getConfig() => _serverConfigCache - .tryFetch(() async => (await authenticatedMediaSupported()) - ? getConfigAuthed() - // ignore: deprecated_member_use_from_same_package - : super.getConfig()); + Future getConfig() => _serverConfigCache.tryFetch( + () async => (await authenticatedMediaSupported()) + ? getConfigAuthed() + // ignore: deprecated_member_use_from_same_package + : super.getConfig(), + ); /// /// @@ -1461,8 +1506,11 @@ class Client extends MatrixApi { /// Returns the mxc url. Please note, that this does **not** encrypt /// the content. Use `Room.sendFileEvent()` for end to end encryption. @override - Future uploadContent(Uint8List file, - {String? filename, String? contentType}) async { + Future uploadContent( + Uint8List file, { + String? filename, + String? contentType, + }) async { final mediaConfig = await getConfig(); final maxMediaSize = mediaConfig.mUploadSize; if (maxMediaSize != null && maxMediaSize < file.lengthInBytes) { @@ -1476,7 +1524,10 @@ class Client extends MatrixApi { final database = this.database; if (database != null && file.length <= database.maxFileSize) { await database.storeFile( - mxc, file, DateTime.now().millisecondsSinceEpoch); + mxc, + file, + DateTime.now().millisecondsSinceEpoch, + ); } return mxc; } @@ -1584,10 +1635,10 @@ class Client extends MatrixApi { static const Set supportedVersions = {'v1.1', 'v1.2'}; static const List supportedDirectEncryptionAlgorithms = [ - AlgorithmTypes.olmV1Curve25519AesSha2 + AlgorithmTypes.olmV1Curve25519AesSha2, ]; static const List supportedGroupEncryptionAlgorithms = [ - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ]; static const int defaultThumbnailSize = 800; @@ -1626,7 +1677,8 @@ class Client extends MatrixApi { /// Callback will be called on presences. @Deprecated( - 'Deprecated, use onPresenceChanged instead which has a timestamp.') + 'Deprecated, use onPresenceChanged instead which has a timestamp.', + ) final CachedStreamController onPresence = CachedStreamController(); /// Callback will be called on presence updates. @@ -1688,7 +1740,8 @@ class Client extends MatrixApi { if (!isLogged()) { if (database == null) { throw Exception( - 'Can not execute getEventByPushNotification() without a database'); + 'Can not execute getEventByPushNotification() without a database', + ); } final clientInfoMap = await database.getClient(clientName); final token = clientInfoMap?.tryGet('token'); @@ -1715,26 +1768,30 @@ class Client extends MatrixApi { final roomName = notification.roomName; final roomAlias = notification.roomAlias; if (roomName != null) { - room.setState(Event( - eventId: 'TEMP', - stateKey: '', - type: EventTypes.RoomName, - content: {'name': roomName}, - room: room, - senderId: 'UNKNOWN', - originServerTs: DateTime.now(), - )); + room.setState( + Event( + eventId: 'TEMP', + stateKey: '', + type: EventTypes.RoomName, + content: {'name': roomName}, + room: room, + senderId: 'UNKNOWN', + originServerTs: DateTime.now(), + ), + ); } if (roomAlias != null) { - room.setState(Event( - eventId: 'TEMP', - stateKey: '', - type: EventTypes.RoomCanonicalAlias, - content: {'alias': roomAlias}, - room: room, - senderId: 'UNKNOWN', - originServerTs: DateTime.now(), - )); + room.setState( + Event( + eventId: 'TEMP', + stateKey: '', + type: EventTypes.RoomCanonicalAlias, + content: {'alias': roomAlias}, + room: room, + senderId: 'UNKNOWN', + originServerTs: DateTime.now(), + ), + ); } // Load the event from the notification or from the database or from server: @@ -1763,9 +1820,11 @@ class Client extends MatrixApi { // No access to the MatrixEvent. Search in /notifications final notificationsResponse = await getNotifications(); matrixEvent ??= notificationsResponse.notifications - .firstWhereOrNull((notification) => - notification.roomId == roomId && - notification.event.eventId == eventId) + .firstWhereOrNull( + (notification) => + notification.roomId == roomId && + notification.event.eventId == eventId, + ) ?.event; } @@ -1829,12 +1888,13 @@ class Client extends MatrixApi { if (storeInDatabase) { await database?.transaction(() async { await database.storeEventUpdate( - EventUpdate( - roomID: roomId, - type: EventUpdateType.timeline, - content: event.toJson(), - ), - this); + EventUpdate( + roomID: roomId, + type: EventUpdateType.timeline, + content: event.toJson(), + ), + this, + ); }); } @@ -2196,8 +2256,9 @@ class Client extends MatrixApi { /// Checks if the token expires in under [expiresIn] time and calls the /// given `onSoftLogout()` if so. You have to provide `onSoftLogout` in the /// Client constructor. Otherwise this will do nothing. - Future ensureNotSoftLoggedOut( - [Duration expiresIn = const Duration(minutes: 1)]) async { + Future ensureNotSoftLoggedOut([ + Duration expiresIn = const Duration(minutes: 1), + ]) async { final tokenExpiresAt = accessTokenExpiresAt; if (onSoftLogout != null && tokenExpiresAt != null && @@ -2284,7 +2345,8 @@ class Client extends MatrixApi { onSyncStatus.add(SyncStatusUpdate(SyncStatus.cleaningUp)); // ignore: unawaited_futures database?.deleteOldFiles( - DateTime.now().subtract(Duration(days: 30)).millisecondsSinceEpoch); + DateTime.now().subtract(Duration(days: 30)).millisecondsSinceEpoch, + ); await updateUserDeviceKeys(); if (encryptionEnabled) { encryption?.onSync(); @@ -2298,8 +2360,12 @@ class Client extends MatrixApi { _retryDelay = Future.value(); onSyncStatus.add(SyncStatusUpdate(SyncStatus.finished)); } on MatrixException catch (e, s) { - onSyncStatus.add(SyncStatusUpdate(SyncStatus.error, - error: SdkError(exception: e, stackTrace: s))); + onSyncStatus.add( + SyncStatusUpdate( + SyncStatus.error, + error: SdkError(exception: e, stackTrace: s), + ), + ); if (e.error == MatrixError.M_UNKNOWN_TOKEN) { if (e.raw.tryGet('soft_logout') == true) { Logs().w( @@ -2313,14 +2379,24 @@ class Client extends MatrixApi { } } on SyncConnectionException catch (e, s) { Logs().w('Syncloop failed: Client has not connection to the server'); - onSyncStatus.add(SyncStatusUpdate(SyncStatus.error, - error: SdkError(exception: e, stackTrace: s))); + onSyncStatus.add( + SyncStatusUpdate( + SyncStatus.error, + error: SdkError(exception: e, stackTrace: s), + ), + ); } catch (e, s) { if (!isLogged() || _disposed || _aborted) return; Logs().e('Error during processing events', e, s); - onSyncStatus.add(SyncStatusUpdate(SyncStatus.error, + onSyncStatus.add( + SyncStatusUpdate( + SyncStatus.error, error: SdkError( - exception: e is Exception ? e : Exception(e), stackTrace: s))); + exception: e is Exception ? e : Exception(e), + stackTrace: s, + ), + ), + ); } } @@ -2386,7 +2462,9 @@ class Client extends MatrixApi { } if (encryptionEnabled) { encryption?.handleDeviceOneTimeKeysCount( - sync.deviceOneTimeKeysCount, sync.deviceUnusedFallbackKeyTypes); + sync.deviceOneTimeKeysCount, + sync.deviceUnusedFallbackKeyTypes, + ); } _sortRooms(); onSync.add(sync); @@ -2453,7 +2531,8 @@ class Client extends MatrixApi { for (final event in _eventsPendingDecryption) { if (event.event.roomID != roomId) continue; if (!sessionIds.contains( - event.event.content['content']?['session_id'])) continue; + event.event.content['content']?['session_id'], + )) continue; final decryptedEvent = await event.event.decrypt(room); if (decryptedEvent.content.tryGet('type') != @@ -2463,25 +2542,35 @@ class Client extends MatrixApi { } await _handleRoomEvents( - room, events, EventUpdateType.decryptedTimelineQueue); + room, + events, + EventUpdateType.decryptedTimelineQueue, + ); - _eventsPendingDecryption.removeWhere((e) => events.any( + _eventsPendingDecryption.removeWhere( + (e) => events.any( (decryptedEvent) => decryptedEvent.content['event_id'] == - e.event.content['event_id'])); + e.event.content['event_id'], + ), + ); } } _eventsPendingDecryption.removeWhere((e) => e.timedOut); } - Future _handleRooms(Map rooms, - {Direction? direction}) async { + Future _handleRooms( + Map rooms, { + Direction? direction, + }) async { var handledRooms = 0; for (final entry in rooms.entries) { - onSyncStatus.add(SyncStatusUpdate( - SyncStatus.processing, - progress: ++handledRooms / rooms.length, - )); + onSyncStatus.add( + SyncStatusUpdate( + SyncStatus.processing, + progress: ++handledRooms / rooms.length, + ), + ); final id = entry.key; final syncRoomUpdate = entry.value; @@ -2539,19 +2628,30 @@ class Client extends MatrixApi { if (syncRoomUpdate is LeftRoomUpdate) { final timelineEvents = syncRoomUpdate.timeline?.events; if (timelineEvents != null && timelineEvents.isNotEmpty) { - await _handleRoomEvents(room, timelineEvents, timelineUpdateType, - store: false); + await _handleRoomEvents( + room, + timelineEvents, + timelineUpdateType, + store: false, + ); } final accountData = syncRoomUpdate.accountData; if (accountData != null && accountData.isNotEmpty) { await _handleRoomEvents( - room, accountData, EventUpdateType.accountData, - store: false); + room, + accountData, + EventUpdateType.accountData, + store: false, + ); } final state = syncRoomUpdate.state; if (state != null && state.isNotEmpty) { - await _handleRoomEvents(room, state, EventUpdateType.state, - store: false); + await _handleRoomEvents( + room, + state, + EventUpdateType.state, + store: false, + ); } } @@ -2587,15 +2687,16 @@ class Client extends MatrixApi { } await _handleRoomEvents( - room, - [ - BasicRoomEvent( - type: LatestReceiptState.eventType, - roomId: room.id, - content: receiptStateContent.toJson(), - ) - ], - EventUpdateType.accountData); + room, + [ + BasicRoomEvent( + type: LatestReceiptState.eventType, + roomId: room.id, + content: receiptStateContent.toJson(), + ), + ], + EventUpdateType.accountData, + ); } } @@ -2603,8 +2704,11 @@ class Client extends MatrixApi { final List<_EventPendingDecryption> _eventsPendingDecryption = []; Future _handleRoomEvents( - Room room, List events, EventUpdateType type, - {bool store = true}) async { + Room room, + List events, + EventUpdateType type, { + bool store = true, + }) async { // Calling events can be omitted if they are outdated from the same sync. So // we collect them first before we handle them. final callEvents = []; @@ -2629,10 +2733,15 @@ class Client extends MatrixApi { // if the event failed to decrypt, add it to the queue if (update.content.tryGet('type') == EventTypes.Encrypted) { - _eventsPendingDecryption.add(_EventPendingDecryption(EventUpdate( - roomID: update.roomID, - type: EventUpdateType.decryptedTimelineQueue, - content: update.content))); + _eventsPendingDecryption.add( + _EventPendingDecryption( + EventUpdate( + roomID: update.roomID, + type: EventUpdateType.decryptedTimelineQueue, + content: update.content, + ), + ), + ); } } @@ -2689,7 +2798,9 @@ class Client extends MatrixApi { DateTime lastStaleCallRun = DateTime(0); Future _updateRoomsByRoomUpdate( - String roomId, SyncRoomUpdate chatUpdate) async { + String roomId, + SyncRoomUpdate chatUpdate, + ) async { // Update the chat list item. // Search the room in the rooms final roomIndex = rooms.indexWhere((r) => r.id == roomId); @@ -2765,7 +2876,8 @@ class Client extends MatrixApi { if ((chatUpdate.timeline?.limited ?? false) && requestHistoryOnLimitedTimeline) { Logs().v( - 'Limited timeline for ${rooms[roomIndex].id} request history now'); + 'Limited timeline for ${rooms[roomIndex].id} request history now', + ); runInRoot(rooms[roomIndex].requestHistory); } } @@ -2801,9 +2913,10 @@ class Client extends MatrixApi { if (event.type == EventTypes.Redaction && ({ room.lastEvent?.eventId, - room.lastEvent?.relationshipEventId + room.lastEvent?.relationshipEventId, }.contains( - event.redacts ?? event.content.tryGet('redacts')))) { + event.redacts ?? event.content.tryGet('redacts'), + ))) { room.lastEvent?.setRedactionEvent(event); break; } @@ -2994,7 +3107,10 @@ class Client extends MatrixApi { // Set the new device key for this device final entry = DeviceKeys.fromMatrixDeviceKeys( - rawDeviceKeyEntry.value, this, oldKeys[deviceId]?.lastActive); + rawDeviceKeyEntry.value, + this, + oldKeys[deviceId]?.lastActive, + ); final ed25519Key = entry.ed25519Key; final curve25519Key = entry.curve25519Key; if (entry.isValid && @@ -3008,24 +3124,30 @@ class Client extends MatrixApi { if (oldPublicKeys != null && oldPublicKeys != curve25519Key + ed25519Key) { Logs().w( - 'Already seen Device ID has been added again. This might be an attack!'); + 'Already seen Device ID has been added again. This might be an attack!', + ); continue; } final oldDeviceId = await database.publicKeySeen(ed25519Key); if (oldDeviceId != null && oldDeviceId != deviceId) { Logs().w( - 'Already seen ED25519 has been added again. This might be an attack!'); + 'Already seen ED25519 has been added again. This might be an attack!', + ); continue; } final oldDeviceId2 = await database.publicKeySeen(curve25519Key); if (oldDeviceId2 != null && oldDeviceId2 != deviceId) { Logs().w( - 'Already seen Curve25519 has been added again. This might be an attack!'); + 'Already seen Curve25519 has been added again. This might be an attack!', + ); continue; } await database.addSeenDeviceId( - userId, deviceId, curve25519Key + ed25519Key); + userId, + deviceId, + curve25519Key + ed25519Key, + ); await database.addSeenPublicKey(ed25519Key, deviceId); await database.addSeenPublicKey(curve25519Key, deviceId); } @@ -3048,14 +3170,16 @@ class Client extends MatrixApi { // Always trust the own device entry.setDirectVerified(true); } - dbActions.add(() => database.storeUserDeviceKey( - userId, - deviceId, - json.encode(entry.toJson()), - entry.directVerified, - entry.blocked, - entry.lastActive.millisecondsSinceEpoch, - )); + dbActions.add( + () => database.storeUserDeviceKey( + userId, + deviceId, + json.encode(entry.toJson()), + entry.directVerified, + entry.blocked, + entry.lastActive.millisecondsSinceEpoch, + ), + ); } else if (oldKeys.containsKey(deviceId)) { // This shouldn't ever happen. The same device ID has gotten // a new public key. So we ignore the update. TODO: ask krille @@ -3106,12 +3230,16 @@ class Client extends MatrixApi { } else { // There is a previous cross-signing key with this usage, that we no // longer need/use. Clear it from the database. - dbActions.add(() => - database.removeUserCrossSigningKey(userId, oldEntry.key)); + dbActions.add( + () => + database.removeUserCrossSigningKey(userId, oldEntry.key), + ); } } final entry = CrossSigningKey.fromMatrixCrossSigningKey( - crossSigningKeyListEntry.value, this); + crossSigningKeyListEntry.value, + this, + ); final publicKey = entry.publicKey; if (entry.isValid && publicKey != null) { final oldKey = oldKeys[publicKey]; @@ -3129,13 +3257,15 @@ class Client extends MatrixApi { // if we should instead use the new key with unknown verified / blocked status userKeys.crossSigningKeys[publicKey] = oldKey; } - dbActions.add(() => database.storeUserCrossSigningKey( - userId, - publicKey, - json.encode(entry.toJson()), - entry.directVerified, - entry.blocked, - )); + dbActions.add( + () => database.storeUserCrossSigningKey( + userId, + publicKey, + json.encode(entry.toJson()), + entry.directVerified, + entry.blocked, + ), + ); } _userDeviceKeys[userId]?.outdated = false; dbActions @@ -3182,17 +3312,24 @@ class Client extends MatrixApi { for (final entry in entries) { // Convert the Json Map to the correct format regarding // https: //matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-sendtodevice-eventtype-txnid - final data = entry.content.map((k, v) => - MapEntry>>( + final data = entry.content.map( + (k, v) => MapEntry>>( + k, + (v as Map).map( + (k, v) => MapEntry>( k, - (v as Map).map((k, v) => MapEntry>( - k, Map.from(v))))); + Map.from(v), + ), + ), + ), + ); try { await super.sendToDevice(entry.type, entry.txnId, data); } on MatrixException catch (e) { Logs().w( - '[To-Device] failed to to_device message from the queue to the server. Ignoring error: $e'); + '[To-Device] failed to to_device message from the queue to the server. Ignoring error: $e', + ); Logs().w('Payload: $data'); } await database.deleteFromToDeviceQueue(entry.id); @@ -3213,14 +3350,18 @@ class Client extends MatrixApi { await super.sendToDevice(eventType, txnId, messages); } catch (e, s) { Logs().w( - '[Client] Problem while sending to_device event, retrying later...', - e, - s); + '[Client] Problem while sending to_device event, retrying later...', + e, + s, + ); final database = this.database; if (database != null) { _toDeviceQueueNeedsProcessing = true; await database.insertIntoToDeviceQueue( - eventType, txnId, json.encode(messages)); + eventType, + txnId, + json.encode(messages), + ); } rethrow; } @@ -3240,7 +3381,10 @@ class Client extends MatrixApi { data[user] = {'*': message}; } await sendToDevice( - eventType, messageId ?? generateUniqueTransactionId(), data); + eventType, + messageId ?? generateUniqueTransactionId(), + data, + ); return; } @@ -3259,10 +3403,12 @@ class Client extends MatrixApi { // Don't send this message to blocked devices, and if specified onlyVerified // then only send it to verified devices if (deviceKeys.isNotEmpty) { - deviceKeys.removeWhere((DeviceKeys deviceKeys) => - deviceKeys.blocked || - (deviceKeys.userId == userID && deviceKeys.deviceId == deviceID) || - (onlyVerified && !deviceKeys.verified)); + deviceKeys.removeWhere( + (DeviceKeys deviceKeys) => + deviceKeys.blocked || + (deviceKeys.userId == userID && deviceKeys.deviceId == deviceID) || + (onlyVerified && !deviceKeys.verified), + ); if (deviceKeys.isEmpty) return; } @@ -3285,7 +3431,10 @@ class Client extends MatrixApi { ); eventType = EventTypes.Encrypted; await sendToDevice( - eventType, messageId ?? generateUniqueTransactionId(), data); + eventType, + messageId ?? generateUniqueTransactionId(), + data, + ); } finally { _sendToDeviceEncryptedLock.unlock(deviceKeys); } @@ -3303,8 +3452,10 @@ class Client extends MatrixApi { if (!encryptionEnabled) return; // be sure to copy our device keys list deviceKeys = List.from(deviceKeys); - deviceKeys.removeWhere((DeviceKeys k) => - k.blocked || (k.userId == userID && k.deviceId == deviceID)); + deviceKeys.removeWhere( + (DeviceKeys k) => + k.blocked || (k.userId == userID && k.deviceId == deviceID), + ); if (deviceKeys.isEmpty) return; message = message.copy(); // make sure we deep-copy the message // make sure all the olm sessions are loaded from database @@ -3321,10 +3472,9 @@ class Client extends MatrixApi { for (; i < deviceKeys.length && i <= 0; i += chunkSize) { Logs().v('Sending chunk $i...'); final chunk = deviceKeys.sublist( - i, - i + chunkSize > deviceKeys.length - ? deviceKeys.length - : i + chunkSize); + i, + i + chunkSize > deviceKeys.length ? deviceKeys.length : i + chunkSize, + ); // and send await sendToDeviceEncrypted(chunk, eventType, message); } @@ -3337,10 +3487,11 @@ class Client extends MatrixApi { await Future.delayed(Duration(milliseconds: 50)); Logs().v('Sending chunk $i...'); final chunk = deviceKeys.sublist( - i, - i + chunkSize > deviceKeys.length - ? deviceKeys.length - : i + chunkSize); + i, + i + chunkSize > deviceKeys.length + ? deviceKeys.length + : i + chunkSize, + ); // and send await sendToDeviceEncrypted(chunk, eventType, message); } @@ -3380,11 +3531,13 @@ class Client extends MatrixApi { /// preference is always given to via over serverName, irrespective of what field /// you are trying to use @override - Future joinRoom(String roomIdOrAlias, - {List? serverName, - List? via, - String? reason, - ThirdPartySigned? thirdPartySigned}) => + Future joinRoom( + String roomIdOrAlias, { + List? serverName, + List? via, + String? reason, + ThirdPartySigned? thirdPartySigned, + }) => super.joinRoom( roomIdOrAlias, serverName: via ?? serverName, @@ -3409,8 +3562,11 @@ class Client extends MatrixApi { password: oldPassword, ); } - await super.changePassword(newPassword, - auth: auth, logoutDevices: logoutDevices); + await super.changePassword( + newPassword, + auth: auth, + logoutDevices: logoutDevices, + ); } on MatrixException catch (matrixException) { if (!matrixException.requireAdditionalAuthentication) { rethrow; @@ -3453,12 +3609,13 @@ class Client extends MatrixApi { } /// A list of mxids of users who are ignored. - List get ignoredUsers => - List.from(_accountData['m.ignored_user_list'] - ?.content - .tryGetMap('ignored_users') - ?.keys ?? - []); + List get ignoredUsers => List.from( + _accountData['m.ignored_user_list'] + ?.content + .tryGetMap('ignored_users') + ?.keys ?? + [], + ); /// Ignore another user. This will clear the local cached messages to /// hide all previous messages from this user. @@ -3468,7 +3625,8 @@ class Client extends MatrixApi { } await setAccountData(userID!, 'm.ignored_user_list', { 'ignored_users': Map.fromEntries( - (ignoredUsers..add(userId)).map((key) => MapEntry(key, {}))), + (ignoredUsers..add(userId)).map((key) => MapEntry(key, {})), + ), }); await clearCache(); return; @@ -3485,7 +3643,8 @@ class Client extends MatrixApi { } await setAccountData(userID!, 'm.ignored_user_list', { 'ignored_users': Map.fromEntries( - (ignoredUsers..remove(userId)).map((key) => MapEntry(key, {}))), + (ignoredUsers..remove(userId)).map((key) => MapEntry(key, {})), + ), }); await clearCache(); return; @@ -3637,7 +3796,8 @@ class Client extends MatrixApi { final pubKey = crossSigningKey.publicKey; if (pubKey != null) { Logs().d( - 'Migrate cross signing key with usage ${crossSigningKey.usage} and verified ${crossSigningKey.directVerified}...'); + 'Migrate cross signing key with usage ${crossSigningKey.usage} and verified ${crossSigningKey.directVerified}...', + ); await database.storeUserCrossSigningKey( userId, pubKey, @@ -3768,7 +3928,7 @@ class FileTooBigMatrixException extends MatrixException { : super.fromJson({ 'errcode': MatrixError.M_TOO_LARGE, 'error': - 'File size ${_formatFileSize(actualFileSize)} exceeds allowed maximum of ${_formatFileSize(maxFileSize)}' + 'File size ${_formatFileSize(actualFileSize)} exceeds allowed maximum of ${_formatFileSize(maxFileSize)}', }); @override diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index d8bc3cf4c..6e3a9313e 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -59,8 +59,11 @@ abstract class DatabaseApi { Future> getRoomList(Client client); - Future getSingleRoom(Client client, String roomId, - {bool loadImportantStates = true}); + Future getSingleRoom( + Client client, + String roomId, { + bool loadImportantStates = true, + }); Future> getAccountData(); @@ -86,7 +89,9 @@ abstract class DatabaseApi { Future getUserProfile(String userId); Future storeUserProfile( - String userId, CachedProfileInformation profile); + String userId, + CachedProfileInformation profile, + ); Future markUserProfileAsOutdated(String userId); @@ -316,7 +321,10 @@ abstract class DatabaseApi { Future> getInboundGroupSessionsToUpload(); Future addSeenDeviceId( - String userId, String deviceId, String publicKeys); + String userId, + String deviceId, + String publicKeys, + ); Future addSeenPublicKey(String publicKey, String deviceId); diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart index 80b98d939..b681de56d 100644 --- a/lib/src/database/hive_collections_database.dart +++ b/lib/src/database/hive_collections_database.dart @@ -35,7 +35,8 @@ import 'package:matrix/src/utils/run_benchmarked.dart'; /// This database does not support file caching! @Deprecated( - 'Use [MatrixSdkDatabase] instead. Don\'t forget to properly migrate!') + 'Use [MatrixSdkDatabase] instead. Don\'t forget to properly migrate!', +) class HiveCollectionsDatabase extends DatabaseApi { static const int version = 7; final String name; @@ -374,8 +375,10 @@ class HiveCollectionsDatabase extends DatabaseApi { .toList(); final rawEvents = await _eventsBox.getAll(keys); return rawEvents - .map((rawEvent) => - rawEvent != null ? Event.fromJson(copyMap(rawEvent), room) : null) + .map( + (rawEvent) => + rawEvent != null ? Event.fromJson(copyMap(rawEvent), room) : null, + ) .whereNotNull() .toList(); } @@ -391,7 +394,8 @@ class HiveCollectionsDatabase extends DatabaseApi { // Get the synced event IDs from the store final timelineKey = TupleKey(room.id, '').toString(); final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? []); + (await _timelineFragmentsBox.get(timelineKey)) ?? [], + ); // Get the local stored SENDING events from the store late final List sendingEventIds; @@ -400,17 +404,20 @@ class HiveCollectionsDatabase extends DatabaseApi { } else { final sendingTimelineKey = TupleKey(room.id, 'SENDING').toString(); sendingEventIds = List.from( - (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? []); + (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? [], + ); } // Combine those two lists while respecting the start and limit parameters. - final end = min(timelineEventIds.length, - start + (limit ?? timelineEventIds.length)); + final end = min( + timelineEventIds.length, + start + (limit ?? timelineEventIds.length), + ); final eventIds = List.from([ ...sendingEventIds, ...(start < timelineEventIds.length && !onlySending ? timelineEventIds.getRange(start, end).toList() - : []) + : []), ]); return await _getEventsByIds(eventIds, room); @@ -427,7 +434,8 @@ class HiveCollectionsDatabase extends DatabaseApi { // Get the synced event IDs from the store final timelineKey = TupleKey(room.id, '').toString(); final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? []); + (await _timelineFragmentsBox.get(timelineKey)) ?? [], + ); // Get the local stored SENDING events from the store late final List sendingEventIds; @@ -436,7 +444,8 @@ class HiveCollectionsDatabase extends DatabaseApi { } else { final sendingTimelineKey = TupleKey(room.id, 'SENDING').toString(); sendingEventIds = List.from( - (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? []); + (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? [], + ); } // Combine those two lists while respecting the start and limit parameters. @@ -481,7 +490,9 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future> getLastSentMessageUserDeviceKey( - String userId, String deviceId) async { + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); if (raw == null) return []; @@ -489,8 +500,12 @@ class HiveCollectionsDatabase extends DatabaseApi { } @override - Future storeOlmSession(String identityKey, String sessionId, - String pickle, int lastReceived) async { + Future storeOlmSession( + String identityKey, + String sessionId, + String pickle, + int lastReceived, + ) async { final rawSessions = (await _olmSessionsBox.get(identityKey)) ?? {}; rawSessions[sessionId] = { 'identity_key': identityKey, @@ -504,7 +519,9 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future> getOlmSessions( - String identityKey, String userId) async { + String identityKey, + String userId, + ) async { final rawSessions = await _olmSessionsBox.get(identityKey); if (rawSessions == null || rawSessions.isEmpty) return []; return rawSessions.values @@ -518,23 +535,31 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future> getOlmSessionsForDevices( - List identityKeys, String userId) async { + List identityKeys, + String userId, + ) async { final sessions = await Future.wait( - identityKeys.map((identityKey) => getOlmSessions(identityKey, userId))); + identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)), + ); return [for (final sublist in sessions) ...sublist]; } @override Future getOutboundGroupSession( - String roomId, String userId) async { + String roomId, + String userId, + ) async { final raw = await _outboundGroupSessionsBox.get(roomId); if (raw == null) return null; return OutboundGroupSession.fromJson(copyMap(raw), userId); } @override - Future getSingleRoom(Client client, String roomId, - {bool loadImportantStates = true}) async { + Future getSingleRoom( + Client client, + String roomId, { + bool loadImportantStates = true, + }) async { // Get raw room from database: final roomData = await _roomsBox.get(roomId); if (roomData == null) return null; @@ -589,9 +614,11 @@ class HiveCollectionsDatabase extends DatabaseApi { for (final states in statesList) { if (states == null) continue; final stateEvents = states.values - .map((raw) => room.membership == Membership.invite - ? StrippedStateEvent.fromJson(copyMap(raw)) - : Event.fromJson(copyMap(raw), room)) + .map( + (raw) => room.membership == Membership.invite + ? StrippedStateEvent.fromJson(copyMap(raw)) + : Event.fromJson(copyMap(raw), room), + ) .toList(); for (final state in stateEvents) { room.setState(state); @@ -637,9 +664,11 @@ class HiveCollectionsDatabase extends DatabaseApi { if (members != null) { for (final member in members) { if (member == null) continue; - room.setState(room.membership == Membership.invite - ? StrippedStateEvent.fromJson(copyMap(member)) - : Event.fromJson(copyMap(member), room)); + room.setState( + room.membership == Membership.invite + ? StrippedStateEvent.fromJson(copyMap(member)) + : Event.fromJson(copyMap(member), room), + ); } } } @@ -657,7 +686,8 @@ class HiveCollectionsDatabase extends DatabaseApi { basicRoomEvent; } else { Logs().w( - 'Found account data for unknown room $roomId. Delete now...'); + 'Found account data for unknown room $roomId. Delete now...', + ); await _roomAccountDataBox .delete(TupleKey(roomId, basicRoomEvent.type).toString()); } @@ -687,7 +717,9 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future> getUnimportantRoomEventStatesForRoom( - List events, Room room) async { + List events, + Room room, + ) async { final keys = (await _roomStateBox.getAllKeys()).where((key) { final tuple = TupleKey.fromString(key); return tuple.parts.first == room.id && !events.contains(tuple.parts[1]); @@ -698,7 +730,8 @@ class HiveCollectionsDatabase extends DatabaseApi { final states = await _roomStateBox.get(key); if (states == null) continue; unimportantEvents.addAll( - states.values.map((raw) => Event.fromJson(copyMap(raw), room))); + states.values.map((raw) => Event.fromJson(copyMap(raw), room)), + ); } return unimportantEvents.where((event) => event.stateKey != null).toList(); } @@ -753,20 +786,21 @@ class HiveCollectionsDatabase extends DatabaseApi { ), ); res[userId] = DeviceKeysList.fromDbJson( - { - 'client_id': client.id, - 'user_id': userId, - 'outdated': await _userDeviceKeysOutdatedBox.get(userId), - }, - childEntries - .where((c) => c != null) - .toList() - .cast>(), - crossSigningEntries - .where((c) => c != null) - .toList() - .cast>(), - client); + { + 'client_id': client.id, + 'user_id': userId, + 'outdated': await _userDeviceKeysOutdatedBox.get(userId), + }, + childEntries + .where((c) => c != null) + .toList() + .cast>(), + crossSigningEntries + .where((c) => c != null) + .toList() + .cast>(), + client, + ); } return res; }); @@ -788,16 +822,17 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future insertClient( - String name, - String homeserverUrl, - String token, - DateTime? tokenExpiresAt, - String? refreshToken, - String userId, - String? deviceId, - String? deviceName, - String? prevBatch, - String? olmAccount) async { + String name, + String homeserverUrl, + String token, + DateTime? tokenExpiresAt, + String? refreshToken, + String userId, + String? deviceId, + String? deviceName, + String? prevBatch, + String? olmAccount, + ) async { await transaction(() async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); @@ -842,7 +877,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future insertIntoToDeviceQueue( - String type, String txnId, String content) async { + String type, + String txnId, + String content, + ) async { final id = DateTime.now().millisecondsSinceEpoch; await _toDeviceQueueBox.put(id.toString(), { 'type': type, @@ -854,11 +892,14 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future markInboundGroupSessionAsUploaded( - String roomId, String sessionId) async { + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId); if (raw == null) { Logs().w( - 'Tried to mark inbound group session as uploaded which was not found in the database!'); + 'Tried to mark inbound group session as uploaded which was not found in the database!', + ); return; } raw['uploaded'] = true; @@ -903,7 +944,9 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future removeUserCrossSigningKey( - String userId, String publicKey) async { + String userId, + String publicKey, + ) async { await _userCrossSigningKeysBox .delete(TupleKey(userId, publicKey).toString()); return; @@ -917,7 +960,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setBlockedUserCrossSigningKey( - bool blocked, String userId, String publicKey) async { + bool blocked, + String userId, + String publicKey, + ) async { final raw = await _userCrossSigningKeysBox .get(TupleKey(userId, publicKey).toString()); raw!['blocked'] = blocked; @@ -930,7 +976,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setBlockedUserDeviceKey( - bool blocked, String userId, String deviceId) async { + bool blocked, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); raw!['blocked'] = blocked; @@ -943,7 +992,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setLastActiveUserDeviceKey( - int lastActive, String userId, String deviceId) async { + int lastActive, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); raw!['last_active'] = lastActive; @@ -955,7 +1007,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setLastSentMessageUserDeviceKey( - String lastSentMessage, String userId, String deviceId) async { + String lastSentMessage, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); raw!['last_sent_message'] = lastSentMessage; @@ -967,7 +1022,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setRoomPrevBatch( - String? prevBatch, String roomId, Client client) async { + String? prevBatch, + String roomId, + Client client, + ) async { final raw = await _roomsBox.get(roomId); if (raw == null) return; final room = Room.fromJson(copyMap(raw), client); @@ -978,7 +1036,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setVerifiedUserCrossSigningKey( - bool verified, String userId, String publicKey) async { + bool verified, + String userId, + String publicKey, + ) async { final raw = (await _userCrossSigningKeysBox .get(TupleKey(userId, publicKey).toString())) ?? {}; @@ -992,7 +1053,10 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future setVerifiedUserDeviceKey( - bool verified, String userId, String deviceId) async { + bool verified, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); raw!['verified'] = verified; @@ -1025,8 +1089,9 @@ class HiveCollectionsDatabase extends DatabaseApi { if (event != null) { event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom)); await _eventsBox.put( - TupleKey(eventUpdate.roomID, event.eventId).toString(), - event.toJson()); + TupleKey(eventUpdate.roomID, event.eventId).toString(), + event.toJson(), + ); if (tmpRoom.lastEvent?.eventId == event.eventId) { await _roomStateBox.put( @@ -1041,7 +1106,7 @@ class HiveCollectionsDatabase extends DatabaseApi { if ({ EventUpdateType.timeline, EventUpdateType.history, - EventUpdateType.decryptedTimelineQueue + EventUpdateType.decryptedTimelineQueue, }.contains(eventUpdate.type)) { final eventId = eventUpdate.content['event_id']; // Is this ID already in the store? @@ -1089,8 +1154,10 @@ class HiveCollectionsDatabase extends DatabaseApi { final transactionId = eventUpdate.content .tryGetMap('unsigned') ?.tryGet('transaction_id'); - await _eventsBox.put(TupleKey(eventUpdate.roomID, eventId).toString(), - eventUpdate.content); + await _eventsBox.put( + TupleKey(eventUpdate.roomID, eventId).toString(), + eventUpdate.content, + ); // Update timeline fragments final key = TupleKey(eventUpdate.roomID, status.isSent ? '' : 'SENDING') @@ -1141,11 +1208,12 @@ class HiveCollectionsDatabase extends DatabaseApi { eventUpdate.type == EventUpdateType.inviteState)) { if (eventUpdate.content['type'] == EventTypes.RoomMember) { await _roomMembersBox.put( - TupleKey( - eventUpdate.roomID, - eventUpdate.content['state_key'], - ).toString(), - eventUpdate.content); + TupleKey( + eventUpdate.roomID, + eventUpdate.content['state_key'], + ).toString(), + eventUpdate.content, + ); } else { final key = TupleKey( eventUpdate.roomID, @@ -1177,33 +1245,39 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future storeInboundGroupSession( - String roomId, - String sessionId, - String pickle, - String content, - String indexes, - String allowedAtIndex, - String senderKey, - String senderClaimedKey) async { + String roomId, + String sessionId, + String pickle, + String content, + String indexes, + String allowedAtIndex, + String senderKey, + String senderClaimedKey, + ) async { await _inboundGroupSessionsBox.put( - sessionId, - StoredInboundGroupSession( - roomId: roomId, - sessionId: sessionId, - pickle: pickle, - content: content, - indexes: indexes, - allowedAtIndex: allowedAtIndex, - senderKey: senderKey, - senderClaimedKeys: senderClaimedKey, - uploaded: false, - ).toJson()); + sessionId, + StoredInboundGroupSession( + roomId: roomId, + sessionId: sessionId, + pickle: pickle, + content: content, + indexes: indexes, + allowedAtIndex: allowedAtIndex, + senderKey: senderKey, + senderClaimedKeys: senderClaimedKey, + uploaded: false, + ).toJson(), + ); return; } @override Future storeOutboundGroupSession( - String roomId, String pickle, String deviceIds, int creationTime) async { + String roomId, + String pickle, + String deviceIds, + int creationTime, + ) async { await _outboundGroupSessionsBox.put(roomId, { 'room_id': roomId, 'pickle': pickle, @@ -1243,49 +1317,52 @@ class HiveCollectionsDatabase extends DatabaseApi { final currentRawRoom = await _roomsBox.get(roomId); if (currentRawRoom == null) { await _roomsBox.put( - roomId, - roomUpdate is JoinedRoomUpdate - ? Room( - client: client, - id: roomId, - membership: membership, - highlightCount: - roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? - 0, - notificationCount: roomUpdate - .unreadNotifications?.notificationCount - ?.toInt() ?? - 0, - prev_batch: roomUpdate.timeline?.prevBatch, - summary: roomUpdate.summary, - lastEvent: lastEvent, - ).toJson() - : Room( - client: client, - id: roomId, - membership: membership, - lastEvent: lastEvent, - ).toJson()); + roomId, + roomUpdate is JoinedRoomUpdate + ? Room( + client: client, + id: roomId, + membership: membership, + highlightCount: + roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? + 0, + notificationCount: roomUpdate + .unreadNotifications?.notificationCount + ?.toInt() ?? + 0, + prev_batch: roomUpdate.timeline?.prevBatch, + summary: roomUpdate.summary, + lastEvent: lastEvent, + ).toJson() + : Room( + client: client, + id: roomId, + membership: membership, + lastEvent: lastEvent, + ).toJson(), + ); } else if (roomUpdate is JoinedRoomUpdate) { final currentRoom = Room.fromJson(copyMap(currentRawRoom), client); await _roomsBox.put( - roomId, - Room( - client: client, - id: roomId, - membership: membership, - highlightCount: - roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? - currentRoom.highlightCount, - notificationCount: - roomUpdate.unreadNotifications?.notificationCount?.toInt() ?? - currentRoom.notificationCount, - prev_batch: - roomUpdate.timeline?.prevBatch ?? currentRoom.prev_batch, - summary: RoomSummary.fromJson(currentRoom.summary.toJson() - ..addAll(roomUpdate.summary?.toJson() ?? {})), - lastEvent: lastEvent, - ).toJson()); + roomId, + Room( + client: client, + id: roomId, + membership: membership, + highlightCount: + roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? + currentRoom.highlightCount, + notificationCount: + roomUpdate.unreadNotifications?.notificationCount?.toInt() ?? + currentRoom.notificationCount, + prev_batch: roomUpdate.timeline?.prevBatch ?? currentRoom.prev_batch, + summary: RoomSummary.fromJson( + currentRoom.summary.toJson() + ..addAll(roomUpdate.summary?.toJson() ?? {}), + ), + lastEvent: lastEvent, + ).toJson(), + ); } } @@ -1295,15 +1372,20 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future storeSSSSCache( - String type, String keyId, String ciphertext, String content) async { + String type, + String keyId, + String ciphertext, + String content, + ) async { await _ssssCacheBox.put( - type, - SSSSCache( - type: type, - keyId: keyId, - ciphertext: ciphertext, - content: content, - ).toJson()); + type, + SSSSCache( + type: type, + keyId: keyId, + ciphertext: ciphertext, + content: content, + ).toJson(), + ); } @override @@ -1314,8 +1396,13 @@ class HiveCollectionsDatabase extends DatabaseApi { } @override - Future storeUserCrossSigningKey(String userId, String publicKey, - String content, bool verified, bool blocked) async { + Future storeUserCrossSigningKey( + String userId, + String publicKey, + String content, + bool verified, + bool blocked, + ) async { await _userCrossSigningKeysBox.put( TupleKey(userId, publicKey).toString(), { @@ -1329,8 +1416,14 @@ class HiveCollectionsDatabase extends DatabaseApi { } @override - Future storeUserDeviceKey(String userId, String deviceId, - String content, bool verified, bool blocked, int lastActive) async { + Future storeUserDeviceKey( + String userId, + String deviceId, + String content, + bool verified, + bool blocked, + int lastActive, + ) async { await _userDeviceKeysBox.put(TupleKey(userId, deviceId).toString(), { 'user_id': userId, 'device_id': deviceId, @@ -1371,8 +1464,10 @@ class HiveCollectionsDatabase extends DatabaseApi { if (tokenExpiresAt == null) { await _clientBox.delete('token_expires_at'); } else { - await _clientBox.put('token_expires_at', - tokenExpiresAt.millisecondsSinceEpoch.toString()); + await _clientBox.put( + 'token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString(), + ); } if (refreshToken == null) { await _clientBox.delete('refresh_token'); @@ -1414,11 +1509,15 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future updateInboundGroupSessionAllowedAtIndex( - String allowedAtIndex, String roomId, String sessionId) async { + String allowedAtIndex, + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId); if (raw == null) { Logs().w( - 'Tried to update inbound group session as uploaded which wasnt found in the database!'); + 'Tried to update inbound group session as uploaded which wasnt found in the database!', + ); return; } raw['allowed_at_index'] = allowedAtIndex; @@ -1428,11 +1527,15 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future updateInboundGroupSessionIndexes( - String indexes, String roomId, String sessionId) async { + String indexes, + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId); if (raw == null) { Logs().w( - 'Tried to update inbound group session indexes of a session which was not found in the database!'); + 'Tried to update inbound group session indexes of a session which was not found in the database!', + ); return; } final json = copyMap(raw); @@ -1552,11 +1655,15 @@ class HiveCollectionsDatabase extends DatabaseApi { } for (final key in json[_inboundGroupSessionsBoxName]!.keys) { await _inboundGroupSessionsBox.put( - key, json[_inboundGroupSessionsBoxName]![key]); + key, + json[_inboundGroupSessionsBoxName]![key], + ); } for (final key in json[_outboundGroupSessionsBoxName]!.keys) { await _outboundGroupSessionsBox.put( - key, json[_outboundGroupSessionsBoxName]![key]); + key, + json[_outboundGroupSessionsBoxName]![key], + ); } for (final key in json[_olmSessionsBoxName]!.keys) { await _olmSessionsBox.put(key, json[_olmSessionsBoxName]![key]); @@ -1566,11 +1673,15 @@ class HiveCollectionsDatabase extends DatabaseApi { } for (final key in json[_userDeviceKeysOutdatedBoxName]!.keys) { await _userDeviceKeysOutdatedBox.put( - key, json[_userDeviceKeysOutdatedBoxName]![key]); + key, + json[_userDeviceKeysOutdatedBoxName]![key], + ); } for (final key in json[_userCrossSigningKeysBoxName]!.keys) { await _userCrossSigningKeysBox.put( - key, json[_userCrossSigningKeysBoxName]![key]); + key, + json[_userCrossSigningKeysBoxName]![key], + ); } for (final key in json[_ssssCacheBoxName]!.keys) { await _ssssCacheBox.put(key, json[_ssssCacheBoxName]![key]); @@ -1580,7 +1691,9 @@ class HiveCollectionsDatabase extends DatabaseApi { } for (final key in json[_timelineFragmentsBoxName]!.keys) { await _timelineFragmentsBox.put( - key, json[_timelineFragmentsBoxName]![key]); + key, + json[_timelineFragmentsBoxName]![key], + ); } for (final key in json[_seenDeviceIdsBoxName]!.keys) { await _seenDeviceIdsBox.put(key, json[_seenDeviceIdsBoxName]![key]); @@ -1629,7 +1742,9 @@ class HiveCollectionsDatabase extends DatabaseApi { @override Future storeUserProfile( - String userId, CachedProfileInformation profile) async { + String userId, + CachedProfileInformation profile, + ) async { return; } } diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index 8086d7f33..a8960b79d 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -39,7 +39,8 @@ import 'package:matrix/src/utils/run_benchmarked.dart'; /// /// This database does not support file caching! @Deprecated( - 'Use [MatrixSdkDatabase] instead. Don\'t forget to properly migrate!') + 'Use [MatrixSdkDatabase] instead. Don\'t forget to properly migrate!', +) class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { static const int version = 6; final String name; @@ -254,11 +255,16 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { convertToJson(raw), Client(''), ); - await addSeenDeviceId(deviceKeys.userId, deviceKeys.deviceId!, - deviceKeys.curve25519Key! + deviceKeys.ed25519Key!); + await addSeenDeviceId( + deviceKeys.userId, + deviceKeys.deviceId!, + deviceKeys.curve25519Key! + deviceKeys.ed25519Key!, + ); await addSeenPublicKey(deviceKeys.ed25519Key!, deviceKeys.deviceId!); await addSeenPublicKey( - deviceKeys.curve25519Key!, deviceKeys.deviceId!); + deviceKeys.curve25519Key!, + deviceKeys.deviceId!, + ); } catch (e) { Logs().w('Can not migrate device $key', e); } @@ -344,18 +350,21 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getAccountData() => - runBenchmarked>('Get all account data from Hive', - () async { - final accountData = {}; - for (final key in _accountDataBox.keys) { - final raw = await _accountDataBox.get(key); - accountData[key.toString().fromHiveKey] = BasicEvent( - type: key.toString().fromHiveKey, - content: convertToJson(raw), - ); - } - return accountData; - }, _accountDataBox.keys.length); + runBenchmarked>( + 'Get all account data from Hive', + () async { + final accountData = {}; + for (final key in _accountDataBox.keys) { + final raw = await _accountDataBox.get(key); + accountData[key.toString().fromHiveKey] = BasicEvent( + type: key.toString().fromHiveKey, + content: convertToJson(raw), + ); + } + return accountData; + }, + _accountDataBox.keys.length, + ); @override Future?> getClient(String name) => @@ -378,10 +387,13 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { /// Loads a whole list of events at once from the store for a specific room Future> _getEventsByIds(List eventIds, Room room) async { - final events = await Future.wait(eventIds.map((String eventId) async { - final entry = await _eventsBox.get(MultiKey(room.id, eventId).toString()); - return entry is Map ? Event.fromJson(convertToJson(entry), room) : null; - })); + final events = await Future.wait( + eventIds.map((String eventId) async { + final entry = + await _eventsBox.get(MultiKey(room.id, eventId).toString()); + return entry is Map ? Event.fromJson(convertToJson(entry), room) : null; + }), + ); return events.whereType().toList(); } @@ -397,7 +409,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { // Get the synced event IDs from the store final timelineKey = MultiKey(room.id, '').toString(); final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? []); + (await _timelineFragmentsBox.get(timelineKey)) ?? [], + ); // Get the local stored SENDING events from the store late final List sendingEventIds; if (start != 0) { @@ -405,18 +418,21 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } else { final sendingTimelineKey = MultiKey(room.id, 'SENDING').toString(); sendingEventIds = List.from( - (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? []); + (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? [], + ); } // Combine those two lists while respecting the start and limit parameters. - final end = min(timelineEventIds.length, - start + (limit ?? timelineEventIds.length)); + final end = min( + timelineEventIds.length, + start + (limit ?? timelineEventIds.length), + ); final eventIds = List.from( [ ...sendingEventIds, ...(start < timelineEventIds.length && !onlySending ? timelineEventIds.getRange(start, end).toList() - : []) + : []), ], ); @@ -435,7 +451,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { final timelineKey = MultiKey(room.id, '').toString(); final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? []); + (await _timelineFragmentsBox.get(timelineKey)) ?? [], + ); // Get the local stored SENDING events from the store late final List sendingEventIds; @@ -444,7 +461,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } else { final sendingTimelineKey = MultiKey(room.id, 'SENDING').toString(); sendingEventIds = List.from( - (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? []); + (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? [], + ); } // Combine those two lists while respecting the start and limit parameters. @@ -474,9 +492,11 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getInboundGroupSessionsToUpload() async { - final sessions = (await Future.wait(_inboundGroupSessionsBox.keys.map( - (sessionId) async => - await _inboundGroupSessionsBox.get(sessionId)))) + final sessions = (await Future.wait( + _inboundGroupSessionsBox.keys.map( + (sessionId) async => await _inboundGroupSessionsBox.get(sessionId), + ), + )) .where((rawSession) => rawSession['uploaded'] == false) .take(500) .map( @@ -490,7 +510,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getLastSentMessageUserDeviceKey( - String userId, String deviceId) async { + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(MultiKey(userId, deviceId).toString()); if (raw == null) return []; @@ -498,8 +520,12 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } @override - Future storeOlmSession(String identityKey, String sessionId, - String pickle, int lastReceived) async { + Future storeOlmSession( + String identityKey, + String sessionId, + String pickle, + int lastReceived, + ) async { final rawSessions = (await _olmSessionsBox.get(identityKey.toHiveKey) as Map?) ?? {}; rawSessions[sessionId] = { @@ -514,7 +540,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getOlmSessions( - String identityKey, String userId) async { + String identityKey, + String userId, + ) async { final rawSessions = await _olmSessionsBox.get(identityKey.toHiveKey) as Map? ?? {}; @@ -540,23 +568,31 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getOlmSessionsForDevices( - List identityKeys, String userId) async { + List identityKeys, + String userId, + ) async { final sessions = await Future.wait( - identityKeys.map((identityKey) => getOlmSessions(identityKey, userId))); + identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)), + ); return [for (final sublist in sessions) ...sublist]; } @override Future getOutboundGroupSession( - String roomId, String userId) async { + String roomId, + String userId, + ) async { final raw = await _outboundGroupSessionsBox.get(roomId.toHiveKey); if (raw == null) return null; return OutboundGroupSession.fromJson(convertToJson(raw), userId); } @override - Future getSingleRoom(Client client, String roomId, - {bool loadImportantStates = true}) async { + Future getSingleRoom( + Client client, + String roomId, { + bool loadImportantStates = true, + }) async { // Get raw room from database: final roomData = await _roomsBox.get(roomId); if (roomData == null) return null; @@ -580,88 +616,96 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } @override - Future> getRoomList(Client client) => - runBenchmarked>('Get room list from hive', () async { - final rooms = {}; - final userID = client.userID; - final importantRoomStates = client.importantStateEvents; - for (final key in _roomsBox.keys) { - // Get the room - final raw = await _roomsBox.get(key); - final room = Room.fromJson(convertToJson(raw), client); - - // let's see if we need any m.room.member events - // We always need the member event for ourself - final membersToPostload = {if (userID != null) userID}; - // If the room is a direct chat, those IDs should be there too - if (room.isDirectChat) { - membersToPostload.add(room.directChatMatrixID!); - } - // the lastEvent message preview might have an author we need to fetch, if it is a group chat - final lastEvent = room.getState(EventTypes.Message); - if (lastEvent != null && !room.isDirectChat) { - membersToPostload.add(lastEvent.senderId); - } - // if the room has no name and no canonical alias, its name is calculated - // based on the heroes of the room - if (room.getState(EventTypes.RoomName) == null && - room.getState(EventTypes.RoomCanonicalAlias) == null) { - // we don't have a name and no canonical alias, so we'll need to - // post-load the heroes - membersToPostload.addAll(room.summary.mHeroes ?? []); - } - // Load members - for (final userId in membersToPostload) { - final state = - await _roomMembersBox.get(MultiKey(room.id, userId).toString()); - if (state == null) { - Logs().w('Unable to post load member $userId'); - continue; + Future> getRoomList(Client client) => runBenchmarked>( + 'Get room list from hive', + () async { + final rooms = {}; + final userID = client.userID; + final importantRoomStates = client.importantStateEvents; + for (final key in _roomsBox.keys) { + // Get the room + final raw = await _roomsBox.get(key); + final room = Room.fromJson(convertToJson(raw), client); + + // let's see if we need any m.room.member events + // We always need the member event for ourself + final membersToPostload = {if (userID != null) userID}; + // If the room is a direct chat, those IDs should be there too + if (room.isDirectChat) { + membersToPostload.add(room.directChatMatrixID!); } - room.setState(room.membership == Membership.invite - ? StrippedStateEvent.fromJson(copyMap(raw)) - : Event.fromJson(convertToJson(state), room)); - } - - // Get the "important" room states. All other states will be loaded once - // `getUnimportantRoomStates()` is called. - for (final type in importantRoomStates) { - final states = await _roomStateBox - .get(MultiKey(room.id, type).toString()) as Map?; - if (states == null) continue; - final stateEvents = states.values - .map((raw) => room.membership == Membership.invite + // the lastEvent message preview might have an author we need to fetch, if it is a group chat + final lastEvent = room.getState(EventTypes.Message); + if (lastEvent != null && !room.isDirectChat) { + membersToPostload.add(lastEvent.senderId); + } + // if the room has no name and no canonical alias, its name is calculated + // based on the heroes of the room + if (room.getState(EventTypes.RoomName) == null && + room.getState(EventTypes.RoomCanonicalAlias) == null) { + // we don't have a name and no canonical alias, so we'll need to + // post-load the heroes + membersToPostload.addAll(room.summary.mHeroes ?? []); + } + // Load members + for (final userId in membersToPostload) { + final state = await _roomMembersBox + .get(MultiKey(room.id, userId).toString()); + if (state == null) { + Logs().w('Unable to post load member $userId'); + continue; + } + room.setState( + room.membership == Membership.invite ? StrippedStateEvent.fromJson(copyMap(raw)) - : Event.fromJson(convertToJson(raw), room)) - .toList(); - for (final state in stateEvents) { - room.setState(state); + : Event.fromJson(convertToJson(state), room), + ); } - } - // Add to the list and continue. - rooms[room.id] = room; - } + // Get the "important" room states. All other states will be loaded once + // `getUnimportantRoomStates()` is called. + for (final type in importantRoomStates) { + final states = await _roomStateBox + .get(MultiKey(room.id, type).toString()) as Map?; + if (states == null) continue; + final stateEvents = states.values + .map( + (raw) => room.membership == Membership.invite + ? StrippedStateEvent.fromJson(copyMap(raw)) + : Event.fromJson(convertToJson(raw), room), + ) + .toList(); + for (final state in stateEvents) { + room.setState(state); + } + } - // Get the room account data - for (final key in _roomAccountDataBox.keys) { - final roomId = MultiKey.fromString(key).parts.first; - if (rooms.containsKey(roomId)) { - final raw = await _roomAccountDataBox.get(key); - final basicRoomEvent = BasicRoomEvent.fromJson( - convertToJson(raw), - ); - rooms[roomId]!.roomAccountData[basicRoomEvent.type] = - basicRoomEvent; - } else { - Logs().w( - 'Found account data for unknown room $roomId. Delete now...'); - await _roomAccountDataBox.delete(key); + // Add to the list and continue. + rooms[room.id] = room; + } + + // Get the room account data + for (final key in _roomAccountDataBox.keys) { + final roomId = MultiKey.fromString(key).parts.first; + if (rooms.containsKey(roomId)) { + final raw = await _roomAccountDataBox.get(key); + final basicRoomEvent = BasicRoomEvent.fromJson( + convertToJson(raw), + ); + rooms[roomId]!.roomAccountData[basicRoomEvent.type] = + basicRoomEvent; + } else { + Logs().w( + 'Found account data for unknown room $roomId. Delete now...', + ); + await _roomAccountDataBox.delete(key); + } } - } - return rooms.values.toList(); - }, _roomsBox.keys.length); + return rooms.values.toList(); + }, + _roomsBox.keys.length, + ); @override Future getSSSSCache(String type) async { @@ -672,15 +716,19 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getToDeviceEventQueue() async => - await Future.wait(_toDeviceQueueBox.keys.map((i) async { - final raw = await _toDeviceQueueBox.get(i); - raw['id'] = i; - return QueuedToDeviceEvent.fromJson(convertToJson(raw)); - }).toList()); + await Future.wait( + _toDeviceQueueBox.keys.map((i) async { + final raw = await _toDeviceQueueBox.get(i); + raw['id'] = i; + return QueuedToDeviceEvent.fromJson(convertToJson(raw)); + }).toList(), + ); @override Future> getUnimportantRoomEventStatesForRoom( - List events, Room room) async { + List events, + Room room, + ) async { final keys = _roomStateBox.keys.where((key) { final tuple = MultiKey.fromString(key); return tuple.parts.first == room.id && !events.contains(tuple.parts[1]); @@ -690,7 +738,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { for (final key in keys) { final Map states = await _roomStateBox.get(key); unimportantEvents.addAll( - states.values.map((raw) => Event.fromJson(convertToJson(raw), room))); + states.values.map((raw) => Event.fromJson(convertToJson(raw), room)), + ); } return unimportantEvents.where((event) => event.stateKey != null).toList(); } @@ -706,36 +755,48 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getUserDeviceKeys(Client client) => runBenchmarked>( - 'Get all user device keys from Hive', () async { - final deviceKeysOutdated = _userDeviceKeysOutdatedBox.keys; - if (deviceKeysOutdated.isEmpty) { - return {}; - } - final res = {}; - for (final userId in deviceKeysOutdated) { - final deviceKeysBoxKeys = _userDeviceKeysBox.keys.where((tuple) { - final tupleKey = MultiKey.fromString(tuple); - return tupleKey.parts.first == userId; - }); - final crossSigningKeysBoxKeys = - _userCrossSigningKeysBox.keys.where((tuple) { - final tupleKey = MultiKey.fromString(tuple); - return tupleKey.parts.first == userId; - }); - res[userId] = DeviceKeysList.fromDbJson( + 'Get all user device keys from Hive', + () async { + final deviceKeysOutdated = _userDeviceKeysOutdatedBox.keys; + if (deviceKeysOutdated.isEmpty) { + return {}; + } + final res = {}; + for (final userId in deviceKeysOutdated) { + final deviceKeysBoxKeys = _userDeviceKeysBox.keys.where((tuple) { + final tupleKey = MultiKey.fromString(tuple); + return tupleKey.parts.first == userId; + }); + final crossSigningKeysBoxKeys = + _userCrossSigningKeysBox.keys.where((tuple) { + final tupleKey = MultiKey.fromString(tuple); + return tupleKey.parts.first == userId; + }); + res[userId] = DeviceKeysList.fromDbJson( { 'client_id': client.id, 'user_id': userId, 'outdated': await _userDeviceKeysOutdatedBox.get(userId), }, - await Future.wait(deviceKeysBoxKeys.map((key) async => - convertToJson(await _userDeviceKeysBox.get(key)))), - await Future.wait(crossSigningKeysBoxKeys.map((key) async => - convertToJson(await _userCrossSigningKeysBox.get(key)))), - client); - } - return res; - }, _userDeviceKeysBox.keys.length); + await Future.wait( + deviceKeysBoxKeys.map( + (key) async => + convertToJson(await _userDeviceKeysBox.get(key)), + ), + ), + await Future.wait( + crossSigningKeysBoxKeys.map( + (key) async => + convertToJson(await _userCrossSigningKeysBox.get(key)), + ), + ), + client, + ); + } + return res; + }, + _userDeviceKeysBox.keys.length, + ); @override Future> getUsers(Room room) async { @@ -751,20 +812,23 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future insertClient( - String name, - String homeserverUrl, - String token, - DateTime? tokenExpiresAt, - String? refreshToken, - String userId, - String? deviceId, - String? deviceName, - String? prevBatch, - String? olmAccount) async { + String name, + String homeserverUrl, + String token, + DateTime? tokenExpiresAt, + String? refreshToken, + String userId, + String? deviceId, + String? deviceName, + String? prevBatch, + String? olmAccount, + ) async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); await _clientBox.put( - 'token_expires_at', tokenExpiresAt?.millisecondsSinceEpoch.toString()); + 'token_expires_at', + tokenExpiresAt?.millisecondsSinceEpoch.toString(), + ); await _clientBox.put('refresh_token', refreshToken); await _clientBox.put('user_id', userId); await _clientBox.put('device_id', deviceId); @@ -777,7 +841,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future insertIntoToDeviceQueue( - String type, String txnId, String content) async { + String type, + String txnId, + String content, + ) async { return await _toDeviceQueueBox.add({ 'type': type, 'txn_id': txnId, @@ -787,11 +854,14 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future markInboundGroupSessionAsUploaded( - String roomId, String sessionId) async { + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId.toHiveKey); if (raw == null) { Logs().w( - 'Tried to mark inbound group session as uploaded which was not found in the database!'); + 'Tried to mark inbound group session as uploaded which was not found in the database!', + ); return; } raw['uploaded'] = true; @@ -833,7 +903,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future removeUserCrossSigningKey( - String userId, String publicKey) async { + String userId, + String publicKey, + ) async { await _userCrossSigningKeysBox .delete(MultiKey(userId, publicKey).toString()); return; @@ -847,7 +919,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setBlockedUserCrossSigningKey( - bool blocked, String userId, String publicKey) async { + bool blocked, + String userId, + String publicKey, + ) async { final raw = await _userCrossSigningKeysBox .get(MultiKey(userId, publicKey).toString()); raw['blocked'] = blocked; @@ -860,7 +935,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setBlockedUserDeviceKey( - bool blocked, String userId, String deviceId) async { + bool blocked, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(MultiKey(userId, deviceId).toString()); raw['blocked'] = blocked; @@ -873,7 +951,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setLastActiveUserDeviceKey( - int lastActive, String userId, String deviceId) async { + int lastActive, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(MultiKey(userId, deviceId).toString()); raw['last_active'] = lastActive; @@ -885,7 +966,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setLastSentMessageUserDeviceKey( - String lastSentMessage, String userId, String deviceId) async { + String lastSentMessage, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(MultiKey(userId, deviceId).toString()); raw['last_sent_message'] = lastSentMessage; @@ -897,7 +981,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setRoomPrevBatch( - String? prevBatch, String roomId, Client client) async { + String? prevBatch, + String roomId, + Client client, + ) async { final raw = await _roomsBox.get(roomId.toHiveKey); if (raw == null) return; final room = Room.fromJson(convertToJson(raw), client); @@ -908,7 +995,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setVerifiedUserCrossSigningKey( - bool verified, String userId, String publicKey) async { + bool verified, + String userId, + String publicKey, + ) async { final raw = (await _userCrossSigningKeysBox .get(MultiKey(userId, publicKey).toString()) as Map?) ?? {}; @@ -922,7 +1012,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future setVerifiedUserDeviceKey( - bool verified, String userId, String deviceId) async { + bool verified, + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(MultiKey(userId, deviceId).toString()); raw['verified'] = verified; @@ -936,7 +1029,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future storeAccountData(String type, String content) async { await _accountDataBox.put( - type.toHiveKey, convertToJson(jsonDecode(content))); + type.toHiveKey, + convertToJson(jsonDecode(content)), + ); return; } @@ -955,8 +1050,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { if (event != null) { event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom)); await _eventsBox.put( - MultiKey(eventUpdate.roomID, event.eventId).toString(), - event.toJson()); + MultiKey(eventUpdate.roomID, event.eventId).toString(), + event.toJson(), + ); if (tmpRoom.lastEvent?.eventId == event.eventId) { await _roomStateBox.put( @@ -971,7 +1067,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { if ({ EventUpdateType.timeline, EventUpdateType.history, - EventUpdateType.decryptedTimelineQueue + EventUpdateType.decryptedTimelineQueue, }.contains(eventUpdate.type)) { final eventId = eventUpdate.content['event_id']; // Is this ID already in the store? @@ -1020,8 +1116,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { .tryGetMap('unsigned') ?.tryGet('transaction_id'); - await _eventsBox.put(MultiKey(eventUpdate.roomID, eventId).toString(), - eventUpdate.content); + await _eventsBox.put( + MultiKey(eventUpdate.roomID, eventId).toString(), + eventUpdate.content, + ); // Update timeline fragments final key = MultiKey(eventUpdate.roomID, status.isSent ? '' : 'SENDING') @@ -1070,11 +1168,12 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { eventUpdate.type == EventUpdateType.inviteState)) { if (eventUpdate.content['type'] == EventTypes.RoomMember) { await _roomMembersBox.put( - MultiKey( - eventUpdate.roomID, - eventUpdate.content['state_key'], - ).toString(), - eventUpdate.content); + MultiKey( + eventUpdate.roomID, + eventUpdate.content['state_key'], + ).toString(), + eventUpdate.content, + ); } else { final key = MultiKey( eventUpdate.roomID, @@ -1106,33 +1205,39 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future storeInboundGroupSession( - String roomId, - String sessionId, - String pickle, - String content, - String indexes, - String allowedAtIndex, - String senderKey, - String senderClaimedKey) async { + String roomId, + String sessionId, + String pickle, + String content, + String indexes, + String allowedAtIndex, + String senderKey, + String senderClaimedKey, + ) async { await _inboundGroupSessionsBox.put( - sessionId.toHiveKey, - StoredInboundGroupSession( - roomId: roomId, - sessionId: sessionId, - pickle: pickle, - content: content, - indexes: indexes, - allowedAtIndex: allowedAtIndex, - senderKey: senderKey, - senderClaimedKeys: senderClaimedKey, - uploaded: false, - ).toJson()); + sessionId.toHiveKey, + StoredInboundGroupSession( + roomId: roomId, + sessionId: sessionId, + pickle: pickle, + content: content, + indexes: indexes, + allowedAtIndex: allowedAtIndex, + senderKey: senderKey, + senderClaimedKeys: senderClaimedKey, + uploaded: false, + ).toJson(), + ); return; } @override Future storeOutboundGroupSession( - String roomId, String pickle, String deviceIds, int creationTime) async { + String roomId, + String pickle, + String deviceIds, + int creationTime, + ) async { await _outboundGroupSessionsBox.put(roomId.toHiveKey, { 'room_id': roomId, 'pickle': pickle, @@ -1152,8 +1257,12 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } @override - Future storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate, - Event? lastEvent, Client client) async { + Future storeRoomUpdate( + String roomId, + SyncRoomUpdate roomUpdate, + Event? lastEvent, + Client client, + ) async { // Leave room if membership is leave if (roomUpdate is LeftRoomUpdate) { await forgetRoom(roomId); @@ -1167,49 +1276,52 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { // Make sure room exists if (!_roomsBox.containsKey(roomId.toHiveKey)) { await _roomsBox.put( - roomId.toHiveKey, - roomUpdate is JoinedRoomUpdate - ? Room( - client: client, - id: roomId, - membership: membership, - highlightCount: - roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? - 0, - notificationCount: roomUpdate - .unreadNotifications?.notificationCount - ?.toInt() ?? - 0, - prev_batch: roomUpdate.timeline?.prevBatch, - summary: roomUpdate.summary, - lastEvent: lastEvent, - ).toJson() - : Room( - client: client, - id: roomId, - membership: membership, - lastEvent: lastEvent, - ).toJson()); + roomId.toHiveKey, + roomUpdate is JoinedRoomUpdate + ? Room( + client: client, + id: roomId, + membership: membership, + highlightCount: + roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? + 0, + notificationCount: roomUpdate + .unreadNotifications?.notificationCount + ?.toInt() ?? + 0, + prev_batch: roomUpdate.timeline?.prevBatch, + summary: roomUpdate.summary, + lastEvent: lastEvent, + ).toJson() + : Room( + client: client, + id: roomId, + membership: membership, + lastEvent: lastEvent, + ).toJson(), + ); } else if (roomUpdate is JoinedRoomUpdate) { final currentRawRoom = await _roomsBox.get(roomId.toHiveKey); final currentRoom = Room.fromJson(convertToJson(currentRawRoom), client); await _roomsBox.put( - roomId.toHiveKey, - Room( - client: client, - id: roomId, - membership: membership, - highlightCount: - roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? - currentRoom.highlightCount, - notificationCount: - roomUpdate.unreadNotifications?.notificationCount?.toInt() ?? - currentRoom.notificationCount, - prev_batch: - roomUpdate.timeline?.prevBatch ?? currentRoom.prev_batch, - summary: RoomSummary.fromJson(currentRoom.summary.toJson() - ..addAll(roomUpdate.summary?.toJson() ?? {})), - ).toJson()); + roomId.toHiveKey, + Room( + client: client, + id: roomId, + membership: membership, + highlightCount: + roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? + currentRoom.highlightCount, + notificationCount: + roomUpdate.unreadNotifications?.notificationCount?.toInt() ?? + currentRoom.notificationCount, + prev_batch: roomUpdate.timeline?.prevBatch ?? currentRoom.prev_batch, + summary: RoomSummary.fromJson( + currentRoom.summary.toJson() + ..addAll(roomUpdate.summary?.toJson() ?? {}), + ), + ).toJson(), + ); } } @@ -1219,15 +1331,20 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future storeSSSSCache( - String type, String keyId, String ciphertext, String content) async { + String type, + String keyId, + String ciphertext, + String content, + ) async { await _ssssCacheBox.put( - type, - SSSSCache( - type: type, - keyId: keyId, - ciphertext: ciphertext, - content: content, - ).toJson()); + type, + SSSSCache( + type: type, + keyId: keyId, + ciphertext: ciphertext, + content: content, + ).toJson(), + ); } @override @@ -1238,8 +1355,13 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } @override - Future storeUserCrossSigningKey(String userId, String publicKey, - String content, bool verified, bool blocked) async { + Future storeUserCrossSigningKey( + String userId, + String publicKey, + String content, + bool verified, + bool blocked, + ) async { await _userCrossSigningKeysBox.put( MultiKey(userId, publicKey).toString(), { @@ -1253,8 +1375,14 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { } @override - Future storeUserDeviceKey(String userId, String deviceId, - String content, bool verified, bool blocked, int lastActive) async { + Future storeUserDeviceKey( + String userId, + String deviceId, + String content, + bool verified, + bool blocked, + int lastActive, + ) async { await _userDeviceKeysBox.put(MultiKey(userId, deviceId).toString(), { 'user_id': userId, 'device_id': deviceId, @@ -1292,7 +1420,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); await _clientBox.put( - 'token_expires_at', tokenExpiresAt?.millisecondsSinceEpoch.toString()); + 'token_expires_at', + tokenExpiresAt?.millisecondsSinceEpoch.toString(), + ); await _clientBox.put('refresh_token', refreshToken); await _clientBox.put('user_id', userId); await _clientBox.put('device_id', deviceId); @@ -1312,11 +1442,15 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future updateInboundGroupSessionAllowedAtIndex( - String allowedAtIndex, String roomId, String sessionId) async { + String allowedAtIndex, + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId.toHiveKey); if (raw == null) { Logs().w( - 'Tried to update inbound group session as uploaded which wasnt found in the database!'); + 'Tried to update inbound group session as uploaded which wasnt found in the database!', + ); return; } raw['allowed_at_index'] = allowedAtIndex; @@ -1326,11 +1460,15 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future updateInboundGroupSessionIndexes( - String indexes, String roomId, String sessionId) async { + String indexes, + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId.toHiveKey); if (raw == null) { Logs().w( - 'Tried to update inbound group session indexes of a session which was not found in the database!'); + 'Tried to update inbound group session indexes of a session which was not found in the database!', + ); return; } raw['indexes'] = indexes; @@ -1340,8 +1478,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future> getAllInboundGroupSessions() async { - final rawSessions = await Future.wait(_inboundGroupSessionsBox.keys - .map((key) => _inboundGroupSessionsBox.get(key))); + final rawSessions = await Future.wait( + _inboundGroupSessionsBox.keys + .map((key) => _inboundGroupSessionsBox.get(key)), + ); return rawSessions .map((raw) => StoredInboundGroupSession.fromJson(convertToJson(raw))) .toList(); @@ -1435,7 +1575,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { @override Future storeUserProfile( - String userId, CachedProfileInformation profile) async { + String userId, + CachedProfileInformation profile, + ) async { return; } } diff --git a/lib/src/database/indexeddb_box.dart b/lib/src/database/indexeddb_box.dart index 98e6df73e..268006a12 100644 --- a/lib/src/database/indexeddb_box.dart +++ b/lib/src/database/indexeddb_box.dart @@ -22,15 +22,18 @@ class BoxCollection with ZoneTransactionMixin { int version = 1, }) async { idbFactory ??= window.indexedDB!; - final db = await idbFactory.open(name, version: version, - onUpgradeNeeded: (VersionChangeEvent event) { - final db = event.target.result; - for (final name in boxNames) { - if (db.objectStoreNames.contains(name)) continue; - - db.createObjectStore(name, autoIncrement: true); - } - }); + final db = await idbFactory.open( + name, + version: version, + onUpgradeNeeded: (VersionChangeEvent event) { + final db = event.target.result; + for (final name in boxNames) { + if (db.objectStoreNames.contains(name)) continue; + + db.createObjectStore(name, autoIncrement: true); + } + }, + ); return BoxCollection(db, boxNames, name); } @@ -147,7 +150,8 @@ class Box { txn ??= boxCollection._db.transaction(name, 'readonly'); final store = txn.objectStore(name); final list = await Future.wait( - keys.map((key) => store.getObject(key).then(_fromValue))); + keys.map((key) => store.getObject(key).then(_fromValue)), + ); for (var i = 0; i < keys.length; i++) { _cache[keys[i]] = list[i]; } diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index 3537c5068..9b37237c7 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -112,7 +112,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { // there was a field of type `dart:io:Directory` here. This one broke the // dart js standalone compiler. Migration via URI as file system identifier. @Deprecated( - 'Breaks support for web standalone. Use [fileStorageLocation] instead.') + 'Breaks support for web standalone. Use [fileStorageLocation] instead.', + ) Object? get fileStoragePath => fileStorageLocation?.toFilePath(); static const String _clientBoxName = 'box_client'; @@ -183,7 +184,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { this.maxFileSize = 0, // TODO : remove deprecated member migration on next major release @Deprecated( - 'Breaks support for web standalone. Use [fileStorageLocation] instead.') + 'Breaks support for web standalone. Use [fileStorageLocation] instead.', + ) dynamic fileStoragePath, Uri? fileStorageLocation, Duration? deleteFilesAfterDuration, @@ -315,7 +317,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { .where((session) => session.uploaded == false) .toList(); Logs().i( - 'Move ${allInboundGroupSessions.length} inbound group sessions to upload to their own queue...'); + 'Move ${allInboundGroupSessions.length} inbound group sessions to upload to their own queue...', + ); await transaction(() async { for (final session in sessionsToUpload) { await _inboundGroupSessionsUploadQueueBox.put( @@ -476,8 +479,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } // Combine those two lists while respecting the start and limit parameters. - final end = min(timelineEventIds.length, - start + (limit ?? timelineEventIds.length)); + final end = min( + timelineEventIds.length, + start + (limit ?? timelineEventIds.length), + ); final eventIds = [ ...sendingEventIds, if (!onlySending && start < timelineEventIds.length) @@ -511,7 +516,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future> getLastSentMessageUserDeviceKey( - String userId, String deviceId) async { + String userId, + String deviceId, + ) async { final raw = await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); if (raw == null) return []; @@ -519,8 +526,12 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } @override - Future storeOlmSession(String identityKey, String sessionId, - String pickle, int lastReceived) async { + Future storeOlmSession( + String identityKey, + String sessionId, + String pickle, + int lastReceived, + ) async { final rawSessions = copyMap((await _olmSessionsBox.get(identityKey)) ?? {}); rawSessions[sessionId] = { 'identity_key': identityKey, @@ -534,7 +545,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future> getOlmSessions( - String identityKey, String userId) async { + String identityKey, + String userId, + ) async { final rawSessions = await _olmSessionsBox.get(identityKey); if (rawSessions == null || rawSessions.isEmpty) return []; return rawSessions.values @@ -548,23 +561,31 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future> getOlmSessionsForDevices( - List identityKeys, String userId) async { + List identityKeys, + String userId, + ) async { final sessions = await Future.wait( - identityKeys.map((identityKey) => getOlmSessions(identityKey, userId))); + identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)), + ); return [for (final sublist in sessions) ...sublist]; } @override Future getOutboundGroupSession( - String roomId, String userId) async { + String roomId, + String userId, + ) async { final raw = await _outboundGroupSessionsBox.get(roomId); if (raw == null) return null; return OutboundGroupSession.fromJson(copyMap(raw), userId); } @override - Future getSingleRoom(Client client, String roomId, - {bool loadImportantStates = true}) async { + Future getSingleRoom( + Client client, + String roomId, { + bool loadImportantStates = true, + }) async { // Get raw room from database: final roomData = await _roomsBox.get(roomId); if (roomData == null) return null; @@ -611,9 +632,11 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } final states = entry.value; final stateEvents = states.values - .map((raw) => room.membership == Membership.invite - ? StrippedStateEvent.fromJson(copyMap(raw)) - : Event.fromJson(copyMap(raw), room)) + .map( + (raw) => room.membership == Membership.invite + ? StrippedStateEvent.fromJson(copyMap(raw)) + : Event.fromJson(copyMap(raw), room), + ) .toList(); for (final state in stateEvents) { room.setState(state); @@ -633,7 +656,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { basicRoomEvent; } else { Logs().w( - 'Found account data for unknown room $roomId. Delete now...'); + 'Found account data for unknown room $roomId. Delete now...', + ); await _roomAccountDataBox .delete(TupleKey(roomId, basicRoomEvent.type).toString()); } @@ -663,7 +687,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future> getUnimportantRoomEventStatesForRoom( - List events, Room room) async { + List events, + Room room, + ) async { final keys = (await _nonPreloadRoomStateBox.getAllKeys()).where((key) { final tuple = TupleKey.fromString(key); return tuple.parts.first == room.id && !events.contains(tuple.parts[1]); @@ -674,7 +700,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { final states = await _nonPreloadRoomStateBox.get(key); if (states == null) continue; unimportantEvents.addAll( - states.values.map((raw) => Event.fromJson(copyMap(raw), room))); + states.values.map((raw) => Event.fromJson(copyMap(raw), room)), + ); } return unimportantEvents.where((event) => event.stateKey != null).toList(); @@ -726,20 +753,21 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { }, ); res[userId] = DeviceKeysList.fromDbJson( - { - 'client_id': client.id, - 'user_id': userId, - 'outdated': deviceKeysOutdated[userId], - }, - childEntries - .where((c) => c != null) - .toList() - .cast>(), - crossSigningEntries - .where((c) => c != null) - .toList() - .cast>(), - client); + { + 'client_id': client.id, + 'user_id': userId, + 'outdated': deviceKeysOutdated[userId], + }, + childEntries + .where((c) => c != null) + .toList() + .cast>(), + crossSigningEntries + .where((c) => c != null) + .toList() + .cast>(), + client, + ); } return res; }); @@ -761,24 +789,27 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future insertClient( - String name, - String homeserverUrl, - String token, - DateTime? tokenExpiresAt, - String? refreshToken, - String userId, - String? deviceId, - String? deviceName, - String? prevBatch, - String? olmAccount) async { + String name, + String homeserverUrl, + String token, + DateTime? tokenExpiresAt, + String? refreshToken, + String userId, + String? deviceId, + String? deviceName, + String? prevBatch, + String? olmAccount, + ) async { await transaction(() async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); if (tokenExpiresAt == null) { await _clientBox.delete('token_expires_at'); } else { - await _clientBox.put('token_expires_at', - tokenExpiresAt.millisecondsSinceEpoch.toString()); + await _clientBox.put( + 'token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString(), + ); } if (refreshToken == null) { await _clientBox.delete('refresh_token'); @@ -813,7 +844,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future insertIntoToDeviceQueue( - String type, String txnId, String content) async { + String type, + String txnId, + String content, + ) async { final id = DateTime.now().millisecondsSinceEpoch; await _toDeviceQueueBox.put(id.toString(), { 'type': type, @@ -825,7 +859,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future markInboundGroupSessionAsUploaded( - String roomId, String sessionId) async { + String roomId, + String sessionId, + ) async { await _inboundGroupSessionsUploadQueueBox.delete(sessionId); return; } @@ -871,7 +907,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future removeUserCrossSigningKey( - String userId, String publicKey) async { + String userId, + String publicKey, + ) async { await _userCrossSigningKeysBox .delete(TupleKey(userId, publicKey).toString()); return; @@ -885,7 +923,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setBlockedUserCrossSigningKey( - bool blocked, String userId, String publicKey) async { + bool blocked, + String userId, + String publicKey, + ) async { final raw = copyMap( await _userCrossSigningKeysBox .get(TupleKey(userId, publicKey).toString()) ?? @@ -901,7 +942,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setBlockedUserDeviceKey( - bool blocked, String userId, String deviceId) async { + bool blocked, + String userId, + String deviceId, + ) async { final raw = copyMap( await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()) ?? {}, ); @@ -915,7 +959,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setLastActiveUserDeviceKey( - int lastActive, String userId, String deviceId) async { + int lastActive, + String userId, + String deviceId, + ) async { final raw = copyMap( await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()) ?? {}, ); @@ -929,7 +976,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setLastSentMessageUserDeviceKey( - String lastSentMessage, String userId, String deviceId) async { + String lastSentMessage, + String userId, + String deviceId, + ) async { final raw = copyMap( await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()) ?? {}, ); @@ -942,7 +992,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setRoomPrevBatch( - String? prevBatch, String roomId, Client client) async { + String? prevBatch, + String roomId, + Client client, + ) async { final raw = await _roomsBox.get(roomId); if (raw == null) return; final room = Room.fromJson(copyMap(raw), client); @@ -953,7 +1006,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setVerifiedUserCrossSigningKey( - bool verified, String userId, String publicKey) async { + bool verified, + String userId, + String publicKey, + ) async { final raw = copyMap( (await _userCrossSigningKeysBox .get(TupleKey(userId, publicKey).toString())) ?? @@ -969,7 +1025,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future setVerifiedUserDeviceKey( - bool verified, String userId, String deviceId) async { + bool verified, + String userId, + String deviceId, + ) async { final raw = copyMap( await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()) ?? {}, ); @@ -1002,8 +1061,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { if (event != null) { event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom)); await _eventsBox.put( - TupleKey(eventUpdate.roomID, event.eventId).toString(), - event.toJson()); + TupleKey(eventUpdate.roomID, event.eventId).toString(), + event.toJson(), + ); if (tmpRoom.lastEvent?.eventId == event.eventId) { if (client.importantStateEvents.contains(event.type)) { @@ -1070,8 +1130,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { final transactionId = eventUpdate.content .tryGetMap('unsigned') ?.tryGet('transaction_id'); - await _eventsBox.put(TupleKey(eventUpdate.roomID, eventId).toString(), - eventUpdate.content); + await _eventsBox.put( + TupleKey(eventUpdate.roomID, eventId).toString(), + eventUpdate.content, + ); // Update timeline fragments final key = TupleKey(eventUpdate.roomID, status.isSent ? '' : 'SENDING') @@ -1122,11 +1184,12 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { eventUpdate.type == EventUpdateType.inviteState)) { if (eventUpdate.content['type'] == EventTypes.RoomMember) { await _roomMembersBox.put( - TupleKey( - eventUpdate.roomID, - eventUpdate.content['state_key'], - ).toString(), - eventUpdate.content); + TupleKey( + eventUpdate.roomID, + eventUpdate.content['state_key'], + ).toString(), + eventUpdate.content, + ); } else { final type = eventUpdate.content['type'] as String; final roomStateBox = client.importantStateEvents.contains(type) @@ -1157,14 +1220,15 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future storeInboundGroupSession( - String roomId, - String sessionId, - String pickle, - String content, - String indexes, - String allowedAtIndex, - String senderKey, - String senderClaimedKey) async { + String roomId, + String sessionId, + String pickle, + String content, + String indexes, + String allowedAtIndex, + String senderKey, + String senderClaimedKey, + ) async { final json = StoredInboundGroupSession( roomId: roomId, sessionId: sessionId, @@ -1186,7 +1250,11 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future storeOutboundGroupSession( - String roomId, String pickle, String deviceIds, int creationTime) async { + String roomId, + String pickle, + String deviceIds, + int creationTime, + ) async { await _outboundGroupSessionsBox.put(roomId, { 'room_id': roomId, 'pickle': pickle, @@ -1206,8 +1274,12 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } @override - Future storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate, - Event? lastEvent, Client client) async { + Future storeRoomUpdate( + String roomId, + SyncRoomUpdate roomUpdate, + Event? lastEvent, + Client client, + ) async { // Leave room if membership is leave if (roomUpdate is LeftRoomUpdate) { await forgetRoom(roomId); @@ -1222,49 +1294,52 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { final currentRawRoom = await _roomsBox.get(roomId); if (currentRawRoom == null) { await _roomsBox.put( - roomId, - roomUpdate is JoinedRoomUpdate - ? Room( - client: client, - id: roomId, - membership: membership, - highlightCount: - roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? - 0, - notificationCount: roomUpdate - .unreadNotifications?.notificationCount - ?.toInt() ?? - 0, - prev_batch: roomUpdate.timeline?.prevBatch, - summary: roomUpdate.summary, - lastEvent: lastEvent, - ).toJson() - : Room( - client: client, - id: roomId, - membership: membership, - lastEvent: lastEvent, - ).toJson()); + roomId, + roomUpdate is JoinedRoomUpdate + ? Room( + client: client, + id: roomId, + membership: membership, + highlightCount: + roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? + 0, + notificationCount: roomUpdate + .unreadNotifications?.notificationCount + ?.toInt() ?? + 0, + prev_batch: roomUpdate.timeline?.prevBatch, + summary: roomUpdate.summary, + lastEvent: lastEvent, + ).toJson() + : Room( + client: client, + id: roomId, + membership: membership, + lastEvent: lastEvent, + ).toJson(), + ); } else if (roomUpdate is JoinedRoomUpdate) { final currentRoom = Room.fromJson(copyMap(currentRawRoom), client); await _roomsBox.put( - roomId, - Room( - client: client, - id: roomId, - membership: membership, - highlightCount: - roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? - currentRoom.highlightCount, - notificationCount: - roomUpdate.unreadNotifications?.notificationCount?.toInt() ?? - currentRoom.notificationCount, - prev_batch: - roomUpdate.timeline?.prevBatch ?? currentRoom.prev_batch, - summary: RoomSummary.fromJson(currentRoom.summary.toJson() - ..addAll(roomUpdate.summary?.toJson() ?? {})), - lastEvent: lastEvent, - ).toJson()); + roomId, + Room( + client: client, + id: roomId, + membership: membership, + highlightCount: + roomUpdate.unreadNotifications?.highlightCount?.toInt() ?? + currentRoom.highlightCount, + notificationCount: + roomUpdate.unreadNotifications?.notificationCount?.toInt() ?? + currentRoom.notificationCount, + prev_batch: roomUpdate.timeline?.prevBatch ?? currentRoom.prev_batch, + summary: RoomSummary.fromJson( + currentRoom.summary.toJson() + ..addAll(roomUpdate.summary?.toJson() ?? {}), + ), + lastEvent: lastEvent, + ).toJson(), + ); } } @@ -1274,15 +1349,20 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future storeSSSSCache( - String type, String keyId, String ciphertext, String content) async { + String type, + String keyId, + String ciphertext, + String content, + ) async { await _ssssCacheBox.put( - type, - SSSSCache( - type: type, - keyId: keyId, - ciphertext: ciphertext, - content: content, - ).toJson()); + type, + SSSSCache( + type: type, + keyId: keyId, + ciphertext: ciphertext, + content: content, + ).toJson(), + ); } @override @@ -1293,8 +1373,13 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } @override - Future storeUserCrossSigningKey(String userId, String publicKey, - String content, bool verified, bool blocked) async { + Future storeUserCrossSigningKey( + String userId, + String publicKey, + String content, + bool verified, + bool blocked, + ) async { await _userCrossSigningKeysBox.put( TupleKey(userId, publicKey).toString(), { @@ -1308,8 +1393,14 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } @override - Future storeUserDeviceKey(String userId, String deviceId, - String content, bool verified, bool blocked, int lastActive) async { + Future storeUserDeviceKey( + String userId, + String deviceId, + String content, + bool verified, + bool blocked, + int lastActive, + ) async { await _userDeviceKeysBox.put(TupleKey(userId, deviceId).toString(), { 'user_id': userId, 'device_id': deviceId, @@ -1350,8 +1441,10 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { if (tokenExpiresAt == null) { await _clientBox.delete('token_expires_at'); } else { - await _clientBox.put('token_expires_at', - tokenExpiresAt.millisecondsSinceEpoch.toString()); + await _clientBox.put( + 'token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString(), + ); } if (refreshToken == null) { await _clientBox.delete('refresh_token'); @@ -1393,11 +1486,15 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future updateInboundGroupSessionAllowedAtIndex( - String allowedAtIndex, String roomId, String sessionId) async { + String allowedAtIndex, + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId); if (raw == null) { Logs().w( - 'Tried to update inbound group session as uploaded which wasnt found in the database!'); + 'Tried to update inbound group session as uploaded which wasnt found in the database!', + ); return; } raw['allowed_at_index'] = allowedAtIndex; @@ -1407,11 +1504,15 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future updateInboundGroupSessionIndexes( - String indexes, String roomId, String sessionId) async { + String indexes, + String roomId, + String sessionId, + ) async { final raw = await _inboundGroupSessionsBox.get(sessionId); if (raw == null) { Logs().w( - 'Tried to update inbound group session indexes of a session which was not found in the database!'); + 'Tried to update inbound group session indexes of a session which was not found in the database!', + ); return; } final json = copyMap(raw); @@ -1510,11 +1611,15 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } for (final key in json[_preloadRoomStateBoxName]!.keys) { await _preloadRoomStateBox.put( - key, json[_preloadRoomStateBoxName]![key]); + key, + json[_preloadRoomStateBoxName]![key], + ); } for (final key in json[_nonPreloadRoomStateBoxName]!.keys) { await _nonPreloadRoomStateBox.put( - key, json[_nonPreloadRoomStateBoxName]![key]); + key, + json[_nonPreloadRoomStateBoxName]![key], + ); } for (final key in json[_roomMembersBoxName]!.keys) { await _roomMembersBox.put(key, json[_roomMembersBoxName]![key]); @@ -1527,15 +1632,21 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } for (final key in json[_inboundGroupSessionsBoxName]!.keys) { await _inboundGroupSessionsBox.put( - key, json[_inboundGroupSessionsBoxName]![key]); + key, + json[_inboundGroupSessionsBoxName]![key], + ); } for (final key in json[_inboundGroupSessionsUploadQueueBoxName]!.keys) { await _inboundGroupSessionsUploadQueueBox.put( - key, json[_inboundGroupSessionsUploadQueueBoxName]![key]); + key, + json[_inboundGroupSessionsUploadQueueBoxName]![key], + ); } for (final key in json[_outboundGroupSessionsBoxName]!.keys) { await _outboundGroupSessionsBox.put( - key, json[_outboundGroupSessionsBoxName]![key]); + key, + json[_outboundGroupSessionsBoxName]![key], + ); } for (final key in json[_olmSessionsBoxName]!.keys) { await _olmSessionsBox.put(key, json[_olmSessionsBoxName]![key]); @@ -1545,11 +1656,15 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } for (final key in json[_userDeviceKeysOutdatedBoxName]!.keys) { await _userDeviceKeysOutdatedBox.put( - key, json[_userDeviceKeysOutdatedBoxName]![key]); + key, + json[_userDeviceKeysOutdatedBoxName]![key], + ); } for (final key in json[_userCrossSigningKeysBoxName]!.keys) { await _userCrossSigningKeysBox.put( - key, json[_userCrossSigningKeysBoxName]![key]); + key, + json[_userCrossSigningKeysBoxName]![key], + ); } for (final key in json[_ssssCacheBoxName]!.keys) { await _ssssCacheBox.put(key, json[_ssssCacheBoxName]![key]); @@ -1559,7 +1674,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } for (final key in json[_timelineFragmentsBoxName]!.keys) { await _timelineFragmentsBox.put( - key, json[_timelineFragmentsBoxName]![key]); + key, + json[_timelineFragmentsBoxName]![key], + ); } for (final key in json[_seenDeviceIdsBoxName]!.keys) { await _seenDeviceIdsBox.put(key, json[_seenDeviceIdsBoxName]![key]); @@ -1585,7 +1702,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { // Get the synced event IDs from the store final timelineKey = TupleKey(room.id, '').toString(); final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? []); + (await _timelineFragmentsBox.get(timelineKey)) ?? [], + ); // Get the local stored SENDING events from the store late final List sendingEventIds; @@ -1594,7 +1712,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { } else { final sendingTimelineKey = TupleKey(room.id, 'SENDING').toString(); sendingEventIds = List.from( - (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? []); + (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? [], + ); } // Combine those two lists while respecting the start and limit parameters. @@ -1667,13 +1786,17 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future getUserProfile(String userId) => - _userProfilesBox.get(userId).then((json) => json == null - ? null - : CachedProfileInformation.fromJson(copyMap(json))); + _userProfilesBox.get(userId).then( + (json) => json == null + ? null + : CachedProfileInformation.fromJson(copyMap(json)), + ); @override Future storeUserProfile( - String userId, CachedProfileInformation profile) => + String userId, + CachedProfileInformation profile, + ) => _userProfilesBox.put( userId, profile.toJson(), diff --git a/lib/src/database/sqflite_box.dart b/lib/src/database/sqflite_box.dart index 010364ffa..4ab23115e 100644 --- a/lib/src/database/sqflite_box.dart +++ b/lib/src/database/sqflite_box.dart @@ -127,7 +127,8 @@ class Box { if (value == null) return null; if (value is! String) { throw Exception( - 'Wrong database type! Expected String but got one of type ${value.runtimeType}'); + 'Wrong database type! Expected String but got one of type ${value.runtimeType}', + ); } switch (V) { case const (int): diff --git a/lib/src/database/sqflite_encryption_helper/io.dart b/lib/src/database/sqflite_encryption_helper/io.dart index 0cd93a32c..d4f3c1d09 100644 --- a/lib/src/database/sqflite_encryption_helper/io.dart +++ b/lib/src/database/sqflite_encryption_helper/io.dart @@ -111,7 +111,8 @@ class SQfLiteEncryptionHelper { } Logs().d( - 'Warning: Found unencrypted sqlite database. Encrypting using SQLCipher.'); + 'Warning: Found unencrypted sqlite database. Encrypting using SQLCipher.', + ); // hell, it's unencrypted. This should not happen. Time to encrypt it. final plainDb = await factory.openDatabase(path); @@ -119,7 +120,8 @@ class SQfLiteEncryptionHelper { final encryptedPath = '$path.encrypted'; await plainDb.execute( - "ATTACH DATABASE '$encryptedPath' AS encrypted KEY '$cipher';"); + "ATTACH DATABASE '$encryptedPath' AS encrypted KEY '$cipher';", + ); await plainDb.execute("SELECT sqlcipher_export('encrypted');"); // ignore: prefer_single_quotes await plainDb.execute("DETACH DATABASE encrypted;"); @@ -164,7 +166,8 @@ class SQfLiteEncryptionHelper { } else { final version = cipherVersion.singleOrNull?['cipher_version']; Logs().d( - 'PRAGMA supported by bundled SQLite. Encryption supported. SQLCipher version: $version.'); + 'PRAGMA supported by bundled SQLite. Encryption supported. SQLCipher version: $version.', + ); } final result = await database.rawQuery("PRAGMA KEY='$cipher';"); diff --git a/lib/src/event.dart b/lib/src/event.dart index 10688fdf9..3446eed0d 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -44,7 +44,8 @@ class Event extends MatrixEvent { ); @Deprecated( - 'Use eventSender instead or senderFromMemoryOrFallback for a synchronous alternative') + 'Use eventSender instead or senderFromMemoryOrFallback for a synchronous alternative', + ) User get sender => senderFromMemoryOrFallback; User get senderFromMemoryOrFallback => @@ -138,7 +139,7 @@ class Event extends MatrixEvent { timeline: TimelineUpdate( events: [MatrixEvent.fromJson(json)], ), - ) + ), }, ), ), @@ -188,22 +189,25 @@ class Event extends MatrixEvent { final originalSource = Event.getMapFromPayload(jsonPayload['original_source']); return Event( - status: eventStatusFromInt(jsonPayload['status'] ?? + status: eventStatusFromInt( + jsonPayload['status'] ?? unsigned[messageSendingStatusKey] ?? - defaultStatus.intValue), - stateKey: jsonPayload['state_key'], - prevContent: prevContent, - content: content, - type: jsonPayload['type'], - eventId: jsonPayload['event_id'] ?? '', - senderId: jsonPayload['sender'], - originServerTs: DateTime.fromMillisecondsSinceEpoch( - jsonPayload['origin_server_ts'] ?? 0), - unsigned: unsigned, - room: room, - originalSource: originalSource.isEmpty - ? null - : MatrixEvent.fromJson(originalSource)); + defaultStatus.intValue, + ), + stateKey: jsonPayload['state_key'], + prevContent: prevContent, + content: content, + type: jsonPayload['type'], + eventId: jsonPayload['event_id'] ?? '', + senderId: jsonPayload['sender'], + originServerTs: DateTime.fromMillisecondsSinceEpoch( + jsonPayload['origin_server_ts'] ?? 0, + ), + unsigned: unsigned, + room: room, + originalSource: + originalSource.isEmpty ? null : MatrixEvent.fromJson(originalSource), + ); } @override @@ -311,9 +315,12 @@ class Event extends MatrixEvent { final receipts = room.receiptState; final receiptsList = receipts.global.otherUsers.entries .where((entry) => entry.value.eventId == eventId) - .map((entry) => Receipt( + .map( + (entry) => Receipt( room.unsafeGetUserFromMemoryOrFallback(entry.key), - entry.value.timestamp)) + entry.value.timestamp, + ), + ) .toList(); // add your own only once @@ -321,21 +328,31 @@ class Event extends MatrixEvent { receipts.mainThread?.latestOwnReceipt; if (own != null && own.eventId == eventId) { receiptsList.add( - Receipt(room.unsafeGetUserFromMemoryOrFallback(room.client.userID!), - own.timestamp), + Receipt( + room.unsafeGetUserFromMemoryOrFallback(room.client.userID!), + own.timestamp, + ), ); } // also add main thread. https://github.com/famedly/product-management/issues/1020 // also deduplicate. - receiptsList.addAll(receipts.mainThread?.otherUsers.entries - .where((entry) => - entry.value.eventId == eventId && - receiptsList.every((element) => element.user.id != entry.key)) - .map((entry) => Receipt( - room.unsafeGetUserFromMemoryOrFallback(entry.key), - entry.value.timestamp)) ?? - []); + receiptsList.addAll( + receipts.mainThread?.otherUsers.entries + .where( + (entry) => + entry.value.eventId == eventId && + receiptsList + .every((element) => element.user.id != entry.key), + ) + .map( + (entry) => Receipt( + room.unsafeGetUserFromMemoryOrFallback(entry.key), + entry.value.timestamp, + ), + ) ?? + [], + ); return receiptsList; } @@ -383,7 +400,7 @@ class Event extends MatrixEvent { timeline: TimelineUpdate( events: [redactedBecause], ), - ) + ), }, ), ), @@ -553,14 +570,15 @@ class Event extends MatrixEvent { /// /// Important! To use this link you have to set a http header like this: /// `headers: {"authorization": "Bearer ${client.accessToken}"}` - Future getAttachmentUri( - {bool getThumbnail = false, - bool useThumbnailMxcUrl = false, - double width = 800.0, - double height = 800.0, - ThumbnailMethod method = ThumbnailMethod.scale, - int minNoThumbSize = _minNoThumbSize, - bool animated = false}) async { + Future getAttachmentUri({ + bool getThumbnail = false, + bool useThumbnailMxcUrl = false, + double width = 800.0, + double height = 800.0, + ThumbnailMethod method = ThumbnailMethod.scale, + int minNoThumbSize = _minNoThumbSize, + bool animated = false, + }) async { if (![EventTypes.Message, EventTypes.Sticker].contains(type) || !hasAttachment || isAttachmentEncrypted) { @@ -607,14 +625,15 @@ class Event extends MatrixEvent { /// Important! To use this link you have to set a http header like this: /// `headers: {"authorization": "Bearer ${client.accessToken}"}` @Deprecated('Use getAttachmentUri() instead') - Uri? getAttachmentUrl( - {bool getThumbnail = false, - bool useThumbnailMxcUrl = false, - double width = 800.0, - double height = 800.0, - ThumbnailMethod method = ThumbnailMethod.scale, - int minNoThumbSize = _minNoThumbSize, - bool animated = false}) { + Uri? getAttachmentUrl({ + bool getThumbnail = false, + bool useThumbnailMxcUrl = false, + double width = 800.0, + double height = 800.0, + ThumbnailMethod method = ThumbnailMethod.scale, + int minNoThumbSize = _minNoThumbSize, + bool animated = false, + }) { if (![EventTypes.Message, EventTypes.Sticker].contains(type) || !hasAttachment || isAttachmentEncrypted) { @@ -680,10 +699,11 @@ class Event extends MatrixEvent { /// true to download the thumbnail instead. Set [fromLocalStoreOnly] to true /// if you want to retrieve the attachment from the local store only without /// making http request. - Future downloadAndDecryptAttachment( - {bool getThumbnail = false, - Future Function(Uri)? downloadCallback, - bool fromLocalStoreOnly = false}) async { + Future downloadAndDecryptAttachment({ + bool getThumbnail = false, + Future Function(Uri)? downloadCallback, + bool fromLocalStoreOnly = false, + }) async { if (![EventTypes.Message, EventTypes.Sticker].contains(type)) { throw ("This event has the type '$type' and so it can't contain an attachment."); } @@ -730,7 +750,10 @@ class Event extends MatrixEvent { uint8list.lengthInBytes < database.maxFileSize; if (storeable) { await database.storeFile( - mxcUrl, uint8list, DateTime.now().millisecondsSinceEpoch); + mxcUrl, + uint8list, + DateTime.now().millisecondsSinceEpoch, + ); } } else if (uint8list == null) { throw ('Unable to download file from local store.'); @@ -771,12 +794,14 @@ class Event extends MatrixEvent { /// it to plain text. /// [removeMarkdown] allow to remove the markdown formating from the event body. /// Usefull form message preview or notifications text. - Future calcLocalizedBody(MatrixLocalizations i18n, - {bool withSenderNamePrefix = false, - bool hideReply = false, - bool hideEdit = false, - bool plaintextBody = false, - bool removeMarkdown = false}) async { + Future calcLocalizedBody( + MatrixLocalizations i18n, { + bool withSenderNamePrefix = false, + bool hideReply = false, + bool hideEdit = false, + bool plaintextBody = false, + bool removeMarkdown = false, + }) async { if (redacted) { await redactedBecause?.fetchSenderUser(); } @@ -799,30 +824,36 @@ class Event extends MatrixEvent { } @Deprecated('Use calcLocalizedBody or calcLocalizedBodyFallback') - String getLocalizedBody(MatrixLocalizations i18n, - {bool withSenderNamePrefix = false, - bool hideReply = false, - bool hideEdit = false, - bool plaintextBody = false, - bool removeMarkdown = false}) => - calcLocalizedBodyFallback(i18n, - withSenderNamePrefix: withSenderNamePrefix, - hideReply: hideReply, - hideEdit: hideEdit, - plaintextBody: plaintextBody, - removeMarkdown: removeMarkdown); + String getLocalizedBody( + MatrixLocalizations i18n, { + bool withSenderNamePrefix = false, + bool hideReply = false, + bool hideEdit = false, + bool plaintextBody = false, + bool removeMarkdown = false, + }) => + calcLocalizedBodyFallback( + i18n, + withSenderNamePrefix: withSenderNamePrefix, + hideReply: hideReply, + hideEdit: hideEdit, + plaintextBody: plaintextBody, + removeMarkdown: removeMarkdown, + ); /// Works similar to `calcLocalizedBody()` but does not wait for the sender /// user to be fetched. If it is not in the cache it will just use the /// fallback and display the localpart of the MXID according to the /// values of `formatLocalpart` and `mxidLocalPartFallback` in the `Client` /// class. - String calcLocalizedBodyFallback(MatrixLocalizations i18n, - {bool withSenderNamePrefix = false, - bool hideReply = false, - bool hideEdit = false, - bool plaintextBody = false, - bool removeMarkdown = false}) { + String calcLocalizedBodyFallback( + MatrixLocalizations i18n, { + bool withSenderNamePrefix = false, + bool hideReply = false, + bool hideEdit = false, + bool plaintextBody = false, + bool removeMarkdown = false, + }) { if (redacted) { if (status.intValue < EventStatus.synced.intValue) { return i18n.cancelledSend; @@ -896,7 +927,9 @@ class Event extends MatrixEvent { // if the message is formatted if (hideReply && mayHaveReplyFallback) { body = body.replaceFirst( - RegExp(r'^>( \*)? <[^>]+>[^\n\r]+\r?\n(> [^\n]*\r?\n)*\r?\n'), ''); + RegExp(r'^>( \*)? <[^>]+>[^\n\r]+\r?\n(> [^\n]*\r?\n)*\r?\n'), + '', + ); } // return the html tags free body @@ -979,11 +1012,13 @@ class Event extends MatrixEvent { // we need to check again if it isn't empty, as we potentially removed all // aggregated edits if (allEditEvents.isNotEmpty) { - allEditEvents.sort((a, b) => a.originServerTs.millisecondsSinceEpoch - - b.originServerTs.millisecondsSinceEpoch > - 0 - ? 1 - : -1); + allEditEvents.sort( + (a, b) => a.originServerTs.millisecondsSinceEpoch - + b.originServerTs.millisecondsSinceEpoch > + 0 + ? 1 + : -1, + ); final rawEvent = allEditEvents.last.toJson(); // update the content of the new event to render if (rawEvent['content']['m.new_content'] is Map) { @@ -1051,9 +1086,14 @@ class Event extends MatrixEvent { if (isRichMessage) { // calcUnlocalizedBody strips out the tags in favor of a :placeholder: final formattedTextStripped = formattedText.replaceAll( - RegExp('.*', - caseSensitive: false, multiLine: false, dotAll: true), - ''); + RegExp( + '.*', + caseSensitive: false, + multiLine: false, + dotAll: true, + ), + '', + ); return _onlyEmojiEmoteRegex.hasMatch(formattedTextStripped); } else { return _onlyEmojiRegex.hasMatch(plaintextBody); @@ -1068,9 +1108,14 @@ class Event extends MatrixEvent { if (isRichMessage) { // calcUnlocalizedBody strips out the tags in favor of a :placeholder: final formattedTextStripped = formattedText.replaceAll( - RegExp('.*', - caseSensitive: false, multiLine: false, dotAll: true), - ''); + RegExp( + '.*', + caseSensitive: false, + multiLine: false, + dotAll: true, + ), + '', + ); return _countEmojiEmoteRegex.allMatches(formattedTextStripped).length; } else { return _countEmojiRegex.allMatches(plaintextBody).length; @@ -1083,7 +1128,8 @@ class Event extends MatrixEvent { final status = unsigned?.tryGet(fileSendingStatusKey); if (status == null) return null; return FileSendingStatus.values.singleWhereOrNull( - (fileSendingStatus) => fileSendingStatus.name == status); + (fileSendingStatus) => fileSendingStatus.name == status, + ); } } diff --git a/lib/src/event_status.dart b/lib/src/event_status.dart index 418924c1c..53a78def9 100644 --- a/lib/src/event_status.dart +++ b/lib/src/event_status.dart @@ -52,7 +52,7 @@ extension EventStatusExtension on EventStatus { bool get isSent => [ EventStatus.sent, EventStatus.synced, - EventStatus.roomState + EventStatus.roomState, ].contains(this); /// Returns `true` if the status is `synced` or `roomState`: diff --git a/lib/src/models/receipts.dart b/lib/src/models/receipts.dart index c43fe3c13..9bb3e3c6c 100644 --- a/lib/src/models/receipts.dart +++ b/lib/src/models/receipts.dart @@ -167,10 +167,11 @@ class LatestReceiptStateForTimeline { factory LatestReceiptStateForTimeline.empty() => LatestReceiptStateForTimeline( - ownPrivate: null, - ownPublic: null, - latestOwnReceipt: null, - otherUsers: {}); + ownPrivate: null, + ownPublic: null, + latestOwnReceipt: null, + otherUsers: {}, + ); factory LatestReceiptStateForTimeline.fromJson(Map json) { final private = json['private']; @@ -229,7 +230,8 @@ class LatestReceiptState { mainThread: main.isNotEmpty ? LatestReceiptStateForTimeline.fromJson(main) : null, byThread: byThread.map( - (k, v) => MapEntry(k, LatestReceiptStateForTimeline.fromJson(v))), + (k, v) => MapEntry(k, LatestReceiptStateForTimeline.fromJson(v)), + ), ); } diff --git a/lib/src/models/timeline_chunk.dart b/lib/src/models/timeline_chunk.dart index a1e7002d0..7cdaef10f 100644 --- a/lib/src/models/timeline_chunk.dart +++ b/lib/src/models/timeline_chunk.dart @@ -5,6 +5,9 @@ class TimelineChunk { String nextBatch; List events; - TimelineChunk( - {required this.events, this.prevBatch = '', this.nextBatch = ''}); + TimelineChunk({ + required this.events, + this.prevBatch = '', + this.nextBatch = '', + }); } diff --git a/lib/src/presence.dart b/lib/src/presence.dart index e249322ed..b0cf566de 100644 --- a/lib/src/presence.dart +++ b/lib/src/presence.dart @@ -31,7 +31,8 @@ class CachedPresence { .singleWhere((type) => type.name == json['presence']), lastActiveTimestamp: json['last_active_timestamp'] != null ? DateTime.fromMillisecondsSinceEpoch( - json['last_active_timestamp'] as int) + json['last_active_timestamp'] as int, + ) : null, statusMsg: json['status_msg'] as String?, currentlyActive: json['currently_active'] as bool?, @@ -55,8 +56,13 @@ class CachedPresence { this.currentlyActive, }); - CachedPresence(this.presence, int? lastActiveAgo, this.statusMsg, - this.currentlyActive, this.userid) { + CachedPresence( + this.presence, + int? lastActiveAgo, + this.statusMsg, + this.currentlyActive, + this.userid, + ) { if (lastActiveAgo != null) { lastActiveTimestamp = DateTime.now().subtract(Duration(milliseconds: lastActiveAgo)); @@ -65,15 +71,21 @@ class CachedPresence { CachedPresence.fromMatrixEvent(Presence event) : this( - event.presence.presence, - event.presence.lastActiveAgo, - event.presence.statusMsg, - event.presence.currentlyActive, - event.senderId); + event.presence.presence, + event.presence.lastActiveAgo, + event.presence.statusMsg, + event.presence.currentlyActive, + event.senderId, + ); CachedPresence.fromPresenceResponse(GetPresenceResponse event, String userid) - : this(event.presence, event.lastActiveAgo, event.statusMsg, - event.currentlyActive, userid); + : this( + event.presence, + event.lastActiveAgo, + event.statusMsg, + event.currentlyActive, + userid, + ); CachedPresence.neverSeen(this.userid) : presence = PresenceType.offline; @@ -91,7 +103,7 @@ class CachedPresence { final json = { 'content': content, 'sender': '@example:localhost', - 'type': 'm.presence' + 'type': 'm.presence', }; return Presence.fromJson(json); diff --git a/lib/src/room.dart b/lib/src/room.dart index 95392b68c..f6671ceab 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -115,9 +115,11 @@ class Room { if (!partial) { return; } - final allStates = await client.database - ?.getUnimportantRoomEventStatesForRoom( - client.importantStateEvents.toList(), this); + final allStates = + await client.database?.getUnimportantRoomEventStatesForRoom( + client.importantStateEvents.toList(), + this, + ); if (allStates != null) { for (final state in allStates) { @@ -216,12 +218,16 @@ class Room { if (heroes == null) return []; - return await Future.wait(heroes.map((hero) async => - (await requestUser( - hero, - ignoreErrors: true, - )) ?? - User(hero, room: this))); + return await Future.wait( + heroes.map( + (hero) async => + (await requestUser( + hero, + ignoreErrors: true, + )) ?? + User(hero, room: this), + ), + ); } /// Returns a localized displayname for this server. If the room is a groupchat @@ -251,8 +257,10 @@ class Room { // removing oneself from the hero list (hero) => hero.isNotEmpty && hero != client.userID, ) - .map((hero) => unsafeGetUserFromMemoryOrFallback(hero) - .calcDisplayname(i18n: i18n)) + .map( + (hero) => unsafeGetUserFromMemoryOrFallback(hero) + .calcDisplayname(i18n: i18n), + ) .join(', '); if (isAbandonedDMRoom) { return i18n.wasDirectChatDisplayName(result); @@ -273,8 +281,9 @@ class Room { if (membership == Membership.leave) { if (directChatMatrixID != null) { return i18n.wasDirectChatDisplayName( - unsafeGetUserFromMemoryOrFallback(directChatMatrixID) - .calcDisplayname(i18n: i18n)); + unsafeGetUserFromMemoryOrFallback(directChatMatrixID) + .calcDisplayname(i18n: i18n), + ); } } return i18n.emptyChat; @@ -450,12 +459,13 @@ class Room { /// Add a tag to the room. Future addTag(String tag, {double? order}) => client.setRoomTag( - client.userID!, - id, - tag, - Tag( - order: order, - )); + client.userID!, + id, + tag, + Tag( + order: order, + ), + ); /// Removes a tag from the room. Future removeTag(String tag) => client.deleteRoomTag( @@ -493,10 +503,10 @@ class Room { bool get markedUnread { return MarkedUnread.fromJson( - roomAccountData[EventType.markedUnread]?.content ?? - roomAccountData[EventType.oldMarkedUnread]?.content ?? - {}) - .unread; + roomAccountData[EventType.markedUnread]?.content ?? + roomAccountData[EventType.oldMarkedUnread]?.content ?? + {}, + ).unread; } /// Checks if the last event has a read marker of the user. @@ -525,8 +535,9 @@ class Room { } LatestReceiptState get receiptState => LatestReceiptState.fromJson( - roomAccountData[LatestReceiptState.eventType]?.content ?? - {}); + roomAccountData[LatestReceiptState.eventType]?.content ?? + {}, + ); /// Returns true if this room is unread. To check if there are new messages /// in muted rooms, use [hasNewMessages]. @@ -564,7 +575,7 @@ class Room { type: EventType.markedUnread, ), ], - ) + ), }, ), ), @@ -600,31 +611,38 @@ class Room { /// Sends a normal text message to this room. Returns the event ID generated /// by the server for this message. - Future sendTextEvent(String message, - {String? txid, - Event? inReplyTo, - String? editEventId, - bool parseMarkdown = true, - bool parseCommands = true, - String msgtype = MessageTypes.Text, - String? threadRootEventId, - String? threadLastEventId}) { + Future sendTextEvent( + String message, { + String? txid, + Event? inReplyTo, + String? editEventId, + bool parseMarkdown = true, + bool parseCommands = true, + String msgtype = MessageTypes.Text, + String? threadRootEventId, + String? threadLastEventId, + }) { if (parseCommands) { - return client.parseAndRunCommand(this, message, - inReplyTo: inReplyTo, - editEventId: editEventId, - txid: txid, - threadRootEventId: threadRootEventId, - threadLastEventId: threadLastEventId); + return client.parseAndRunCommand( + this, + message, + inReplyTo: inReplyTo, + editEventId: editEventId, + txid: txid, + threadRootEventId: threadRootEventId, + threadLastEventId: threadLastEventId, + ); } final event = { 'msgtype': msgtype, 'body': message, }; if (parseMarkdown) { - final html = markdown(event['body'], - getEmotePacks: () => getImagePacksFlat(ImagePackUsage.emoticon), - getMention: getMention); + final html = markdown( + event['body'], + getEmotePacks: () => getImagePacksFlat(ImagePackUsage.emoticon), + getMention: getMention, + ); // if the decoded html is the same as the body, there is no need in sending a formatted message if (HtmlUnescape().convert(html.replaceAll(RegExp(r'
\n?'), '\n')) != event['body']) { @@ -645,13 +663,17 @@ class Room { /// Sends a reaction to an event with an [eventId] and the content [key] into a room. /// Returns the event ID generated by the server for this reaction. Future sendReaction(String eventId, String key, {String? txid}) { - return sendEvent({ - 'm.relates_to': { - 'rel_type': RelationshipTypes.reaction, - 'event_id': eventId, - 'key': key, + return sendEvent( + { + 'm.relates_to': { + 'rel_type': RelationshipTypes.reaction, + 'event_id': eventId, + 'key': key, + }, }, - }, type: EventTypes.Reaction, txid: txid); + type: EventTypes.Reaction, + txid: txid, + ); } /// Sends the location with description [body] and geo URI [geoUri] into a room. @@ -845,10 +867,10 @@ class Room { 'ext': true, 'k': encryptedFile.k, 'key_ops': ['encrypt', 'decrypt'], - 'kty': 'oct' + 'kty': 'oct', }, 'iv': encryptedFile.iv, - 'hashes': {'sha256': encryptedFile.sha256} + 'hashes': {'sha256': encryptedFile.sha256}, }, 'info': { ...file.info, @@ -864,16 +886,16 @@ class Room { 'ext': true, 'k': encryptedThumbnail.k, 'key_ops': ['encrypt', 'decrypt'], - 'kty': 'oct' + 'kty': 'oct', }, 'iv': encryptedThumbnail.iv, - 'hashes': {'sha256': encryptedThumbnail.sha256} + 'hashes': {'sha256': encryptedThumbnail.sha256}, }, if (thumbnail != null) 'thumbnail_info': thumbnail.info, if (thumbnail?.blurhash != null && file is MatrixImageFile && file.blurhash == null) - 'xyz.amorgan.blurhash': thumbnail!.blurhash + 'xyz.amorgan.blurhash': thumbnail!.blurhash, }, if (extraContent != null) ...extraContent, }; @@ -898,12 +920,16 @@ class Room { /// encryption security level. Future calcEncryptionHealthState() async { final users = await requestParticipants(); - users.removeWhere((u) => - !{Membership.invite, Membership.join}.contains(u.membership) || - !client.userDeviceKeys.containsKey(u.id)); + users.removeWhere( + (u) => + !{Membership.invite, Membership.join}.contains(u.membership) || + !client.userDeviceKeys.containsKey(u.id), + ); - if (users.any((u) => - client.userDeviceKeys[u.id]!.verified != UserVerifiedStatus.verified)) { + if (users.any( + (u) => + client.userDeviceKeys[u.id]!.verified != UserVerifiedStatus.verified, + )) { return EncryptionHealthState.unverifiedDevices; } @@ -983,9 +1009,14 @@ class Room { ? inReplyTo.formattedText : htmlEscape.convert(inReplyTo.body).replaceAll('\n', '
')) .replaceAll( - RegExp(r'.*', - caseSensitive: false, multiLine: false, dotAll: true), - ''); + RegExp( + r'.*', + caseSensitive: false, + multiLine: false, + dotAll: true, + ), + '', + ); final repliedHtml = content.tryGet('formatted_body') ?? htmlEscape .convert(content.tryGet('body') ?? '') @@ -1017,7 +1048,7 @@ class Room { 'm.in_reply_to': { 'event_id': threadLastEventId, }, - } + }, }; } @@ -1085,7 +1116,8 @@ class Room { .add(Duration(milliseconds: e.retryAfterMs!)) .isAfter(timeoutDate)) { Logs().w( - 'Ratelimited while sending message, waiting for ${e.retryAfterMs}ms'); + 'Ratelimited while sending message, waiting for ${e.retryAfterMs}ms', + ); await Future.delayed(Duration(milliseconds: e.retryAfterMs!)); } else if (e is MatrixException || e is EventTooLarge || @@ -1202,7 +1234,8 @@ class Room { if (users is! Map) { if (users != null) { Logs().v( - 'Repairing Power Level "users" has the wrong type "${powerLevelMapCopy['users'].runtimeType}"'); + 'Repairing Power Level "users" has the wrong type "${powerLevelMapCopy['users'].runtimeType}"', + ); } users = powerLevelMapCopy['users'] = {}; } @@ -1232,10 +1265,11 @@ class Room { /// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before** /// the historical events will be published in the onEvent stream. /// Returns the actual count of received timeline events. - Future requestHistory( - {int historyCount = defaultHistoryCount, - void Function()? onHistoryReceived, - direction = Direction.b}) async { + Future requestHistory({ + int historyCount = defaultHistoryCount, + void Function()? onHistoryReceived, + direction = Direction.b, + }) async { final prev_batch = this.prev_batch; final storeInDatabase = !isArchived; @@ -1258,43 +1292,43 @@ class Room { if (!((resp.chunk.isNotEmpty) && resp.end != null)) return; await client.handleSync( - SyncUpdate( - nextBatch: '', - rooms: RoomsUpdate( - join: membership == Membership.join - ? { - id: JoinedRoomUpdate( - state: resp.state, - timeline: TimelineUpdate( - limited: false, - events: direction == Direction.b - ? resp.chunk - : resp.chunk.reversed.toList(), - prevBatch: direction == Direction.b - ? resp.end - : resp.start, - ), - ) - } - : null, - leave: membership != Membership.join - ? { - id: LeftRoomUpdate( - state: resp.state, - timeline: TimelineUpdate( - limited: false, - events: direction == Direction.b - ? resp.chunk - : resp.chunk.reversed.toList(), - prevBatch: direction == Direction.b - ? resp.end - : resp.start, - ), - ), - } - : null), + SyncUpdate( + nextBatch: '', + rooms: RoomsUpdate( + join: membership == Membership.join + ? { + id: JoinedRoomUpdate( + state: resp.state, + timeline: TimelineUpdate( + limited: false, + events: direction == Direction.b + ? resp.chunk + : resp.chunk.reversed.toList(), + prevBatch: + direction == Direction.b ? resp.end : resp.start, + ), + ), + } + : null, + leave: membership != Membership.join + ? { + id: LeftRoomUpdate( + state: resp.state, + timeline: TimelineUpdate( + limited: false, + events: direction == Direction.b + ? resp.chunk + : resp.chunk.reversed.toList(), + prevBatch: + direction == Direction.b ? resp.end : resp.start, + ), + ), + } + : null, ), - direction: Direction.b); + ), + direction: Direction.b, + ); } if (client.database != null) { @@ -1368,8 +1402,11 @@ class Room { /// read receipt's location. /// If you set `public` to false, only a private receipt will be sent. A private receipt is always sent if `mRead` is set. If no value is provided, the default from the `client` is used. /// You can leave out the `eventId`, which will not update the read marker but just send receipts, but there are few cases where that makes sense. - Future setReadMarker(String? eventId, - {String? mRead, bool? public}) async { + Future setReadMarker( + String? eventId, { + String? mRead, + bool? public, + }) async { await client.setReadMarker( id, mFullyRead: eventId, @@ -1381,15 +1418,16 @@ class Room { } Future getEventContext(String eventId) async { - final resp = await client.getEventContext(id, eventId, - limit: Room.defaultHistoryCount - // filter: jsonEncode(StateFilter(lazyLoadMembers: true).toJson()), - ); + final resp = await client.getEventContext( + id, eventId, + limit: Room.defaultHistoryCount, + // filter: jsonEncode(StateFilter(lazyLoadMembers: true).toJson()), + ); final events = [ if (resp.eventsAfter != null) ...resp.eventsAfter!.reversed, if (resp.event != null) resp.event!, - if (resp.eventsBefore != null) ...resp.eventsBefore! + if (resp.eventsBefore != null) ...resp.eventsBefore!, ].map((e) => Event.fromMatrixEvent(e, this)).toList(); // Try again to decrypt encrypted events but don't update the database. @@ -1406,7 +1444,10 @@ class Room { } final chunk = TimelineChunk( - nextBatch: resp.end ?? '', prevBatch: resp.start ?? '', events: events); + nextBatch: resp.end ?? '', + prevBatch: resp.start ?? '', + events: events, + ); return chunk; } @@ -1415,9 +1456,12 @@ class Room { /// specified. In general you want to use `setReadMarker` instead to set private /// and public receipt as well as the marker at the same time. @Deprecated( - 'Use setReadMarker with mRead set instead. That allows for more control and there are few cases to not send a marker at the same time.') - Future postReceipt(String eventId, - {ReceiptType type = ReceiptType.mRead}) async { + 'Use setReadMarker with mRead set instead. That allows for more control and there are few cases to not send a marker at the same time.', + ) + Future postReceipt( + String eventId, { + ReceiptType type = ReceiptType.mRead, + }) async { await client.postReceipt( id, ReceiptType.mRead, @@ -1435,13 +1479,14 @@ class Room { /// [onChange], [onRemove], [onInsert] and the [onHistoryReceived] callbacks. /// This method can also retrieve the timeline at a specific point by setting /// the [eventContextId] - Future getTimeline( - {void Function(int index)? onChange, - void Function(int index)? onRemove, - void Function(int insertID)? onInsert, - void Function()? onNewEvent, - void Function()? onUpdate, - String? eventContextId}) async { + Future getTimeline({ + void Function(int index)? onChange, + void Function(int index)? onRemove, + void Function(int insertID)? onInsert, + void Function()? onNewEvent, + void Function()? onUpdate, + String? eventContextId, + }) async { await postLoad(); List events; @@ -1478,13 +1523,14 @@ class Room { } final timeline = Timeline( - room: this, - chunk: chunk, - onChange: onChange, - onRemove: onRemove, - onInsert: onInsert, - onNewEvent: onNewEvent, - onUpdate: onUpdate); + room: this, + chunk: chunk, + onChange: onChange, + onRemove: onRemove, + onInsert: onInsert, + onNewEvent: onNewEvent, + onUpdate: onUpdate, + ); // Fetch all users from database we have got here. if (eventContextId == null) { @@ -1536,12 +1582,13 @@ class Room { /// List `membershipFilter` defines with what membership do you want the /// participants, default set to /// [[Membership.join, Membership.invite, Membership.knock]] - List getParticipants( - [List membershipFilter = const [ - Membership.join, - Membership.invite, - Membership.knock, - ]]) { + List getParticipants([ + List membershipFilter = const [ + Membership.join, + Membership.invite, + Membership.knock, + ], + ]) { final members = states[EventTypes.RoomMember]; if (members != null) { return members.entries @@ -1560,14 +1607,15 @@ class Room { /// [[Membership.join, Membership.invite, Membership.knock]] /// Set [cache] to `false` if you do not want to cache the users in memory /// for this session which is highly recommended for large public rooms. - Future> requestParticipants( - [List membershipFilter = const [ - Membership.join, - Membership.invite, - Membership.knock, - ], - bool suppressWarning = false, - bool cache = true]) async { + Future> requestParticipants([ + List membershipFilter = const [ + Membership.join, + Membership.invite, + Membership.knock, + ], + bool suppressWarning = false, + bool cache = true, + ]) async { if (!participantListComplete || partial) { // we aren't fully loaded, maybe the users are in the database // We always need to check the database in the partial case, since state @@ -1624,7 +1672,8 @@ class Room { } @Deprecated( - 'The method was renamed unsafeGetUserFromMemoryOrFallback. Please prefer requestParticipants.') + 'The method was renamed unsafeGetUserFromMemoryOrFallback. Please prefer requestParticipants.', + ) User getUserByMXIDSync(String mxID) { return unsafeGetUserFromMemoryOrFallback(mxID); } @@ -1801,12 +1850,14 @@ class Room { final cache = _inflightUserRequests[parameters] ??= AsyncCache.ephemeral(); try { - final user = await cache.fetch(() => _requestUser( - mxID, - ignoreErrors: ignoreErrors, - requestState: requestState, - requestProfile: requestProfile, - )); + final user = await cache.fetch( + () => _requestUser( + mxID, + ignoreErrors: ignoreErrors, + requestState: requestState, + requestProfile: requestProfile, + ), + ); _inflightUserRequests.remove(parameters); return user; } catch (_) { @@ -1937,7 +1988,7 @@ class Room { final eventsMap = newPowerLevelMap.tryGetMap('events') ?? {}; eventsMap.addAll({ - EventTypes.GroupCallMember: getDefaultPowerLevel(currentPowerLevelsMap) + EventTypes.GroupCallMember: getDefaultPowerLevel(currentPowerLevelsMap), }); newPowerLevelMap.addAll({'events': eventsMap}); await client.setRoomStateWithKey( @@ -2099,7 +2150,7 @@ class Room { id, [PushRuleAction.dontNotify], conditions: [ - PushCondition(kind: 'event_match', key: 'room_id', pattern: id) + PushCondition(kind: 'event_match', key: 'room_id', pattern: id), ], ); } @@ -2107,8 +2158,11 @@ class Room { } /// Redacts this event. Throws `ErrorResponse` on error. - Future redactEvent(String eventId, - {String? reason, String? txid}) async { + Future redactEvent( + String eventId, { + String? reason, + String? txid, + }) async { // Create new transaction id String messageID; final now = DateTime.now().millisecondsSinceEpoch; @@ -2167,7 +2221,8 @@ class Room { ?.content .tryGet('guest_access'); return GuestAccess.values.singleWhereOrNull( - (element) => element.text == guestAccessString) ?? + (element) => element.text == guestAccessString, + ) ?? GuestAccess.forbidden; } @@ -2193,7 +2248,8 @@ class Room { ?.content .tryGet('history_visibility'); return HistoryVisibility.values.singleWhereOrNull( - (element) => element.text == historyVisibilityString); + (element) => element.text == historyVisibilityString, + ); } /// Changes the history visibility. You should check first if the user is able to change it. @@ -2259,8 +2315,10 @@ class Room { await client.encryption?.keyManager.request(this, sessionId, senderKey); } - Future _handleFakeSync(SyncUpdate syncUpdate, - {Direction? direction}) async { + Future _handleFakeSync( + SyncUpdate syncUpdate, { + Direction? direction, + }) async { if (client.database != null) { await client.database?.transaction(() async { await client.handleSync(syncUpdate, direction: direction); @@ -2309,9 +2367,11 @@ class Room { .where((child) => child.via.isNotEmpty) .toList() ?? []) - ..sort((a, b) => a.order.isEmpty || b.order.isEmpty - ? b.order.compareTo(a.order) - : a.order.compareTo(b.order)); + ..sort( + (a, b) => a.order.isEmpty || b.order.isEmpty + ? b.order.compareTo(a.order) + : a.order.compareTo(b.order), + ); /// Adds or edits a child of this space. Future setSpaceChild( @@ -2337,7 +2397,8 @@ class Room { Future matrixToInviteLink() async { if (canonicalAlias.isNotEmpty) { return Uri.parse( - 'https://matrix.to/#/${Uri.encodeComponent(canonicalAlias)}'); + 'https://matrix.to/#/${Uri.encodeComponent(canonicalAlias)}', + ); } final List queryParameters = []; final users = await requestParticipants([Membership.join]); @@ -2347,8 +2408,9 @@ class Room { temp.removeWhere((user) => user.powerLevel < 50); if (currentPowerLevelsMap != null) { // just for weird rooms - temp.removeWhere((user) => - user.powerLevel < getDefaultPowerLevel(currentPowerLevelsMap)); + temp.removeWhere( + (user) => user.powerLevel < getDefaultPowerLevel(currentPowerLevelsMap), + ); } if (temp.isNotEmpty) { @@ -2368,10 +2430,9 @@ class Room { } } } - final sortedServers = Map.fromEntries(servers.entries.toList() - ..sort((e1, e2) => e2.value.compareTo(e1.value))) - .keys - .take(3); + final sortedServers = Map.fromEntries( + servers.entries.toList()..sort((e1, e2) => e2.value.compareTo(e1.value)), + ).keys.take(3); for (final server in sortedServers) { if (!queryParameters.contains(server)) { queryParameters.add(server); @@ -2386,7 +2447,8 @@ class Room { queryString += 'via=${queryParameters[i]}'; } return Uri.parse( - 'https://matrix.to/#/${Uri.encodeComponent(id)}$queryString'); + 'https://matrix.to/#/${Uri.encodeComponent(id)}$queryString', + ); } /// Remove a child from this space by setting the `via` to an empty list. diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index 1a629e6ac..36977bffa 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -84,8 +84,9 @@ class Timeline { (room.prev_batch != null && events.last.type != EventTypes.RoomCreate); } - Future requestHistory( - {int historyCount = Room.defaultHistoryCount}) async { + Future requestHistory({ + int historyCount = Room.defaultHistoryCount, + }) async { if (isRequestingHistory) { return; } @@ -97,8 +98,9 @@ class Timeline { bool get canRequestFuture => !allowNewEvent; - Future requestFuture( - {int historyCount = Room.defaultHistoryCount}) async { + Future requestFuture({ + int historyCount = Room.defaultHistoryCount, + }) async { if (allowNewEvent) { return; // we shouldn't force to add new events if they will autatically be added } @@ -109,9 +111,10 @@ class Timeline { isRequestingFuture = false; } - Future _requestEvents( - {int historyCount = Room.defaultHistoryCount, - required Direction direction}) async { + Future _requestEvents({ + int historyCount = Room.defaultHistoryCount, + required Direction direction, + }) async { onUpdate?.call(); try { @@ -184,9 +187,10 @@ class Timeline { /// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before** /// the historical events will be published in the onEvent stream. /// Returns the actual count of received timeline events. - Future getRoomEvents( - {int historyCount = Room.defaultHistoryCount, - direction = Direction.b}) async { + Future getRoomEvents({ + int historyCount = Room.defaultHistoryCount, + direction = Direction.b, + }) async { final resp = await room.client.getRoomEvents( room.id, direction, @@ -212,10 +216,12 @@ class Timeline { newNextBatch != null) { if (type == EventUpdateType.history) { Logs().w( - '[nav] we can still request history prevBatch: $type $newPrevBatch'); + '[nav] we can still request history prevBatch: $type $newPrevBatch', + ); } else { Logs().w( - '[nav] we can still request timeline nextBatch: $type $newNextBatch'); + '[nav] we can still request timeline nextBatch: $type $newNextBatch', + ); } } @@ -229,8 +235,9 @@ class Timeline { if (allowNewEvent) { Logs().d('We now allow sync update into the timeline.'); newEvents.addAll( - await room.client.database?.getEventList(room, onlySending: true) ?? - []); + await room.client.database?.getEventList(room, onlySending: true) ?? + [], + ); } } @@ -272,14 +279,15 @@ class Timeline { return resp.chunk.length; } - Timeline( - {required this.room, - this.onUpdate, - this.onChange, - this.onInsert, - this.onRemove, - this.onNewEvent, - required this.chunk}) { + Timeline({ + required this.room, + this.onUpdate, + this.onChange, + this.onInsert, + this.onRemove, + this.onNewEvent, + required this.chunk, + }) { sub = room.client.onEvent.stream.listen(_handleEventUpdate); // If the timeline is limited we want to clear our events cache @@ -430,11 +438,14 @@ class Timeline { } void _removeEventFromSet(Set eventSet, Event event) { - eventSet.removeWhere((e) => - e.matchesEventOrTransactionId(event.eventId) || - event.unsigned != null && - e.matchesEventOrTransactionId( - event.unsigned?.tryGet('transaction_id'))); + eventSet.removeWhere( + (e) => + e.matchesEventOrTransactionId(event.eventId) || + event.unsigned != null && + e.matchesEventOrTransactionId( + event.unsigned?.tryGet('transaction_id'), + ), + ); } void addAggregatedEvent(Event event) { @@ -483,17 +494,20 @@ class Timeline { if (!allowNewEvent) return; - final status = eventStatusFromInt(eventUpdate.content['status'] ?? - (eventUpdate.content['unsigned'] is Map - ? eventUpdate.content['unsigned'][messageSendingStatusKey] - : null) ?? - EventStatus.synced.intValue); + final status = eventStatusFromInt( + eventUpdate.content['status'] ?? + (eventUpdate.content['unsigned'] is Map + ? eventUpdate.content['unsigned'][messageSendingStatusKey] + : null) ?? + EventStatus.synced.intValue, + ); final i = _findEvent( - event_id: eventUpdate.content['event_id'], - unsigned_txid: eventUpdate.content['unsigned'] is Map - ? eventUpdate.content['unsigned']['transaction_id'] - : null); + event_id: eventUpdate.content['event_id'], + unsigned_txid: eventUpdate.content['unsigned'] is Map + ? eventUpdate.content['unsigned']['transaction_id'] + : null, + ); if (i < events.length) { // if the old status is larger than the new one, we also want to preserve the old status @@ -517,7 +531,8 @@ class Timeline { if (eventUpdate.type == EventUpdateType.history && events.indexWhere( - (e) => e.eventId == eventUpdate.content['event_id']) != + (e) => e.eventId == eventUpdate.content['event_id'], + ) != -1) return; var index = events.length; if (eventUpdate.type == EventUpdateType.history) { @@ -548,10 +563,12 @@ class Timeline { } } - events[index].setRedactionEvent(Event.fromJson( - eventUpdate.content, - room, - )); + events[index].setRedactionEvent( + Event.fromJson( + eventUpdate.content, + room, + ), + ); onChange?.call(index); } } diff --git a/lib/src/user.dart b/lib/src/user.dart index 82b780a20..ef12b3caa 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -72,12 +72,15 @@ class User extends StrippedStateEvent { /// invite /// leave /// ban - Membership get membership => Membership.values.firstWhere((e) { - if (content['membership'] != null) { - return e.toString() == 'Membership.${content['membership']}'; - } - return false; - }, orElse: () => Membership.join); + Membership get membership => Membership.values.firstWhere( + (e) { + if (content['membership'] != null) { + return e.toString() == 'Membership.${content['membership']}'; + } + return false; + }, + orElse: () => Membership.join, + ); /// The avatar if the user has one. Uri? get avatarUrl { @@ -94,10 +97,11 @@ class User extends StrippedStateEvent { /// the first character of each word becomes uppercase. /// If [mxidLocalPartFallback] is true, then the local part of the mxid will be shown /// if there is no other displayname available. If not then this will return "Unknown user". - String calcDisplayname( - {bool? formatLocalpart, - bool? mxidLocalPartFallback, - MatrixLocalizations i18n = const MatrixDefaultLocalizations()}) { + String calcDisplayname({ + bool? formatLocalpart, + bool? mxidLocalPartFallback, + MatrixLocalizations i18n = const MatrixDefaultLocalizations(), + }) { formatLocalpart ??= room.client.formatLocalpart; mxidLocalPartFallback ??= room.client.mxidLocalPartFallback; final displayName = this.displayName; @@ -203,10 +207,12 @@ class User extends StrippedStateEvent { // get all the users with the same display name final allUsersWithSameDisplayname = room.getParticipants(); - allUsersWithSameDisplayname.removeWhere((user) => - user.id == id || - (user.displayName?.isEmpty ?? true) || - user.displayName != displayName); + allUsersWithSameDisplayname.removeWhere( + (user) => + user.id == id || + (user.displayName?.isEmpty ?? true) || + user.displayName != displayName, + ); if (allUsersWithSameDisplayname.isEmpty) { return identifier; } diff --git a/lib/src/utils/commands_extension.dart b/lib/src/utils/commands_extension.dart index 2f6d2fc55..6f711e378 100644 --- a/lib/src/utils/commands_extension.dart +++ b/lib/src/utils/commands_extension.dart @@ -25,7 +25,9 @@ extension CommandsClientExtension on Client { /// Add a command to the command handler. `command` is its name, and `callback` is the /// callback to invoke void addCommand( - String command, FutureOr Function(CommandArgs) callback) { + String command, + FutureOr Function(CommandArgs) callback, + ) { commands[command.toLowerCase()] = callback; } @@ -313,12 +315,13 @@ class CommandArgs { String? threadRootEventId; String? threadLastEventId; - CommandArgs( - {required this.msg, - this.editEventId, - this.inReplyTo, - required this.room, - this.txid, - this.threadRootEventId, - this.threadLastEventId}); + CommandArgs({ + required this.msg, + this.editEventId, + this.inReplyTo, + required this.room, + this.txid, + this.threadRootEventId, + this.threadLastEventId, + }); } diff --git a/lib/src/utils/compute_callback.dart b/lib/src/utils/compute_callback.dart index 75ffe8d3e..d81f40836 100644 --- a/lib/src/utils/compute_callback.dart +++ b/lib/src/utils/compute_callback.dart @@ -1,15 +1,22 @@ import 'dart:async'; typedef ComputeCallback = Future Function( - FutureOr Function(Q message) callback, Q message, - {String? debugLabel}); + FutureOr Function(Q message) callback, + Q message, { + String? debugLabel, +}); // keep types in sync with [computeCallbackFromRunInBackground] typedef ComputeRunner = Future Function( - FutureOr Function(U arg) function, U arg); + FutureOr Function(U arg) function, + U arg, +); ComputeCallback computeCallbackFromRunInBackground(ComputeRunner runner) { - return (FutureOr Function(U arg) callback, U arg, - {String? debugLabel}) => + return ( + FutureOr Function(U arg) callback, + U arg, { + String? debugLabel, + }) => runner.call(callback, arg); } diff --git a/lib/src/utils/crypto/ffi.dart b/lib/src/utils/crypto/ffi.dart index ed32e2c90..8cba110f6 100644 --- a/lib/src/utils/crypto/ffi.dart +++ b/lib/src/utils/crypto/ffi.dart @@ -43,23 +43,25 @@ final libcrypto = () { final PKCS5_PBKDF2_HMAC = libcrypto.lookupFunction< IntPtr Function( - Pointer pass, - IntPtr passlen, - Pointer salt, - IntPtr saltlen, - IntPtr iter, - Pointer digest, - IntPtr keylen, - Pointer out), + Pointer pass, + IntPtr passlen, + Pointer salt, + IntPtr saltlen, + IntPtr iter, + Pointer digest, + IntPtr keylen, + Pointer out, + ), int Function( - Pointer pass, - int passlen, - Pointer salt, - int saltlen, - int iter, - Pointer digest, - int keylen, - Pointer out)>('PKCS5_PBKDF2_HMAC'); + Pointer pass, + int passlen, + Pointer salt, + int saltlen, + int iter, + Pointer digest, + int keylen, + Pointer out, + )>('PKCS5_PBKDF2_HMAC'); final EVP_sha1 = libcrypto.lookupFunction Function(), Pointer Function()>('EVP_sha1'); @@ -82,54 +84,71 @@ final EVP_CIPHER_CTX_new = libcrypto.lookupFunction< final EVP_EncryptInit_ex = libcrypto.lookupFunction< Pointer Function( - Pointer ctx, - Pointer alg, - Pointer some, - Pointer key, - Pointer iv), + Pointer ctx, + Pointer alg, + Pointer some, + Pointer key, + Pointer iv, + ), Pointer Function( - Pointer ctx, - Pointer alg, - Pointer some, - Pointer key, - Pointer iv)>('EVP_EncryptInit_ex'); + Pointer ctx, + Pointer alg, + Pointer some, + Pointer key, + Pointer iv, + )>('EVP_EncryptInit_ex'); final EVP_EncryptUpdate = libcrypto.lookupFunction< - Pointer Function(Pointer ctx, Pointer output, - Pointer outputLen, Pointer input, IntPtr inputLen), Pointer Function( - Pointer ctx, - Pointer output, - Pointer outputLen, - Pointer input, - int inputLen)>('EVP_EncryptUpdate'); + Pointer ctx, + Pointer output, + Pointer outputLen, + Pointer input, + IntPtr inputLen, + ), + Pointer Function( + Pointer ctx, + Pointer output, + Pointer outputLen, + Pointer input, + int inputLen, + )>('EVP_EncryptUpdate'); final EVP_EncryptFinal_ex = libcrypto.lookupFunction< Pointer Function( - Pointer ctx, Pointer data, Pointer len), - Pointer Function(Pointer ctx, Pointer data, - Pointer len)>('EVP_EncryptFinal_ex'); + Pointer ctx, + Pointer data, + Pointer len, + ), + Pointer Function( + Pointer ctx, + Pointer data, + Pointer len, + )>('EVP_EncryptFinal_ex'); final EVP_CIPHER_CTX_free = libcrypto.lookupFunction< Pointer Function(Pointer ctx), Pointer Function( - Pointer ctx)>('EVP_CIPHER_CTX_free'); + Pointer ctx, + )>('EVP_CIPHER_CTX_free'); final EVP_Digest = libcrypto.lookupFunction< IntPtr Function( - Pointer data, - IntPtr len, - Pointer hash, - Pointer hsize, - Pointer alg, - Pointer engine), + Pointer data, + IntPtr len, + Pointer hash, + Pointer hsize, + Pointer alg, + Pointer engine, + ), int Function( - Pointer data, - int len, - Pointer hash, - Pointer hsize, - Pointer alg, - Pointer engine)>('EVP_Digest'); + Pointer data, + int len, + Pointer hash, + Pointer hsize, + Pointer alg, + Pointer engine, + )>('EVP_Digest'); final EVP_MD_size = () { // EVP_MD_size was renamed to EVP_MD_get_size in Openssl3.0. diff --git a/lib/src/utils/crypto/js.dart b/lib/src/utils/crypto/js.dart index 48429ca04..aa2dd2c82 100644 --- a/lib/src/utils/crypto/js.dart +++ b/lib/src/utils/crypto/js.dart @@ -35,7 +35,10 @@ abstract class Cipher { String name; Object params(Uint8List iv); Future encrypt( - Uint8List input, Uint8List key, Uint8List iv) async { + Uint8List input, + Uint8List key, + Uint8List iv, + ) async { final subtleKey = await importKey('raw', key, name, false, ['encrypt']); return (await subtle.encrypt(params(iv), subtleKey, input)).asUint8List(); } @@ -51,14 +54,24 @@ class _AesCtr extends Cipher { AesCtrParams(name: name, counter: iv, length: 64); } -Future pbkdf2(Uint8List passphrase, Uint8List salt, Hash hash, - int iterations, int bits) async { +Future pbkdf2( + Uint8List passphrase, + Uint8List salt, + Hash hash, + int iterations, + int bits, +) async { final raw = await importKey('raw', passphrase, 'PBKDF2', false, ['deriveBits']); final res = await deriveBits( - Pbkdf2Params( - name: 'PBKDF2', hash: hash.name, salt: salt, iterations: iterations), - raw, - bits); + Pbkdf2Params( + name: 'PBKDF2', + hash: hash.name, + salt: salt, + iterations: iterations, + ), + raw, + bits, + ); return Uint8List.view(res); } diff --git a/lib/src/utils/crypto/native.dart b/lib/src/utils/crypto/native.dart index 1ef0613de..c70cded92 100644 --- a/lib/src/utils/crypto/native.dart +++ b/lib/src/utils/crypto/native.dart @@ -90,7 +90,12 @@ class _AesCtr extends Cipher { } FutureOr pbkdf2( - Uint8List passphrase, Uint8List salt, Hash hash, int iterations, int bits) { + Uint8List passphrase, + Uint8List salt, + Hash hash, + int iterations, + int bits, +) { final outLen = bits ~/ 8; final mem = malloc.call(passphrase.length + salt.length + outLen); final saltMem = mem.elementAt(passphrase.length); @@ -98,8 +103,16 @@ FutureOr pbkdf2( try { mem.asTypedList(passphrase.length).setAll(0, passphrase); saltMem.asTypedList(salt.length).setAll(0, salt); - PKCS5_PBKDF2_HMAC(mem, passphrase.length, saltMem, salt.length, iterations, - hash.ptr, outLen, outMem); + PKCS5_PBKDF2_HMAC( + mem, + passphrase.length, + saltMem, + salt.length, + iterations, + hash.ptr, + outLen, + outMem, + ); return Uint8List.fromList(outMem.asTypedList(outLen)); } finally { malloc.free(mem); diff --git a/lib/src/utils/crypto/subtle.dart b/lib/src/utils/crypto/subtle.dart index 0b6797903..d1cb7c13f 100644 --- a/lib/src/utils/crypto/subtle.dart +++ b/lib/src/utils/crypto/subtle.dart @@ -50,13 +50,24 @@ Future decrypt(dynamic algorithm, dynamic key, Uint8List data) { } @JS('crypto.subtle.importKey') -external dynamic _importKey(String format, dynamic keyData, dynamic algorithm, - bool extractable, List keyUsages); - -Future importKey(String format, dynamic keyData, dynamic algorithm, - bool extractable, List keyUsages) { +external dynamic _importKey( + String format, + dynamic keyData, + dynamic algorithm, + bool extractable, + List keyUsages, +); + +Future importKey( + String format, + dynamic keyData, + dynamic algorithm, + bool extractable, + List keyUsages, +) { return promiseToFuture( - _importKey(format, keyData, algorithm, extractable, keyUsages)); + _importKey(format, keyData, algorithm, extractable, keyUsages), + ); } @JS('crypto.subtle.exportKey') @@ -67,13 +78,30 @@ Future exportKey(String algorithm, dynamic key) { } @JS('crypto.subtle.deriveKey') -external dynamic _deriveKey(dynamic algorithm, dynamic baseKey, - dynamic derivedKeyAlgorithm, bool extractable, List keyUsages); - -Future deriveKey(dynamic algorithm, dynamic baseKey, - dynamic derivedKeyAlgorithm, bool extractable, List keyUsages) { - return promiseToFuture(_deriveKey( - algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)); +external dynamic _deriveKey( + dynamic algorithm, + dynamic baseKey, + dynamic derivedKeyAlgorithm, + bool extractable, + List keyUsages, +); + +Future deriveKey( + dynamic algorithm, + dynamic baseKey, + dynamic derivedKeyAlgorithm, + bool extractable, + List keyUsages, +) { + return promiseToFuture( + _deriveKey( + algorithm, + baseKey, + derivedKeyAlgorithm, + extractable, + keyUsages, + ), + ); } @JS('crypto.subtle.deriveBits') diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index db235c2a6..c4f3cb090 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -95,7 +95,10 @@ class DeviceKeysList { } else { // start verification with verified devices final request = KeyVerification( - encryption: encryption, userId: userId, deviceId: '*'); + encryption: encryption, + userId: userId, + deviceId: '*', + ); await request.start(); encryption.keyVerificationManager.addRequest(request); return request; @@ -103,11 +106,11 @@ class DeviceKeysList { } DeviceKeysList.fromDbJson( - Map dbEntry, - List> childEntries, - List> crossSigningEntries, - this.client) - : userId = dbEntry['user_id'] ?? '' { + Map dbEntry, + List> childEntries, + List> crossSigningEntries, + this.client, + ) : userId = dbEntry['user_id'] ?? '' { outdated = dbEntry['outdated']; deviceKeys = {}; for (final childEntry in childEntries) { @@ -195,8 +198,11 @@ abstract class SignableKey extends MatrixSignableKey { return String.fromCharCodes(canonicalJson.encode(data)); } - bool _verifySignature(String pubKey, String signature, - {bool isSignatureWithoutLibolmValid = false}) { + bool _verifySignature( + String pubKey, + String signature, { + bool isSignatureWithoutLibolmValid = false, + }) { olm.Utility olmutil; try { olmutil = olm.Utility(); @@ -313,10 +319,11 @@ abstract class SignableKey extends MatrixSignableKey { } // or else we just recurse into that key and check if it works out final haveChain = key.hasValidSignatureChain( - verifiedOnly: verifiedOnly, - visited: visited_, - onlyValidateUserIds: onlyValidateUserIds, - verifiedByTheirMasterKey: verifiedByTheirMasterKey); + verifiedOnly: verifiedOnly, + visited: visited_, + onlyValidateUserIds: onlyValidateUserIds, + verifiedByTheirMasterKey: verifiedByTheirMasterKey, + ); if (haveChain) { return true; } @@ -396,8 +403,9 @@ class CrossSigningKey extends SignableKey { } CrossSigningKey.fromMatrixCrossSigningKey( - MatrixCrossSigningKey key, Client client) - : super.fromJson(key.toJson().copy(), client) { + MatrixCrossSigningKey key, + Client client, + ) : super.fromJson(key.toJson().copy(), client) { final json = toJson(); identifier = key.publicKey; usage = json['usage'].cast(); @@ -445,8 +453,10 @@ class DeviceKeys extends SignableKey { // without libolm we still want to be able to add devices. In that case we ofc just can't // verify the signature _verifySignature( - ed25519Key!, signatures![userId]!['ed25519:$deviceId']!, - isSignatureWithoutLibolmValid: true)); + ed25519Key!, + signatures![userId]!['ed25519:$deviceId']!, + isSignatureWithoutLibolmValid: true, + )); @override bool get blocked => super.blocked || !selfSigned; @@ -480,9 +490,11 @@ class DeviceKeys extends SignableKey { ?.setBlockedUserDeviceKey(newBlocked, userId, deviceId!); } - DeviceKeys.fromMatrixDeviceKeys(MatrixDeviceKeys keys, Client client, - [DateTime? lastActiveTs]) - : super.fromJson(keys.toJson().copy(), client) { + DeviceKeys.fromMatrixDeviceKeys( + MatrixDeviceKeys keys, + Client client, [ + DateTime? lastActiveTs, + ]) : super.fromJson(keys.toJson().copy(), client) { final json = toJson(); identifier = keys.deviceId; algorithms = json['algorithms'].cast(); @@ -518,7 +530,10 @@ class DeviceKeys extends SignableKey { } final request = KeyVerification( - encryption: encryption, userId: userId, deviceId: deviceId!); + encryption: encryption, + userId: userId, + deviceId: deviceId!, + ); await request.start(); encryption.keyVerificationManager.addRequest(request); diff --git a/lib/src/utils/event_localizations.dart b/lib/src/utils/event_localizations.dart index d98617b24..e03b9d0c7 100644 --- a/lib/src/utils/event_localizations.dart +++ b/lib/src/utils/event_localizations.dart @@ -28,46 +28,61 @@ abstract class EventLocalizations { // Thus, it seems easier to offload that logic into `Event.getLocalizedBody()` and pass the // `body` variable around here. static String _localizedBodyNormalMessage( - Event event, MatrixLocalizations i18n, String body) { + Event event, + MatrixLocalizations i18n, + String body, + ) { switch (event.messageType) { case MessageTypes.Image: return i18n.sentAPicture( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.File: return i18n.sentAFile( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.Audio: return i18n.sentAnAudio( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.Video: return i18n.sentAVideo( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.Location: return i18n.sharedTheLocation( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.Sticker: return i18n.sentASticker( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.Emote: return '* $body'; case EventTypes.KeyVerificationRequest: return i18n.requestedKeyVerification( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case EventTypes.KeyVerificationCancel: return i18n.canceledKeyVerification( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case EventTypes.KeyVerificationDone: return i18n.completedKeyVerification( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case EventTypes.KeyVerificationReady: return i18n.isReadyForKeyVerification( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case EventTypes.KeyVerificationAccept: return i18n.acceptedKeyVerification( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case EventTypes.KeyVerificationStart: return i18n.startedKeyVerification( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); case MessageTypes.BadEncrypted: String errorText; switch (event.body) { @@ -102,27 +117,35 @@ abstract class EventLocalizations { String Function(Event event, MatrixLocalizations i18n, String body)?> localizationsMap = { EventTypes.Sticker: (event, i18n, body) => i18n.sentASticker( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.Redaction: (event, i18n, body) => i18n.redactedAnEvent(event), EventTypes.RoomAliases: (event, i18n, body) => i18n.changedTheRoomAliases( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.RoomCanonicalAlias: (event, i18n, body) => i18n.changedTheRoomInvitationLink( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.RoomCreate: (event, i18n, body) => i18n.createdTheChat( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded, EventTypes.RoomJoinRules: (event, i18n, body) { - final joinRules = JoinRules.values.firstWhereOrNull((r) => - r.toString().replaceAll('JoinRules.', '') == - event.content['join_rule']); + final joinRules = JoinRules.values.firstWhereOrNull( + (r) => + r.toString().replaceAll('JoinRules.', '') == + event.content['join_rule'], + ); if (joinRules == null) { return i18n.changedTheJoinRules( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); } else { return i18n.changedTheJoinRulesTo( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - joinRules.getLocalizedString(i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + joinRules.getLocalizedString(i18n), + ); } }, EventTypes.RoomMember: (event, i18n, body) { @@ -188,58 +211,75 @@ abstract class EventLocalizations { }, EventTypes.RoomPowerLevels: (event, i18n, body) => i18n.changedTheChatPermissions( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - event.content.tryGet('name') ?? ''), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + event.content.tryGet('name') ?? '', + ), EventTypes.RoomTopic: (event, i18n, body) => i18n.changedTheChatDescriptionTo( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - event.content.tryGet('topic') ?? ''), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + event.content.tryGet('topic') ?? '', + ), EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.GuestAccess: (event, i18n, body) { - final guestAccess = GuestAccess.values.firstWhereOrNull((r) => - r.toString().replaceAll('GuestAccess.', '') == - event.content['guest_access']); + final guestAccess = GuestAccess.values.firstWhereOrNull( + (r) => + r.toString().replaceAll('GuestAccess.', '') == + event.content['guest_access'], + ); if (guestAccess == null) { return i18n.changedTheGuestAccessRules( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); } else { return i18n.changedTheGuestAccessRulesTo( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - guestAccess.getLocalizedString(i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + guestAccess.getLocalizedString(i18n), + ); } }, EventTypes.HistoryVisibility: (event, i18n, body) { - final historyVisibility = HistoryVisibility.values.firstWhereOrNull((r) => - r.toString().replaceAll('HistoryVisibility.', '') == - event.content['history_visibility']); + final historyVisibility = HistoryVisibility.values.firstWhereOrNull( + (r) => + r.toString().replaceAll('HistoryVisibility.', '') == + event.content['history_visibility'], + ); if (historyVisibility == null) { return i18n.changedTheHistoryVisibility( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); } else { return i18n.changedTheHistoryVisibilityTo( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - historyVisibility.getLocalizedString(i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + historyVisibility.getLocalizedString(i18n), + ); } }, EventTypes.Encryption: (event, i18n, body) { var localizedBody = i18n.activatedEndToEndEncryption( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)); + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ); if (event.room.client.encryptionEnabled == false) { localizedBody += '. ${i18n.needPantalaimonWarning}'; } return localizedBody; }, EventTypes.CallAnswer: (event, i18n, body) => i18n.answeredTheCall( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.CallHangup: (event, i18n, body) => i18n.endedTheCall( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.CallInvite: (event, i18n, body) => i18n.startedACall( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.CallCandidates: (event, i18n, body) => i18n.sentCallInformations( - event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), + event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), + ), EventTypes.Encrypted: (event, i18n, body) => _localizedBodyNormalMessage(event, i18n, body), EventTypes.Message: (event, i18n, body) => diff --git a/lib/src/utils/event_update.dart b/lib/src/utils/event_update.dart index fb9538719..8d5918f8a 100644 --- a/lib/src/utils/event_update.dart +++ b/lib/src/utils/event_update.dart @@ -53,8 +53,11 @@ class EventUpdate { // The json payload of the content of this event. final Map content; - EventUpdate( - {required this.roomID, required this.type, required this.content}); + EventUpdate({ + required this.roomID, + required this.type, + required this.content, + }); Future decrypt(Room room, {bool store = false}) async { final encryption = room.client.encryption; @@ -65,10 +68,16 @@ class EventUpdate { } try { final decrpytedEvent = await encryption.decryptRoomEvent( - room.id, Event.fromJson(content, room), - store: store, updateType: type); + room.id, + Event.fromJson(content, room), + store: store, + updateType: type, + ); return EventUpdate( - roomID: roomID, type: type, content: decrpytedEvent.toJson()); + roomID: roomID, + type: type, + content: decrpytedEvent.toJson(), + ); } catch (e, s) { Logs().e('[LibOlm] Could not decrypt megolm event', e, s); return this; diff --git a/lib/src/utils/html_to_text.dart b/lib/src/utils/html_to_text.dart index d5ac20c38..110d715ee 100644 --- a/lib/src/utils/html_to_text.dart +++ b/lib/src/utils/html_to_text.dart @@ -32,9 +32,14 @@ class HtmlToText { // miss-matching tags, and this way we actually correctly identify what we want to strip and, well, // strip it. final renderHtml = html.replaceAll( - RegExp('.*', - caseSensitive: false, multiLine: false, dotAll: true), - ''); + RegExp( + '.*', + caseSensitive: false, + multiLine: false, + dotAll: true, + ), + '', + ); final opts = _ConvertOpts(); var reply = _walkNode(opts, parseFragment(renderHtml)); @@ -63,7 +68,9 @@ class HtmlToText { text = text.substring(match.end); // remove the closing tag text = text.replaceAll( - RegExp(r'$', multiLine: false, caseSensitive: false), ''); + RegExp(r'$', multiLine: false, caseSensitive: false), + '', + ); text = HtmlUnescape().convert(text); if (text.isNotEmpty) { if (text[0] != '\n') { @@ -108,8 +115,10 @@ class HtmlToText { _listBulletPoints[opts.listDepth % _listBulletPoints.length]; return entries - .map((s) => - '${' ' * opts.listDepth}$bulletPoint ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}') + .map( + (s) => + '${' ' * opts.listDepth}$bulletPoint ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}', + ) .join('\n'); } @@ -124,15 +133,20 @@ class HtmlToText { : 1; return entries - .mapIndexed((index, s) => - '${' ' * opts.listDepth}${start + index}. ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}') + .mapIndexed( + (index, s) => + '${' ' * opts.listDepth}${start + index}. ${s.replaceAll('\n', '\n${' ' * opts.listDepth} ')}', + ) .join('\n'); } static const _listBulletPoints = ['●', '○', '■', '‣']; - static List _listChildNodes(_ConvertOpts opts, Element node, - [Iterable? types]) { + static List _listChildNodes( + _ConvertOpts opts, + Element node, [ + Iterable? types, + ]) { final replies = []; for (final child in node.nodes) { if (types != null && diff --git a/lib/src/utils/http_timeout.dart b/lib/src/utils/http_timeout.dart index 3646d8dec..ee284502b 100644 --- a/lib/src/utils/http_timeout.dart +++ b/lib/src/utils/http_timeout.dart @@ -21,7 +21,9 @@ import 'dart:async'; import 'package:http/http.dart' as http; http.StreamedResponse replaceStream( - http.StreamedResponse base, Stream> stream) => + http.StreamedResponse base, + Stream> stream, +) => http.StreamedResponse( http.ByteStream(stream), base.statusCode, diff --git a/lib/src/utils/image_pack_extension.dart b/lib/src/utils/image_pack_extension.dart index 03525e712..3873a532d 100644 --- a/lib/src/utils/image_pack_extension.dart +++ b/lib/src/utils/image_pack_extension.dart @@ -44,13 +44,14 @@ extension ImagePackRoomExtension on Room { } packs .putIfAbsent( - finalSlug, - () => ImagePackContent.fromJson({}) - ..pack.displayName = imagePack.pack.displayName ?? - room?.getLocalizedDisplayname() ?? - finalSlug - ..pack.avatarUrl = imagePack.pack.avatarUrl ?? room?.avatar - ..pack.attribution = imagePack.pack.attribution) + finalSlug, + () => ImagePackContent.fromJson({}) + ..pack.displayName = imagePack.pack.displayName ?? + room?.getLocalizedDisplayname() ?? + finalSlug + ..pack.avatarUrl = imagePack.pack.avatarUrl ?? room?.avatar + ..pack.attribution = imagePack.pack.attribution, + ) .images[entry.key] = image; allMxcs.add(image.url); } @@ -71,8 +72,11 @@ extension ImagePackRoomExtension on Room { final stateKey = stateKeyEntry.key; final fallbackSlug = '${room.getLocalizedDisplayname()}-${stateKey.isNotEmpty ? '$stateKey-' : ''}${room.id}'; - addImagePack(room.getState('im.ponies.room_emotes', stateKey), - room: room, slug: fallbackSlug); + addImagePack( + room.getState('im.ponies.room_emotes', stateKey), + room: room, + slug: fallbackSlug, + ); } } } @@ -81,11 +85,13 @@ extension ImagePackRoomExtension on Room { final allRoomEmotes = states['im.ponies.room_emotes']; if (allRoomEmotes != null) { for (final entry in allRoomEmotes.entries) { - addImagePack(entry.value, - room: this, - slug: (entry.value.stateKey?.isNotEmpty == true) - ? entry.value.stateKey - : 'room'); + addImagePack( + entry.value, + room: this, + slug: (entry.value.stateKey?.isNotEmpty == true) + ? entry.value.stateKey + : 'room', + ); } } return packs; @@ -94,6 +100,8 @@ extension ImagePackRoomExtension on Room { /// Get a flat view of all the image packs of a specified [usage], that is a map of all /// slugs to a map of the image code to their mxc url Map> getImagePacksFlat([ImagePackUsage? usage]) => - getImagePacks(usage).map((k, v) => - MapEntry(k, v.images.map((k, v) => MapEntry(k, v.url.toString())))); + getImagePacks(usage).map( + (k, v) => + MapEntry(k, v.images.map((k, v) => MapEntry(k, v.url.toString()))), + ); } diff --git a/lib/src/utils/markdown.dart b/lib/src/utils/markdown.dart index 7e8ba81df..4b1cec4be 100644 --- a/lib/src/utils/markdown.dart +++ b/lib/src/utils/markdown.dart @@ -155,7 +155,7 @@ class BlockLatexSyntax extends BlockSyntax { // we use .substring(2) as childLines will *always* contain the first two '$$' final latex = childLines.join('\n').trim().substring(2).trim(); final element = Element('div', [ - Element('pre', [Element.text('code', htmlEscape.convert(latex))]) + Element('pre', [Element.text('code', htmlEscape.convert(latex))]), ]); element.attributes['data-mx-maths'] = htmlAttrEscape.convert(latex); return element; @@ -165,7 +165,8 @@ class BlockLatexSyntax extends BlockSyntax { class PillSyntax extends InlineSyntax { PillSyntax() : super( - r'([@#!][^\s:]*:(?:[^\s]+\.\w+|[\d\.]+|\[[a-fA-F0-9:]+\])(?::\d+)?)'); + r'([@#!][^\s:]*:(?:[^\s]+\.\w+|[\d\.]+|\[[a-fA-F0-9:]+\])(?::\d+)?)', + ); @override bool onMatch(InlineParser parser, Match match) { @@ -269,8 +270,11 @@ String markdown( } extension on String { - String convertLinebreaksToBr(String tagName, - {bool exclude = false, String replaceWith = '
'}) { + String convertLinebreaksToBr( + String tagName, { + bool exclude = false, + String replaceWith = '
', + }) { final parts = split('$tagName>'); var convertLinebreaks = exclude; for (var i = 0; i < parts.length; i++) { diff --git a/lib/src/utils/matrix_default_localizations.dart b/lib/src/utils/matrix_default_localizations.dart index aea246054..ecdb75859 100644 --- a/lib/src/utils/matrix_default_localizations.dart +++ b/lib/src/utils/matrix_default_localizations.dart @@ -61,7 +61,9 @@ class MatrixDefaultLocalizations extends MatrixLocalizations { @override String changedTheGuestAccessRulesTo( - String senderName, String localizedString) => + String senderName, + String localizedString, + ) => '$senderName changed the guest access rules to $localizedString'; @override @@ -70,7 +72,9 @@ class MatrixDefaultLocalizations extends MatrixLocalizations { @override String changedTheHistoryVisibilityTo( - String senderName, String localizedString) => + String senderName, + String localizedString, + ) => '$senderName changed the history visibility to $localizedString'; @override diff --git a/lib/src/utils/matrix_file.dart b/lib/src/utils/matrix_file.dart index f9af3c99b..288eec012 100644 --- a/lib/src/utils/matrix_file.dart +++ b/lib/src/utils/matrix_file.dart @@ -48,11 +48,16 @@ class MatrixFile { /// derivatives the MIME type from the [bytes] and correspondingly creates a /// [MatrixFile], [MatrixImageFile], [MatrixAudioFile] or a [MatrixVideoFile] - factory MatrixFile.fromMimeType( - {required Uint8List bytes, required String name, String? mimeType}) { - final msgType = msgTypeFromMime(mimeType ?? - lookupMimeType(name, headerBytes: bytes) ?? - 'application/octet-stream'); + factory MatrixFile.fromMimeType({ + required Uint8List bytes, + required String name, + String? mimeType, + }) { + final msgType = msgTypeFromMime( + mimeType ?? + lookupMimeType(name, headerBytes: bytes) ?? + 'application/octet-stream', + ); if (msgType == MessageTypes.Image) { return MatrixImageFile(bytes: bytes, name: name, mimeType: mimeType); } @@ -134,8 +139,8 @@ class MatrixImageFile extends MatrixFile { int maxDimension = 1600, String? mimeType, Future Function( - MatrixImageFileResizeArguments)? - customImageResizer, + MatrixImageFileResizeArguments, + )? customImageResizer, @Deprecated('Use [nativeImplementations] instead') ComputeRunner? compute, NativeImplementations nativeImplementations = NativeImplementations.dummy, }) async { @@ -146,9 +151,10 @@ class MatrixImageFile extends MatrixFile { final image = MatrixImageFile(name: name, mimeType: mimeType, bytes: bytes); return await image.generateThumbnail( - dimension: maxDimension, - customImageResizer: customImageResizer, - nativeImplementations: nativeImplementations) ?? + dimension: maxDimension, + customImageResizer: customImageResizer, + nativeImplementations: nativeImplementations, + ) ?? image; } @@ -187,8 +193,8 @@ class MatrixImageFile extends MatrixFile { Future generateThumbnail({ int dimension = Client.defaultThumbnailSize, Future Function( - MatrixImageFileResizeArguments)? - customImageResizer, + MatrixImageFileResizeArguments, + )? customImageResizer, @Deprecated('Use [nativeImplementations] instead') ComputeRunner? compute, NativeImplementations nativeImplementations = NativeImplementations.dummy, }) async { @@ -212,7 +218,9 @@ class MatrixImageFile extends MatrixFile { // we should take the opportunity to update the image dimension setImageSizeIfNull( - width: resizedData.originalWidth, height: resizedData.originalHeight); + width: resizedData.originalWidth, + height: resizedData.originalHeight, + ); // the thumbnail should rather return null than the enshrined image if (resizedData.width > dimension || resizedData.height > dimension) { @@ -233,7 +241,8 @@ class MatrixImageFile extends MatrixFile { /// you would likely want to use [NativeImplementations] and /// [Client.nativeImplementations] instead static MatrixImageFileResizedResponse? calcMetadataImplementation( - Uint8List bytes) { + Uint8List bytes, + ) { final image = decodeImage(bytes); if (image == null) return null; @@ -252,12 +261,15 @@ class MatrixImageFile extends MatrixFile { /// you would likely want to use [NativeImplementations] and /// [Client.nativeImplementations] instead static MatrixImageFileResizedResponse? resizeImplementation( - MatrixImageFileResizeArguments arguments) { + MatrixImageFileResizeArguments arguments, + ) { final image = decodeImage(arguments.bytes); - final resized = copyResize(image!, - height: image.height > image.width ? arguments.maxDimension : null, - width: image.width >= image.height ? arguments.maxDimension : null); + final resized = copyResize( + image!, + height: image.height > image.width ? arguments.maxDimension : null, + width: image.width >= image.height ? arguments.maxDimension : null, + ); final encoded = encodeNamedImage(arguments.fileName, resized); if (encoded == null) return null; @@ -302,7 +314,8 @@ class MatrixImageFileResizedResponse { ) => MatrixImageFileResizedResponse( bytes: Uint8List.fromList( - (json['bytes'] as Iterable).whereType().toList()), + (json['bytes'] as Iterable).whereType().toList(), + ), width: json['width'], height: json['height'], originalHeight: json['originalHeight'], @@ -354,13 +367,14 @@ class MatrixVideoFile extends MatrixFile { final int? height; final int? duration; - MatrixVideoFile( - {required super.bytes, - required super.name, - super.mimeType, - this.width, - this.height, - this.duration}); + MatrixVideoFile({ + required super.bytes, + required super.name, + super.mimeType, + this.width, + this.height, + this.duration, + }); @override String get msgType => 'm.video'; @@ -377,11 +391,12 @@ class MatrixVideoFile extends MatrixFile { class MatrixAudioFile extends MatrixFile { final int? duration; - MatrixAudioFile( - {required super.bytes, - required super.name, - super.mimeType, - this.duration}); + MatrixAudioFile({ + required super.bytes, + required super.name, + super.mimeType, + this.duration, + }); @override String get msgType => 'm.audio'; diff --git a/lib/src/utils/matrix_id_string_extension.dart b/lib/src/utils/matrix_id_string_extension.dart index 530ebfe77..2993ddef1 100644 --- a/lib/src/utils/matrix_id_string_extension.dart +++ b/lib/src/utils/matrix_id_string_extension.dart @@ -82,14 +82,16 @@ extension MatrixIdExtension on String { return uri.replace(pathSegments: identifiers); } else if (toLowerCase().startsWith(matrixToPrefix)) { return Uri.tryParse( - '//${substring(matrixToPrefix.length - 1).replaceAllMapped(RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'), (m) => m[0]!.replaceAllMapped(RegExp(m.group(1) != null ? '' : '[/?]'), (m) => Uri.encodeComponent(m.group(0)!))).replaceAll('#', '%23')}'); + '//${substring(matrixToPrefix.length - 1).replaceAllMapped(RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'), (m) => m[0]!.replaceAllMapped(RegExp(m.group(1) != null ? '' : '[/?]'), (m) => Uri.encodeComponent(m.group(0)!))).replaceAll('#', '%23')}', + ); } else { return Uri( - pathSegments: RegExp(r'/((?:[#!@+][^:]*:)?[^/?]*)(?:\?.*$)?') - .allMatches('/$this') - .map((m) => m[1]!), - query: RegExp(r'(?:/(?:[#!@+][^:]*:)?[^/?]*)*\?(.*$)') - .firstMatch('/$this')?[1]); + pathSegments: RegExp(r'/((?:[#!@+][^:]*:)?[^/?]*)(?:\?.*$)?') + .allMatches('/$this') + .map((m) => m[1]!), + query: RegExp(r'(?:/(?:[#!@+][^:]*:)?[^/?]*)*\?(.*$)') + .firstMatch('/$this')?[1], + ); } } @@ -121,10 +123,11 @@ class MatrixIdentifierStringExtensionResults { final Set via; final String? action; - MatrixIdentifierStringExtensionResults( - {required this.primaryIdentifier, - this.secondaryIdentifier, - this.queryString, - this.via = const {}, - this.action}); + MatrixIdentifierStringExtensionResults({ + required this.primaryIdentifier, + this.secondaryIdentifier, + this.queryString, + this.via = const {}, + this.action, + }); } diff --git a/lib/src/utils/matrix_localizations.dart b/lib/src/utils/matrix_localizations.dart index d76451f48..b36e2ad34 100644 --- a/lib/src/utils/matrix_localizations.dart +++ b/lib/src/utils/matrix_localizations.dart @@ -131,12 +131,16 @@ abstract class MatrixLocalizations { String changedTheGuestAccessRules(String senderName); String changedTheGuestAccessRulesTo( - String senderName, String localizedString); + String senderName, + String localizedString, + ); String changedTheHistoryVisibility(String senderName); String changedTheHistoryVisibilityTo( - String senderName, String localizedString); + String senderName, + String localizedString, + ); String activatedEndToEndEncryption(String senderName); diff --git a/lib/src/utils/native_implementations.dart b/lib/src/utils/native_implementations.dart index ee243ebb3..eab0426db 100644 --- a/lib/src/utils/native_implementations.dart +++ b/lib/src/utils/native_implementations.dart @@ -140,14 +140,17 @@ class NativeImplementationsIsolate extends NativeImplementations { // ignore: deprecated_member_use_from_same_package /// known from [Client.runInBackground] factory NativeImplementationsIsolate.fromRunInBackground( - ComputeRunner runInBackground) { + ComputeRunner runInBackground, + ) { return NativeImplementationsIsolate( computeCallbackFromRunInBackground(runInBackground), ); } Future runInBackground( - FutureOr Function(U arg) function, U arg) async { + FutureOr Function(U arg) function, + U arg, + ) async { final compute = this.compute; return await compute(function, arg); } diff --git a/lib/src/utils/pushrule_evaluator.dart b/lib/src/utils/pushrule_evaluator.dart index a531325fe..f36f560ca 100644 --- a/lib/src/utils/pushrule_evaluator.dart +++ b/lib/src/utils/pushrule_evaluator.dart @@ -193,8 +193,12 @@ class _OptimizedRules { actions = EvaluatedPushRuleAction.fromActions(rule.actions); } - EvaluatedPushRuleAction? match(Map event, String? displayName, - int memberCount, Room room) { + EvaluatedPushRuleAction? match( + Map event, + String? displayName, + int memberCount, + Room room, + ) { if (patterns.any((pat) => !pat.match(event))) { return null; } @@ -207,8 +211,10 @@ class _OptimizedRules { return null; } - final regex = RegExp('(^|\\W)${RegExp.escape(displayName)}(\$|\\W)', - caseSensitive: false); + final regex = RegExp( + '(^|\\W)${RegExp.escape(displayName)}(\$|\\W)', + caseSensitive: false, + ); if (!regex.hasMatch(body)) { return null; } @@ -217,9 +223,12 @@ class _OptimizedRules { if (notificationPermissions.isNotEmpty) { final sender = event.tryGet('sender'); if (sender == null || - notificationPermissions.any((notificationType) => - !room.canSendNotification(sender, - notificationType: notificationType))) { + notificationPermissions.any( + (notificationType) => !room.canSendNotification( + sender, + notificationType: notificationType, + ), + )) { return null; } } @@ -258,7 +267,10 @@ class PushruleEvaluator { actions: c.actions, conditions: [ PushCondition( - kind: 'event_match', key: 'content.body', pattern: c.pattern) + kind: 'event_match', + key: 'content.body', + pattern: c.pattern, + ), ], ruleId: c.ruleId, default$: c.default$, @@ -284,7 +296,10 @@ class PushruleEvaluator { } Map _flattenJson( - Map obj, Map flattened, String prefix) { + Map obj, + Map flattened, + String prefix, + ) { for (final entry in obj.entries) { final key = prefix == '' ? entry.key : '$prefix.${entry.key}'; final value = entry.value; diff --git a/lib/src/utils/sync_update_item_count.dart b/lib/src/utils/sync_update_item_count.dart index b980faed3..d4da939f1 100644 --- a/lib/src/utils/sync_update_item_count.dart +++ b/lib/src/utils/sync_update_item_count.dart @@ -16,26 +16,30 @@ extension SyncUpdateItemCount on SyncUpdate { int get _joinRoomsItemCount => rooms?.join?.values.fold( - 0, - (prev, room) => - prev + - (room.accountData?.length ?? 0) + - (room.state?.length ?? 0) + - (room.timeline?.events?.length ?? 0)) ?? + 0, + (prev, room) => + prev + + (room.accountData?.length ?? 0) + + (room.state?.length ?? 0) + + (room.timeline?.events?.length ?? 0), + ) ?? 0; int get _inviteRoomsItemCount => rooms?.invite?.values.fold( - 0, (prev, room) => prev + (room.inviteState?.length ?? 0)) ?? + 0, + (prev, room) => prev + (room.inviteState?.length ?? 0), + ) ?? 0; int get _leaveRoomsItemCount => rooms?.leave?.values.fold( - 0, - (prev, room) => - prev + - (room.accountData?.length ?? 0) + - (room.state?.length ?? 0) + - (room.timeline?.events?.length ?? 0)) ?? + 0, + (prev, room) => + prev + + (room.accountData?.length ?? 0) + + (room.state?.length ?? 0) + + (room.timeline?.events?.length ?? 0), + ) ?? 0; } diff --git a/lib/src/utils/to_device_event.dart b/lib/src/utils/to_device_event.dart index acc936d1b..aa8b65dc1 100644 --- a/lib/src/utils/to_device_event.dart +++ b/lib/src/utils/to_device_event.dart @@ -34,7 +34,10 @@ class ToDeviceEvent extends BasicEventWithSender { factory ToDeviceEvent.fromJson(Map json) { final event = BasicEventWithSender.fromJson(json); return ToDeviceEvent( - sender: event.senderId, type: event.type, content: event.content); + sender: event.senderId, + type: event.type, + content: event.content, + ); } } diff --git a/lib/src/utils/uia_request.dart b/lib/src/utils/uia_request.dart index 9cad2e634..018ed400b 100644 --- a/lib/src/utils/uia_request.dart +++ b/lib/src/utils/uia_request.dart @@ -100,7 +100,9 @@ class UiaRequest { } Set getNextStages( - List flows, List completed) { + List flows, + List completed, + ) { final nextStages = {}; for (final flow in flows) { // check the flow starts with the completed stages diff --git a/lib/src/utils/uri_extension.dart b/lib/src/utils/uri_extension.dart index 7ac92c3e1..95b9fa601 100644 --- a/lib/src/utils/uri_extension.dart +++ b/lib/src/utils/uri_extension.dart @@ -59,11 +59,13 @@ extension MxcUriExtension on Uri { /// /// Important! To use this link you have to set a http header like this: /// `headers: {"authorization": "Bearer ${client.accessToken}"}` - Future getThumbnailUri(Client client, - {num? width, - num? height, - ThumbnailMethod? method = ThumbnailMethod.crop, - bool? animated = false}) async { + Future getThumbnailUri( + Client client, { + num? width, + num? height, + ThumbnailMethod? method = ThumbnailMethod.crop, + bool? animated = false, + }) async { if (!isScheme('mxc')) return Uri(); final homeserver = client.homeserver; if (homeserver == null) { @@ -97,7 +99,8 @@ extension MxcUriExtension on Uri { Uri getDownloadLink(Client matrix) => isScheme('mxc') ? matrix.homeserver != null ? matrix.homeserver?.resolve( - '_matrix/media/v3/download/$host${hasPort ? ':$port' : ''}$path') ?? + '_matrix/media/v3/download/$host${hasPort ? ':$port' : ''}$path', + ) ?? Uri() : Uri() : Uri(); @@ -108,11 +111,13 @@ extension MxcUriExtension on Uri { /// If `animated` (default false) is set to true, an animated thumbnail is requested /// as per MSC2705. Thumbnails only animate if the media repository supports that. @Deprecated('Use `getThumbnailUri()` instead') - Uri getThumbnail(Client matrix, - {num? width, - num? height, - ThumbnailMethod? method = ThumbnailMethod.crop, - bool? animated = false}) { + Uri getThumbnail( + Client matrix, { + num? width, + num? height, + ThumbnailMethod? method = ThumbnailMethod.crop, + bool? animated = false, + }) { if (!isScheme('mxc')) return Uri(); final homeserver = matrix.homeserver; if (homeserver == null) { diff --git a/lib/src/utils/web_worker/native_implementations_web_worker.dart b/lib/src/utils/web_worker/native_implementations_web_worker.dart index 8672d43dc..6cd71a69d 100644 --- a/lib/src/utils/web_worker/native_implementations_web_worker.dart +++ b/lib/src/utils/web_worker/native_implementations_web_worker.dart @@ -71,7 +71,10 @@ class NativeImplementationsWebWorker extends NativeImplementations { } catch (e, s) { if (!retryInDummy) { Logs().e( - 'Web worker computation error. Ignoring and returning null', e, s); + 'Web worker computation error. Ignoring and returning null', + e, + s, + ); return null; } Logs().e('Web worker computation error. Fallback to main thread', e, s); @@ -94,7 +97,10 @@ class NativeImplementationsWebWorker extends NativeImplementations { } catch (e, s) { if (!retryInDummy) { Logs().e( - 'Web worker computation error. Ignoring and returning null', e, s); + 'Web worker computation error. Ignoring and returning null', + e, + s, + ); return null; } Logs().e('Web worker computation error. Fallback to main thread', e, s); @@ -149,4 +155,5 @@ class WebWorkerError extends Error { /// converts a stringifyed, obfuscated [StackTrace] into a [StackTrace] typedef WebWorkerStackTraceCallback = FutureOr Function( - String obfuscatedStackTrace); + String obfuscatedStackTrace, +); diff --git a/lib/src/utils/web_worker/native_implementations_web_worker_stub.dart b/lib/src/utils/web_worker/native_implementations_web_worker_stub.dart index 88edeb2cd..2fb87c1af 100644 --- a/lib/src/utils/web_worker/native_implementations_web_worker_stub.dart +++ b/lib/src/utils/web_worker/native_implementations_web_worker_stub.dart @@ -33,4 +33,5 @@ class WebWorkerError extends Error { /// converts a stringifyed, obfuscated [StackTrace] into a [StackTrace] typedef WebWorkerStackTraceCallback = FutureOr Function( - String obfuscatedStackTrace); + String obfuscatedStackTrace, +); diff --git a/lib/src/utils/web_worker/web_worker.dart b/lib/src/utils/web_worker/web_worker.dart index d9e579108..89180856a 100644 --- a/lib/src/utils/web_worker/web_worker.dart +++ b/lib/src/utils/web_worker/web_worker.dart @@ -46,14 +46,18 @@ Future startWebWorker() async { switch (operation.name) { case WebWorkerOperations.shrinkImage: final result = MatrixImageFile.resizeImplementation( - MatrixImageFileResizeArguments.fromJson( - Map.from(operation.data as Map))); + MatrixImageFileResizeArguments.fromJson( + Map.from(operation.data as Map), + ), + ); sendResponse(operation.label as double, result?.toJson()); break; case WebWorkerOperations.calcImageMetadata: final result = MatrixImageFile.calcMetadataImplementation( - Uint8List.fromList( - (operation.data as JsArray).whereType().toList())); + Uint8List.fromList( + (operation.data as JsArray).whereType().toList(), + ), + ); sendResponse(operation.label as double, result?.toJson()); break; default: diff --git a/lib/src/voip/backend/call_backend_model.dart b/lib/src/voip/backend/call_backend_model.dart index 40607f77c..e0873a740 100644 --- a/lib/src/voip/backend/call_backend_model.dart +++ b/lib/src/voip/backend/call_backend_model.dart @@ -24,7 +24,8 @@ abstract class CallBackend { ); } else { throw MatrixSDKVoipException( - 'Invalid type: $type in CallBackend.fromJson'); + 'Invalid type: $type in CallBackend.fromJson', + ); } } diff --git a/lib/src/voip/backend/livekit_backend.dart b/lib/src/voip/backend/livekit_backend.dart index 2ace7e272..e9cdbea60 100644 --- a/lib/src/voip/backend/livekit_backend.dart +++ b/lib/src/voip/backend/livekit_backend.dart @@ -75,7 +75,9 @@ class LiveKitBackend extends CallBackend { /// /// also does the sending for you Future _makeNewSenderKey( - GroupCallSession groupCall, bool delayBeforeUsingKeyOurself) async { + GroupCallSession groupCall, + bool delayBeforeUsingKeyOurself, + ) async { final key = secureRandomBytes(32); final keyIndex = _getNewEncryptionKeyIndex(); Logs().i('[VOIP E2EE] Generated new key $key at index $keyIndex'); @@ -99,7 +101,8 @@ class LiveKitBackend extends CallBackend { if (keyProvider == null) { throw MatrixSDKVoipException( - '_ratchetKey called but KeyProvider was null'); + '_ratchetKey called but KeyProvider was null', + ); } final myKeys = _encryptionKeysMap[groupCall.localParticipant]; @@ -120,7 +123,8 @@ class LiveKitBackend extends CallBackend { } Logs().i( - '[VOIP E2EE] Ratched latest key to $ratchetedKey at idx $latestLocalKeyIndex'); + '[VOIP E2EE] Ratched latest key to $ratchetedKey at idx $latestLocalKeyIndex', + ); await _setEncryptionKey( groupCall, @@ -179,9 +183,13 @@ class LiveKitBackend extends CallBackend { // stil decrypt everything final useKeyTimeout = Future.delayed(useKeyDelay, () async { Logs().i( - '[VOIP E2EE] setting key changed event for ${participant.id} idx $encryptionKeyIndex key $encryptionKeyBin'); + '[VOIP E2EE] setting key changed event for ${participant.id} idx $encryptionKeyIndex key $encryptionKeyBin', + ); await groupCall.voip.delegate.keyProvider?.onSetEncryptionKey( - participant, encryptionKeyBin, encryptionKeyIndex); + participant, + encryptionKeyBin, + encryptionKeyIndex, + ); if (participant.isLocal) { _currentLocalKeyIndex = encryptionKeyIndex; } @@ -189,9 +197,13 @@ class LiveKitBackend extends CallBackend { _setNewKeyTimeouts.add(useKeyTimeout); } else { Logs().i( - '[VOIP E2EE] setting key changed event for ${participant.id} idx $encryptionKeyIndex key $encryptionKeyBin'); + '[VOIP E2EE] setting key changed event for ${participant.id} idx $encryptionKeyIndex key $encryptionKeyBin', + ); await groupCall.voip.delegate.keyProvider?.onSetEncryptionKey( - participant, encryptionKeyBin, encryptionKeyIndex); + participant, + encryptionKeyBin, + encryptionKeyIndex, + ); if (participant.isLocal) { _currentLocalKeyIndex = encryptionKeyIndex; } @@ -214,7 +226,8 @@ class LiveKitBackend extends CallBackend { if (myKeys == null || myLatestKey == null) { Logs().w( - '[VOIP E2EE] _sendEncryptionKeysEvent Tried to send encryption keys event but no keys found!'); + '[VOIP E2EE] _sendEncryptionKeysEvent Tried to send encryption keys event but no keys found!', + ); await _makeNewSenderKey(groupCall, false); await _sendEncryptionKeysEvent( groupCall, @@ -261,7 +274,8 @@ class LiveKitBackend extends CallBackend { ) async { if (remoteParticipants.isEmpty) return; Logs().v( - '[VOIP] _sendToDeviceEvent: sending ${data.toString()} to ${remoteParticipants.map((e) => e.id)} '); + '[VOIP] _sendToDeviceEvent: sending ${data.toString()} to ${remoteParticipants.map((e) => e.id)} ', + ); final txid = VoIP.customTxid ?? groupCall.client.generateUniqueTransactionId(); final mustEncrypt = @@ -284,7 +298,7 @@ class LiveKitBackend extends CallBackend { } } else { unencryptedDataToSend.addAll({ - participant.userId: {participant.deviceId!: data} + participant.userId: {participant.deviceId!: data}, }); } } @@ -352,11 +366,13 @@ class LiveKitBackend extends CallBackend { if (keyContent.keys.isEmpty) { Logs().w( - '[VOIP E2EE] Received m.call.encryption_keys where keys is empty: callId=$callId'); + '[VOIP E2EE] Received m.call.encryption_keys where keys is empty: callId=$callId', + ); return; } else { Logs().i( - '[VOIP E2EE]: onCallEncryption, got keys from ${p.id} ${keyContent.toJson()}'); + '[VOIP E2EE]: onCallEncryption, got keys from ${p.id} ${keyContent.toJson()}', + ); } for (final key in keyContent.keys) { @@ -400,7 +416,8 @@ class LiveKitBackend extends CallBackend { ) .isNotEmpty) { Logs().d( - '[VOIP] onCallEncryptionKeyRequest: request checks out, sending key on index: $latestLocalKeyIndex to $userId:$deviceId'); + '[VOIP] onCallEncryptionKeyRequest: request checks out, sending key on index: $latestLocalKeyIndex to $userId:$deviceId', + ); await _sendEncryptionKeysEvent( groupCall, _latestLocalKeyIndex, @@ -409,7 +426,7 @@ class LiveKitBackend extends CallBackend { groupCall.voip, userId: userId, deviceId: deviceId, - ) + ), ], ); } @@ -470,8 +487,10 @@ class LiveKitBackend extends CallBackend { /// get everything else from your livekit sdk in your client @override - Future initLocalStream(GroupCallSession groupCall, - {WrappedMediaStream? stream}) async { + Future initLocalStream( + GroupCallSession groupCall, { + WrappedMediaStream? stream, + }) async { return null; } @@ -506,25 +525,35 @@ class LiveKitBackend extends CallBackend { @override Future setDeviceMuted( - GroupCallSession groupCall, bool muted, MediaInputKind kind) async { + GroupCallSession groupCall, + bool muted, + MediaInputKind kind, + ) async { return; } @override - Future setScreensharingEnabled(GroupCallSession groupCall, bool enabled, - String desktopCapturerSourceId) async { + Future setScreensharingEnabled( + GroupCallSession groupCall, + bool enabled, + String desktopCapturerSourceId, + ) async { return; } @override - Future setupP2PCallWithNewMember(GroupCallSession groupCall, - CallParticipant rp, CallMembership mem) async { + Future setupP2PCallWithNewMember( + GroupCallSession groupCall, + CallParticipant rp, + CallMembership mem, + ) async { return; } @override Future setupP2PCallsWithExistingMembers( - GroupCallSession groupCall) async { + GroupCallSession groupCall, + ) async { return; } diff --git a/lib/src/voip/backend/mesh_backend.dart b/lib/src/voip/backend/mesh_backend.dart index c490dd4a7..0e0245b4e 100644 --- a/lib/src/voip/backend/mesh_backend.dart +++ b/lib/src/voip/backend/mesh_backend.dart @@ -61,7 +61,9 @@ class MeshBackend extends CallBackend { } Future _getUserMedia( - GroupCallSession groupCall, CallType type) async { + GroupCallSession groupCall, + CallType type, + ) async { final mediaConstraints = { 'audio': UserMediaConstraints.micMediaConstraints, 'video': type == CallType.kVideo @@ -92,15 +94,19 @@ class MeshBackend extends CallBackend { } CallSession? _getCallForParticipant( - GroupCallSession groupCall, CallParticipant participant) { - return _callSessions.singleWhereOrNull((call) => - call.groupCallId == groupCall.groupCallId && - CallParticipant( - groupCall.voip, - userId: call.remoteUserId!, - deviceId: call.remoteDeviceId, - ) == - participant); + GroupCallSession groupCall, + CallParticipant participant, + ) { + return _callSessions.singleWhereOrNull( + (call) => + call.groupCallId == groupCall.groupCallId && + CallParticipant( + groupCall.voip, + userId: call.remoteUserId!, + deviceId: call.remoteDeviceId, + ) == + participant, + ); } Future _addCall(GroupCallSession groupCall, CallSession call) async { @@ -113,12 +119,15 @@ class MeshBackend extends CallBackend { Future _initCall(GroupCallSession groupCall, CallSession call) async { if (call.remoteUserId == null) { throw MatrixSDKVoipException( - 'Cannot init call without proper invitee user and device Id'); + 'Cannot init call without proper invitee user and device Id', + ); } - call.onCallStateChanged.stream.listen(((event) async { - await _onCallStateChanged(call, event); - })); + call.onCallStateChanged.stream.listen( + ((event) async { + await _onCallStateChanged(call, event); + }), + ); call.onCallReplaced.stream.listen((CallSession newCall) async { await _replaceCall(groupCall, call, newCall); @@ -168,8 +177,11 @@ class MeshBackend extends CallBackend { } /// Removes a peer call from group calls. - Future _removeCall(GroupCallSession groupCall, CallSession call, - CallErrorCode hangupReason) async { + Future _removeCall( + GroupCallSession groupCall, + CallSession call, + CallErrorCode hangupReason, + ) async { await _disposeCall(groupCall, call, hangupReason); _callSessions.removeWhere((element) => call.callId == element.callId); @@ -177,11 +189,15 @@ class MeshBackend extends CallBackend { groupCall.onGroupCallEvent.add(GroupCallStateChange.callsChanged); } - Future _disposeCall(GroupCallSession groupCall, CallSession call, - CallErrorCode hangupReason) async { + Future _disposeCall( + GroupCallSession groupCall, + CallSession call, + CallErrorCode hangupReason, + ) async { if (call.remoteUserId == null) { throw MatrixSDKVoipException( - 'Cannot init call without proper invitee user and device Id'); + 'Cannot init call without proper invitee user and device Id', + ); } if (call.hangupReason == CallErrorCode.replaced) { @@ -220,10 +236,13 @@ class MeshBackend extends CallBackend { } Future _onStreamsChanged( - GroupCallSession groupCall, CallSession call) async { + GroupCallSession groupCall, + CallSession call, + ) async { if (call.remoteUserId == null) { throw MatrixSDKVoipException( - 'Cannot init call without proper invitee user and device Id'); + 'Cannot init call without proper invitee user and device Id', + ); } final currentUserMediaStream = _getUserMediaStreamByParticipantId( @@ -243,19 +262,23 @@ class MeshBackend extends CallBackend { } else if (currentUserMediaStream != null && remoteUsermediaStream != null) { await _replaceUserMediaStream( - groupCall, currentUserMediaStream, remoteUsermediaStream); + groupCall, + currentUserMediaStream, + remoteUsermediaStream, + ); } else if (currentUserMediaStream != null && remoteUsermediaStream == null) { await _removeUserMediaStream(groupCall, currentUserMediaStream); } } - final currentScreenshareStream = - _getScreenshareStreamByParticipantId(CallParticipant( - groupCall.voip, - userId: call.remoteUserId!, - deviceId: call.remoteDeviceId, - ).id); + final currentScreenshareStream = _getScreenshareStreamByParticipantId( + CallParticipant( + groupCall.voip, + userId: call.remoteUserId!, + deviceId: call.remoteDeviceId, + ).id, + ); final remoteScreensharingStream = call.remoteScreenSharingStream; final remoteScreenshareStreamChanged = remoteScreensharingStream != currentScreenshareStream; @@ -267,7 +290,10 @@ class MeshBackend extends CallBackend { } else if (currentScreenshareStream != null && remoteScreensharingStream != null) { await _replaceScreenshareStream( - groupCall, currentScreenshareStream, remoteScreensharingStream); + groupCall, + currentScreenshareStream, + remoteScreensharingStream, + ); } else if (currentScreenshareStream != null && remoteScreensharingStream == null) { await _removeScreenshareStream(groupCall, currentScreenshareStream); @@ -302,9 +328,11 @@ class MeshBackend extends CallBackend { // https://www.w3.org/TR/webrtc-stats/#summary final otherPartyAudioLevel = statsReport - .singleWhereOrNull((element) => - element.type == 'inbound-rtp' && - element.values['kind'] == 'audio') + .singleWhereOrNull( + (element) => + element.type == 'inbound-rtp' && + element.values['kind'] == 'audio', + ) ?.values['audioLevel']; if (otherPartyAudioLevel != null) { _audioLevelsMap[stream.participant] = otherPartyAudioLevel; @@ -313,9 +341,11 @@ class MeshBackend extends CallBackend { // https://www.w3.org/TR/webrtc-stats/#dom-rtcstatstype-media-source // firefox does not seem to have this though. Works on chrome and android final ownAudioLevel = statsReport - .singleWhereOrNull((element) => - element.type == 'media-source' && - element.values['kind'] == 'audio') + .singleWhereOrNull( + (element) => + element.type == 'media-source' && + element.values['kind'] == 'audio', + ) ?.values['audioLevel']; if (groupCall.localParticipant != null && ownAudioLevel != null && @@ -345,7 +375,8 @@ class MeshBackend extends CallBackend { } WrappedMediaStream? _getScreenshareStreamByParticipantId( - String participantId) { + String participantId, + ) { final stream = _screenshareStreams .where((stream) => stream.participant.id == participantId); if (stream.isNotEmpty) { @@ -355,7 +386,9 @@ class MeshBackend extends CallBackend { } void _addScreenshareStream( - GroupCallSession groupCall, WrappedMediaStream stream) { + GroupCallSession groupCall, + WrappedMediaStream stream, + ) { _screenshareStreams.add(stream); onStreamAdd.add(stream); groupCall.onGroupCallEvent @@ -368,11 +401,13 @@ class MeshBackend extends CallBackend { WrappedMediaStream replacementStream, ) async { final streamIndex = _screenshareStreams.indexWhere( - (stream) => stream.participant.id == existingStream.participant.id); + (stream) => stream.participant.id == existingStream.participant.id, + ); if (streamIndex == -1) { throw MatrixSDKVoipException( - 'Couldn\'t find screenshare stream to replace'); + 'Couldn\'t find screenshare stream to replace', + ); } _screenshareStreams.replaceRange(streamIndex, 1, [replacementStream]); @@ -391,11 +426,13 @@ class MeshBackend extends CallBackend { if (streamIndex == -1) { throw MatrixSDKVoipException( - 'Couldn\'t find screenshare stream to remove'); + 'Couldn\'t find screenshare stream to remove', + ); } _screenshareStreams.removeWhere( - (element) => element.participant.id == stream.participant.id); + (element) => element.participant.id == stream.participant.id, + ); onStreamRemoved.add(stream); @@ -449,11 +486,13 @@ class MeshBackend extends CallBackend { WrappedMediaStream replacementStream, ) async { final streamIndex = _userMediaStreams.indexWhere( - (stream) => stream.participant.id == existingStream.participant.id); + (stream) => stream.participant.id == existingStream.participant.id, + ); if (streamIndex == -1) { throw MatrixSDKVoipException( - 'Couldn\'t find user media stream to replace'); + 'Couldn\'t find user media stream to replace', + ); } _userMediaStreams.replaceRange(streamIndex, 1, [replacementStream]); @@ -468,15 +507,18 @@ class MeshBackend extends CallBackend { WrappedMediaStream stream, ) async { final streamIndex = _userMediaStreams.indexWhere( - (element) => element.participant.id == stream.participant.id); + (element) => element.participant.id == stream.participant.id, + ); if (streamIndex == -1) { throw MatrixSDKVoipException( - 'Couldn\'t find user media stream to remove'); + 'Couldn\'t find user media stream to remove', + ); } _userMediaStreams.removeWhere( - (element) => element.participant.id == stream.participant.id); + (element) => element.participant.id == stream.participant.id, + ); _audioLevelsMap.remove(stream.participant); onStreamRemoved.add(stream); @@ -527,11 +569,14 @@ class MeshBackend extends CallBackend { /// This allows you to configure the camera before joining the call without /// having to reopen the stream and possibly losing settings. @override - Future initLocalStream(GroupCallSession groupCall, - {WrappedMediaStream? stream}) async { + Future initLocalStream( + GroupCallSession groupCall, { + WrappedMediaStream? stream, + }) async { if (groupCall.state != GroupCallState.localCallFeedUninitialized) { throw MatrixSDKVoipException( - 'Cannot initialize local call feed in the ${groupCall.state} state.'); + 'Cannot initialize local call feed in the ${groupCall.state} state.', + ); } groupCall.setState(GroupCallState.initializingLocalCallFeed); @@ -575,7 +620,10 @@ class MeshBackend extends CallBackend { @override Future setDeviceMuted( - GroupCallSession groupCall, bool muted, MediaInputKind kind) async { + GroupCallSession groupCall, + bool muted, + MediaInputKind kind, + ) async { if (!await hasMediaDevice(groupCall.voip.delegate, kind)) { return; } @@ -585,7 +633,9 @@ class MeshBackend extends CallBackend { case MediaInputKind.audioinput: localUserMediaStream!.setAudioMuted(muted); setTracksEnabled( - localUserMediaStream!.stream!.getAudioTracks(), !muted); + localUserMediaStream!.stream!.getAudioTracks(), + !muted, + ); for (final call in _callSessions) { await call.setMicrophoneMuted(muted); } @@ -593,7 +643,9 @@ class MeshBackend extends CallBackend { case MediaInputKind.videoinput: localUserMediaStream!.setVideoMuted(muted); setTracksEnabled( - localUserMediaStream!.stream!.getVideoTracks(), !muted); + localUserMediaStream!.stream!.getVideoTracks(), + !muted, + ); for (final call in _callSessions) { await call.setLocalVideoMuted(muted); } @@ -607,7 +659,9 @@ class MeshBackend extends CallBackend { } Future _onIncomingCall( - GroupCallSession groupCall, CallSession newCall) async { + GroupCallSession groupCall, + CallSession newCall, + ) async { // The incoming calls may be for another room, which we will ignore. if (newCall.room.id != groupCall.room.id) { return; @@ -621,7 +675,8 @@ class MeshBackend extends CallBackend { if (newCall.groupCallId == null || newCall.groupCallId != groupCall.groupCallId) { Logs().v( - 'Incoming call with groupCallId ${newCall.groupCallId} ignored because it doesn\'t match the current group call'); + 'Incoming call with groupCallId ${newCall.groupCallId} ignored because it doesn\'t match the current group call', + ); await newCall.reject(); return; } @@ -640,7 +695,8 @@ class MeshBackend extends CallBackend { } Logs().v( - 'GroupCallSession: incoming call from: ${newCall.remoteUserId}${newCall.remoteDeviceId}${newCall.remotePartyId}'); + 'GroupCallSession: incoming call from: ${newCall.remoteUserId}${newCall.remoteDeviceId}${newCall.remotePartyId}', + ); // Check if the user calling has an existing call and use this call instead. if (existingCall != null) { @@ -674,7 +730,8 @@ class MeshBackend extends CallBackend { }; } Logs().v( - 'Screensharing permissions granted. Setting screensharing enabled on all calls'); + 'Screensharing permissions granted. Setting screensharing enabled on all calls', + ); _localScreenshareStream = WrappedMediaStream( stream: stream, participant: groupCall.localParticipant!, @@ -693,8 +750,9 @@ class MeshBackend extends CallBackend { .add(GroupCallStateChange.localScreenshareStateChanged); for (final call in _callSessions) { await call.addLocalStream( - await localScreenshareStream!.stream!.clone(), - localScreenshareStream!.purpose); + await localScreenshareStream!.stream!.clone(), + localScreenshareStream!.purpose, + ); } await groupCall.sendMemberStateEvent(); @@ -766,7 +824,8 @@ class MeshBackend extends CallBackend { @override Future setupP2PCallsWithExistingMembers( - GroupCallSession groupCall) async { + GroupCallSession groupCall, + ) async { for (final call in _callSessions) { await _onIncomingCall(groupCall, call); } @@ -790,7 +849,8 @@ class MeshBackend extends CallBackend { await existingCall.hangup(reason: CallErrorCode.unknownError); } else { Logs().e( - '[VOIP] onMemberStateChanged Not updating _participants list, already have a ongoing call with ${rp.id}'); + '[VOIP] onMemberStateChanged Not updating _participants list, already have a ongoing call with ${rp.id}', + ); return; } } @@ -824,10 +884,14 @@ class MeshBackend extends CallBackend { // party id set to when answered newCall.remoteSessionId = mem.membershipId; - await newCall.placeCallWithStreams(_getLocalStreams(), - requestScreenSharing: mem.feeds?.any((element) => - element['purpose'] == SDPStreamMetadataPurpose.Screenshare) ?? - false); + await newCall.placeCallWithStreams( + _getLocalStreams(), + requestScreenSharing: mem.feeds?.any( + (element) => + element['purpose'] == SDPStreamMetadataPurpose.Screenshare, + ) ?? + false, + ); await _addCall(groupCall, newCall); } @@ -835,9 +899,11 @@ class MeshBackend extends CallBackend { @override List>? getCurrentFeeds() { return _getLocalStreams() - .map((feed) => ({ - 'purpose': feed.purpose, - })) + .map( + (feed) => ({ + 'purpose': feed.purpose, + }), + ) .toList(); } @@ -849,32 +915,46 @@ class MeshBackend extends CallBackend { /// get everything is livekit specific mesh calls shouldn't be affected by these @override - Future onCallEncryption(GroupCallSession groupCall, String userId, - String deviceId, Map content) async { + Future onCallEncryption( + GroupCallSession groupCall, + String userId, + String deviceId, + Map content, + ) async { return; } @override - Future onCallEncryptionKeyRequest(GroupCallSession groupCall, - String userId, String deviceId, Map content) async { + Future onCallEncryptionKeyRequest( + GroupCallSession groupCall, + String userId, + String deviceId, + Map content, + ) async { return; } @override Future onLeftParticipant( - GroupCallSession groupCall, List anyLeft) async { + GroupCallSession groupCall, + List anyLeft, + ) async { return; } @override Future onNewParticipant( - GroupCallSession groupCall, List anyJoined) async { + GroupCallSession groupCall, + List anyJoined, + ) async { return; } @override - Future requestEncrytionKey(GroupCallSession groupCall, - List remoteParticipants) async { + Future requestEncrytionKey( + GroupCallSession groupCall, + List remoteParticipants, + ) async { return; } diff --git a/lib/src/voip/call_session.dart b/lib/src/voip/call_session.dart index bab1afceb..852dac8d6 100644 --- a/lib/src/voip/call_session.dart +++ b/lib/src/voip/call_session.dart @@ -147,7 +147,8 @@ class CallSession { WrappedMediaStream? get localUserMediaStream { final stream = getLocalStreams.where( - (element) => element.purpose == SDPStreamMetadataPurpose.Usermedia); + (element) => element.purpose == SDPStreamMetadataPurpose.Usermedia, + ); if (stream.isNotEmpty) { return stream.first; } @@ -156,7 +157,8 @@ class CallSession { WrappedMediaStream? get localScreenSharingStream { final stream = getLocalStreams.where( - (element) => element.purpose == SDPStreamMetadataPurpose.Screenshare); + (element) => element.purpose == SDPStreamMetadataPurpose.Screenshare, + ); if (stream.isNotEmpty) { return stream.first; } @@ -165,7 +167,8 @@ class CallSession { WrappedMediaStream? get remoteUserMediaStream { final stream = getRemoteStreams.where( - (element) => element.purpose == SDPStreamMetadataPurpose.Usermedia); + (element) => element.purpose == SDPStreamMetadataPurpose.Usermedia, + ); if (stream.isNotEmpty) { return stream.first; } @@ -174,7 +177,8 @@ class CallSession { WrappedMediaStream? get remoteScreenSharingStream { final stream = getRemoteStreams.where( - (element) => element.purpose == SDPStreamMetadataPurpose.Screenshare); + (element) => element.purpose == SDPStreamMetadataPurpose.Screenshare, + ); if (stream.isNotEmpty) { return stream.first; } @@ -190,8 +194,10 @@ class CallSession { // check if we have a video track locally and have transceivers setup correctly. return localUserMediaVideoTrack != null && - transceivers.singleWhereOrNull((transceiver) => - transceiver.sender.track?.id == localUserMediaVideoTrack.id) != + transceivers.singleWhereOrNull( + (transceiver) => + transceiver.sender.track?.id == localUserMediaVideoTrack.id, + ) != null; } @@ -209,8 +215,13 @@ class CallSession { } // incoming call - Future initWithInvite(CallType type, RTCSessionDescription offer, - SDPStreamMetadata? metadata, int lifetime, bool isGroupCall) async { + Future initWithInvite( + CallType type, + RTCSessionDescription offer, + SDPStreamMetadata? metadata, + int lifetime, + bool isGroupCall, + ) async { if (!isGroupCall) { // glare fixes final prevCallId = voip.incomingCallRoomId[room.id]; @@ -223,13 +234,15 @@ class CallSession { Logs().d('[glare] invite or answer sent, lex compare now'); if (callId.compareTo(prevCall.callId) > 0) { Logs().d( - '[glare] new call $callId needs to be canceled because the older one ${prevCall.callId} has a smaller lex'); + '[glare] new call $callId needs to be canceled because the older one ${prevCall.callId} has a smaller lex', + ); await hangup(reason: CallErrorCode.unknownError); voip.currentCID = VoipId(roomId: room.id, callId: prevCall.callId); } else { Logs().d( - '[glare] nice, lex of newer call $callId is smaller auto accept this here'); + '[glare] nice, lex of newer call $callId is smaller auto accept this here', + ); /// These fixes do not work all the time because sometimes the code /// is at an unrecoverable stage (invite already sent when we were @@ -240,7 +253,8 @@ class CallSession { } } else { Logs().d( - '[glare] ${prevCall.callId} was still preparing prev call, nvm now cancel it'); + '[glare] ${prevCall.callId} was still preparing prev call, nvm now cancel it', + ); await prevCall.hangup(reason: CallErrorCode.unknownError); } } @@ -308,13 +322,21 @@ class CallSession { final metadata = SDPStreamMetadata({ localUserMediaStream!.stream!.id: SDPStreamPurpose( - purpose: SDPStreamMetadataPurpose.Usermedia, - audio_muted: localUserMediaStream!.stream!.getAudioTracks().isEmpty, - video_muted: localUserMediaStream!.stream!.getVideoTracks().isEmpty) + purpose: SDPStreamMetadataPurpose.Usermedia, + audio_muted: localUserMediaStream!.stream!.getAudioTracks().isEmpty, + video_muted: localUserMediaStream!.stream!.getVideoTracks().isEmpty, + ), }); - final res = await sendAnswerCall(room, callId, answer.sdp!, localPartyId, - type: answer.type!, capabilities: callCapabilities, metadata: metadata); + final res = await sendAnswerCall( + room, + callId, + answer.sdp!, + localPartyId, + type: answer.type!, + capabilities: callCapabilities, + metadata: metadata, + ); Logs().v('[VOIP] answer res => $res'); } @@ -360,9 +382,9 @@ class CallSession { if (requestScreenSharing) { await pc!.addTransceiver( - kind: RTCRtpMediaType.RTCRtpMediaTypeVideo, - init: - RTCRtpTransceiverInit(direction: TransceiverDirection.RecvOnly)); + kind: RTCRtpMediaType.RTCRtpMediaTypeVideo, + init: RTCRtpTransceiverInit(direction: TransceiverDirection.RecvOnly), + ); } setCallState(CallState.kCreateOffer); @@ -372,7 +394,9 @@ class CallSession { } Future onAnswerReceived( - RTCSessionDescription answer, SDPStreamMetadata? metadata) async { + RTCSessionDescription answer, + SDPStreamMetadata? metadata, + ) async { if (metadata != null) { _updateRemoteSDPStreamMetadata(metadata); } @@ -387,12 +411,18 @@ class CallSession { if (remotePartyId != null) { /// Send select_answer event. await sendSelectCallAnswer( - opts.room, callId, localPartyId, remotePartyId!); + opts.room, + callId, + localPartyId, + remotePartyId!, + ); } } Future onNegotiateReceived( - SDPStreamMetadata? metadata, RTCSessionDescription description) async { + SDPStreamMetadata? metadata, + RTCSessionDescription description, + ) async { final polite = direction == CallDirection.kIncoming; // Here we follow the perfect negotiation logic from @@ -425,12 +455,13 @@ class CallSession { } await sendCallNegotiate( - room, - callId, - CallTimeouts.defaultCallEventLifetime.inMilliseconds, - localPartyId, - answer.sdp!, - type: answer.type!); + room, + callId, + CallTimeouts.defaultCallEventLifetime.inMilliseconds, + localPartyId, + answer.sdp!, + type: answer.type!, + ); await pc!.setLocalDescription(answer); } } catch (e, s) { @@ -464,7 +495,8 @@ class CallSession { _remoteSDPStreamMetadata?.sdpStreamMetadatas .forEach((streamId, sdpStreamMetadata) { Logs().i( - 'Stream purpose update: \nid = "$streamId", \npurpose = "${sdpStreamMetadata.purpose}", \naudio_muted = ${sdpStreamMetadata.audio_muted}, \nvideo_muted = ${sdpStreamMetadata.video_muted}'); + 'Stream purpose update: \nid = "$streamId", \npurpose = "${sdpStreamMetadata.purpose}", \naudio_muted = ${sdpStreamMetadata.audio_muted}, \nvideo_muted = ${sdpStreamMetadata.video_muted}', + ); }); for (final wpstream in getRemoteStreams) { final streamId = wpstream.stream!.id; @@ -498,7 +530,8 @@ class CallSession { if (!candidate.isValid) { Logs().w( - '[VOIP] onCandidatesReceived => skip invalid candidate ${candidate.toMap()}'); + '[VOIP] onCandidatesReceived => skip invalid candidate ${candidate.toMap()}', + ); continue; } @@ -530,11 +563,13 @@ class CallSession { // Skip if there is nothing to do if (enabled && localScreenSharingStream != null) { Logs().w( - 'There is already a screensharing stream - there is nothing to do!'); + 'There is already a screensharing stream - there is nothing to do!', + ); return true; } else if (!enabled && localScreenSharingStream == null) { Logs().w( - 'There already isn\'t a screensharing stream - there is nothing to do!'); + 'There already isn\'t a screensharing stream - there is nothing to do!', + ); return false; } @@ -635,7 +670,8 @@ class CallSession { final metadata = _remoteSDPStreamMetadata?.sdpStreamMetadatas[stream.id]; if (metadata == null) { Logs().i( - 'Ignoring stream with id ${stream.id} because we didn\'t get any metadata about it'); + 'Ignoring stream with id ${stream.id} because we didn\'t get any metadata about it', + ); return; } @@ -762,24 +798,28 @@ class CallSession { // remove any screen sharing or remote transceivers, these don't need // to be replaced anyway. final transceivers = await pc!.getTransceivers(); - transceivers.removeWhere((transceiver) => - transceiver.sender.track == null || - (localScreenSharingStream != null && - localScreenSharingStream!.stream != null && - localScreenSharingStream!.stream! - .getTracks() - .map((e) => e.id) - .contains(transceiver.sender.track?.id))); + transceivers.removeWhere( + (transceiver) => + transceiver.sender.track == null || + (localScreenSharingStream != null && + localScreenSharingStream!.stream != null && + localScreenSharingStream!.stream! + .getTracks() + .map((e) => e.id) + .contains(transceiver.sender.track?.id)), + ); // in an ideal case the following should happen // - audio track gets replaced // - new video track gets added for (final newTrack in streamTracks) { final transceiver = transceivers.singleWhereOrNull( - (transceiver) => transceiver.sender.track!.kind == newTrack.kind); + (transceiver) => transceiver.sender.track!.kind == newTrack.kind, + ); if (transceiver != null) { Logs().d( - '[VOIP] replacing ${transceiver.sender.track} in transceiver'); + '[VOIP] replacing ${transceiver.sender.track} in transceiver', + ); final oldSender = transceiver.sender; await oldSender.replaceTrack(newTrack); await transceiver.setDirection( @@ -811,9 +851,9 @@ class CallSession { _remoteOnHold = onHold; final transceivers = await pc!.getTransceivers(); for (final transceiver in transceivers) { - await transceiver.setDirection(onHold - ? TransceiverDirection.SendOnly - : TransceiverDirection.SendRecv); + await transceiver.setDirection( + onHold ? TransceiverDirection.SendOnly : TransceiverDirection.SendRecv, + ); } await updateMuteStatus(); fireCallEvent(CallStateChange.kRemoteHoldUnhold); @@ -858,14 +898,16 @@ class CallSession { final metadata = SDPStreamMetadata({ if (localUserMediaStream != null) localUserMediaStream!.stream!.id: SDPStreamPurpose( - purpose: SDPStreamMetadataPurpose.Usermedia, - audio_muted: localUserMediaStream!.audioMuted, - video_muted: localUserMediaStream!.videoMuted), + purpose: SDPStreamMetadataPurpose.Usermedia, + audio_muted: localUserMediaStream!.audioMuted, + video_muted: localUserMediaStream!.videoMuted, + ), if (localScreenSharingStream != null) localScreenSharingStream!.stream!.id: SDPStreamPurpose( - purpose: SDPStreamMetadataPurpose.Screenshare, - audio_muted: localScreenSharingStream!.audioMuted, - video_muted: localScreenSharingStream!.videoMuted), + purpose: SDPStreamMetadataPurpose.Screenshare, + audio_muted: localScreenSharingStream!.audioMuted, + video_muted: localScreenSharingStream!.videoMuted, + ), }); await pc!.setLocalDescription(answer); @@ -898,7 +940,8 @@ class CallSession { setCallState(CallState.kEnding); if (state != CallState.kRinging && state != CallState.kFledgling) { Logs().e( - '[VOIP] Call must be in \'ringing|fledgling\' state to reject! (current state was: ${state.toString()}) Calling hangup instead'); + '[VOIP] Call must be in \'ringing|fledgling\' state to reject! (current state was: ${state.toString()}) Calling hangup instead', + ); await hangup(reason: CallErrorCode.userHangup, shouldEmit: shouldEmit); return; } @@ -909,8 +952,10 @@ class CallSession { } } - Future hangup( - {required CallErrorCode reason, bool shouldEmit = true}) async { + Future hangup({ + required CallErrorCode reason, + bool shouldEmit = true, + }) async { setCallState(CallState.kEnding); await terminate(CallParty.kLocal, reason, shouldEmit); try { @@ -1002,7 +1047,10 @@ class CallSession { if (shouldTerminate) { await terminate( - CallParty.kRemote, reason ?? CallErrorCode.userHangup, true); + CallParty.kRemote, + reason ?? CallErrorCode.userHangup, + true, + ); } else { Logs().e('[VOIP] Call is in state: ${state.toString()}: ignoring reject'); } @@ -1011,7 +1059,8 @@ class CallSession { Future _gotLocalOffer(RTCSessionDescription offer) async { if (callHasEnded) { Logs().d( - 'Ignoring newly created offer on call ID ${opts.callId} because the call has ended'); + 'Ignoring newly created offer on call ID ${opts.callId} because the call has ended', + ); return; } @@ -1020,7 +1069,10 @@ class CallSession { } catch (err) { Logs().d('Error setting local description! ${err.toString()}'); await terminate( - CallParty.kLocal, CallErrorCode.setLocalDescription, true); + CallParty.kLocal, + CallErrorCode.setLocalDescription, + true, + ); return; } @@ -1038,13 +1090,14 @@ class CallSession { final metadata = _getLocalSDPStreamMetadata(); if (state == CallState.kCreateOffer) { await sendInviteToCall( - room, - callId, - CallTimeouts.callInviteLifetime.inMilliseconds, - localPartyId, - offer.sdp!, - capabilities: callCapabilities, - metadata: metadata); + room, + callId, + CallTimeouts.callInviteLifetime.inMilliseconds, + localPartyId, + offer.sdp!, + capabilities: callCapabilities, + metadata: metadata, + ); // just incase we ended the call but already sent the invite // raraley happens during glares if (state == CallState.kEnded) { @@ -1069,14 +1122,15 @@ class CallSession { }); } else { await sendCallNegotiate( - room, - callId, - CallTimeouts.defaultCallEventLifetime.inMilliseconds, - localPartyId, - offer.sdp!, - type: offer.type!, - capabilities: callCapabilities, - metadata: metadata); + room, + callId, + CallTimeouts.defaultCallEventLifetime.inMilliseconds, + localPartyId, + offer.sdp!, + type: offer.type!, + capabilities: callCapabilities, + metadata: metadata, + ); } } @@ -1151,7 +1205,7 @@ class CallSession { _missedCall = false; } else if ({ RTCIceConnectionState.RTCIceConnectionStateFailed, - RTCIceConnectionState.RTCIceConnectionStateDisconnected + RTCIceConnectionState.RTCIceConnectionStateDisconnected, }.contains(state)) { if (iceRestartedCount < 3) { await restartIce(); @@ -1199,10 +1253,14 @@ class CallSession { localUserMediaStream!.isVideoMuted()) || _remoteOnHold; - _setTracksEnabled(localUserMediaStream?.stream?.getAudioTracks() ?? [], - !micShouldBeMuted); - _setTracksEnabled(localUserMediaStream?.stream?.getVideoTracks() ?? [], - !vidShouldBeMuted); + _setTracksEnabled( + localUserMediaStream?.stream?.getAudioTracks() ?? [], + !micShouldBeMuted, + ); + _setTracksEnabled( + localUserMediaStream?.stream?.getVideoTracks() ?? [], + !vidShouldBeMuted, + ); await sendSDPStreamMetadataChanged( room, @@ -1270,7 +1328,7 @@ class CallSession { Future _createPeerConnection() async { final configuration = { 'iceServers': opts.iceServers, - 'sdpSemantics': 'unified-plan' + 'sdpSemantics': 'unified-plan', }; final pc = await voip.delegate.createPeerConnection(configuration); pc.onTrack = (RTCTrackEvent event) async { @@ -1290,7 +1348,9 @@ class CallSession { } Future createDataChannel( - String label, RTCDataChannelInit dataChannelDict) async { + String label, + RTCDataChannelInit dataChannelDict, + ) async { await pc?.createDataChannel(label, dataChannelDict); } @@ -1339,7 +1399,11 @@ class CallSession { } _localCandidates.clear(); final res = await sendCallCandidates( - opts.room, callId, localPartyId, candidates); + opts.room, + callId, + localPartyId, + candidates, + ); Logs().v('[VOIP] sendCallCandidates res => $res'); } } catch (e) { @@ -1350,7 +1414,8 @@ class CallSession { if (_candidateSendTries > 5) { Logs().d( - 'Failed to send candidates on attempt $_candidateSendTries Giving up on this call.'); + 'Failed to send candidates on attempt $_candidateSendTries Giving up on this call.', + ); await hangup(reason: CallErrorCode.iceTimeout); return; } @@ -1407,13 +1472,15 @@ class CallSession { } if (selectedPartyId == null) { Logs().w( - 'Got nonsensical select_answer with null/undefined selected_party_id: ignoring'); + 'Got nonsensical select_answer with null/undefined selected_party_id: ignoring', + ); return; } if (selectedPartyId != localPartyId) { Logs().w( - 'Got select_answer for party ID $selectedPartyId: we are party ID $localPartyId.'); + 'Got select_answer for party ID $selectedPartyId: we are party ID $localPartyId.', + ); // The other party has picked somebody else's answer await terminate(CallParty.kRemote, CallErrorCode.answeredElsewhere, true); } @@ -1430,12 +1497,17 @@ class CallSession { /// intended for any member of the room other than the sender of the event. /// [party_id] The party ID for call, Can be set to client.deviceId. Future sendInviteToCall( - Room room, String callId, int lifetime, String party_id, String sdp, - {String type = 'offer', - String version = voipProtoVersion, - String? txid, - CallCapabilities? capabilities, - SDPStreamMetadata? metadata}) async { + Room room, + String callId, + int lifetime, + String party_id, + String sdp, { + String type = 'offer', + String version = voipProtoVersion, + String? txid, + CallCapabilities? capabilities, + SDPStreamMetadata? metadata, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1471,8 +1543,13 @@ class CallSession { /// [party_id] The party ID for call, Can be set to client.deviceId. /// [selected_party_id] The party ID for the selected answer. Future sendSelectCallAnswer( - Room room, String callId, String party_id, String selected_party_id, - {String version = voipProtoVersion, String? txid}) async { + Room room, + String callId, + String party_id, + String selected_party_id, { + String version = voipProtoVersion, + String? txid, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1495,8 +1572,13 @@ class CallSession { /// [callId] is a unique identifier for the call. /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. /// [party_id] The party ID for call, Can be set to client.deviceId. - Future sendCallReject(Room room, String callId, String party_id, - {String version = voipProtoVersion, String? txid}) async { + Future sendCallReject( + Room room, + String callId, + String party_id, { + String version = voipProtoVersion, + String? txid, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1518,12 +1600,17 @@ class CallSession { /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. /// [party_id] The party ID for call, Can be set to client.deviceId. Future sendCallNegotiate( - Room room, String callId, int lifetime, String party_id, String sdp, - {String type = 'offer', - String version = voipProtoVersion, - String? txid, - CallCapabilities? capabilities, - SDPStreamMetadata? metadata}) async { + Room room, + String callId, + int lifetime, + String party_id, + String sdp, { + String type = 'offer', + String version = voipProtoVersion, + String? txid, + CallCapabilities? capabilities, + SDPStreamMetadata? metadata, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1596,12 +1683,16 @@ class CallSession { /// [sdp] The SDP text of the session description. /// [party_id] The party ID for call, Can be set to client.deviceId. Future sendAnswerCall( - Room room, String callId, String sdp, String party_id, - {String type = 'answer', - String version = voipProtoVersion, - String? txid, - CallCapabilities? capabilities, - SDPStreamMetadata? metadata}) async { + Room room, + String callId, + String sdp, + String party_id, { + String type = 'answer', + String version = voipProtoVersion, + String? txid, + CallCapabilities? capabilities, + SDPStreamMetadata? metadata, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1624,8 +1715,13 @@ class CallSession { /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. /// [party_id] The party ID for call, Can be set to client.deviceId. Future sendHangupCall( - Room room, String callId, String party_id, String? hangupCause, - {String version = voipProtoVersion, String? txid}) async { + Room room, + String callId, + String party_id, + String? hangupCause, { + String version = voipProtoVersion, + String? txid, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1656,8 +1752,13 @@ class CallSession { /// [party_id] The party ID for call, Can be set to client.deviceId. /// [metadata] The sdp_stream_metadata object. Future sendSDPStreamMetadataChanged( - Room room, String callId, String party_id, SDPStreamMetadata metadata, - {String version = voipProtoVersion, String? txid}) async { + Room room, + String callId, + String party_id, + SDPStreamMetadata metadata, { + String version = voipProtoVersion, + String? txid, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1682,8 +1783,13 @@ class CallSession { /// [party_id] The party ID for call, Can be set to client.deviceId. /// [callReplaces] transfer info Future sendCallReplaces( - Room room, String callId, String party_id, CallReplaces callReplaces, - {String version = voipProtoVersion, String? txid}) async { + Room room, + String callId, + String party_id, + CallReplaces callReplaces, { + String version = voipProtoVersion, + String? txid, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1707,9 +1813,14 @@ class CallSession { /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. /// [party_id] The party ID for call, Can be set to client.deviceId. /// [assertedIdentity] the asserted identity - Future sendAssertedIdentity(Room room, String callId, - String party_id, AssertedIdentity assertedIdentity, - {String version = voipProtoVersion, String? txid}) async { + Future sendAssertedIdentity( + Room room, + String callId, + String party_id, + AssertedIdentity assertedIdentity, { + String version = voipProtoVersion, + String? txid, + }) async { final content = { 'call_id': callId, 'party_id': party_id, @@ -1753,19 +1864,24 @@ class CallSession { await client.userDeviceKeysLoading; if (client.userDeviceKeys[remoteUserId]?.deviceKeys[remoteDeviceId] != null) { - await client.sendToDeviceEncrypted([ - client.userDeviceKeys[remoteUserId]!.deviceKeys[remoteDeviceId]! - ], type, data); + await client.sendToDeviceEncrypted( + [ + client.userDeviceKeys[remoteUserId]!.deviceKeys[remoteDeviceId]!, + ], + type, + data, + ); } else { Logs().w( - '[VOIP] _sendCallContent missing device keys for $remoteUserId'); + '[VOIP] _sendCallContent missing device keys for $remoteUserId', + ); } } else { await client.sendToDevice( type, txid, { - remoteUserId!: {remoteDeviceId!: data} + remoteUserId!: {remoteDeviceId!: data}, }, ); } diff --git a/lib/src/voip/group_call_session.dart b/lib/src/voip/group_call_session.dart index 5aa1880b7..1d804f9a4 100644 --- a/lib/src/voip/group_call_session.dart +++ b/lib/src/voip/group_call_session.dart @@ -173,17 +173,20 @@ class GroupCallSession { _resendMemberStateEventTimer!.cancel(); } _resendMemberStateEventTimer = Timer.periodic( - CallTimeouts.updateExpireTsTimerDuration, ((timer) async { - Logs().d('sendMemberStateEvent updating member event with timer'); - if (state != GroupCallState.ended || - state != GroupCallState.localCallFeedUninitialized) { - await sendMemberStateEvent(); - } else { - Logs().d( - '[VOIP] deteceted groupCall in state $state, removing state event'); - await removeMemberStateEvent(); - } - })); + CallTimeouts.updateExpireTsTimerDuration, + ((timer) async { + Logs().d('sendMemberStateEvent updating member event with timer'); + if (state != GroupCallState.ended || + state != GroupCallState.localCallFeedUninitialized) { + await sendMemberStateEvent(); + } else { + Logs().d( + '[VOIP] deteceted groupCall in state $state, removing state event', + ); + await removeMemberStateEvent(); + } + }), + ); } Future removeMemberStateEvent() { @@ -218,7 +221,8 @@ class GroupCallSession { for (final mem in ignoredMems) { Logs().v( - '[VOIP] Ignored ${mem.userId}\'s mem event ${mem.toJson()} while updating _participants list for callId: $groupCallId, expiry status: ${mem.isExpired}'); + '[VOIP] Ignored ${mem.userId}\'s mem event ${mem.toJson()} while updating _participants list for callId: $groupCallId, expiry status: ${mem.isExpired}', + ); } final Set newP = {}; @@ -236,7 +240,8 @@ class GroupCallSession { if (state != GroupCallState.entered) { Logs().w( - '[VOIP] onMemberStateChanged groupCall state is currently $state, skipping member update'); + '[VOIP] onMemberStateChanged groupCall state is currently $state, skipping member update', + ); continue; } @@ -253,7 +258,8 @@ class GroupCallSession { ..remove(localParticipant); if (nonLocalAnyJoined.isNotEmpty && state == GroupCallState.entered) { Logs().v( - 'nonLocalAnyJoined: ${nonLocalAnyJoined.map((e) => e.id).toString()} roomId: ${room.id} groupCallId: $groupCallId'); + 'nonLocalAnyJoined: ${nonLocalAnyJoined.map((e) => e.id).toString()} roomId: ${room.id} groupCallId: $groupCallId', + ); await backend.onNewParticipant(this, nonLocalAnyJoined.toList()); } _participants.addAll(anyJoined); @@ -265,7 +271,8 @@ class GroupCallSession { ..remove(localParticipant); if (nonLocalAnyLeft.isNotEmpty && state == GroupCallState.entered) { Logs().v( - 'nonLocalAnyLeft: ${nonLocalAnyLeft.map((e) => e.id).toString()} roomId: ${room.id} groupCallId: $groupCallId'); + 'nonLocalAnyLeft: ${nonLocalAnyLeft.map((e) => e.id).toString()} roomId: ${room.id} groupCallId: $groupCallId', + ); await backend.onLeftParticipant(this, nonLocalAnyLeft.toList()); } _participants.removeAll(anyLeft); @@ -275,7 +282,8 @@ class GroupCallSession { onGroupCallEvent.add(GroupCallStateChange.participantsChanged); Logs().d( - '[VOIP] onMemberStateChanged current list: ${_participants.map((e) => e.id).toString()}'); + '[VOIP] onMemberStateChanged current list: ${_participants.map((e) => e.id).toString()}', + ); } } } diff --git a/lib/src/voip/models/call_events.dart b/lib/src/voip/models/call_events.dart index 59783c099..ffa298c9e 100644 --- a/lib/src/voip/models/call_events.dart +++ b/lib/src/voip/models/call_events.dart @@ -105,10 +105,11 @@ class SDPStreamPurpose { bool audio_muted; bool video_muted; - SDPStreamPurpose( - {required this.purpose, - this.audio_muted = false, - this.video_muted = false}); + SDPStreamPurpose({ + required this.purpose, + this.audio_muted = false, + this.video_muted = false, + }); factory SDPStreamPurpose.fromJson(Map json) => SDPStreamPurpose( audio_muted: json['audio_muted'] as bool? ?? false, @@ -133,8 +134,11 @@ class SDPStreamMetadata { SDPStreamMetadata(this.sdpStreamMetadatas); factory SDPStreamMetadata.fromJson(Map json) => - SDPStreamMetadata(json.map( - (key, value) => MapEntry(key, SDPStreamPurpose.fromJson(value)))); + SDPStreamMetadata( + json.map( + (key, value) => MapEntry(key, SDPStreamPurpose.fromJson(value)), + ), + ); Map toJson() => sdpStreamMetadatas.map((key, value) => MapEntry(key, value.toJson())); } diff --git a/lib/src/voip/models/key_provider.dart b/lib/src/voip/models/key_provider.dart index 8fc121771..4b40b875e 100644 --- a/lib/src/voip/models/key_provider.dart +++ b/lib/src/voip/models/key_provider.dart @@ -10,7 +10,10 @@ enum E2EEKeyMode { abstract class EncryptionKeyProvider { Future onSetEncryptionKey( - CallParticipant participant, Uint8List key, int index); + CallParticipant participant, + Uint8List key, + int index, + ); Future onRatchetKey(CallParticipant participant, int index); @@ -42,11 +45,13 @@ class EncryptionKeysEventContent { factory EncryptionKeysEventContent.fromJson(Map json) => EncryptionKeysEventContent( - (json['keys'] as List) - .map( - (e) => EncryptionKeyEntry.fromJson(e as Map)) - .toList(), - json['call_id'] as String); + (json['keys'] as List) + .map( + (e) => EncryptionKeyEntry.fromJson(e as Map), + ) + .toList(), + json['call_id'] as String, + ); Map toJson() => { 'keys': keys.map((e) => e.toJson()).toList(), diff --git a/lib/src/voip/models/webrtc_delegate.dart b/lib/src/voip/models/webrtc_delegate.dart index ea6e2d1e8..c499cdb84 100644 --- a/lib/src/voip/models/webrtc_delegate.dart +++ b/lib/src/voip/models/webrtc_delegate.dart @@ -6,8 +6,9 @@ import 'package:matrix/matrix.dart'; abstract class WebRTCDelegate { MediaDevices get mediaDevices; Future createPeerConnection( - Map configuration, - [Map constraints = const {}]); + Map configuration, [ + Map constraints = const {}, + ]); Future playRingtone(); Future stopRingtone(); Future handleNewCall(CallSession session); diff --git a/lib/src/voip/utils/conn_tester.dart b/lib/src/voip/utils/conn_tester.dart index d501de161..b72a5c2d2 100644 --- a/lib/src/voip/utils/conn_tester.dart +++ b/lib/src/voip/utils/conn_tester.dart @@ -17,7 +17,7 @@ class ConnectionTester { 'iceServers': iceServers, 'sdpSemantics': 'unified-plan', 'iceCandidatePoolSize': 1, - 'iceTransportPolicy': 'relay' + 'iceTransportPolicy': 'relay', }; pc1 = await delegate.createPeerConnection(configuration); pc2 = await delegate.createPeerConnection(configuration); @@ -78,9 +78,11 @@ class ConnectionTester { return connected; } - Future waitUntilAsync(Future Function() test, - {final int maxIterations = 1000, - final Duration step = const Duration(milliseconds: 10)}) async { + Future waitUntilAsync( + Future Function() test, { + final int maxIterations = 1000, + final Duration step = const Duration(milliseconds: 10), + }) async { int iterations = 0; for (; iterations < maxIterations; iterations++) { await Future.delayed(step); @@ -90,7 +92,8 @@ class ConnectionTester { } if (iterations >= maxIterations) { throw TimeoutException( - 'Condition not reached within ${iterations * step.inMilliseconds}ms'); + 'Condition not reached within ${iterations * step.inMilliseconds}ms', + ); } return iterations; } @@ -112,7 +115,7 @@ class ConnectionTester { { 'username': _turnServerCredentials!.username, 'credential': _turnServerCredentials!.password, - 'url': _turnServerCredentials!.uris[0] + 'url': _turnServerCredentials!.uris[0], } ]; } diff --git a/lib/src/voip/utils/famedly_call_extension.dart b/lib/src/voip/utils/famedly_call_extension.dart index 23719e2d2..ae634d862 100644 --- a/lib/src/voip/utils/famedly_call_extension.dart +++ b/lib/src/voip/utils/famedly_call_extension.dart @@ -80,7 +80,8 @@ extension FamedlyCallMemberEventsExtension on Room { /// passing no `CallMembership` removes it from the state event. Future updateFamedlyCallMemberStateEvent( - CallMembership callMembership) async { + CallMembership callMembership, + ) async { final ownMemberships = getCallMembershipsForUser(client.userID!); // do not bother removing other deviceId expired events because we have no @@ -93,7 +94,7 @@ extension FamedlyCallMemberEventsExtension on Room { ownMemberships.add(callMembership); final newContent = { - 'memberships': List.from(ownMemberships.map((e) => e.toJson())) + 'memberships': List.from(ownMemberships.map((e) => e.toJson())), }; await setFamedlyCallMemberEvent(newContent); @@ -107,14 +108,16 @@ extension FamedlyCallMemberEventsExtension on Room { }) async { final ownMemberships = getCallMembershipsForUser(client.userID!); - ownMemberships.removeWhere((mem) => - mem.callId == groupCallId && - mem.deviceId == deviceId && - mem.application == application && - mem.scope == scope); + ownMemberships.removeWhere( + (mem) => + mem.callId == groupCallId && + mem.deviceId == deviceId && + mem.application == application && + mem.scope == scope, + ); final newContent = { - 'memberships': List.from(ownMemberships.map((e) => e.toJson())) + 'memberships': List.from(ownMemberships.map((e) => e.toJson())), }; await setFamedlyCallMemberEvent(newContent); } @@ -145,12 +148,18 @@ extension FamedlyCallMemberEventsExtension on Room { List getCallMembershipsFromEvent(MatrixEvent event) { if (event.roomId != id) return []; return getCallMembershipsFromEventContent( - event.content, event.senderId, event.roomId!); + event.content, + event.senderId, + event.roomId!, + ); } /// returns a list of memberships from a famedly call matrix event List getCallMembershipsFromEventContent( - Map content, String senderId, String roomId) { + Map content, + String senderId, + String roomId, + ) { final mems = content.tryGetList('memberships'); final callMems = []; for (final m in mems ?? []) { diff --git a/lib/src/voip/utils/stream_helper.dart b/lib/src/voip/utils/stream_helper.dart index 61da7c5fb..7f16907b1 100644 --- a/lib/src/voip/utils/stream_helper.dart +++ b/lib/src/voip/utils/stream_helper.dart @@ -28,7 +28,9 @@ void setTracksEnabled(List tracks, bool enabled) { } Future hasMediaDevice( - WebRTCDelegate delegate, MediaInputKind mediaInputKind) async { + WebRTCDelegate delegate, + MediaInputKind mediaInputKind, +) async { final devices = await delegate.mediaDevices.enumerateDevices(); return devices .where((device) => device.kind == mediaInputKind.name) diff --git a/lib/src/voip/utils/user_media_constraints.dart b/lib/src/voip/utils/user_media_constraints.dart index 14bdf14f6..1d58144ca 100644 --- a/lib/src/voip/utils/user_media_constraints.dart +++ b/lib/src/voip/utils/user_media_constraints.dart @@ -2,7 +2,7 @@ class UserMediaConstraints { static const Map micMediaConstraints = { 'echoCancellation': true, 'noiseSuppression': true, - 'autoGainControl': false + 'autoGainControl': false, }; static const Map camMediaConstraints = { diff --git a/lib/src/voip/utils/voip_constants.dart b/lib/src/voip/utils/voip_constants.dart index 5d20e6b39..37aacfb91 100644 --- a/lib/src/voip/utils/voip_constants.dart +++ b/lib/src/voip/utils/voip_constants.dart @@ -40,7 +40,8 @@ class CallTimeouts { class CallConstants { static final callEventsRegxp = RegExp( - r'm.call.|org.matrix.call.|org.matrix.msc3401.call.|com.famedly.call.'); + r'm.call.|org.matrix.call.|org.matrix.msc3401.call.|com.famedly.call.', + ); static const callEndedEventTypes = { EventTypes.CallAnswer, diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index d1ab3d023..5c101600f 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -125,7 +125,8 @@ class VoIP { if (CallConstants.omitWhenCallEndedTypes.contains(event.type) && event.content.tryGet('call_id') == callId) { Logs().v( - 'Ommit "${event.type}" event for an already terminated call'); + 'Ommit "${event.type}" event for an already terminated call', + ); return true; } @@ -146,7 +147,8 @@ class VoIP { (callEvent.content.tryGet('lifetime') ?? CallTimeouts.callInviteLifetime.inMilliseconds)) { Logs().w( - '[VOIP] Ommiting invite event ${callEvent.eventId} as age was older than lifetime'); + '[VOIP] Ommiting invite event ${callEvent.eventId} as age was older than lifetime', + ); return true; } return false; @@ -186,7 +188,8 @@ class VoIP { groupCallSession = groupCalls[VoipId(roomId: roomId, callId: confId)]; } else { Logs().w( - '[VOIP] Ignoring to_device event of type ${event.type} but did not find group call for id: $confId'); + '[VOIP] Ignoring to_device event of type ${event.type} but did not find group call for id: $confId', + ); return; } @@ -195,17 +198,20 @@ class VoIP { final destSessionId = event.content.tryGet('dest_session_id'); if (destSessionId != currentSessionId) { Logs().w( - '[VOIP] Ignoring to_device event of type ${event.type} did not match currentSessionId: $currentSessionId, dest_session_id was set to $destSessionId'); + '[VOIP] Ignoring to_device event of type ${event.type} did not match currentSessionId: $currentSessionId, dest_session_id was set to $destSessionId', + ); return; } } else if (groupCallSession == null || remoteDeviceId == null) { Logs().w( - '[VOIP] _handleCallEvent ${event.type} recieved but either groupCall ${groupCallSession?.groupCallId} or deviceId $remoteDeviceId was null, ignoring'); + '[VOIP] _handleCallEvent ${event.type} recieved but either groupCall ${groupCallSession?.groupCallId} or deviceId $remoteDeviceId was null, ignoring', + ); return; } } else { Logs().w( - '[VOIP] _handleCallEvent can only handle Event or ToDeviceEvent, it got ${event.runtimeType}'); + '[VOIP] _handleCallEvent can only handle Event or ToDeviceEvent, it got ${event.runtimeType}', + ); return; } @@ -213,14 +219,16 @@ class VoIP { if (room == null) { Logs().w( - '[VOIP] _handleCallEvent call event does not contain a room_id, ignoring'); + '[VOIP] _handleCallEvent call event does not contain a room_id, ignoring', + ); return; } else if (client.userID != null && client.deviceID != null && remoteUserId == client.userID && remoteDeviceId == client.deviceID) { Logs().v( - 'Ignoring call event ${event.type} for room ${room.id} from our own device'); + 'Ignoring call event ${event.type} for room ${room.id} from our own device', + ); return; } else if (!event.type .startsWith(EventTypes.GroupCallMemberEncryptionKeys)) { @@ -237,37 +245,43 @@ class VoIP { !{EventTypes.CallInvite, EventTypes.GroupCallMemberInvite} .contains(event.type)) { Logs().w( - 'Ignoring call event ${event.type} for room ${room.id} because we do not have the call'); + 'Ignoring call event ${event.type} for room ${room.id} because we do not have the call', + ); return; } else if (call != null) { // multiple checks to make sure the events sent are from the the // expected party if (call.room.id != room.id) { Logs().w( - 'Ignoring call event ${event.type} for room ${room.id} claiming to be for call in room ${call.room.id}'); + 'Ignoring call event ${event.type} for room ${room.id} claiming to be for call in room ${call.room.id}', + ); return; } if (call.remoteUserId != null && call.remoteUserId != remoteUserId) { Logs().d( - 'Ignoring call event ${event.type} for room ${room.id} from sender $remoteUserId, expected sender: ${call.remoteUserId}'); + 'Ignoring call event ${event.type} for room ${room.id} from sender $remoteUserId, expected sender: ${call.remoteUserId}', + ); return; } if (call.remotePartyId != null && call.remotePartyId != partyId) { Logs().w( - 'Ignoring call event ${event.type} for room ${room.id} from sender with a different party_id $partyId, expected party_id: ${call.remotePartyId}'); + 'Ignoring call event ${event.type} for room ${room.id} from sender with a different party_id $partyId, expected party_id: ${call.remotePartyId}', + ); return; } if ((call.remotePartyId != null && call.remotePartyId == localPartyId)) { Logs().v( - 'Ignoring call event ${event.type} for room ${room.id} from our own partyId'); + 'Ignoring call event ${event.type} for room ${room.id} from our own partyId', + ); return; } } } } Logs().v( - '[VOIP] Handling event of type: ${event.type}, content ${event.content} from sender ${event.senderId} rp: $remoteUserId:$remoteDeviceId'); + '[VOIP] Handling event of type: ${event.type}, content ${event.content} from sender ${event.senderId} rp: $remoteUserId:$remoteDeviceId', + ); switch (event.type) { case EventTypes.CallInvite: @@ -313,11 +327,19 @@ class VoIP { break; case EventTypes.GroupCallMemberEncryptionKeys: await groupCallSession!.backend.onCallEncryption( - groupCallSession, remoteUserId, remoteDeviceId!, content); + groupCallSession, + remoteUserId, + remoteDeviceId!, + content, + ); break; case EventTypes.GroupCallMemberEncryptionKeysRequest: await groupCallSession!.backend.onCallEncryptionKeyRequest( - groupCallSession, remoteUserId, remoteDeviceId!, content); + groupCallSession, + remoteUserId, + remoteDeviceId!, + content, + ); break; } } @@ -336,10 +358,15 @@ class VoIP { } } - Future onCallInvite(Room room, String remoteUserId, - String? remoteDeviceId, Map content) async { + Future onCallInvite( + Room room, + String remoteUserId, + String? remoteDeviceId, + Map content, + ) async { Logs().v( - '[VOIP] onCallInvite $remoteUserId:$remoteDeviceId => ${client.userID}:${client.deviceID}, \ncontent => ${content.toString()}'); + '[VOIP] onCallInvite $remoteUserId:$remoteDeviceId => ${client.userID}:${client.deviceID}, \ncontent => ${content.toString()}', + ); final String callId = content['call_id']; final int lifetime = content['lifetime']; @@ -348,7 +375,8 @@ class VoIP { final call = calls[VoipId(roomId: room.id, callId: callId)]; Logs().d( - '[glare] got new call ${content.tryGet('call_id')} and currently room id is mapped to ${incomingCallRoomId.tryGet(room.id)}'); + '[glare] got new call ${content.tryGet('call_id')} and currently room id is mapped to ${incomingCallRoomId.tryGet(room.id)}', + ); if (call != null && call.state == CallState.kEnded) { // Session already exist. @@ -371,7 +399,8 @@ class VoIP { if (content['capabilities'] != null) { final capabilities = CallCapabilities.fromJson(content['capabilities']); Logs().v( - '[VOIP] CallCapabilities: dtmf => ${capabilities.dtmf}, transferee => ${capabilities.transferee}'); + '[VOIP] CallCapabilities: dtmf => ${capabilities.dtmf}, transferee => ${capabilities.transferee}', + ); } var callType = CallType.kVoice; @@ -382,7 +411,8 @@ class VoIP { sdpStreamMetadata.sdpStreamMetadatas .forEach((streamId, SDPStreamPurpose purpose) { Logs().v( - '[VOIP] [$streamId] => purpose: ${purpose.purpose}, audioMuted: ${purpose.audio_muted}, videoMuted: ${purpose.video_muted}'); + '[VOIP] [$streamId] => purpose: ${purpose.purpose}, audioMuted: ${purpose.audio_muted}, videoMuted: ${purpose.video_muted}', + ); if (!purpose.video_muted) { callType = CallType.kVideo; @@ -419,7 +449,8 @@ class VoIP { (confId == null || currentGroupCID != VoipId(roomId: room.id, callId: confId))) { Logs().v( - '[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.'); + '[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.', + ); // no need to emit here because handleNewCall was never triggered yet await newCall.reject(reason: CallErrorCode.userBusy, shouldEmit: false); await delegate.handleMissedCall(newCall); @@ -449,7 +480,12 @@ class VoIP { currentCID = VoipId(roomId: room.id, callId: callId); await newCall.initWithInvite( - callType, offer, sdpStreamMetadata, lifetime, confId != null); + callType, + offer, + sdpStreamMetadata, + lifetime, + confId != null, + ); // Popup CallingPage for incoming call. if (confId == null && !newCall.callHasEnded) { @@ -462,8 +498,12 @@ class VoIP { } } - Future onCallAnswer(Room room, String remoteUserId, - String? remoteDeviceId, Map content) async { + Future onCallAnswer( + Room room, + String remoteUserId, + String? remoteDeviceId, + Map content, + ) async { Logs().v('[VOIP] onCallAnswer => ${content.toString()}'); final String callId = content['call_id']; @@ -478,31 +518,37 @@ class VoIP { if (call.room.id != room.id) { Logs().w( - 'Ignoring call answer for room ${room.id} claiming to be for call in room ${call.room.id}'); + 'Ignoring call answer for room ${room.id} claiming to be for call in room ${call.room.id}', + ); return; } if (call.remoteUserId == null) { Logs().i( - '[VOIP] you probably called the room without setting a userId in invite, setting the calls remote user id to what I get from m.call.answer now'); + '[VOIP] you probably called the room without setting a userId in invite, setting the calls remote user id to what I get from m.call.answer now', + ); call.remoteUserId = remoteUserId; } if (call.remoteDeviceId == null) { Logs().i( - '[VOIP] you probably called the room without setting a userId in invite, setting the calls remote user id to what I get from m.call.answer now'); + '[VOIP] you probably called the room without setting a userId in invite, setting the calls remote user id to what I get from m.call.answer now', + ); call.remoteDeviceId = remoteDeviceId; } if (call.remotePartyId != null) { Logs().d( - 'Ignoring call answer from party ${content['party_id']}, we are already with ${call.remotePartyId}'); + 'Ignoring call answer from party ${content['party_id']}, we are already with ${call.remotePartyId}', + ); return; } else { call.remotePartyId = content['party_id']; } final answer = RTCSessionDescription( - content['answer']['sdp'], content['answer']['type']); + content['answer']['sdp'], + content['answer']['type'], + ); SDPStreamMetadata? metadata; if (content[sdpStreamMetadataKey] != null) { @@ -535,11 +581,13 @@ class VoIP { if (call != null) { // hangup in any case, either if the other party hung up or we did on another device await call.terminate( - CallParty.kRemote, - CallErrorCode.values.firstWhereOrNull( - (element) => element.reason == content['reason']) ?? - CallErrorCode.userHangup, - true); + CallParty.kRemote, + CallErrorCode.values.firstWhereOrNull( + (element) => element.reason == content['reason'], + ) ?? + CallErrorCode.userHangup, + true, + ); } else { Logs().v('[VOIP] onCallHangup: Session [$callId] not found!'); } @@ -556,7 +604,8 @@ class VoIP { if (call != null) { await call.onRejectReceived( CallErrorCode.values.firstWhereOrNull( - (element) => element.reason == content['reason']) ?? + (element) => element.reason == content['reason'], + ) ?? CallErrorCode.userHangup, ); } else { @@ -565,7 +614,9 @@ class VoIP { } Future onCallSelectAnswer( - Room room, Map content) async { + Room room, + Map content, + ) async { final String callId = content['call_id']; Logs().d('SelectAnswer received for call ID $callId'); final String selectedPartyId = content['selected_party_id']; @@ -574,7 +625,8 @@ class VoIP { if (call != null) { if (call.room.id != room.id) { Logs().w( - 'Ignoring call select answer for room ${room.id} claiming to be for call in room ${call.room.id}'); + 'Ignoring call select answer for room ${room.id} claiming to be for call in room ${call.room.id}', + ); return; } await call.onSelectAnswerReceived(selectedPartyId); @@ -582,7 +634,9 @@ class VoIP { } Future onSDPStreamMetadataChangedReceived( - Room room, Map content) async { + Room room, + Map content, + ) async { final String callId = content['call_id']; Logs().d('SDP Stream metadata received for call ID $callId'); @@ -593,12 +647,15 @@ class VoIP { return; } await call.onSDPStreamMetadataReceived( - SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey])); + SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey]), + ); } } Future onAssertedIdentityReceived( - Room room, Map content) async { + Room room, + Map content, + ) async { final String callId = content['call_id']; Logs().d('Asserted identity received for call ID $callId'); @@ -609,7 +666,8 @@ class VoIP { return; } call.onAssertedIdentityReceived( - AssertedIdentity.fromJson(content['asserted_identity'])); + AssertedIdentity.fromJson(content['asserted_identity']), + ); } } @@ -630,8 +688,10 @@ class VoIP { if (content[sdpStreamMetadataKey] != null) { metadata = SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey]); } - await call.onNegotiateReceived(metadata, - RTCSessionDescription(description['sdp'], description['type'])); + await call.onNegotiateReceived( + metadata, + RTCSessionDescription(description['sdp'], description['type']), + ); } catch (e, s) { Logs().e('[VOIP] Failed to complete negotiation', e, s); } @@ -668,7 +728,7 @@ class VoIP { { 'username': _turnServerCredentials!.username, 'credential': _turnServerCredentials!.password, - 'urls': _turnServerCredentials!.uris + 'urls': _turnServerCredentials!.uris, } ]; } @@ -832,7 +892,8 @@ class VoIP { }) async { if (membership.isExpired) { Logs().d( - 'Ignoring expired membership in passive groupCall creator. ${membership.toJson()}'); + 'Ignoring expired membership in passive groupCall creator. ${membership.toJson()}', + ); return; } @@ -859,7 +920,8 @@ class VoIP { ); if (groupCalls.containsKey( - VoipId(roomId: membership.roomId, callId: membership.callId))) { + VoipId(roomId: membership.roomId, callId: membership.callId), + )) { return; } diff --git a/test/calls_test.dart b/test/calls_test.dart index 2e3c43729..14f76dc0c 100644 --- a/test/calls_test.dart +++ b/test/calls_test.dart @@ -36,78 +36,126 @@ void main() { iceServers: [], ), ); - await call.sendInviteToCall(room, '1234', 1234, '4567', 'sdp', - txid: '1234'); + await call.sendInviteToCall( + room, + '1234', + 1234, + '4567', + 'sdp', + txid: '1234', + ); await call.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234'); await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234'); - await call.sendSelectCallAnswer(room, '1234', '4567', '6789', - txid: '1234'); + await call.sendSelectCallAnswer( + room, + '1234', + '4567', + '6789', + txid: '1234', + ); await call.sendCallReject(room, '1234', '4567', txid: '1234'); - await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp', - txid: '1234'); - await call.sendHangupCall(room, '1234', '4567', 'userHangup', - txid: '1234'); + await call.sendCallNegotiate( + room, + '1234', + 1234, + '4567', + 'sdp', + txid: '1234', + ); + await call.sendHangupCall( + room, + '1234', + '4567', + 'userHangup', + txid: '1234', + ); await call.sendAssertedIdentity( - room, - '1234', - '4567', - AssertedIdentity() - ..displayName = 'name' - ..id = 'some_id', - txid: '1234'); - await call.sendCallReplaces(room, '1234', '4567', CallReplaces(), - txid: '1234'); + room, + '1234', + '4567', + AssertedIdentity() + ..displayName = 'name' + ..id = 'some_id', + txid: '1234', + ); + await call.sendCallReplaces( + room, + '1234', + '4567', + CallReplaces(), + txid: '1234', + ); await call.sendSDPStreamMetadataChanged( - room, '1234', '4567', SDPStreamMetadata({}), - txid: '1234'); + room, + '1234', + '4567', + SDPStreamMetadata({}), + txid: '1234', + ); }); test('Call lifetime and age', () async { expect(voip.currentCID, null); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': '1702472924955oq1uQbNAfU7wAaEA', - 'party_id': 'DPCIPPBGPO', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'newevent', - originServerTs: DateTime.utc(1969), - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': '1702472924955oq1uQbNAfU7wAaEA', + 'party_id': 'DPCIPPBGPO', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'newevent', + originServerTs: DateTime.utc(1969), + ), + ], + ), + ), + }, + ), + ), + ); await Future.delayed(Duration(seconds: 2)); // confirm that no call got created after 3 seconds, which is // expected in this case because the originTs was old asf expect(voip.currentCID, null); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - unsigned: {'age': 60001}, - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': 'unsignedTsInvalidCall', - 'party_id': 'DPCIPPBGPO', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'newevent', - originServerTs: DateTime.now(), - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + unsigned: {'age': 60001}, + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': 'unsignedTsInvalidCall', + 'party_id': 'DPCIPPBGPO', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'newevent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); await Future.delayed(Duration(seconds: 2)); // confirm that no call got created after 3 seconds, which is // expected in this case because age was older than lifetime @@ -115,65 +163,84 @@ void main() { }); test('Call connection and hanging up', () async { expect(voip.currentCID, null); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': 'originTsValidCall', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'callerInviteEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); - await matrix.handleSync(SyncUpdate( + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': 'originTsValidCall', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'callerInviteEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.candidates', - content: { - 'call_id': 'originTsValidCall', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'candidates': [ - { - 'candidate': 'candidate:01UDP2122252543uwu50184typhost', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - }, - { - 'candidate': - 'candidate:31TCP2105524479uwu9typhosttcptypeactive', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - } + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.candidates', + content: { + 'call_id': 'originTsValidCall', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'candidates': [ + { + 'candidate': + 'candidate:01UDP2122252543uwu50184typhost', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + }, + { + 'candidate': + 'candidate:31TCP2105524479uwu9typhosttcptypeactive', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + } + ], + }, + senderId: '@alice:testing.com', + eventId: 'callerCallCandidatesEvent', + originServerTs: DateTime.now(), + ), ], - }, - senderId: '@alice:testing.com', - eventId: 'callerCallCandidatesEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + ), + ), + }, + ), + ), + ); while (voip.currentCID != VoipId(roomId: room.id, callId: 'originTsValidCall')) { // call invite looks valid, call should be created now :D await Future.delayed(Duration(milliseconds: 50)); Logs().d('Waiting for currentCID to update'); } - expect(voip.currentCID, - VoipId(roomId: room.id, callId: 'originTsValidCall')); + expect( + voip.currentCID, + VoipId(roomId: room.id, callId: 'originTsValidCall'), + ); final call = voip.calls[voip.currentCID]!; expect(call.state, CallState.kRinging); await call.answer(txid: '1234'); @@ -183,47 +250,56 @@ void main() { // we send them manually anyway because our stub sends empty list of // candidates await call.sendCallCandidates( - room, - 'originTsValidCall', - 'GHTYAJCE', - [ - { - 'candidate': 'candidate:0 1 UDP 2122252543 uwu 50184 typ host', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - }, - { - 'candidate': - 'candidate:3 1 TCP 2105524479 uwu 9 typ host tcptype active', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - } - ], - txid: '1234'); + room, + 'originTsValidCall', + 'GHTYAJCE', + [ + { + 'candidate': 'candidate:0 1 UDP 2122252543 uwu 50184 typ host', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + }, + { + 'candidate': + 'candidate:3 1 TCP 2105524479 uwu 9 typ host tcptype active', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + } + ], + txid: '1234', + ); expect(call.state, CallState.kConnecting); // caller sends select answer - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.select_answer', - content: { - 'call_id': 'originTsValidCall', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'lifetime': 10000, - 'selected_party_id': 'GHTYAJCE' - }, - senderId: '@alice:testing.com', - eventId: 'callerSelectAnswerEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.select_answer', + content: { + 'call_id': 'originTsValidCall', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'lifetime': 10000, + 'selected_party_id': 'GHTYAJCE', + }, + senderId: '@alice:testing.com', + eventId: 'callerSelectAnswerEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); call.pc!.onIceConnectionState! .call(RTCIceConnectionState.RTCIceConnectionStateChecking); @@ -242,57 +318,74 @@ void main() { test('Call answered elsewhere', () async { expect(voip.currentCID, null); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': 'answer_elseWhere', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'callerInviteEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); - await matrix.handleSync(SyncUpdate( + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': 'answer_elseWhere', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'callerInviteEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.candidates', - content: { - 'call_id': 'answer_elseWhere', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'candidates': [ - { - 'candidate': 'candidate:01UDP2122252543uwu50184typhost', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - }, - { - 'candidate': - 'candidate:31TCP2105524479uwu9typhosttcptypeactive', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - } + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.candidates', + content: { + 'call_id': 'answer_elseWhere', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'candidates': [ + { + 'candidate': + 'candidate:01UDP2122252543uwu50184typhost', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + }, + { + 'candidate': + 'candidate:31TCP2105524479uwu9typhosttcptypeactive', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + } + ], + }, + senderId: '@alice:testing.com', + eventId: 'callerCallCandidatesEvent', + originServerTs: DateTime.now(), + ), ], - }, - senderId: '@alice:testing.com', - eventId: 'callerCallCandidatesEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + ), + ), + }, + ), + ), + ); while (voip.currentCID != VoipId(roomId: room.id, callId: 'answer_elseWhere')) { // call invite looks valid, call should be created now :D @@ -300,32 +393,42 @@ void main() { Logs().d('Waiting for currentCID to update'); } expect( - voip.currentCID, VoipId(roomId: room.id, callId: 'answer_elseWhere')); + voip.currentCID, + VoipId(roomId: room.id, callId: 'answer_elseWhere'), + ); final call = voip.calls[voip.currentCID]!; expect(call.state, CallState.kRinging); // caller sends select answer - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.select_answer', - content: { - 'call_id': 'answer_elseWhere', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'lifetime': 10000, - 'selected_party_id': - 'not_us' // selected some other device for answer - }, - senderId: '@alice:testing.com', - eventId: 'callerSelectAnswerEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.select_answer', + content: { + 'call_id': 'answer_elseWhere', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'lifetime': 10000, + 'selected_party_id': + 'not_us', // selected some other device for answer + }, + senderId: '@alice:testing.com', + eventId: 'callerSelectAnswerEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); // wait for select answer to end the call await Future.delayed(Duration(seconds: 2)); // call ended because answered elsewhere @@ -335,57 +438,74 @@ void main() { test('Reject incoming call', () async { expect(voip.currentCID, null); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': 'reject_call', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'callerInviteEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); - await matrix.handleSync(SyncUpdate( + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': 'reject_call', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'callerInviteEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.candidates', - content: { - 'call_id': 'reject_call', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'candidates': [ - { - 'candidate': 'candidate:01UDP2122252543uwu50184typhost', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - }, - { - 'candidate': - 'candidate:31TCP2105524479uwu9typhosttcptypeactive', - 'sdpMid': '0', - 'sdpMLineIndex': 0 - } + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.candidates', + content: { + 'call_id': 'reject_call', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'candidates': [ + { + 'candidate': + 'candidate:01UDP2122252543uwu50184typhost', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + }, + { + 'candidate': + 'candidate:31TCP2105524479uwu9typhosttcptypeactive', + 'sdpMid': '0', + 'sdpMLineIndex': 0, + } + ], + }, + senderId: '@alice:testing.com', + eventId: 'callerCallCandidatesEvent', + originServerTs: DateTime.now(), + ), ], - }, - senderId: '@alice:testing.com', - eventId: 'callerCallCandidatesEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + ), + ), + }, + ), + ), + ); while ( voip.currentCID != VoipId(roomId: room.id, callId: 'reject_call')) { // call invite looks valid, call should be created now :D @@ -414,29 +534,39 @@ void main() { expect(firstCall.state, CallState.kInviteSent); // KABOOM YOU JUST GLARED await Future.delayed(Duration(seconds: 3)); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': 'zzzz_glare_2nd_call', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'callerInviteEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': 'zzzz_glare_2nd_call', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'callerInviteEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); await Future.delayed(Duration(seconds: 3)); expect( - voip.currentCID, VoipId(roomId: room.id, callId: firstCall.callId)); + voip.currentCID, + VoipId(roomId: room.id, callId: firstCall.callId), + ); await firstCall.hangup(reason: CallErrorCode.userBusy); }); test('Glare before invite was sent', () async { @@ -450,29 +580,39 @@ void main() { // KABOOM YOU JUST GLARED, but this tiem you were still preparing your call // so just cancel that instead await Future.delayed(Duration(seconds: 3)); - await matrix.handleSync(SyncUpdate( + await matrix.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - type: 'm.call.invite', - content: { - 'lifetime': 60000, - 'call_id': 'zzzz_glare_2nd_call', - 'party_id': 'GHTYAJCE_caller', - 'version': '1', - 'offer': {'type': 'offer', 'sdp': 'sdp'} - }, - senderId: '@alice:testing.com', - eventId: 'callerInviteEvent', - originServerTs: DateTime.now(), - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent( + type: 'm.call.invite', + content: { + 'lifetime': 60000, + 'call_id': 'zzzz_glare_2nd_call', + 'party_id': 'GHTYAJCE_caller', + 'version': '1', + 'offer': {'type': 'offer', 'sdp': 'sdp'}, + }, + senderId: '@alice:testing.com', + eventId: 'callerInviteEvent', + originServerTs: DateTime.now(), + ), + ], + ), + ), + }, + ), + ), + ); await Future.delayed(Duration(seconds: 3)); - expect(voip.currentCID, - VoipId(roomId: room.id, callId: 'zzzz_glare_2nd_call')); + expect( + voip.currentCID, + VoipId(roomId: room.id, callId: 'zzzz_glare_2nd_call'), + ); }); test('getFamedlyCallEvents sort order', () { @@ -491,7 +631,7 @@ void main() { roomId: room.id, membershipId: voip.currentSessionId, ).toJson(), - ] + ], }, type: EventTypes.GroupCallMember, eventId: 'asdfasdf', @@ -514,7 +654,7 @@ void main() { roomId: room.id, membershipId: voip.currentSessionId, ).toJson(), - ] + ], }, type: EventTypes.GroupCallMember, eventId: 'asdfasdf', @@ -537,7 +677,7 @@ void main() { roomId: room.id, membershipId: voip.currentSessionId, ).toJson(), - ] + ], }, type: EventTypes.GroupCallMember, eventId: 'asdfasdf', @@ -562,7 +702,7 @@ void main() { roomId: room.id, membershipId: voip.currentSessionId, ).toJson(), - ] + ], }, type: EventTypes.GroupCallMember, eventId: 'asdfasdf', @@ -572,22 +712,38 @@ void main() { stateKey: '@test3:example.com', ), ); - expect(room.getFamedlyCallEvents().entries.elementAt(0).key, - '@test3:example.com'); - expect(room.getFamedlyCallEvents().entries.elementAt(1).key, - '@test2:example.com'); - expect(room.getFamedlyCallEvents().entries.elementAt(2).key, - '@test2.0:example.com'); - expect(room.getFamedlyCallEvents().entries.elementAt(3).key, - '@test1:example.com'); - expect(room.getCallMembershipsFromRoom().entries.elementAt(0).key, - '@test3:example.com'); - expect(room.getCallMembershipsFromRoom().entries.elementAt(1).key, - '@test2:example.com'); - expect(room.getCallMembershipsFromRoom().entries.elementAt(2).key, - '@test2.0:example.com'); - expect(room.getCallMembershipsFromRoom().entries.elementAt(3).key, - '@test1:example.com'); + expect( + room.getFamedlyCallEvents().entries.elementAt(0).key, + '@test3:example.com', + ); + expect( + room.getFamedlyCallEvents().entries.elementAt(1).key, + '@test2:example.com', + ); + expect( + room.getFamedlyCallEvents().entries.elementAt(2).key, + '@test2.0:example.com', + ); + expect( + room.getFamedlyCallEvents().entries.elementAt(3).key, + '@test1:example.com', + ); + expect( + room.getCallMembershipsFromRoom().entries.elementAt(0).key, + '@test3:example.com', + ); + expect( + room.getCallMembershipsFromRoom().entries.elementAt(1).key, + '@test2:example.com', + ); + expect( + room.getCallMembershipsFromRoom().entries.elementAt(2).key, + '@test2.0:example.com', + ); + expect( + room.getCallMembershipsFromRoom().entries.elementAt(3).key, + '@test1:example.com', + ); }); test('Enabling group calls', () async { @@ -601,7 +757,7 @@ void main() { content: { 'events': {EventTypes.GroupCallMember: 100}, 'state_default': 50, - 'users_default': 0 + 'users_default': 0, }, originServerTs: DateTime.now(), stateKey: '', @@ -619,7 +775,7 @@ void main() { content: { 'events': {EventTypes.GroupCallMember: 27}, 'state_default': 50, - 'users_default': 49 + 'users_default': 49, }, originServerTs: DateTime.now(), stateKey: '', @@ -631,17 +787,18 @@ void main() { // state_default 50 and user_default 0, use enableGroupCall room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.power_levels', - room: room, - eventId: '123', - content: { - 'state_default': 50, - 'users': {'@test:fakeServer.notExisting': 100}, - 'users_default': 0 - }, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123', + content: { + 'state_default': 50, + 'users': {'@test:fakeServer.notExisting': 100}, + 'users_default': 0, + }, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.canJoinGroupCall, true); // because admin expect(room.groupCallsEnabledForEveryone, false); @@ -693,27 +850,28 @@ void main() { test('group call participants count', () { room.setState( Event( - senderId: '@test1:example.com', - type: EventTypes.GroupCallMember, - room: room, - eventId: '1234177', - content: { - 'memberships': [ - CallMembership( - userId: '@test1:example.com', - callId: 'participants_count', - backend: MeshBackend(), - deviceId: '1111', - expiresTs: DateTime.now() - .subtract(Duration(hours: 1)) - .millisecondsSinceEpoch, - roomId: room.id, - membershipId: voip.currentSessionId, - ).toJson(), - ] - }, - originServerTs: DateTime.now(), - stateKey: '@test1:example.com'), + senderId: '@test1:example.com', + type: EventTypes.GroupCallMember, + room: room, + eventId: '1234177', + content: { + 'memberships': [ + CallMembership( + userId: '@test1:example.com', + callId: 'participants_count', + backend: MeshBackend(), + deviceId: '1111', + expiresTs: DateTime.now() + .subtract(Duration(hours: 1)) + .millisecondsSinceEpoch, + roomId: room.id, + membershipId: voip.currentSessionId, + ).toJson(), + ], + }, + originServerTs: DateTime.now(), + stateKey: '@test1:example.com', + ), ); expect(room.groupCallParticipantCount('participants_count'), 0); @@ -721,52 +879,54 @@ void main() { room.setState( Event( - senderId: '@test2:example.com', - type: EventTypes.GroupCallMember, - room: room, - eventId: '1234177', - content: { - 'memberships': [ - CallMembership( - userId: '@test2:example.com', - callId: 'participants_count', - backend: MeshBackend(), - deviceId: '1111', - expiresTs: DateTime.now() - .add(Duration(hours: 1)) - .millisecondsSinceEpoch, - roomId: room.id, - membershipId: voip.currentSessionId, - ).toJson(), - ] - }, - originServerTs: DateTime.now(), - stateKey: '@test2:example.com'), + senderId: '@test2:example.com', + type: EventTypes.GroupCallMember, + room: room, + eventId: '1234177', + content: { + 'memberships': [ + CallMembership( + userId: '@test2:example.com', + callId: 'participants_count', + backend: MeshBackend(), + deviceId: '1111', + expiresTs: DateTime.now() + .add(Duration(hours: 1)) + .millisecondsSinceEpoch, + roomId: room.id, + membershipId: voip.currentSessionId, + ).toJson(), + ], + }, + originServerTs: DateTime.now(), + stateKey: '@test2:example.com', + ), ); expect(room.groupCallParticipantCount('participants_count'), 1); expect(room.hasActiveGroupCall, true); room.setState( Event( - senderId: '@test3:example.com', - type: EventTypes.GroupCallMember, - room: room, - eventId: '1231234124123', - content: { - 'memberships': [ - CallMembership( - userId: '@test3:example.com', - callId: 'participants_count', - backend: MeshBackend(), - deviceId: '1111', - expiresTs: DateTime.now().millisecondsSinceEpoch, - roomId: room.id, - membershipId: voip.currentSessionId, - ).toJson(), - ] - }, - originServerTs: DateTime.now(), - stateKey: '@test3:example.com'), + senderId: '@test3:example.com', + type: EventTypes.GroupCallMember, + room: room, + eventId: '1231234124123', + content: { + 'memberships': [ + CallMembership( + userId: '@test3:example.com', + callId: 'participants_count', + backend: MeshBackend(), + deviceId: '1111', + expiresTs: DateTime.now().millisecondsSinceEpoch, + roomId: room.id, + membershipId: voip.currentSessionId, + ).toJson(), + ], + }, + originServerTs: DateTime.now(), + stateKey: '@test3:example.com', + ), ); expect(room.groupCallParticipantCount('participants_count'), 2); diff --git a/test/canonical_json_test.dart b/test/canonical_json_test.dart index 9cdddd36b..f8efb1513 100644 --- a/test/canonical_json_test.dart +++ b/test/canonical_json_test.dart @@ -38,17 +38,19 @@ void main() { 'display_name': 'John Doe', 'three_pids': [ {'medium': 'email', 'address': 'john.doe@example.org'}, - {'medium': 'msisdn', 'address': '123456789'} - ] - } - } + {'medium': 'msisdn', 'address': '123456789'}, + ], + }, + }, }, '{"a":null}': {'a': null}, }; for (final entry in textMap.entries) { test(entry.key, () async { expect( - entry.key, String.fromCharCodes(canonicalJson.encode(entry.value))); + entry.key, + String.fromCharCodes(canonicalJson.encode(entry.value)), + ); }); } }); diff --git a/test/client_test.dart b/test/client_test.dart index 554456357..dedabb09b 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -45,8 +45,11 @@ void main() { final dbPath = join(Directory.current.path, 'test.sqlite'); setUp(() async { - expect(await File(dbPath).exists(), false, - reason: '$dbPath should not exist'); + expect( + await File(dbPath).exists(), + false, + reason: '$dbPath should not exist', + ); clientOnPath = await getClient( databasePath: dbPath, ); @@ -120,8 +123,10 @@ void main() { } catch (exception) { expect(exception.toString().isNotEmpty, true); } - await matrix.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await matrix.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); expect(matrix.homeserver.toString(), 'https://fakeserver.notexisting'); final available = await matrix.checkUsernameAvailability('testuser'); @@ -154,8 +159,10 @@ void main() { expect(sync.nextBatch == matrix.prevBatch, true); expect(matrix.accountData.length, 10); - expect(matrix.getDirectChatFromUserId('@bob:example.com'), - '!726s6s6q:example.com'); + expect( + matrix.getDirectChatFromUserId('@bob:example.com'), + '!726s6s6q:example.com', + ); expect(matrix.rooms[2].directChatMatrixID, '@bob:example.com'); expect(matrix.directChats, matrix.accountData['m.direct']?.content); // ignore: deprecated_member_use_from_same_package @@ -165,27 +172,35 @@ void main() { expect(matrix.rooms[2].typingUsers[0].id, '@alice:example.com'); expect(matrix.rooms[2].roomAccountData.length, 3); expect(matrix.rooms[2].encrypted, true); - expect(matrix.rooms[2].encryptionAlgorithm, - Client.supportedGroupEncryptionAlgorithms.first); expect( - matrix.rooms[2].receiptState.global.otherUsers['@alice:example.com'] - ?.ts, - 1436451550453); + matrix.rooms[2].encryptionAlgorithm, + Client.supportedGroupEncryptionAlgorithms.first, + ); expect( - matrix.rooms[2].receiptState.global.otherUsers['@alice:example.com'] - ?.eventId, - '\$7365636s6r6432:example.com'); + matrix + .rooms[2].receiptState.global.otherUsers['@alice:example.com']?.ts, + 1436451550453, + ); + expect( + matrix.rooms[2].receiptState.global.otherUsers['@alice:example.com'] + ?.eventId, + '\$7365636s6r6432:example.com', + ); final inviteRoom = matrix.rooms .singleWhere((room) => room.membership == Membership.invite); expect(inviteRoom.name, 'My Room Name'); expect(inviteRoom.states[EventTypes.RoomMember]?.length, 1); expect(matrix.rooms.length, 3); - expect(matrix.rooms[2].canonicalAlias, - "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}"); - // ignore: deprecated_member_use_from_same_package - expect(matrix.presences['@alice:example.com']?.presence, - PresenceType.online); + expect( + matrix.rooms[2].canonicalAlias, + "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}", + ); + expect( + // ignore: deprecated_member_use_from_same_package + matrix.presences['@alice:example.com']?.presence, + PresenceType.online, + ); expect(presenceCounter, 1); expect(accountDataCounter, 10); @@ -197,53 +212,60 @@ void main() { expect(matrix.userDeviceKeys['@alice:example.com']?.outdated, false); expect(matrix.userDeviceKeys['@alice:example.com']?.deviceKeys.length, 2); expect( - matrix.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'] - ?.verified, - false); + matrix.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'] + ?.verified, + false, + ); expect(matrix.wellKnown, isNull); - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'device_lists': { - 'changed': [ - '@alice:example.com', - ], - 'left': [ - '@bob:example.com', - ], - } - })); + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'device_lists': { + 'changed': [ + '@alice:example.com', + ], + 'left': [ + '@bob:example.com', + ], + }, + }), + ); await Future.delayed(Duration(milliseconds: 50)); expect(matrix.userDeviceKeys.length, 3); expect(matrix.userDeviceKeys['@alice:example.com']?.outdated, true); - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - '!726s6s6q:example.com': { - 'state': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.room.canonical_alias', - 'content': {'alias': ''}, - 'state_key': '', - 'origin_server_ts': 1417731086799, - 'event_id': '66697273743033:example.com' - } - ] - } - } - } - } - })); + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + '!726s6s6q:example.com': { + 'state': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.room.canonical_alias', + 'content': {'alias': ''}, + 'state_key': '', + 'origin_server_ts': 1417731086799, + 'event_id': '66697273743033:example.com', + } + ], + }, + }, + }, + }, + }), + ); await Future.delayed(Duration(milliseconds: 50)); expect( - matrix.getRoomByAlias( - "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}"), - null); + matrix.getRoomByAlias( + "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}", + ), + null, + ); await matrix.onEvent.close(); @@ -291,8 +313,10 @@ void main() { expect(eventUpdateList[9].roomID, '!726s6s6q:example.com'); expect(eventUpdateList[9].type, EventUpdateType.accountData); - expect(eventUpdateList[10].content['type'], - 'org.example.custom.room.config'); + expect( + eventUpdateList[10].content['type'], + 'org.example.custom.room.config', + ); expect(eventUpdateList[10].roomID, '!726s6s6q:example.com'); expect(eventUpdateList[10].type, EventUpdateType.accountData); @@ -353,10 +377,15 @@ void main() { final roomFromDb = await matrix.database?.getSingleRoom(matrix, roomId); expect( - matrix.getRoomById(roomId)?.roomAccountData[key]?.content, content); + matrix.getRoomById(roomId)?.roomAccountData[key]?.content, + content, + ); expect(roomFromList?.roomAccountData[key]?.content, content); - expect(roomFromDb?.roomAccountData[key]?.content, content, - skip: 'The single room function does not load account data'); + expect( + roomFromDb?.roomAccountData[key]?.content, + content, + skip: 'The single room function does not load account data', + ); }); test('Logout', () async { @@ -389,8 +418,10 @@ void main() { } catch (exception) { expect(exception.toString().isNotEmpty, true); } - await matrix.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await matrix.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); expect(matrix.homeserver.toString(), 'https://fakeserver.notexisting'); final available = await matrix.checkUsernameAvailability('testuser'); @@ -426,21 +457,26 @@ void main() { test('Logout but this time server is dead', () async { final oldapi = FakeMatrixApi.currentApi?.api; expect( - (FakeMatrixApi.currentApi?.api['PUT']?.keys.where((element) => - element.startsWith('/client/v3/room_keys/keys?version')))?.length, - 1); + (FakeMatrixApi.currentApi?.api['PUT']?.keys.where( + (element) => element.startsWith('/client/v3/room_keys/keys?version'), + ))?.length, + 1, + ); // not a huge fan, open to ideas FakeMatrixApi.currentApi?.api = {}; // random sanity test to test if the test to test the breaking server hack works. expect( - (FakeMatrixApi.currentApi?.api['PUT']?.keys.where((element) => - element.startsWith('/client/v3/room_keys/keys?version')))?.length, - null); + (FakeMatrixApi.currentApi?.api['PUT']?.keys.where( + (element) => element.startsWith('/client/v3/room_keys/keys?version'), + ))?.length, + null, + ); try { await matrix.logout(); } catch (e) { Logs().w( - 'Ignore red warnings for this test, test is to check if database is cleared even if server breaks '); + 'Ignore red warnings for this test, test is to check if database is cleared even if server breaks ', + ); } expect(matrix.accessToken == null, true); @@ -460,12 +496,16 @@ void main() { databaseBuilder: getDatabase, ); - await matrix.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await matrix.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); - final loginResp = await matrix.login(LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: 'test'), - password: '1234'); + final loginResp = await matrix.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: 'test'), + password: '1234', + ); expect(loginResp.userId.isNotEmpty, true); }); @@ -493,179 +533,191 @@ void main() { final roomId = '!726s6s6q:example.com'; final room = matrix.getRoomById(roomId)!; // put an important state event in-memory - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - roomId: { - 'state': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.room.name', - 'content': {'name': 'foxies'}, - 'state_key': '', - 'origin_server_ts': 1417731086799, - 'event_id': '66697273743033:example.com' - } - ] - } - } - } - } - })); + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + roomId: { + 'state': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.room.name', + 'content': {'name': 'foxies'}, + 'state_key': '', + 'origin_server_ts': 1417731086799, + 'event_id': '66697273743033:example.com', + } + ], + }, + }, + }, + }, + }), + ); expect(room.getState('m.room.name')?.content['name'], 'foxies'); // drop an unimportant state event from in-memory handling - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - roomId: { - 'state': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'com.famedly.custom', - 'content': {'name': 'foxies'}, - 'state_key': '', - 'origin_server_ts': 1417731086799, - 'event_id': '66697273743033:example.com' - } - ] - } - } - } - } - })); + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + roomId: { + 'state': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'com.famedly.custom', + 'content': {'name': 'foxies'}, + 'state_key': '', + 'origin_server_ts': 1417731086799, + 'event_id': '66697273743033:example.com', + } + ], + }, + }, + }, + }, + }), + ); expect(room.getState('com.famedly.custom'), null); // persist normal room messages - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - roomId: { - 'timeline': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.room.message', - 'content': { - 'msgtype': 'm.text', - 'body': 'meow' - }, - 'origin_server_ts': 1417731086799, - 'event_id': '\$last:example.com' - } - ] - } - } - } - } - })); + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + roomId: { + 'timeline': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.room.message', + 'content': { + 'msgtype': 'm.text', + 'body': 'meow', + }, + 'origin_server_ts': 1417731086799, + 'event_id': '\$last:example.com', + } + ], + }, + }, + }, + }, + }), + ); expect(room.lastEvent!.content['body'], 'meow'); // ignore edits - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - roomId: { - 'timeline': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.room.message', - 'content': { - 'msgtype': 'm.text', - 'body': '* floooof', - 'm.new_content': { + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + roomId: { + 'timeline': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.room.message', + 'content': { 'msgtype': 'm.text', - 'body': 'floooof', + 'body': '* floooof', + 'm.new_content': { + 'msgtype': 'm.text', + 'body': 'floooof', + }, + 'm.relates_to': { + 'rel_type': 'm.replace', + 'event_id': '\$other:example.com', + }, }, - 'm.relates_to': { - 'rel_type': 'm.replace', - 'event_id': '\$other:example.com' - }, - }, - 'origin_server_ts': 1417731086799, - 'event_id': '\$edit:example.com' - } - ] - } - } - } - } - })); + 'origin_server_ts': 1417731086799, + 'event_id': '\$edit:example.com', + } + ], + }, + }, + }, + }, + }), + ); expect(room.lastEvent!.content['body'], 'meow'); // accept edits to the last event - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - roomId: { - 'timeline': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.room.message', - 'content': { - 'msgtype': 'm.text', - 'body': '* floooof', - 'm.new_content': { + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + roomId: { + 'timeline': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.room.message', + 'content': { 'msgtype': 'm.text', - 'body': 'floooof', - }, - 'm.relates_to': { - 'rel_type': 'm.replace', - 'event_id': '\$last:example.com' + 'body': '* floooof', + 'm.new_content': { + 'msgtype': 'm.text', + 'body': 'floooof', + }, + 'm.relates_to': { + 'rel_type': 'm.replace', + 'event_id': '\$last:example.com', + }, }, - }, - 'origin_server_ts': 1417731086799, - 'event_id': '\$edit:example.com' - } - ] - } - } - } - } - })); + 'origin_server_ts': 1417731086799, + 'event_id': '\$edit:example.com', + } + ], + }, + }, + }, + }, + }), + ); expect(room.lastEvent!.content['body'], '* floooof'); // accepts a consecutive edit - await matrix.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fakesync', - 'rooms': { - 'join': { - roomId: { - 'timeline': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.room.message', - 'content': { - 'msgtype': 'm.text', - 'body': '* foxies', - 'm.new_content': { + await matrix.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fakesync', + 'rooms': { + 'join': { + roomId: { + 'timeline': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.room.message', + 'content': { 'msgtype': 'm.text', - 'body': 'foxies', + 'body': '* foxies', + 'm.new_content': { + 'msgtype': 'm.text', + 'body': 'foxies', + }, + 'm.relates_to': { + 'rel_type': 'm.replace', + 'event_id': '\$last:example.com', + }, }, - 'm.relates_to': { - 'rel_type': 'm.replace', - 'event_id': '\$last:example.com' - }, - }, - 'origin_server_ts': 1417731086799, - 'event_id': '\$edit2:example.com' - } - ] - } - } - } - } - })); + 'origin_server_ts': 1417731086799, + 'event_id': '\$edit2:example.com', + } + ], + }, + }, + }, + }, + }), + ); expect(room.lastEvent!.content['body'], '* foxies'); }); @@ -689,16 +741,18 @@ void main() { rooms: RoomsUpdate( join: { matrix.rooms.first.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent( - eventId: 'abcd', - type: EventTypes.RoomMember, - content: {'membership': 'join'}, - senderId: '@alice:example.com', - stateKey: '@alice:example.com', - originServerTs: DateTime.now(), - ), - ]), + timeline: TimelineUpdate( + events: [ + MatrixEvent( + eventId: 'abcd', + type: EventTypes.RoomMember, + content: {'membership': 'join'}, + senderId: '@alice:example.com', + stateKey: '@alice:example.com', + originServerTs: DateTime.now(), + ), + ], + ), ), }, ), @@ -715,12 +769,27 @@ void main() { client.rooms.clear(); await client.database?.clearCache(); - await client.handleSync(SyncUpdate.fromJson(jsonDecode( - '{"next_batch":"s198510_227245_8_1404_23586_11_51065_267416_0_2639","rooms":{"invite":{"!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de":{"invite_state":{"events":[{"type":"m.room.create","content":{"type":"de.gematik.tim.roomtype.default.v1","room_version":"10","creator":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":""},{"type":"m.room.encryption","content":{"algorithm":"m.megolm.v1.aes-sha2"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":""},{"type":"m.room.join_rules","content":{"join_rule":"invite"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":""},{"type":"m.room.member","content":{"membership":"join","displayname":"Tóboggen, Veronika Freifrau"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"},{"type":"m.room.member","content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de"}]}}}},"presence":{"events":[{"type":"m.presence","content":{"presence":"online","last_active_ago":5948,"currently_active":true},"sender":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de"}]},"device_one_time_keys_count":{"signed_curve25519":66},"device_unused_fallback_key_types":["signed_curve25519"],"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"]}'))); - await client.handleSync(SyncUpdate.fromJson(jsonDecode( - '{"next_batch":"s198511_227245_8_1404_23588_11_51066_267416_0_2639","account_data":{"events":[{"type":"m.direct","content":{"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de":["!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de"]}}]},"device_one_time_keys_count":{"signed_curve25519":65},"device_unused_fallback_key_types":["signed_curve25519"],"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"]}'))); - await client.handleSync(SyncUpdate.fromJson(jsonDecode( - '{"next_batch":"s198512_227245_8_1404_23588_11_51066_267416_0_2639","rooms":{"join":{"!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de":{"summary":{"m.heroes":["@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"],"m.joined_member_count":2,"m.invited_member_count":0},"state":{"events":[{"type":"m.room.create","content":{"type":"de.gematik.tim.roomtype.default.v1","room_version":"10","creator":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$qSgXGXjly6p5Kwbdb_PMBC_EF7nzHDbM23mvJFVeoiE","origin_server_ts":1709565579735,"unsigned":{"age":2255}}]},"timeline":{"events":[{"type":"m.room.member","content":{"membership":"join","displayname":"Tóboggen, Veronika Freifrau"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","event_id":"\$rQzxxTrSd9Y0koxIGlkalPAV_lwu94jLOA-8PSunY24","origin_server_ts":1709565579871,"unsigned":{"age":2119,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.power_levels","content":{"users":{"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de":100,"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de":100},"users_default":0,"events":{"m.room.name":50,"m.room.power_levels":100,"m.room.history_visibility":100,"m.room.canonical_alias":50,"m.room.avatar":50,"m.room.tombstone":100,"m.room.server_acl":100,"m.room.encryption":100},"events_default":0,"state_default":50,"ban":50,"kick":50,"redact":50,"invite":0,"historical":100},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$d6sgGs8PmkAbC3Iw3CkPT1QSub2zFTTvytegOxkPYPs","origin_server_ts":1709565579966,"unsigned":{"age":2024,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.join_rules","content":{"join_rule":"invite"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$EnA2Podch5181X4G1ZX34zaFGS_V4ZCZzLkBEfS_qyg","origin_server_ts":1709565579979,"unsigned":{"age":2011,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.history_visibility","content":{"history_visibility":"shared"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$6tNNo6ZkpZZrHrn8ZjXhMqI0CNv-VNNBw4R0h3_O-Tc","origin_server_ts":1709565579979,"unsigned":{"age":2011,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.guest_access","content":{"guest_access":"can_join"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$ViuL_LpN1sY9oYcGwycNjtp6FcGj__smUg8mzj3oa2o","origin_server_ts":1709565579980,"unsigned":{"age":2010,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.encryption","content":{"algorithm":"m.megolm.v1.aes-sha2"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$_e0az7OP7D78QU7DItiRAtlHlZmA07B5wenR93x5V1E","origin_server_ts":1709565579981,"unsigned":{"age":2009,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.member","content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","event_id":"\$4sZ3CF67SUh0n5WG0ZKS47Epj9B_d842RJjnrQmUKQo","origin_server_ts":1709565580185,"unsigned":{"age":1805,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.notsoencrypted","content":{"algorithm":"m.megolm.v1.aes-sha2","ciphertext":"AwgAEpACEZw8Ymg99Yfl7VsXRIdczlQ3+YSJ6te3o6ka/XXP0h4ZsgR2bu1Q8puQ77fOpwX5dPnrrCi5SQg9Zv5/u+0QbFV4FKE/k03Vxao/tiswb6wST14x9kYkwViOrZe7fzg7VF9tCi8U88TqxGsPDDOVjO+WNxG8I9ldP1zvPsxYzVSyGPhaB5E+q6llwlXcQ56wvpf7Ke7gX4Ly2Dlxa8Bmy7aUSCBoWAt/xFRdzCOsE9qI8oxzuvk4RF0H/7bY+4DkGTsP1rIYgA7Q0JueIFb47Yu6pK26BCKo1yPAR8qvpe8vGBICm4slMbKaJN4RqBHtR0zc12E5DXud91o3mArqTksv1NEbI1F4XgDREl76WBw8a7MafDSuun09JuWpGxzPHvLVOUVny6tTJPRutsZLkmnTeMTiXnsPexUiY7UTYlzOMeeoUSTDuJXJz6CM+gSc52CiKoHK/gE","device_id":"TNLOYXJFXM","sender_key":"e9W0gpUcSEKOQ8P/xIdroHUpP7yG4EjQfueiAngESRk","session_id":"hhZ8TBs9Xp0dmuvC6XpDBYsAKnTqb8WiBhZMzHcbBXI"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","event_id":"\$KKIZX8cuB3S3uzS7CDtRlTkcaJRW73e2HW2NuW6OTEg","origin_server_ts":1709565580991,"unsigned":{"age":999,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.member","content":{"membership":"join","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","event_id":"\$UNSLEyhC_93oQlt0tWoai4CCd3LH2GJJexM0WN2wxCA","origin_server_ts":1709565581813,"unsigned":{"replaces_state":"\$4sZ3CF67SUh0n5WG0ZKS47Epj9B_d842RJjnrQmUKQo","prev_content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"prev_sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","age":177,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.member","content":{"membership":"join","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","event_id":"\$UNSLEyhC_93oQlt0tWoai4CCd3LH2GJJexM0WN2wxCA","origin_server_ts":1709565581813,"unsigned":{"replaces_state":"\$4sZ3CF67SUh0n5WG0ZKS47Epj9B_d842RJjnrQmUKQo","prev_content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"prev_sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","age":177,"com.famedly.famedlysdk.message_sending_status":2}}],"limited":true,"prev_batch":"s198503_227245_8_1404_23588_11_51066_267416_0_2639"},"ephemeral":{"events":[]},"account_data":{"events":[]},"unread_notifications":{"highlight_count":0,"notification_count":0}}}},"presence":{"events":[{"type":"m.presence","content":{"presence":"online","last_active_ago":843,"currently_active":true},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"}]},"device_lists":{"changed":["@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"],"left":[]},"device_one_time_keys_count":{"signed_curve25519":65},"device_unused_fallback_key_types":["signed_curve25519"],"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"]}'))); + await client.handleSync( + SyncUpdate.fromJson( + jsonDecode( + '{"next_batch":"s198510_227245_8_1404_23586_11_51065_267416_0_2639","rooms":{"invite":{"!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de":{"invite_state":{"events":[{"type":"m.room.create","content":{"type":"de.gematik.tim.roomtype.default.v1","room_version":"10","creator":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":""},{"type":"m.room.encryption","content":{"algorithm":"m.megolm.v1.aes-sha2"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":""},{"type":"m.room.join_rules","content":{"join_rule":"invite"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":""},{"type":"m.room.member","content":{"membership":"join","displayname":"Tóboggen, Veronika Freifrau"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"},{"type":"m.room.member","content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de"}]}}}},"presence":{"events":[{"type":"m.presence","content":{"presence":"online","last_active_ago":5948,"currently_active":true},"sender":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de"}]},"device_one_time_keys_count":{"signed_curve25519":66},"device_unused_fallback_key_types":["signed_curve25519"],"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"]}', + ), + ), + ); + await client.handleSync( + SyncUpdate.fromJson( + jsonDecode( + '{"next_batch":"s198511_227245_8_1404_23588_11_51066_267416_0_2639","account_data":{"events":[{"type":"m.direct","content":{"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de":["!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de"]}}]},"device_one_time_keys_count":{"signed_curve25519":65},"device_unused_fallback_key_types":["signed_curve25519"],"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"]}', + ), + ), + ); + await client.handleSync( + SyncUpdate.fromJson( + jsonDecode( + '{"next_batch":"s198512_227245_8_1404_23588_11_51066_267416_0_2639","rooms":{"join":{"!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de":{"summary":{"m.heroes":["@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"],"m.joined_member_count":2,"m.invited_member_count":0},"state":{"events":[{"type":"m.room.create","content":{"type":"de.gematik.tim.roomtype.default.v1","room_version":"10","creator":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$qSgXGXjly6p5Kwbdb_PMBC_EF7nzHDbM23mvJFVeoiE","origin_server_ts":1709565579735,"unsigned":{"age":2255}}]},"timeline":{"events":[{"type":"m.room.member","content":{"membership":"join","displayname":"Tóboggen, Veronika Freifrau"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","event_id":"\$rQzxxTrSd9Y0koxIGlkalPAV_lwu94jLOA-8PSunY24","origin_server_ts":1709565579871,"unsigned":{"age":2119,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.power_levels","content":{"users":{"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de":100,"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de":100},"users_default":0,"events":{"m.room.name":50,"m.room.power_levels":100,"m.room.history_visibility":100,"m.room.canonical_alias":50,"m.room.avatar":50,"m.room.tombstone":100,"m.room.server_acl":100,"m.room.encryption":100},"events_default":0,"state_default":50,"ban":50,"kick":50,"redact":50,"invite":0,"historical":100},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$d6sgGs8PmkAbC3Iw3CkPT1QSub2zFTTvytegOxkPYPs","origin_server_ts":1709565579966,"unsigned":{"age":2024,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.join_rules","content":{"join_rule":"invite"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$EnA2Podch5181X4G1ZX34zaFGS_V4ZCZzLkBEfS_qyg","origin_server_ts":1709565579979,"unsigned":{"age":2011,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.history_visibility","content":{"history_visibility":"shared"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$6tNNo6ZkpZZrHrn8ZjXhMqI0CNv-VNNBw4R0h3_O-Tc","origin_server_ts":1709565579979,"unsigned":{"age":2011,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.guest_access","content":{"guest_access":"can_join"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$ViuL_LpN1sY9oYcGwycNjtp6FcGj__smUg8mzj3oa2o","origin_server_ts":1709565579980,"unsigned":{"age":2010,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.encryption","content":{"algorithm":"m.megolm.v1.aes-sha2"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"","event_id":"\$_e0az7OP7D78QU7DItiRAtlHlZmA07B5wenR93x5V1E","origin_server_ts":1709565579981,"unsigned":{"age":2009,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.member","content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","event_id":"\$4sZ3CF67SUh0n5WG0ZKS47Epj9B_d842RJjnrQmUKQo","origin_server_ts":1709565580185,"unsigned":{"age":1805,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.notsoencrypted","content":{"algorithm":"m.megolm.v1.aes-sha2","ciphertext":"AwgAEpACEZw8Ymg99Yfl7VsXRIdczlQ3+YSJ6te3o6ka/XXP0h4ZsgR2bu1Q8puQ77fOpwX5dPnrrCi5SQg9Zv5/u+0QbFV4FKE/k03Vxao/tiswb6wST14x9kYkwViOrZe7fzg7VF9tCi8U88TqxGsPDDOVjO+WNxG8I9ldP1zvPsxYzVSyGPhaB5E+q6llwlXcQ56wvpf7Ke7gX4Ly2Dlxa8Bmy7aUSCBoWAt/xFRdzCOsE9qI8oxzuvk4RF0H/7bY+4DkGTsP1rIYgA7Q0JueIFb47Yu6pK26BCKo1yPAR8qvpe8vGBICm4slMbKaJN4RqBHtR0zc12E5DXud91o3mArqTksv1NEbI1F4XgDREl76WBw8a7MafDSuun09JuWpGxzPHvLVOUVny6tTJPRutsZLkmnTeMTiXnsPexUiY7UTYlzOMeeoUSTDuJXJz6CM+gSc52CiKoHK/gE","device_id":"TNLOYXJFXM","sender_key":"e9W0gpUcSEKOQ8P/xIdroHUpP7yG4EjQfueiAngESRk","session_id":"hhZ8TBs9Xp0dmuvC6XpDBYsAKnTqb8WiBhZMzHcbBXI"},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","event_id":"\$KKIZX8cuB3S3uzS7CDtRlTkcaJRW73e2HW2NuW6OTEg","origin_server_ts":1709565580991,"unsigned":{"age":999,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.member","content":{"membership":"join","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","event_id":"\$UNSLEyhC_93oQlt0tWoai4CCd3LH2GJJexM0WN2wxCA","origin_server_ts":1709565581813,"unsigned":{"replaces_state":"\$4sZ3CF67SUh0n5WG0ZKS47Epj9B_d842RJjnrQmUKQo","prev_content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"prev_sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","age":177,"com.famedly.famedlysdk.message_sending_status":2}},{"type":"m.room.member","content":{"membership":"join","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"sender":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","state_key":"@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","event_id":"\$UNSLEyhC_93oQlt0tWoai4CCd3LH2GJJexM0WN2wxCA","origin_server_ts":1709565581813,"unsigned":{"replaces_state":"\$4sZ3CF67SUh0n5WG0ZKS47Epj9B_d842RJjnrQmUKQo","prev_content":{"is_direct":true,"membership":"invite","displayname":"Düsterbehn-Hardenbergshausen, Michael von"},"prev_sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de","age":177,"com.famedly.famedlysdk.message_sending_status":2}}],"limited":true,"prev_batch":"s198503_227245_8_1404_23588_11_51066_267416_0_2639"},"ephemeral":{"events":[]},"account_data":{"events":[]},"unread_notifications":{"highlight_count":0,"notification_count":0}}}},"presence":{"events":[{"type":"m.presence","content":{"presence":"online","last_active_ago":843,"currently_active":true},"sender":"@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"}]},"device_lists":{"changed":["@duesterbehn-hardenbergshausen_michael_von:tim-alpha.staging.famedly.de","@toboggen_veronika_freifrau:tim-alpha.staging.famedly.de"],"left":[]},"device_one_time_keys_count":{"signed_curve25519":65},"device_unused_fallback_key_types":["signed_curve25519"],"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"]}', + ), + ), + ); //await client.handleSync(SyncUpdate.fromJson(jsonDecode(''))); final room = client .getRoomById('!bWEUQDujMKwjxkCXYr:tim-alpha.staging.famedly.de')!; @@ -728,7 +797,9 @@ void main() { final participants = await room.requestParticipants(); expect( - participants.where((u) => u.membership == Membership.join).length, 2); + participants.where((u) => u.membership == Membership.join).length, + 2, + ); await client.abortSync(); client.rooms.clear(); @@ -800,8 +871,13 @@ void main() { await client.abortSync(); client.rooms.clear(); await client.database?.clearCache(); - await client.handleSync(SyncUpdate.fromJson(jsonDecode( - '{"next_batch":"s82_571_2_6_39_1_2_34_1","account_data":{"events":[{"type":"m.push_rules","content":{"global":{"underride":[{"conditions":[{"kind":"event_match","key":"type","pattern":"m.call.invite"}],"actions":["notify",{"set_tweak":"sound","value":"ring"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.call","default":true,"enabled":true},{"conditions":[{"kind":"room_member_count","is":"2"},{"kind":"event_match","key":"type","pattern":"m.room.message"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.room_one_to_one","default":true,"enabled":true},{"conditions":[{"kind":"room_member_count","is":"2"},{"kind":"event_match","key":"type","pattern":"m.room.encrypted"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.encrypted_room_one_to_one","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.message"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.message","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.encrypted"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.encrypted","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"im.vector.modular.widgets"},{"kind":"event_match","key":"content.type","pattern":"jitsi"},{"kind":"event_match","key":"state_key","pattern":"*"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".im.vector.jitsi","default":true,"enabled":true}],"sender":[],"room":[],"content":[{"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight"}],"pattern":"056d6976-fb61-47cf-86f0-147387461565","rule_id":".m.rule.contains_user_name","default":true,"enabled":true}],"override":[{"conditions":[],"actions":["dont_notify"],"rule_id":".m.rule.master","default":true,"enabled":false},{"conditions":[{"kind":"event_match","key":"content.msgtype","pattern":"m.notice"}],"actions":["dont_notify"],"rule_id":".m.rule.suppress_notices","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.member"},{"kind":"event_match","key":"content.membership","pattern":"invite"},{"kind":"event_match","key":"state_key","pattern":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.invite_for_me","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.member"}],"actions":["dont_notify"],"rule_id":".m.rule.member_event","default":true,"enabled":true},{"conditions":[{"kind":"contains_display_name"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight"}],"rule_id":".m.rule.contains_display_name","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"content.body","pattern":"@room"},{"kind":"sender_notification_permission","key":"room"}],"actions":["notify",{"set_tweak":"highlight","value":true}],"rule_id":".m.rule.roomnotif","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.tombstone"},{"kind":"event_match","key":"state_key","pattern":""}],"actions":["notify",{"set_tweak":"highlight","value":true}],"rule_id":".m.rule.tombstone","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.reaction"}],"actions":["dont_notify"],"rule_id":".m.rule.reaction","default":true,"enabled":true}]},"device":{}}}]},"presence":{"events":[{"type":"m.presence","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"presence":"online","last_active_ago":43,"currently_active":true}}]},"device_one_time_keys_count":{"signed_curve25519":66},"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"],"device_unused_fallback_key_types":["signed_curve25519"],"rooms":{"join":{"!MEgZosbiZqjSjbHFqI:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de":{"timeline":{"events":[{"type":"m.room.member","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"Lars Kaiser"},"state_key":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647296944593,"unsigned":{"age":545455},"event_id":"\$mk9kFUEAKBZJgarWApLyYqOZQQocLIVV8tWp_gJEZFU"},{"type":"m.room.power_levels","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"users":{"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de":100},"users_default":0,"events":{"m.room.name":50,"m.room.power_levels":100,"m.room.history_visibility":100,"m.room.canonical_alias":50,"m.room.avatar":50,"m.room.tombstone":100,"m.room.server_acl":100,"m.room.encryption":100},"events_default":0,"state_default":50,"ban":50,"kick":50,"redact":50,"invite":50,"historical":100},"state_key":"","origin_server_ts":1647296944690,"unsigned":{"age":545358},"event_id":"\$3wL2YgVNQzgfl8y_ksi3BPMqRs94jb_m0WRonL1HNpY"},{"type":"m.room.canonical_alias","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"alias":"#user-discovery:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"},"state_key":"","origin_server_ts":1647296944806,"unsigned":{"age":545242},"event_id":"\$yXaVETL9F4jSN9rpRNyT_kUoctzD07n5Z4AIHziP7DQ"},{"type":"m.room.join_rules","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"join_rule":"public"},"state_key":"","origin_server_ts":1647296944894,"unsigned":{"age":545154},"event_id":"\$jBDHhgpNqr125eWUsGVw4r7ZG2hgr0BTzzR77S-ubvY"},{"type":"m.room.history_visibility","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"history_visibility":"shared"},"state_key":"","origin_server_ts":1647296944965,"unsigned":{"age":545083},"event_id":"\$kMessP7gAphUKW7mzOLlJT6NT8IsVGPmGir3_1uBNCE"},{"type":"m.room.name","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"name":"User Discovery"},"state_key":"","origin_server_ts":1647296945062,"unsigned":{"age":544986},"event_id":"\$Bo9Ut_0vcr3FuxCRye4IHEMxUxIIcSwc-ePnMzx-hYU"},{"type":"m.room.member","sender":"@test:fakeServer.notExisting","content":{"membership":"join","displayname":"1c2e5c2b-f958-45a5-9fcb-eef3969c31df"},"state_key":"@test:fakeServer.notExisting","origin_server_ts":1647296989893,"unsigned":{"age":500155},"event_id":"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE"},{"type":"m.room.member","sender":"@test:fakeServer.notExisting","content":{"membership":"join","displayname":"Some First Name Some Last Name"},"state_key":"@test:fakeServer.notExisting","origin_server_ts":1647296990076,"unsigned":{"replaces_state":"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE","prev_content":{"membership":"join","displayname":"1c2e5c2b-f958-45a5-9fcb-eef3969c31df"},"prev_sender":"@test:fakeServer.notExisting","age":499972},"event_id":"\$3Ut97nFBgOtsrnRPW-pqr28z7ETNMttj7GcjkIv4zWw"},{"type":"m.room.member","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"056d6976-fb61-47cf-86f0-147387461565"},"state_key":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647297489154,"unsigned":{"age":894},"event_id":"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0"},{"type":"m.room.member","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"Another User"},"state_key":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647297489290,"unsigned":{"replaces_state":"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0","prev_content":{"membership":"join","displayname":"056d6976-fb61-47cf-86f0-147387461565"},"prev_sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","age":758},"event_id":"\$dtQblqCbjr3TGc3WmrQ4YTkHaXJ2PcO0TAYDr9K7iQc"}],"prev_batch":"t2-62_571_2_6_39_1_2_34_1","limited":true},"state":{"events":[{"type":"m.room.create","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"m.federate":false,"room_version":"9","creator":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"},"state_key":"","origin_server_ts":1647296944511,"unsigned":{"age":545537},"event_id":"\$PAWKKULBVOLnqfrAAtXZz8tHEPXXjgRVbJJLifwQWbE"}]},"account_data":{"events":[]},"ephemeral":{"events":[]},"unread_notifications":{"notification_count":0,"highlight_count":0},"summary":{"m.joined_member_count":3,"m.invited_member_count":0},"org.matrix.msc2654.unread_count":0}}}}'))); + await client.handleSync( + SyncUpdate.fromJson( + jsonDecode( + '{"next_batch":"s82_571_2_6_39_1_2_34_1","account_data":{"events":[{"type":"m.push_rules","content":{"global":{"underride":[{"conditions":[{"kind":"event_match","key":"type","pattern":"m.call.invite"}],"actions":["notify",{"set_tweak":"sound","value":"ring"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.call","default":true,"enabled":true},{"conditions":[{"kind":"room_member_count","is":"2"},{"kind":"event_match","key":"type","pattern":"m.room.message"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.room_one_to_one","default":true,"enabled":true},{"conditions":[{"kind":"room_member_count","is":"2"},{"kind":"event_match","key":"type","pattern":"m.room.encrypted"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.encrypted_room_one_to_one","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.message"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.message","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.encrypted"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.encrypted","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"im.vector.modular.widgets"},{"kind":"event_match","key":"content.type","pattern":"jitsi"},{"kind":"event_match","key":"state_key","pattern":"*"}],"actions":["notify",{"set_tweak":"highlight","value":false}],"rule_id":".im.vector.jitsi","default":true,"enabled":true}],"sender":[],"room":[],"content":[{"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight"}],"pattern":"056d6976-fb61-47cf-86f0-147387461565","rule_id":".m.rule.contains_user_name","default":true,"enabled":true}],"override":[{"conditions":[],"actions":["dont_notify"],"rule_id":".m.rule.master","default":true,"enabled":false},{"conditions":[{"kind":"event_match","key":"content.msgtype","pattern":"m.notice"}],"actions":["dont_notify"],"rule_id":".m.rule.suppress_notices","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.member"},{"kind":"event_match","key":"content.membership","pattern":"invite"},{"kind":"event_match","key":"state_key","pattern":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight","value":false}],"rule_id":".m.rule.invite_for_me","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.member"}],"actions":["dont_notify"],"rule_id":".m.rule.member_event","default":true,"enabled":true},{"conditions":[{"kind":"contains_display_name"}],"actions":["notify",{"set_tweak":"sound","value":"default"},{"set_tweak":"highlight"}],"rule_id":".m.rule.contains_display_name","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"content.body","pattern":"@room"},{"kind":"sender_notification_permission","key":"room"}],"actions":["notify",{"set_tweak":"highlight","value":true}],"rule_id":".m.rule.roomnotif","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.room.tombstone"},{"kind":"event_match","key":"state_key","pattern":""}],"actions":["notify",{"set_tweak":"highlight","value":true}],"rule_id":".m.rule.tombstone","default":true,"enabled":true},{"conditions":[{"kind":"event_match","key":"type","pattern":"m.reaction"}],"actions":["dont_notify"],"rule_id":".m.rule.reaction","default":true,"enabled":true}]},"device":{}}}]},"presence":{"events":[{"type":"m.presence","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"presence":"online","last_active_ago":43,"currently_active":true}}]},"device_one_time_keys_count":{"signed_curve25519":66},"org.matrix.msc2732.device_unused_fallback_key_types":["signed_curve25519"],"device_unused_fallback_key_types":["signed_curve25519"],"rooms":{"join":{"!MEgZosbiZqjSjbHFqI:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de":{"timeline":{"events":[{"type":"m.room.member","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"Lars Kaiser"},"state_key":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647296944593,"unsigned":{"age":545455},"event_id":"\$mk9kFUEAKBZJgarWApLyYqOZQQocLIVV8tWp_gJEZFU"},{"type":"m.room.power_levels","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"users":{"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de":100},"users_default":0,"events":{"m.room.name":50,"m.room.power_levels":100,"m.room.history_visibility":100,"m.room.canonical_alias":50,"m.room.avatar":50,"m.room.tombstone":100,"m.room.server_acl":100,"m.room.encryption":100},"events_default":0,"state_default":50,"ban":50,"kick":50,"redact":50,"invite":50,"historical":100},"state_key":"","origin_server_ts":1647296944690,"unsigned":{"age":545358},"event_id":"\$3wL2YgVNQzgfl8y_ksi3BPMqRs94jb_m0WRonL1HNpY"},{"type":"m.room.canonical_alias","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"alias":"#user-discovery:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"},"state_key":"","origin_server_ts":1647296944806,"unsigned":{"age":545242},"event_id":"\$yXaVETL9F4jSN9rpRNyT_kUoctzD07n5Z4AIHziP7DQ"},{"type":"m.room.join_rules","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"join_rule":"public"},"state_key":"","origin_server_ts":1647296944894,"unsigned":{"age":545154},"event_id":"\$jBDHhgpNqr125eWUsGVw4r7ZG2hgr0BTzzR77S-ubvY"},{"type":"m.room.history_visibility","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"history_visibility":"shared"},"state_key":"","origin_server_ts":1647296944965,"unsigned":{"age":545083},"event_id":"\$kMessP7gAphUKW7mzOLlJT6NT8IsVGPmGir3_1uBNCE"},{"type":"m.room.name","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"name":"User Discovery"},"state_key":"","origin_server_ts":1647296945062,"unsigned":{"age":544986},"event_id":"\$Bo9Ut_0vcr3FuxCRye4IHEMxUxIIcSwc-ePnMzx-hYU"},{"type":"m.room.member","sender":"@test:fakeServer.notExisting","content":{"membership":"join","displayname":"1c2e5c2b-f958-45a5-9fcb-eef3969c31df"},"state_key":"@test:fakeServer.notExisting","origin_server_ts":1647296989893,"unsigned":{"age":500155},"event_id":"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE"},{"type":"m.room.member","sender":"@test:fakeServer.notExisting","content":{"membership":"join","displayname":"Some First Name Some Last Name"},"state_key":"@test:fakeServer.notExisting","origin_server_ts":1647296990076,"unsigned":{"replaces_state":"\$fYCf2qtlHwzcdLgwjHb2EOdStv3isAlIUy2Esh5qfVE","prev_content":{"membership":"join","displayname":"1c2e5c2b-f958-45a5-9fcb-eef3969c31df"},"prev_sender":"@test:fakeServer.notExisting","age":499972},"event_id":"\$3Ut97nFBgOtsrnRPW-pqr28z7ETNMttj7GcjkIv4zWw"},{"type":"m.room.member","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"056d6976-fb61-47cf-86f0-147387461565"},"state_key":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647297489154,"unsigned":{"age":894},"event_id":"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0"},{"type":"m.room.member","sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"membership":"join","displayname":"Another User"},"state_key":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","origin_server_ts":1647297489290,"unsigned":{"replaces_state":"\$6EsjHSLQDVDW9WDH1c5Eu57VaPGZmOPtNRjCjtWPLV0","prev_content":{"membership":"join","displayname":"056d6976-fb61-47cf-86f0-147387461565"},"prev_sender":"@056d6976-fb61-47cf-86f0-147387461565:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","age":758},"event_id":"\$dtQblqCbjr3TGc3WmrQ4YTkHaXJ2PcO0TAYDr9K7iQc"}],"prev_batch":"t2-62_571_2_6_39_1_2_34_1","limited":true},"state":{"events":[{"type":"m.room.create","sender":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de","content":{"m.federate":false,"room_version":"9","creator":"@8640f1e6-a824-4f9c-9924-2d8fc40bc030:c3d35860-36fe-45d1-8e16-936cf50513fb.gedisa-staging.famedly.de"},"state_key":"","origin_server_ts":1647296944511,"unsigned":{"age":545537},"event_id":"\$PAWKKULBVOLnqfrAAtXZz8tHEPXXjgRVbJJLifwQWbE"}]},"account_data":{"events":[]},"ephemeral":{"events":[]},"unread_notifications":{"notification_count":0,"highlight_count":0},"summary":{"m.joined_member_count":3,"m.invited_member_count":0},"org.matrix.msc2654.unread_count":0}}}}', + ), + ), + ); final profile = await client.getUserProfile(client.userID!); expect(profile.displayname, 'Some First Name Some Last Name'); expect(profile.outdated, false); @@ -819,9 +895,11 @@ void main() { 'body': 'Hello world', }); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - true); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + true, + ); }); test('sendToDeviceEncryptedChunked', () async { FakeMatrixApi.calledEndpoints.clear(); @@ -835,11 +913,13 @@ void main() { }); await Future.delayed(Duration(milliseconds: 100)); expect( - FakeMatrixApi.calledEndpoints.keys - .where((k) => - k.startsWith('/client/v3/sendToDevice/m.room.encrypted')) - .length, - 1); + FakeMatrixApi.calledEndpoints.keys + .where( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ) + .length, + 1, + ); final deviceKeys = []; for (var i = 0; i < 30; i++) { @@ -877,18 +957,22 @@ void main() { }); // it should send the first chunk right away expect( - FakeMatrixApi.calledEndpoints.keys - .where((k) => - k.startsWith('/client/v3/sendToDevice/m.room.encrypted')) - .length, - 1); + FakeMatrixApi.calledEndpoints.keys + .where( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ) + .length, + 1, + ); await Future.delayed(Duration(milliseconds: 100)); expect( - FakeMatrixApi.calledEndpoints.keys - .where((k) => - k.startsWith('/client/v3/sendToDevice/m.room.encrypted')) - .length, - 2); + FakeMatrixApi.calledEndpoints.keys + .where( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ) + .length, + 2, + ); }); test('send to_device queue', () async { // we test: @@ -925,29 +1009,39 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.sendToDevice('raccoon', 'raccoon_txnid', raccoonContent); expect( - json.decode(FakeMatrixApi + json.decode( + FakeMatrixApi .calledEndpoints['/client/v3/sendToDevice/foxies/floof_txnid'] - ?[0])['messages'], - foxContent); + ?[0], + )['messages'], + foxContent, + ); expect( - json.decode(FakeMatrixApi.calledEndpoints[ - '/client/v3/sendToDevice/raccoon/raccoon_txnid']?[0])['messages'], - raccoonContent); + json.decode( + FakeMatrixApi.calledEndpoints[ + '/client/v3/sendToDevice/raccoon/raccoon_txnid']?[0], + )['messages'], + raccoonContent, + ); FakeMatrixApi.calledEndpoints.clear(); await client.sendToDevice('bunny', 'bunny_txnid', bunnyContent); expect( - FakeMatrixApi - .calledEndpoints['/client/v3/sendToDevice/foxies/floof_txnid'], - null); + FakeMatrixApi + .calledEndpoints['/client/v3/sendToDevice/foxies/floof_txnid'], + null, + ); expect( - FakeMatrixApi - .calledEndpoints['/client/v3/sendToDevice/raccoon/raccoon_txnid'], - null); + FakeMatrixApi + .calledEndpoints['/client/v3/sendToDevice/raccoon/raccoon_txnid'], + null, + ); expect( - json.decode(FakeMatrixApi - .calledEndpoints['/client/v3/sendToDevice/bunny/bunny_txnid'] - ?[0])['messages'], - bunnyContent); + json.decode( + FakeMatrixApi + .calledEndpoints['/client/v3/sendToDevice/bunny/bunny_txnid']?[0], + )['messages'], + bunnyContent, + ); await client.dispose(closeDatabase: true); }); test('send to_device queue multiple', () async { @@ -985,7 +1079,8 @@ void main() { .catchError((e) => null); // ignore the error await FakeMatrixApi.firstWhereValue( - '/client/v3/sendToDevice/foxies/floof_txnid'); + '/client/v3/sendToDevice/foxies/floof_txnid', + ); FakeMatrixApi.calledEndpoints.clear(); await client @@ -993,7 +1088,8 @@ void main() { .catchError((e) => null); await FakeMatrixApi.firstWhereValue( - '/client/v3/sendToDevice/foxies/floof_txnid'); + '/client/v3/sendToDevice/foxies/floof_txnid', + ); FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.failToDevice = false; @@ -1001,9 +1097,11 @@ void main() { await client.sendToDevice('bunny', 'bunny_txnid', bunnyContent); await FakeMatrixApi.firstWhereValue( - '/client/v3/sendToDevice/foxies/floof_txnid'); + '/client/v3/sendToDevice/foxies/floof_txnid', + ); await FakeMatrixApi.firstWhereValue( - '/client/v3/sendToDevice/bunny/bunny_txnid'); + '/client/v3/sendToDevice/bunny/bunny_txnid', + ); final foxcall = FakeMatrixApi .calledEndpoints['/client/v3/sendToDevice/foxies/floof_txnid']?[0]; expect(foxcall != null, true); @@ -1030,69 +1128,74 @@ void main() { expect( json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last), + FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last, + ), { 'initial_state': [ { 'content': {'algorithm': 'm.megolm.v1.aes-sha2'}, - 'type': 'm.room.encryption' + 'type': 'm.room.encryption', } ], 'name': 'Testgroup', - 'preset': 'private_chat' + 'preset': 'private_chat', }, ); await matrix.createGroupChat( - groupName: 'Testgroup', - waitForSync: false, - groupCall: true, - powerLevelContentOverride: {'events_default': 12}); + groupName: 'Testgroup', + waitForSync: false, + groupCall: true, + powerLevelContentOverride: {'events_default': 12}, + ); expect( json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last), + FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last, + ), { 'initial_state': [ { 'content': {'algorithm': 'm.megolm.v1.aes-sha2'}, - 'type': 'm.room.encryption' + 'type': 'm.room.encryption', } ], 'name': 'Testgroup', 'power_level_content_override': { 'events_default': 12, - 'events': {'com.famedly.call.member': 12} + 'events': {'com.famedly.call.member': 12}, }, - 'preset': 'private_chat' + 'preset': 'private_chat', }, ); await matrix.createGroupChat( - groupName: 'Testgroup', - waitForSync: false, - groupCall: true, - powerLevelContentOverride: { - 'events_default': 12, - 'events': {'com.famedly.call.member': 14} - }); + groupName: 'Testgroup', + waitForSync: false, + groupCall: true, + powerLevelContentOverride: { + 'events_default': 12, + 'events': {'com.famedly.call.member': 14}, + }, + ); expect( json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last), + FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last, + ), { 'initial_state': [ { 'content': {'algorithm': 'm.megolm.v1.aes-sha2'}, - 'type': 'm.room.encryption' + 'type': 'm.room.encryption', } ], 'name': 'Testgroup', 'power_level_content_override': { 'events_default': 12, - 'events': {'com.famedly.call.member': 14} + 'events': {'com.famedly.call.member': 14}, }, - 'preset': 'private_chat' + 'preset': 'private_chat', }, ); @@ -1104,19 +1207,20 @@ void main() { expect( json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last), + FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last, + ), { 'initial_state': [ { 'content': {'algorithm': 'm.megolm.v1.aes-sha2'}, - 'type': 'm.room.encryption' + 'type': 'm.room.encryption', } ], 'name': 'Testgroup', 'power_level_content_override': { - 'events': {'com.famedly.call.member': 0} + 'events': {'com.famedly.call.member': 0}, }, - 'preset': 'private_chat' + 'preset': 'private_chat', }, ); }); @@ -1160,10 +1264,14 @@ void main() { expect(client2.deviceName, client1.deviceName); expect(client2.rooms.length, 3); if (client2.encryptionEnabled) { - expect(client2.encryption?.fingerprintKey, - client1.encryption?.fingerprintKey); expect( - client2.encryption?.identityKey, client1.encryption?.identityKey); + client2.encryption?.fingerprintKey, + client1.encryption?.fingerprintKey, + ); + expect( + client2.encryption?.identityKey, + client1.encryption?.identityKey, + ); expect(client2.rooms[1].id, client1.rooms[1].id); } @@ -1175,12 +1283,14 @@ void main() { }); test('ignoredUsers', () async { expect(matrix.ignoredUsers, []); - matrix.accountData['m.ignored_user_list'] = - BasicEvent(type: 'm.ignored_user_list', content: { - 'ignored_users': { - '@charley:stupid.abc': {}, + matrix.accountData['m.ignored_user_list'] = BasicEvent( + type: 'm.ignored_user_list', + content: { + 'ignored_users': { + '@charley:stupid.abc': {}, + }, }, - }); + ); expect(matrix.ignoredUsers, ['@charley:stupid.abc']); await matrix.ignoreUser('@charley2:stupid.abc'); await matrix.unignoreUser('@charley:stupid.abc'); @@ -1190,8 +1300,10 @@ void main() { final response = await client.uploadContent(Uint8List(0), filename: 'file.jpeg'); expect(response.toString(), 'mxc://example.com/AQwafuaFswefuhsfAFAgsw'); - expect(await client.database?.getFile(response) != null, - client.database?.supportsFileStoring); + expect( + await client.database?.getFile(response) != null, + client.database?.supportsFileStoring, + ); await client.dispose(closeDatabase: true); }); @@ -1200,7 +1312,9 @@ void main() { expect(client.wellKnown, null); await client.getWellknown(); expect( - client.wellKnown?.mHomeserver.baseUrl.host, 'fakeserver.notexisting'); + client.wellKnown?.mHomeserver.baseUrl.host, + 'fakeserver.notexisting', + ); await client.dispose(); }); @@ -1252,27 +1366,35 @@ void main() { expect(user1 == user1, true); expect(user1 == user2, false); expect( - user1 == - User('@user1:example.org', - room: Room(id: '!room2', client: matrix)), - false); + user1 == + User( + '@user1:example.org', + room: Room(id: '!room2', client: matrix), + ), + false, + ); expect( - user1 == - User('@user1:example.org', - room: Room(id: '!room1', client: matrix), - membership: 'leave'), - false); + user1 == + User( + '@user1:example.org', + room: Room(id: '!room1', client: matrix), + membership: 'leave', + ), + false, + ); // ignore: unrelated_type_equality_checks expect(user1 == 'beep', false); // rooms expect( - Room(id: '!room1', client: matrix) == - Room(id: '!room1', client: matrix), - true); + Room(id: '!room1', client: matrix) == + Room(id: '!room1', client: matrix), + true, + ); expect( - Room(id: '!room1', client: matrix) == - Room(id: '!room2', client: matrix), - false); + Room(id: '!room1', client: matrix) == + Room(id: '!room2', client: matrix), + false, + ); // ignore: unrelated_type_equality_checks expect(Room(id: '!room1', client: matrix) == 'beep', false); }); @@ -1297,8 +1419,9 @@ void main() { ); FakeMatrixApi.client = moorClient; await moorClient.checkHomeserver( - Uri.parse('https://fakeServer.notExisting'), - checkWellKnown: false); + Uri.parse('https://fakeServer.notExisting'), + checkWellKnown: false, + ); await moorClient.init( newToken: 'abcd', newUserID: '@test:fakeServer.notExisting', @@ -1385,32 +1508,47 @@ void main() { final client = await getClient(); await Future.delayed(Duration(milliseconds: 50)); - expect(client.rooms.length, 3, - reason: - 'Count of invited+joined before loadArchive() rooms does not match'); - expect(client.archivedRooms.length, 0, - reason: - 'Count of archived rooms before loadArchive() does not match'); + expect( + client.rooms.length, + 3, + reason: + 'Count of invited+joined before loadArchive() rooms does not match', + ); + expect( + client.archivedRooms.length, + 0, + reason: 'Count of archived rooms before loadArchive() does not match', + ); await client.loadArchive(); - expect(client.rooms.length, 3, - reason: 'Count of invited+joined rooms does not match'); - expect(client.archivedRooms.length, 2, - reason: 'Count of archived rooms does not match'); + expect( + client.rooms.length, + 3, + reason: 'Count of invited+joined rooms does not match', + ); + expect( + client.archivedRooms.length, + 2, + reason: 'Count of archived rooms does not match', + ); expect( - client.archivedRooms.firstWhereOrNull( - (r) => r.room.id == '!5345234234:example.com') != - null, - true, - reason: '!5345234234:example.com not found as archived room'); + client.archivedRooms.firstWhereOrNull( + (r) => r.room.id == '!5345234234:example.com', + ) != + null, + true, + reason: '!5345234234:example.com not found as archived room', + ); expect( - client.archivedRooms.firstWhereOrNull( - (r) => r.room.id == '!5345234235:example.com') != - null, - true, - reason: '!5345234235:example.com not found as archived room'); + client.archivedRooms.firstWhereOrNull( + (r) => r.room.id == '!5345234235:example.com', + ) != + null, + true, + reason: '!5345234235:example.com not found as archived room', + ); await client.dispose(); }); diff --git a/test/commands_test.dart b/test/commands_test.dart index c78fa50d8..5528da274 100644 --- a/test/commands_test.dart +++ b/test/commands_test.dart @@ -29,37 +29,48 @@ void main() { late Client client; late Room room; - Map getLastMessagePayload( - [String type = 'm.room.message', String? stateKey]) { + Map getLastMessagePayload([ + String type = 'm.room.message', + String? stateKey, + ]) { final state = stateKey != null; - return json.decode(FakeMatrixApi.calledEndpoints.entries - .firstWhere((e) => e.key.startsWith( - '/client/v3/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey.isNotEmpty == true ? '/${Uri.encodeComponent(stateKey)}' : ''}')) - .value - .first); + return json.decode( + FakeMatrixApi.calledEndpoints.entries + .firstWhere( + (e) => e.key.startsWith( + '/client/v3/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey.isNotEmpty == true ? '/${Uri.encodeComponent(stateKey)}' : ''}', + ), + ) + .value + .first, + ); } test('setupClient', () async { client = await getClient(); room = Room(id: '!1234:fakeServer.notExisting', client: client); - room.setState(Event( - type: 'm.room.power_levels', - content: {}, - room: room, - stateKey: '', - eventId: '\$fakeeventid', - originServerTs: DateTime.now(), - senderId: '@fakeuser:fakeServer.notExisting', - )); - room.setState(Event( - type: 'm.room.member', - content: {'membership': 'join'}, - room: room, - stateKey: client.userID, - eventId: '\$fakeeventid', - originServerTs: DateTime.now(), - senderId: '@fakeuser:fakeServer.notExisting', - )); + room.setState( + Event( + type: 'm.room.power_levels', + content: {}, + room: room, + stateKey: '', + eventId: '\$fakeeventid', + originServerTs: DateTime.now(), + senderId: '@fakeuser:fakeServer.notExisting', + ), + ); + room.setState( + Event( + type: 'm.room.member', + content: {'membership': 'join'}, + room: room, + stateKey: client.userID, + eventId: '\$fakeeventid', + originServerTs: DateTime.now(), + senderId: '@fakeuser:fakeServer.notExisting', + ), + ); }); test('send', () async { @@ -132,20 +143,22 @@ void main() { test('react', () async { FakeMatrixApi.calledEndpoints.clear(); - await room.sendTextEvent('/react 🦊', - inReplyTo: Event( - eventId: '\$event', - type: 'm.room.message', - content: { - 'msgtype': 'm.text', - 'body': 'yay', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'yay', - }, - originServerTs: DateTime.now(), - senderId: client.userID!, - room: room, - )); + await room.sendTextEvent( + '/react 🦊', + inReplyTo: Event( + eventId: '\$event', + type: 'm.room.message', + content: { + 'msgtype': 'm.text', + 'body': 'yay', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'yay', + }, + originServerTs: DateTime.now(), + senderId: client.userID!, + room: room, + ), + ); final sent = getLastMessagePayload('m.reaction'); expect(sent, { 'm.relates_to': { @@ -171,7 +184,7 @@ void main() { 'rel_type': 'm.thread', 'event_id': '\$parent_event', 'is_falling_back': true, - 'm.in_reply_to': {'event_id': '\$parent_event'} + 'm.in_reply_to': {'event_id': '\$parent_event'}, }, }); }); @@ -198,27 +211,29 @@ void main() { 'rel_type': 'm.thread', 'event_id': '\$parent_event', 'is_falling_back': true, - 'm.in_reply_to': {'event_id': '\$parent_event'} + 'm.in_reply_to': {'event_id': '\$parent_event'}, }, }); }); test('thread_reply', () async { FakeMatrixApi.calledEndpoints.clear(); - await room.sendTextEvent('reply', - inReplyTo: Event( - eventId: '\$parent_event', - type: 'm.room.message', - content: { - 'msgtype': 'm.text', - 'body': 'reply', - }, - originServerTs: DateTime.now(), - senderId: client.userID!, - room: room, - ), - threadRootEventId: '\$parent_event', - threadLastEventId: '\$parent_event'); + await room.sendTextEvent( + 'reply', + inReplyTo: Event( + eventId: '\$parent_event', + type: 'm.room.message', + content: { + 'msgtype': 'm.text', + 'body': 'reply', + }, + originServerTs: DateTime.now(), + senderId: client.userID!, + room: room, + ), + threadRootEventId: '\$parent_event', + threadLastEventId: '\$parent_event', + ); final sent = getLastMessagePayload(); expect(sent, { 'msgtype': 'm.text', @@ -230,7 +245,7 @@ void main() { 'rel_type': 'm.thread', 'event_id': '\$parent_event', 'is_falling_back': false, - 'm.in_reply_to': {'event_id': '\$parent_event'} + 'm.in_reply_to': {'event_id': '\$parent_event'}, }, }); }); @@ -250,7 +265,7 @@ void main() { 'rel_type': 'm.thread', 'event_id': '\$parent_event', 'is_falling_back': true, - 'm.in_reply_to': {'event_id': '\$last_event'} + 'm.in_reply_to': {'event_id': '\$last_event'}, }, }); }); @@ -259,23 +274,24 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/join !newroom:example.com'); expect( - FakeMatrixApi - .calledEndpoints['/client/v3/join/!newroom%3Aexample.com'] - ?.first != - null, - true); + FakeMatrixApi.calledEndpoints['/client/v3/join/!newroom%3Aexample.com'] + ?.first != + null, + true, + ); }); test('leave', () async { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/leave'); expect( - FakeMatrixApi - .calledEndpoints[ - '/client/v3/rooms/!1234%3AfakeServer.notExisting/leave'] - ?.first != - null, - true); + FakeMatrixApi + .calledEndpoints[ + '/client/v3/rooms/!1234%3AfakeServer.notExisting/leave'] + ?.first != + null, + true, + ); }); test('op', () async { @@ -283,14 +299,14 @@ void main() { await room.sendTextEvent('/op @user:example.org'); var sent = getLastMessagePayload('m.room.power_levels', ''); expect(sent, { - 'users': {'@user:example.org': 50} + 'users': {'@user:example.org': 50}, }); FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/op @user:example.org 100'); sent = getLastMessagePayload('m.room.power_levels', ''); expect(sent, { - 'users': {'@user:example.org': 100} + 'users': {'@user:example.org': 100}, }); }); @@ -298,10 +314,12 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/kick @baduser:example.org'); expect( - json.decode(FakeMatrixApi - .calledEndpoints[ - '/client/v3/rooms/!1234%3AfakeServer.notExisting/kick'] - ?.first), + json.decode( + FakeMatrixApi + .calledEndpoints[ + '/client/v3/rooms/!1234%3AfakeServer.notExisting/kick'] + ?.first, + ), { 'user_id': '@baduser:example.org', }); @@ -311,10 +329,12 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/ban @baduser:example.org'); expect( - json.decode(FakeMatrixApi - .calledEndpoints[ - '/client/v3/rooms/!1234%3AfakeServer.notExisting/ban'] - ?.first), + json.decode( + FakeMatrixApi + .calledEndpoints[ + '/client/v3/rooms/!1234%3AfakeServer.notExisting/ban'] + ?.first, + ), { 'user_id': '@baduser:example.org', }); @@ -324,10 +344,12 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/unban @baduser:example.org'); expect( - json.decode(FakeMatrixApi - .calledEndpoints[ - '/client/v3/rooms/!1234%3AfakeServer.notExisting/unban'] - ?.first), + json.decode( + FakeMatrixApi + .calledEndpoints[ + '/client/v3/rooms/!1234%3AfakeServer.notExisting/unban'] + ?.first, + ), { 'user_id': '@baduser:example.org', }); @@ -337,10 +359,12 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/invite @baduser:example.org'); expect( - json.decode(FakeMatrixApi - .calledEndpoints[ - '/client/v3/rooms/!1234%3AfakeServer.notExisting/invite'] - ?.first), + json.decode( + FakeMatrixApi + .calledEndpoints[ + '/client/v3/rooms/!1234%3AfakeServer.notExisting/invite'] + ?.first, + ), { 'user_id': '@baduser:example.org', }); @@ -371,11 +395,12 @@ void main() { await room.sendTextEvent('/dm @alice:example.com --no-encryption'); expect( json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.first), + FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.first, + ), { 'invite': ['@alice:example.com'], 'is_direct': true, - 'preset': 'trusted_private_chat' + 'preset': 'trusted_private_chat', }); }); @@ -383,51 +408,64 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/create @alice:example.com --no-encryption'); expect( - json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.first), - {'preset': 'private_chat'}); + json.decode( + FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.first, + ), + {'preset': 'private_chat'}, + ); }); test('discardsession', () async { await client.encryption?.keyManager.createOutboundGroupSession(room.id); expect( - client.encryption?.keyManager.getOutboundGroupSession(room.id) != - null, - true); + client.encryption?.keyManager.getOutboundGroupSession(room.id) != null, + true, + ); await room.sendTextEvent('/discardsession'); expect( - client.encryption?.keyManager.getOutboundGroupSession(room.id) != - null, - false); + client.encryption?.keyManager.getOutboundGroupSession(room.id) != null, + false, + ); }); test('markasdm', () async { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/markasdm @test:fakeServer.notExisting'); expect( - json.decode(FakeMatrixApi + json.decode( + FakeMatrixApi .calledEndpoints[ '/client/v3/user/%40test%3AfakeServer.notExisting/account_data/m.direct'] - ?.first)?['@alice:example.com'], - ['!1234:fakeServer.notExisting']); + ?.first, + )?['@alice:example.com'], + ['!1234:fakeServer.notExisting'], + ); expect( - json.decode(FakeMatrixApi + json.decode( + FakeMatrixApi .calledEndpoints[ '/client/v3/user/%40test%3AfakeServer.notExisting/account_data/m.direct'] - ?.first)?['@test:fakeServer.notExisting'], - ['!1234:fakeServer.notExisting']); + ?.first, + )?['@test:fakeServer.notExisting'], + ['!1234:fakeServer.notExisting'], + ); expect( - json - .decode(FakeMatrixApi + json + .decode( + FakeMatrixApi .calledEndpoints[ '/client/v3/user/%40test%3AfakeServer.notExisting/account_data/m.direct'] - ?.first) - .entries - .any((e) => + ?.first, + ) + .entries + .any( + (e) => e.key != '@test:fakeServer.notExisting' && e.key != '@alice:example.com' && - e.value.contains('!1234:fakeServer.notExisting')), - false); + e.value.contains('!1234:fakeServer.notExisting'), + ), + false, + ); FakeMatrixApi.calledEndpoints.clear(); }); @@ -436,23 +474,31 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await room.sendTextEvent('/markasgroup'); expect( - json - .decode(FakeMatrixApi + json + .decode( + FakeMatrixApi .calledEndpoints[ '/client/v3/user/%40test%3AfakeServer.notExisting/account_data/m.direct'] - ?.first) - ?.containsKey('@alice:example.com'), - false); + ?.first, + ) + ?.containsKey('@alice:example.com'), + false, + ); expect( - json - .decode(FakeMatrixApi + json + .decode( + FakeMatrixApi .calledEndpoints[ '/client/v3/user/%40test%3AfakeServer.notExisting/account_data/m.direct'] - ?.first) - .entries - .any((e) => (e.value as List) - .contains('!1234:fakeServer.notExisting')), - false); + ?.first, + ) + .entries + .any( + (e) => (e.value as List) + .contains('!1234:fakeServer.notExisting'), + ), + false, + ); }); test('clearcache', () async { diff --git a/test/database_api_test.dart b/test/database_api_test.dart index 0cfc44e6a..5065f74ac 100644 --- a/test/database_api_test.dart +++ b/test/database_api_test.dart @@ -80,7 +80,10 @@ void main() { }); test('storeFile', () async { await database.storeFile( - Uri.parse('mxc://test'), Uint8List.fromList([0]), 0); + Uri.parse('mxc://test'), + Uint8List.fromList([0]), + 0, + ); final file = await database.getFile(Uri.parse('mxc://test')); expect(file != null, database.supportsFileStoring); }); @@ -189,7 +192,9 @@ void main() { await database.storeAccountData('m.abc+de', '{"foo":"bar"}'); final events2 = await database.getAccountData(); expect( - events2.values.any((element) => element.type == 'm.abc+de'), true); + events2.values.any((element) => element.type == 'm.abc+de'), + true, + ); }); test('Database can write and read 5MB data', () async { final hugeDataObject = {'foo': createLargeString('A', 5 * 1024 * 1024)}; @@ -208,28 +213,29 @@ void main() { }); test('storeEventUpdate', () async { await database.storeEventUpdate( - EventUpdate( - roomID: '!testroom:example.com', - type: EventUpdateType.timeline, - content: { - 'type': EventTypes.Message, - 'content': { - 'body': '* edit 3', + EventUpdate( + roomID: '!testroom:example.com', + type: EventUpdateType.timeline, + content: { + 'type': EventTypes.Message, + 'content': { + 'body': '* edit 3', + 'msgtype': 'm.text', + 'm.new_content': { + 'body': 'edit 3', 'msgtype': 'm.text', - 'm.new_content': { - 'body': 'edit 3', - 'msgtype': 'm.text', - }, - 'm.relates_to': { - 'event_id': '\$source', - 'rel_type': RelationshipTypes.edit, - }, }, - 'event_id': '\$event:example.com', - 'sender': '@bob:example.org', + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, }, - ), - Client('testclient')); + 'event_id': '\$event:example.com', + 'sender': '@bob:example.org', + }, + ), + Client('testclient'), + ); }); test('storeEventUpdate (state)', () async { final roomid = '!testrooma:example.com'; @@ -350,8 +356,10 @@ void main() { expect(room?.name, 'update3'); }); test('getEventById', () async { - final event = await database.getEventById('\$event:example.com', - Room(id: '!testroom:example.com', client: Client('testclient'))); + final event = await database.getEventById( + '\$event:example.com', + Room(id: '!testroom:example.com', client: Client('testclient')), + ); expect(event?.type, EventTypes.Message); }); test('getEventList', () async { @@ -361,20 +369,27 @@ void main() { expect(events.single.type, EventTypes.Message); }); test('getUser', () async { - final user = await database.getUser('@bob:example.org', - Room(id: '!testroom:example.com', client: Client('testclient'))); + final user = await database.getUser( + '@bob:example.org', + Room(id: '!testroom:example.com', client: Client('testclient')), + ); expect(user, null); }); test('getUsers', () async { final users = await database.getUsers( - Room(id: '!testroom:example.com', client: Client('testclient'))); + Room(id: '!testroom:example.com', client: Client('testclient')), + ); expect(users.isEmpty, true); }); test('removeEvent', () async { await database.removeEvent( - '\$event:example.com', '!testroom:example.com'); - final event = await database.getEventById('\$event:example.com', - Room(id: '!testroom:example.com', client: Client('testclient'))); + '\$event:example.com', + '!testroom:example.com', + ); + final event = await database.getEventById( + '\$event:example.com', + Room(id: '!testroom:example.com', client: Client('testclient')), + ); expect(event, null); }); test('getAllInboundGroupSessions', () async { @@ -383,7 +398,9 @@ void main() { }); test('getInboundGroupSession', () async { await database.getInboundGroupSession( - '!testroom:example.com', 'sessionId'); + '!testroom:example.com', + 'sessionId', + ); }); test('getInboundGroupSessionsToUpload', () async { await database.getInboundGroupSessionsToUpload(); @@ -407,7 +424,9 @@ void main() { }); test('markInboundGroupSessionAsUploaded', () async { await database.markInboundGroupSessionAsUploaded( - '!testroom:example.com', 'sessionId'); + '!testroom:example.com', + 'sessionId', + ); }); test('markInboundGroupSessionsAsNeedingUpload', () async { await database.markInboundGroupSessionsAsNeedingUpload(); @@ -469,15 +488,15 @@ void main() { 'identity_key': 'identityKey', 'pickle': 'pickle', 'session_id': 'sessionId', - 'last_received': 0 + 'last_received': 0, }, 'sessionId2': { 'identity_key': 'identityKey', 'pickle': 'pickle', 'session_id': 'sessionId2', - 'last_received': 0 - } - } + 'last_received': 0, + }, + }, }, ); }); @@ -626,7 +645,9 @@ void main() { '@alice:example.com', CachedProfileInformation.fromProfile( ProfileInformation( - avatarUrl: Uri.parse('mxc://test'), displayname: 'Alice M'), + avatarUrl: Uri.parse('mxc://test'), + displayname: 'Alice M', + ), outdated: false, updated: DateTime.now(), ), diff --git a/test/device_keys_list_test.dart b/test/device_keys_list_test.dart index 3332aa74b..b6932136e 100644 --- a/test/device_keys_list_test.dart +++ b/test/device_keys_list_test.dart @@ -41,18 +41,18 @@ void main() { 'device_id': 'JLAFKJWSCS', 'algorithms': [ AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 + AlgorithmTypes.megolmV1AesSha2, ], 'keys': { 'curve25519:JLAFKJWSCS': '3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI', - 'ed25519:JLAFKJWSCS': 'lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI' + 'ed25519:JLAFKJWSCS': 'lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI', }, 'signatures': { '@alice:example.com': { 'ed25519:JLAFKJWSCS': - 'dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA' - } + 'dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA', + }, }, 'unsigned': {'device_display_name': "Alice's mobile phone"}, }; @@ -80,37 +80,45 @@ void main() { }); test('reject devices without self-signature', () async { - var key = DeviceKeys.fromJson({ - 'user_id': '@test:fakeServer.notExisting', - 'device_id': 'BADDEVICE', - 'algorithms': [ - AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 - ], - 'keys': { - 'curve25519:BADDEVICE': 'ds6+bItpDiWyRaT/b0ofoz1R+GCy7YTbORLJI4dmYho', - 'ed25519:BADDEVICE': 'CdDKVf44LO2QlfWopP6VWmqedSrRaf9rhHKvdVyH38w' + var key = DeviceKeys.fromJson( + { + 'user_id': '@test:fakeServer.notExisting', + 'device_id': 'BADDEVICE', + 'algorithms': [ + AlgorithmTypes.olmV1Curve25519AesSha2, + AlgorithmTypes.megolmV1AesSha2, + ], + 'keys': { + 'curve25519:BADDEVICE': + 'ds6+bItpDiWyRaT/b0ofoz1R+GCy7YTbORLJI4dmYho', + 'ed25519:BADDEVICE': 'CdDKVf44LO2QlfWopP6VWmqedSrRaf9rhHKvdVyH38w', + }, }, - }, client); + client, + ); expect(key.isValid, false); expect(key.selfSigned, false); - key = DeviceKeys.fromJson({ - 'user_id': '@test:fakeServer.notExisting', - 'device_id': 'BADDEVICE', - 'algorithms': [ - AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 - ], - 'keys': { - 'curve25519:BADDEVICE': 'ds6+bItpDiWyRaT/b0ofoz1R+GCy7YTbORLJI4dmYho', - 'ed25519:BADDEVICE': 'CdDKVf44LO2QlfWopP6VWmqedSrRaf9rhHKvdVyH38w' - }, - 'signatures': { - '@test:fakeServer.notExisting': { - 'ed25519:BADDEVICE': 'invalid', + key = DeviceKeys.fromJson( + { + 'user_id': '@test:fakeServer.notExisting', + 'device_id': 'BADDEVICE', + 'algorithms': [ + AlgorithmTypes.olmV1Curve25519AesSha2, + AlgorithmTypes.megolmV1AesSha2, + ], + 'keys': { + 'curve25519:BADDEVICE': + 'ds6+bItpDiWyRaT/b0ofoz1R+GCy7YTbORLJI4dmYho', + 'ed25519:BADDEVICE': 'CdDKVf44LO2QlfWopP6VWmqedSrRaf9rhHKvdVyH38w', + }, + 'signatures': { + '@test:fakeServer.notExisting': { + 'ed25519:BADDEVICE': 'invalid', + }, }, }, - }, client); + client, + ); expect(key.isValid, false); expect(key.selfSigned, false); }); @@ -119,26 +127,29 @@ void main() { final key = client.userDeviceKeys[client.userID]!.deviceKeys['OTHERDEVICE']!; client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] = - DeviceKeys.fromJson({ - 'user_id': '@test:fakeServer.notExisting', - 'device_id': 'UNSIGNEDDEVICE', - 'algorithms': [ - AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 - ], - 'keys': { - 'curve25519:UNSIGNEDDEVICE': - 'ds6+bItpDiWyRaT/b0ofoz1R+GCy7YTbORLJI4dmYho', - 'ed25519:UNSIGNEDDEVICE': - 'CdDKVf44LO2QlfWopP6VWmqedSrRaf9rhHKvdVyH38w' - }, - 'signatures': { - '@test:fakeServer.notExisting': { + DeviceKeys.fromJson( + { + 'user_id': '@test:fakeServer.notExisting', + 'device_id': 'UNSIGNEDDEVICE', + 'algorithms': [ + AlgorithmTypes.olmV1Curve25519AesSha2, + AlgorithmTypes.megolmV1AesSha2, + ], + 'keys': { + 'curve25519:UNSIGNEDDEVICE': + 'ds6+bItpDiWyRaT/b0ofoz1R+GCy7YTbORLJI4dmYho', 'ed25519:UNSIGNEDDEVICE': - 'f2p1kv6PIz+hnoFYnHEurhUKIyRsdxwR2RTKT1EnQ3aF2zlZOjmnndOCtIT24Q8vs2PovRw+/jkHKj4ge2yDDw', + 'CdDKVf44LO2QlfWopP6VWmqedSrRaf9rhHKvdVyH38w', + }, + 'signatures': { + '@test:fakeServer.notExisting': { + 'ed25519:UNSIGNEDDEVICE': + 'f2p1kv6PIz+hnoFYnHEurhUKIyRsdxwR2RTKT1EnQ3aF2zlZOjmnndOCtIT24Q8vs2PovRw+/jkHKj4ge2yDDw', + }, }, }, - }, client); + client, + ); expect(client.shareKeysWithUnverifiedDevices, true); expect(key.encryptToDevice, true); client.shareKeysWithUnverifiedDevices = false; @@ -161,17 +172,19 @@ void main() { expect(key.verified, true); // still verified via cross-sgining expect(key.encryptToDevice, true); expect( - client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] - ?.encryptToDevice, - true); + client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] + ?.encryptToDevice, + true, + ); expect(masterKey.verified, true); await masterKey.setBlocked(true); expect(masterKey.verified, false); expect( - client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] - ?.encryptToDevice, - true); + client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] + ?.encryptToDevice, + true, + ); await masterKey.setBlocked(false); expect(masterKey.verified, true); @@ -179,18 +192,20 @@ void main() { await key.setVerified(true); await Future.delayed(Duration(milliseconds: 10)); expect( - FakeMatrixApi.calledEndpoints.keys - .any((k) => k == '/client/v3/keys/signatures/upload'), - true); + FakeMatrixApi.calledEndpoints.keys + .any((k) => k == '/client/v3/keys/signatures/upload'), + true, + ); expect(key.directVerified, true); FakeMatrixApi.calledEndpoints.clear(); await key.setVerified(false); await Future.delayed(Duration(milliseconds: 10)); expect( - FakeMatrixApi.calledEndpoints.keys - .any((k) => k == '/client/v3/keys/signatures/upload'), - false); + FakeMatrixApi.calledEndpoints.keys + .any((k) => k == '/client/v3/keys/signatures/upload'), + false, + ); expect(key.directVerified, false); client.userDeviceKeys[client.userID]?.deviceKeys.remove('UNSIGNEDDEVICE'); }); @@ -204,10 +219,11 @@ void main() { expect(user.deviceKeys['OTHERDEVICE']?.crossVerified, true); expect(user.selfSigningKey?.crossVerified, true); expect( - user - .getKey('F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY') - ?.crossVerified, - true); + user + .getKey('F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY') + ?.crossVerified, + true, + ); expect(user.userSigningKey?.crossVerified, true); expect(user.verified, UserVerifiedStatus.verified); user.masterKey?.setDirectVerified(false); @@ -222,10 +238,14 @@ void main() { user.masterKey?.setDirectVerified(true); user.deviceKeys['GHTYAJCE']?.signatures?[client.userID] ?.removeWhere((k, v) => k != 'ed25519:GHTYAJCE'); - expect(user.deviceKeys['GHTYAJCE']?.verified, - true); // it's our own device, should be direct verified - expect(user.deviceKeys['GHTYAJCE']?.signed, - false); // not verified for others + expect( + user.deviceKeys['GHTYAJCE']?.verified, + true, + ); // it's our own device, should be direct verified + expect( + user.deviceKeys['GHTYAJCE']?.signed, + false, + ); // not verified for others user.deviceKeys['OTHERDEVICE']?.signatures?.clear(); expect(user.verified, UserVerifiedStatus.unknownDevice); }); diff --git a/test/encryption/bootstrap_test.dart b/test/encryption/bootstrap_test.dart index 046e5e1a4..6b4d1dbcc 100644 --- a/test/encryption/bootstrap_test.dart +++ b/test/encryption/bootstrap_test.dart @@ -40,180 +40,204 @@ void main() { client = await getClient(); }); - test('setup', () async { - Bootstrap? bootstrap; - bootstrap = client.encryption!.bootstrap( - onUpdate: (bootstrap) async { - if (bootstrap.state == BootstrapState.askWipeSsss) { - bootstrap.wipeSsss(true); - } else if (bootstrap.state == BootstrapState.askNewSsss) { - await bootstrap.newSsss('foxies'); - } else if (bootstrap.state == BootstrapState.askWipeCrossSigning) { - await bootstrap.wipeCrossSigning(true); - } else if (bootstrap.state == BootstrapState.askSetupCrossSigning) { - await bootstrap.askSetupCrossSigning( - setupMasterKey: true, - setupSelfSigningKey: true, - setupUserSigningKey: true, - ); - } else if (bootstrap.state == BootstrapState.askWipeOnlineKeyBackup) { - bootstrap.wipeOnlineKeyBackup(true); - } else if (bootstrap.state == - BootstrapState.askSetupOnlineKeyBackup) { - await bootstrap.askSetupOnlineKeyBackup(true); - } - }, - ); - while (bootstrap.state != BootstrapState.done) { - await Future.delayed(Duration(milliseconds: 50)); - } - final defaultKey = client.encryption!.ssss.open(); - await defaultKey.unlock(passphrase: 'foxies'); - - // test all the x-signing keys match up - for (final keyType in {'master', 'user_signing', 'self_signing'}) { - final privateKey = base64 - .decode(await defaultKey.getStored('m.cross_signing.$keyType')); - final keyObj = olm.PkSigning(); - try { - final pubKey = keyObj.init_with_seed(privateKey); - expect( + test( + 'setup', + () async { + Bootstrap? bootstrap; + bootstrap = client.encryption!.bootstrap( + onUpdate: (bootstrap) async { + if (bootstrap.state == BootstrapState.askWipeSsss) { + bootstrap.wipeSsss(true); + } else if (bootstrap.state == BootstrapState.askNewSsss) { + await bootstrap.newSsss('foxies'); + } else if (bootstrap.state == BootstrapState.askWipeCrossSigning) { + await bootstrap.wipeCrossSigning(true); + } else if (bootstrap.state == BootstrapState.askSetupCrossSigning) { + await bootstrap.askSetupCrossSigning( + setupMasterKey: true, + setupSelfSigningKey: true, + setupUserSigningKey: true, + ); + } else if (bootstrap.state == + BootstrapState.askWipeOnlineKeyBackup) { + bootstrap.wipeOnlineKeyBackup(true); + } else if (bootstrap.state == + BootstrapState.askSetupOnlineKeyBackup) { + await bootstrap.askSetupOnlineKeyBackup(true); + } + }, + ); + while (bootstrap.state != BootstrapState.done) { + await Future.delayed(Duration(milliseconds: 50)); + } + final defaultKey = client.encryption!.ssss.open(); + await defaultKey.unlock(passphrase: 'foxies'); + + // test all the x-signing keys match up + for (final keyType in {'master', 'user_signing', 'self_signing'}) { + final privateKey = base64 + .decode(await defaultKey.getStored('m.cross_signing.$keyType')); + final keyObj = olm.PkSigning(); + try { + final pubKey = keyObj.init_with_seed(privateKey); + expect( pubKey, client.userDeviceKeys[client.userID] ?.getCrossSigningKey(keyType) - ?.publicKey); - } finally { - keyObj.free(); + ?.publicKey, + ); + } finally { + keyObj.free(); + } } - } - await defaultKey.store('foxes', 'floof'); - await Future.delayed(Duration(milliseconds: 50)); - oldSecret = - json.decode(json.encode(client.accountData['foxes']!.content)); - origKeyId = defaultKey.keyId; - }, timeout: Timeout(Duration(minutes: 2))); - - test('change recovery passphrase', () async { - Bootstrap? bootstrap; - bootstrap = client.encryption!.bootstrap( - onUpdate: (bootstrap) async { - if (bootstrap.state == BootstrapState.askWipeSsss) { - bootstrap.wipeSsss(false); - } else if (bootstrap.state == BootstrapState.askUseExistingSsss) { - bootstrap.useExistingSsss(false); - } else if (bootstrap.state == BootstrapState.askUnlockSsss) { - await bootstrap.oldSsssKeys![client.encryption!.ssss.defaultKeyId]! - .unlock(passphrase: 'foxies'); - bootstrap.unlockedSsss(); - } else if (bootstrap.state == BootstrapState.askNewSsss) { - await bootstrap.newSsss('newfoxies'); - } else if (bootstrap.state == BootstrapState.askWipeCrossSigning) { - await bootstrap.wipeCrossSigning(false); - } else if (bootstrap.state == BootstrapState.askWipeOnlineKeyBackup) { - bootstrap.wipeOnlineKeyBackup(false); - } - }, - ); - while (bootstrap.state != BootstrapState.done) { + await defaultKey.store('foxes', 'floof'); await Future.delayed(Duration(milliseconds: 50)); - } - final defaultKey = client.encryption!.ssss.open(); - await defaultKey.unlock(passphrase: 'newfoxies'); - - // test all the x-signing keys match up - for (final keyType in {'master', 'user_signing', 'self_signing'}) { - final privateKey = base64 - .decode(await defaultKey.getStored('m.cross_signing.$keyType')); - final keyObj = olm.PkSigning(); - try { - final pubKey = keyObj.init_with_seed(privateKey); - expect( + oldSecret = + json.decode(json.encode(client.accountData['foxes']!.content)); + origKeyId = defaultKey.keyId; + }, + timeout: Timeout(Duration(minutes: 2)), + ); + + test( + 'change recovery passphrase', + () async { + Bootstrap? bootstrap; + bootstrap = client.encryption!.bootstrap( + onUpdate: (bootstrap) async { + if (bootstrap.state == BootstrapState.askWipeSsss) { + bootstrap.wipeSsss(false); + } else if (bootstrap.state == BootstrapState.askUseExistingSsss) { + bootstrap.useExistingSsss(false); + } else if (bootstrap.state == BootstrapState.askUnlockSsss) { + await bootstrap + .oldSsssKeys![client.encryption!.ssss.defaultKeyId]! + .unlock(passphrase: 'foxies'); + bootstrap.unlockedSsss(); + } else if (bootstrap.state == BootstrapState.askNewSsss) { + await bootstrap.newSsss('newfoxies'); + } else if (bootstrap.state == BootstrapState.askWipeCrossSigning) { + await bootstrap.wipeCrossSigning(false); + } else if (bootstrap.state == + BootstrapState.askWipeOnlineKeyBackup) { + bootstrap.wipeOnlineKeyBackup(false); + } + }, + ); + while (bootstrap.state != BootstrapState.done) { + await Future.delayed(Duration(milliseconds: 50)); + } + final defaultKey = client.encryption!.ssss.open(); + await defaultKey.unlock(passphrase: 'newfoxies'); + + // test all the x-signing keys match up + for (final keyType in {'master', 'user_signing', 'self_signing'}) { + final privateKey = base64 + .decode(await defaultKey.getStored('m.cross_signing.$keyType')); + final keyObj = olm.PkSigning(); + try { + final pubKey = keyObj.init_with_seed(privateKey); + expect( pubKey, client.userDeviceKeys[client.userID] ?.getCrossSigningKey(keyType) - ?.publicKey); - } finally { - keyObj.free(); + ?.publicKey, + ); + } finally { + keyObj.free(); + } } - } - expect(await defaultKey.getStored('foxes'), 'floof'); - }, timeout: Timeout(Duration(minutes: 2))); - - test('change passphrase with multiple keys', () async { - await client.setAccountData(client.userID!, 'foxes', oldSecret); - await Future.delayed(Duration(milliseconds: 50)); + expect(await defaultKey.getStored('foxes'), 'floof'); + }, + timeout: Timeout(Duration(minutes: 2)), + ); - Bootstrap? bootstrap; - bootstrap = client.encryption!.bootstrap( - onUpdate: (bootstrap) async { - if (bootstrap.state == BootstrapState.askWipeSsss) { - bootstrap.wipeSsss(false); - } else if (bootstrap.state == BootstrapState.askUseExistingSsss) { - bootstrap.useExistingSsss(false); - } else if (bootstrap.state == BootstrapState.askUnlockSsss) { - await bootstrap.oldSsssKeys![client.encryption!.ssss.defaultKeyId]! - .unlock(passphrase: 'newfoxies'); - await bootstrap.oldSsssKeys![origKeyId]! - .unlock(passphrase: 'foxies'); - bootstrap.unlockedSsss(); - } else if (bootstrap.state == BootstrapState.askNewSsss) { - await bootstrap.newSsss('supernewfoxies'); - } else if (bootstrap.state == BootstrapState.askWipeCrossSigning) { - await bootstrap.wipeCrossSigning(false); - } else if (bootstrap.state == BootstrapState.askWipeOnlineKeyBackup) { - bootstrap.wipeOnlineKeyBackup(false); - } - }, - ); - while (bootstrap.state != BootstrapState.done) { + test( + 'change passphrase with multiple keys', + () async { + await client.setAccountData(client.userID!, 'foxes', oldSecret); await Future.delayed(Duration(milliseconds: 50)); - } - final defaultKey = client.encryption!.ssss.open(); - await defaultKey.unlock(passphrase: 'supernewfoxies'); - - // test all the x-signing keys match up - for (final keyType in {'master', 'user_signing', 'self_signing'}) { - final privateKey = base64 - .decode(await defaultKey.getStored('m.cross_signing.$keyType')); - final keyObj = olm.PkSigning(); - try { - final pubKey = keyObj.init_with_seed(privateKey); - expect( + + Bootstrap? bootstrap; + bootstrap = client.encryption!.bootstrap( + onUpdate: (bootstrap) async { + if (bootstrap.state == BootstrapState.askWipeSsss) { + bootstrap.wipeSsss(false); + } else if (bootstrap.state == BootstrapState.askUseExistingSsss) { + bootstrap.useExistingSsss(false); + } else if (bootstrap.state == BootstrapState.askUnlockSsss) { + await bootstrap + .oldSsssKeys![client.encryption!.ssss.defaultKeyId]! + .unlock(passphrase: 'newfoxies'); + await bootstrap.oldSsssKeys![origKeyId]! + .unlock(passphrase: 'foxies'); + bootstrap.unlockedSsss(); + } else if (bootstrap.state == BootstrapState.askNewSsss) { + await bootstrap.newSsss('supernewfoxies'); + } else if (bootstrap.state == BootstrapState.askWipeCrossSigning) { + await bootstrap.wipeCrossSigning(false); + } else if (bootstrap.state == + BootstrapState.askWipeOnlineKeyBackup) { + bootstrap.wipeOnlineKeyBackup(false); + } + }, + ); + while (bootstrap.state != BootstrapState.done) { + await Future.delayed(Duration(milliseconds: 50)); + } + final defaultKey = client.encryption!.ssss.open(); + await defaultKey.unlock(passphrase: 'supernewfoxies'); + + // test all the x-signing keys match up + for (final keyType in {'master', 'user_signing', 'self_signing'}) { + final privateKey = base64 + .decode(await defaultKey.getStored('m.cross_signing.$keyType')); + final keyObj = olm.PkSigning(); + try { + final pubKey = keyObj.init_with_seed(privateKey); + expect( pubKey, client.userDeviceKeys[client.userID] ?.getCrossSigningKey(keyType) - ?.publicKey); - } finally { - keyObj.free(); + ?.publicKey, + ); + } finally { + keyObj.free(); + } } - } - expect(await defaultKey.getStored('foxes'), 'floof'); - }, timeout: Timeout(Duration(minutes: 2))); + expect(await defaultKey.getStored('foxes'), 'floof'); + }, + timeout: Timeout(Duration(minutes: 2)), + ); - test('setup new ssss', () async { - client.accountData.clear(); - Bootstrap? bootstrap; - bootstrap = client.encryption!.bootstrap( - onUpdate: (bootstrap) async { - if (bootstrap.state == BootstrapState.askNewSsss) { - await bootstrap.newSsss('thenewestfoxies'); - } else if (bootstrap.state == BootstrapState.askSetupCrossSigning) { - await bootstrap.askSetupCrossSigning(); - } else if (bootstrap.state == - BootstrapState.askSetupOnlineKeyBackup) { - await bootstrap.askSetupOnlineKeyBackup(false); - } - }, - ); - while (bootstrap.state != BootstrapState.done) { - await Future.delayed(Duration(milliseconds: 50)); - } - final defaultKey = client.encryption!.ssss.open(); - await defaultKey.unlock(passphrase: 'thenewestfoxies'); - }, timeout: Timeout(Duration(minutes: 2))); + test( + 'setup new ssss', + () async { + client.accountData.clear(); + Bootstrap? bootstrap; + bootstrap = client.encryption!.bootstrap( + onUpdate: (bootstrap) async { + if (bootstrap.state == BootstrapState.askNewSsss) { + await bootstrap.newSsss('thenewestfoxies'); + } else if (bootstrap.state == BootstrapState.askSetupCrossSigning) { + await bootstrap.askSetupCrossSigning(); + } else if (bootstrap.state == + BootstrapState.askSetupOnlineKeyBackup) { + await bootstrap.askSetupOnlineKeyBackup(false); + } + }, + ); + while (bootstrap.state != BootstrapState.done) { + await Future.delayed(Duration(milliseconds: 50)); + } + final defaultKey = client.encryption!.ssss.open(); + await defaultKey.unlock(passphrase: 'thenewestfoxies'); + }, + timeout: Timeout(Duration(minutes: 2)), + ); test('bad ssss', () async { client.accountData.clear(); diff --git a/test/encryption/cross_signing_test.dart b/test/encryption/cross_signing_test.dart index 43592edd1..a901c0a77 100644 --- a/test/encryption/cross_signing_test.dart +++ b/test/encryption/cross_signing_test.dart @@ -48,33 +48,38 @@ void main() { await client.encryption!.crossSigning.selfSign(recoveryKey: ssssKey); expect(key.directVerified, true); expect( - FakeMatrixApi.calledEndpoints - .containsKey('/client/v3/keys/signatures/upload'), - true); + FakeMatrixApi.calledEndpoints + .containsKey('/client/v3/keys/signatures/upload'), + true, + ); expect(await client.encryption!.crossSigning.isCached(), true); }); test('signable', () async { expect( - client.encryption!.crossSigning - .signable([client.userDeviceKeys[client.userID!]!.masterKey!]), - true); + client.encryption!.crossSigning + .signable([client.userDeviceKeys[client.userID!]!.masterKey!]), + true, + ); expect( - client.encryption!.crossSigning.signable([ - client.userDeviceKeys[client.userID!]!.deviceKeys[client.deviceID!]! - ]), - false); + client.encryption!.crossSigning.signable([ + client.userDeviceKeys[client.userID!]!.deviceKeys[client.deviceID!]!, + ]), + false, + ); expect( - client.encryption!.crossSigning.signable([ - client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']! - ]), - true); + client.encryption!.crossSigning.signable([ + client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!, + ]), + true, + ); expect( - client.encryption!.crossSigning.signable([ - client - .userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']! - ]), - false); + client.encryption!.crossSigning.signable([ + client + .userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']!, + ]), + false, + ); }); test('sign', () async { @@ -82,22 +87,29 @@ void main() { await client.encryption!.crossSigning.sign([ client.userDeviceKeys[client.userID!]!.masterKey!, client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!, - client.userDeviceKeys['@othertest:fakeServer.notExisting']!.masterKey! + client.userDeviceKeys['@othertest:fakeServer.notExisting']!.masterKey!, ]); - final body = json.decode(FakeMatrixApi - .calledEndpoints['/client/v3/keys/signatures/upload']!.first); - expect(body['@test:fakeServer.notExisting']?.containsKey('OTHERDEVICE'), - true); + final body = json.decode( + FakeMatrixApi + .calledEndpoints['/client/v3/keys/signatures/upload']!.first, + ); expect( - body['@test:fakeServer.notExisting'].containsKey( - client.userDeviceKeys[client.userID]!.masterKey!.publicKey), - true); + body['@test:fakeServer.notExisting']?.containsKey('OTHERDEVICE'), + true, + ); expect( - body['@othertest:fakeServer.notExisting'].containsKey(client - .userDeviceKeys['@othertest:fakeServer.notExisting'] - ?.masterKey - ?.publicKey), - true); + body['@test:fakeServer.notExisting'].containsKey( + client.userDeviceKeys[client.userID]!.masterKey!.publicKey, + ), + true, + ); + expect( + body['@othertest:fakeServer.notExisting'].containsKey( + client.userDeviceKeys['@othertest:fakeServer.notExisting']?.masterKey + ?.publicKey, + ), + true, + ); }); test('dispose client', () async { diff --git a/test/encryption/encrypt_decrypt_to_device_test.dart b/test/encryption/encrypt_decrypt_to_device_test.dart index 5916e0ec1..e1f9bc561 100644 --- a/test/encryption/encrypt_decrypt_to_device_test.dart +++ b/test/encryption/encrypt_decrypt_to_device_test.dart @@ -32,8 +32,11 @@ void main() { Logs().level = Level.error; late Client client; - final otherClient = Client('othertestclient', - httpClient: FakeMatrixApi(), databaseBuilder: getDatabase); + final otherClient = Client( + 'othertestclient', + httpClient: FakeMatrixApi(), + databaseBuilder: getDatabase, + ); late DeviceKeys device; late Map payload; @@ -47,8 +50,9 @@ void main() { client = await getClient(); await client.abortSync(); await otherClient.checkHomeserver( - Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); await otherClient.init( newToken: 'abc', newUserID: '@othertest:fakeServer.notExisting', @@ -60,18 +64,21 @@ void main() { await otherClient.abortSync(); await Future.delayed(Duration(milliseconds: 10)); - device = DeviceKeys.fromJson({ - 'user_id': client.userID, - 'device_id': client.deviceID, - 'algorithms': [ - AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 - ], - 'keys': { - 'curve25519:${client.deviceID}': client.identityKey, - 'ed25519:${client.deviceID}': client.fingerprintKey, + device = DeviceKeys.fromJson( + { + 'user_id': client.userID, + 'device_id': client.deviceID, + 'algorithms': [ + AlgorithmTypes.olmV1Curve25519AesSha2, + AlgorithmTypes.megolmV1AesSha2, + ], + 'keys': { + 'curve25519:${client.deviceID}': client.identityKey, + 'ed25519:${client.deviceID}': client.fingerprintKey, + }, }, - }, client); + client, + ); }); test('encryptToDeviceMessage', () async { @@ -94,7 +101,10 @@ void main() { test('decryptToDeviceEvent nocache', () async { client.encryption!.olmManager.olmSessions.clear(); payload = await otherClient.encryption!.encryptToDeviceMessage( - [device], 'm.to_device', {'hello': 'superfoxies'}); + [device], + 'm.to_device', + {'hello': 'superfoxies'}, + ); final encryptedEvent = ToDeviceEvent( sender: '@othertest:fakeServer.notExisting', type: EventTypes.Encrypted, diff --git a/test/encryption/key_manager_test.dart b/test/encryption/key_manager_test.dart index c4178526a..4319aca7a 100644 --- a/test/encryption/key_manager_test.dart +++ b/test/encryption/key_manager_test.dart @@ -43,71 +43,86 @@ void main() { client.encryption!.keyManager.clearInboundGroupSessions(); var event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - }, - encryptedContent: { - 'sender_key': validSenderKey, - }); + sender: '@alice:example.com', + type: 'm.room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + }, + encryptedContent: { + 'sender_key': validSenderKey, + }, + ); await client.encryption!.keyManager.handleToDeviceEvent(event); expect( - client.encryption!.keyManager.getInboundGroupSession( - '!726s6s6q:example.com', validSessionId) != - null, - true); + client.encryption!.keyManager.getInboundGroupSession( + '!726s6s6q:example.com', + validSessionId, + ) != + null, + true, + ); // now test a few invalid scenarios // not encrypted client.encryption!.keyManager.clearInboundGroupSessions(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - }); + sender: '@alice:example.com', + type: 'm.room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + }, + ); await client.encryption!.keyManager.handleToDeviceEvent(event); expect( - client.encryption!.keyManager.getInboundGroupSession( - '!726s6s6q:example.com', validSessionId) != - null, - false); + client.encryption!.keyManager.getInboundGroupSession( + '!726s6s6q:example.com', + validSessionId, + ) != + null, + false, + ); }); test('outbound group session', () async { final roomId = '!726s6s6q:example.com'; expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); var sess = await client.encryption!.keyManager .createOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - true); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + true, + ); await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - true); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + true, + ); var inbound = client.encryption!.keyManager.getInboundGroupSession( - roomId, sess.outboundGroupSession!.session_id()); + roomId, + sess.outboundGroupSession!.session_id(), + ); expect(inbound != null, true); expect( - inbound!.allowedAtIndex['@alice:example.com'] - ?['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'], - 0); + inbound!.allowedAtIndex['@alice:example.com'] + ?['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'], + 0, + ); expect( - inbound.allowedAtIndex['@alice:example.com'] - ?['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'], - 0); + inbound.allowedAtIndex['@alice:example.com'] + ?['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'], + 0, + ); // rotate after too many messages for (final _ in Iterable.generate(300)) { @@ -116,8 +131,9 @@ void main() { await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); // rotate if device is blocked sess = await client.encryption!.keyManager @@ -127,8 +143,9 @@ void main() { await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); client.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']! .blocked = false; @@ -140,15 +157,17 @@ void main() { .blocked = true; await client.encryption!.keyManager.prepareOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - true); - expect( - client.encryption!.keyManager - .getOutboundGroupSession(roomId)! - .outboundGroupSession! - .session_key() != - oldSessKey, - true); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + true, + ); + expect( + client.encryption!.keyManager + .getOutboundGroupSession(roomId)! + .outboundGroupSession! + .session_key() != + oldSessKey, + true, + ); client.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']! .blocked = false; @@ -159,8 +178,9 @@ void main() { await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); // rotate if user leaves sess = await client.encryption!.keyManager @@ -172,8 +192,9 @@ void main() { await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); member.content['membership'] = 'join'; room.summary.mJoinedMemberCount = room.summary.mJoinedMemberCount! + 1; @@ -181,45 +202,56 @@ void main() { sess = await client.encryption!.keyManager .createOutboundGroupSession(roomId); sess.outboundGroupSession!.encrypt( - 'foxies'); // so that the new device will have a different index + 'foxies', + ); // so that the new device will have a different index client.userDeviceKeys['@alice:example.com']?.deviceKeys['NEWDEVICE'] = - DeviceKeys.fromJson({ - 'user_id': '@alice:example.com', - 'device_id': 'NEWDEVICE', - 'algorithms': [ - AlgorithmTypes.olmV1Curve25519AesSha2, - AlgorithmTypes.megolmV1AesSha2 - ], - 'keys': { - 'curve25519:NEWDEVICE': 'bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8', - 'ed25519:NEWDEVICE': 'ZZhPdvWYg3MRpGy2MwtI+4MHXe74wPkBli5hiEOUi8Y' + DeviceKeys.fromJson( + { + 'user_id': '@alice:example.com', + 'device_id': 'NEWDEVICE', + 'algorithms': [ + AlgorithmTypes.olmV1Curve25519AesSha2, + AlgorithmTypes.megolmV1AesSha2, + ], + 'keys': { + 'curve25519:NEWDEVICE': + 'bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8', + 'ed25519:NEWDEVICE': 'ZZhPdvWYg3MRpGy2MwtI+4MHXe74wPkBli5hiEOUi8Y', + }, + 'signatures': { + '@alice:example.com': { + 'ed25519:NEWDEVICE': + '94GSg8N9vNB8wyWHJtKaaX3MGNWPVOjBatJM+TijY6B1RlDFJT5Cl1h/tjr17AoQz0CDdOf6uFhrYsBkH1/ABg', + }, + }, }, - 'signatures': { - '@alice:example.com': { - 'ed25519:NEWDEVICE': - '94GSg8N9vNB8wyWHJtKaaX3MGNWPVOjBatJM+TijY6B1RlDFJT5Cl1h/tjr17AoQz0CDdOf6uFhrYsBkH1/ABg' - } - } - }, client); + client, + ); await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - true); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + true, + ); inbound = client.encryption!.keyManager.getInboundGroupSession( - roomId, sess.outboundGroupSession!.session_id()); + roomId, + sess.outboundGroupSession!.session_id(), + ); expect( - inbound!.allowedAtIndex['@alice:example.com'] - ?['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'], - 0); + inbound!.allowedAtIndex['@alice:example.com'] + ?['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'], + 0, + ); expect( - inbound.allowedAtIndex['@alice:example.com'] - ?['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'], - 0); + inbound.allowedAtIndex['@alice:example.com'] + ?['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'], + 0, + ); expect( - inbound.allowedAtIndex['@alice:example.com'] - ?['bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8'], - 1); + inbound.allowedAtIndex['@alice:example.com'] + ?['bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8'], + 1, + ); // do not rotate if new user is added member.content['membership'] = 'leave'; @@ -231,8 +263,9 @@ void main() { await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - true); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + true, + ); // force wipe sess = await client.encryption!.keyManager @@ -240,20 +273,23 @@ void main() { await client.encryption!.keyManager .clearOrUseOutboundGroupSession(roomId, wipe: true); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); // load from database sess = await client.encryption!.keyManager .createOutboundGroupSession(roomId); client.encryption!.keyManager.clearOutboundGroupSessions(); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - false); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + false, + ); await client.encryption!.keyManager.loadOutboundGroupSession(roomId); expect( - client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, - true); + client.encryption!.keyManager.getOutboundGroupSession(roomId) != null, + true, + ); }); test('inbound group session', () async { @@ -265,48 +301,54 @@ void main() { 'room_id': '!726s6s6q:example.com', 'session_id': 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU', 'session_key': - 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw' + 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw', }; client.encryption!.keyManager.clearInboundGroupSessions(); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) != - null, - false); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) != + null, + false, + ); await client.encryption!.keyManager .setInboundGroupSession(roomId, sessionId, senderKey, sessionContent); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) != - null, - true); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) != + null, + true, + ); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) != - null, - true); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) != + null, + true, + ); client.encryption!.keyManager.clearInboundGroupSessions(); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) != - null, - false); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) != + null, + false, + ); await client.encryption!.keyManager .loadInboundGroupSession(roomId, sessionId); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) != - null, - true); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) != + null, + true, + ); client.encryption!.keyManager.clearInboundGroupSessions(); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) != - null, - false); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) != + null, + false, + ); }); test('setInboundGroupSession', () async { @@ -323,31 +365,41 @@ void main() { .timeout(const Duration(seconds: 5)); client.rooms.add(room); // we build up an encrypted message so that we can test if it successfully decrypted afterwards - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: '', - rooms: RoomsUpdate(join: { - room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - Event( - senderId: '@test:example.com', - type: 'm.room.encrypted', - room: room, - eventId: '12345', - originServerTs: DateTime.now(), - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'ciphertext': session.encrypt(json.encode({ - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'foxies'}, - })), - 'device_id': client.deviceID, - 'sender_key': client.identityKey, - 'session_id': sessionId, - }, - stateKey: '', - ) - ])) - }))); + rooms: RoomsUpdate( + join: { + room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + Event( + senderId: '@test:example.com', + type: 'm.room.encrypted', + room: room, + eventId: '12345', + originServerTs: DateTime.now(), + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'ciphertext': session.encrypt( + json.encode({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'foxies'}, + }), + ), + 'device_id': client.deviceID, + 'sender_key': client.identityKey, + 'session_id': sessionId, + }, + stateKey: '', + ), + ], + ), + ), + }, + ), + ), + ); expect(room.lastEvent?.type, 'm.room.encrypted'); // set a payload... var sessionPayload = { @@ -360,20 +412,26 @@ void main() { 'sender_claimed_ed25519_key': client.fingerprintKey, }; await client.encryption!.keyManager.setInboundGroupSession( - roomId, sessionId, senderKey, sessionPayload, - forwarded: true); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.inboundGroupSession - ?.first_known_index(), - 1); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.forwardingCurve25519KeyChain - .length, - 1); + roomId, + sessionId, + senderKey, + sessionPayload, + forwarded: true, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.inboundGroupSession + ?.first_known_index(), + 1, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.forwardingCurve25519KeyChain + .length, + 1, + ); // not set one with a higher first known index sessionPayload = { @@ -386,20 +444,26 @@ void main() { 'sender_claimed_ed25519_key': client.fingerprintKey, }; await client.encryption!.keyManager.setInboundGroupSession( - roomId, sessionId, senderKey, sessionPayload, - forwarded: true); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.inboundGroupSession - ?.first_known_index(), - 1); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.forwardingCurve25519KeyChain - .length, - 1); + roomId, + sessionId, + senderKey, + sessionPayload, + forwarded: true, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.inboundGroupSession + ?.first_known_index(), + 1, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.forwardingCurve25519KeyChain + .length, + 1, + ); // set one with a lower first known index sessionPayload = { @@ -412,20 +476,26 @@ void main() { 'sender_claimed_ed25519_key': client.fingerprintKey, }; await client.encryption!.keyManager.setInboundGroupSession( - roomId, sessionId, senderKey, sessionPayload, - forwarded: true); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.inboundGroupSession - ?.first_known_index(), - 0); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.forwardingCurve25519KeyChain - .length, - 1); + roomId, + sessionId, + senderKey, + sessionPayload, + forwarded: true, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.inboundGroupSession + ?.first_known_index(), + 0, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.forwardingCurve25519KeyChain + .length, + 1, + ); // not set one with a longer forwarding chain sessionPayload = { @@ -438,20 +508,26 @@ void main() { 'sender_claimed_ed25519_key': client.fingerprintKey, }; await client.encryption!.keyManager.setInboundGroupSession( - roomId, sessionId, senderKey, sessionPayload, - forwarded: true); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.inboundGroupSession - ?.first_known_index(), - 0); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.forwardingCurve25519KeyChain - .length, - 1); + roomId, + sessionId, + senderKey, + sessionPayload, + forwarded: true, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.inboundGroupSession + ?.first_known_index(), + 0, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.forwardingCurve25519KeyChain + .length, + 1, + ); // set one with a shorter forwarding chain sessionPayload = { @@ -464,20 +540,26 @@ void main() { 'sender_claimed_ed25519_key': client.fingerprintKey, }; await client.encryption!.keyManager.setInboundGroupSession( - roomId, sessionId, senderKey, sessionPayload, - forwarded: true); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.inboundGroupSession - ?.first_known_index(), - 0); - expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.forwardingCurve25519KeyChain - .length, - 0); + roomId, + sessionId, + senderKey, + sessionPayload, + forwarded: true, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.inboundGroupSession + ?.first_known_index(), + 0, + ); + expect( + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.forwardingCurve25519KeyChain + .length, + 0, + ); // test that it decrypted the last event expect(room.lastEvent?.type, 'm.room.message'); @@ -497,10 +579,10 @@ void main() { // Ensure the device came from sync expect( - client.userDeviceKeys['@alice:example.com'] - ?.deviceKeys['JLAFKJWSCS'] != - null, - true); + client.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'] != + null, + true, + ); // Alice removes her device client.userDeviceKeys['@alice:example.com']?.deviceKeys @@ -515,27 +597,28 @@ void main() { 'device_id': 'JLAFKJWSCS', 'algorithms': [ 'm.olm.v1.curve25519-aes-sha2', - 'm.megolm.v1.aes-sha2' + 'm.megolm.v1.aes-sha2', ], 'keys': { 'curve25519:JLAFKJWSCS': 'WbwrNyD7nvtmcLQ0TTuVPFGJq6JznfjrVsjIpmBqvDw', - 'ed25519:JLAFKJWSCS': 'vl0d54pTVRcvBgUzoQFa8e6TldHWG9O8bh0iuIvgd/I' + 'ed25519:JLAFKJWSCS': 'vl0d54pTVRcvBgUzoQFa8e6TldHWG9O8bh0iuIvgd/I', }, 'signatures': { '@alice:example.com': { 'ed25519:JLAFKJWSCS': - 's/L86jLa8BTroL8GsBeqO0gRLC3ZrSA7Gch6UoLI2SefC1+1ycmnP9UGbLPh3qBJOmlhczMpBLZwelg87qNNDA' - } - } + 's/L86jLa8BTroL8GsBeqO0gRLC3ZrSA7Gch6UoLI2SefC1+1ycmnP9UGbLPh3qBJOmlhczMpBLZwelg87qNNDA', + }, + }, }; return oldResp; }; client.userDeviceKeys['@alice:example.com']!.outdated = true; await client.updateUserDeviceKeys(); expect( - client.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'], - null); + client.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'], + null, + ); }); test('dispose client', () async { diff --git a/test/encryption/key_request_test.dart b/test/encryption/key_request_test.dart index 4fe24e666..fb0af33a1 100644 --- a/test/encryption/key_request_test.dart +++ b/test/encryption/key_request_test.dart @@ -52,8 +52,11 @@ void main() { final matrix = await getClient(); final requestRoom = matrix.getRoomById('!726s6s6q:example.com')!; await matrix.encryption!.keyManager.request( - requestRoom, 'sessionId', validSenderKey, - tryOnlineBackup: false); + requestRoom, + 'sessionId', + validSenderKey, + tryOnlineBackup: false, + ); var foundEvent = false; for (final entry in FakeMatrixApi.calledEndpoints.entries) { final payload = jsonDecode(entry.value.first); @@ -90,25 +93,28 @@ void main() { .loadInboundGroupSession('!726s6s6q:example.com', validSessionId); // test a successful share var event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'sender_key': validSenderKey, - 'session_id': validSessionId, - }, - 'request_id': 'request_1', - 'requesting_device_id': 'OTHERDEVICE', - }); + sender: '@alice:example.com', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'sender_key': validSenderKey, + 'session_id': validSessionId, + }, + 'request_id': 'request_1', + 'requesting_device_id': 'OTHERDEVICE', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); Logs().i(FakeMatrixApi.calledEndpoints.keys.toString()); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - true); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + true, + ); // test a successful foreign share FakeMatrixApi.calledEndpoints.clear(); @@ -117,25 +123,28 @@ void main() { .deviceKeys['OTHERDEVICE']!.curve25519Key!: 0, }; event = ToDeviceEvent( - sender: '@test:fakeServer.notExisting', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'sender_key': validSenderKey, - 'session_id': validSessionId, - }, - 'request_id': 'request_a1', - 'requesting_device_id': 'OTHERDEVICE', - }); + sender: '@test:fakeServer.notExisting', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'sender_key': validSenderKey, + 'session_id': validSessionId, + }, + 'request_id': 'request_a1', + 'requesting_device_id': 'OTHERDEVICE', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); Logs().i(FakeMatrixApi.calledEndpoints.keys.toString()); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - true); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + true, + ); session.allowedAtIndex.remove('@test:fakeServer.notExisting'); // test various fail scenarios @@ -143,129 +152,147 @@ void main() { // unknown person FakeMatrixApi.calledEndpoints.clear(); event = ToDeviceEvent( - sender: '@test:fakeServer.notExisting', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'sender_key': validSenderKey, - 'session_id': validSessionId, - }, - 'request_id': 'request_a2', - 'requesting_device_id': 'OTHERDEVICE', - }); + sender: '@test:fakeServer.notExisting', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'sender_key': validSenderKey, + 'session_id': validSessionId, + }, + 'request_id': 'request_a2', + 'requesting_device_id': 'OTHERDEVICE', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); Logs().i(FakeMatrixApi.calledEndpoints.keys.toString()); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // no body FakeMatrixApi.calledEndpoints.clear(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'request_id': 'request_2', - 'requesting_device_id': 'OTHERDEVICE', - }); + sender: '@alice:example.com', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'request_id': 'request_2', + 'requesting_device_id': 'OTHERDEVICE', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // request by ourself FakeMatrixApi.calledEndpoints.clear(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'sender_key': validSenderKey, - 'session_id': validSessionId, - }, - 'request_id': 'request_3', - 'requesting_device_id': 'JLAFKJWSCS', - }); + sender: '@alice:example.com', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'sender_key': validSenderKey, + 'session_id': validSessionId, + }, + 'request_id': 'request_3', + 'requesting_device_id': 'JLAFKJWSCS', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // device not found FakeMatrixApi.calledEndpoints.clear(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'sender_key': validSenderKey, - 'session_id': validSessionId, - }, - 'request_id': 'request_4', - 'requesting_device_id': 'blubb', - }); + sender: '@alice:example.com', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'sender_key': validSenderKey, + 'session_id': validSessionId, + }, + 'request_id': 'request_4', + 'requesting_device_id': 'blubb', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // unknown room FakeMatrixApi.calledEndpoints.clear(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!invalid:example.com', - 'sender_key': validSenderKey, - 'session_id': validSessionId, - }, - 'request_id': 'request_5', - 'requesting_device_id': 'OTHERDEVICE', - }); + sender: '@alice:example.com', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!invalid:example.com', + 'sender_key': validSenderKey, + 'session_id': validSessionId, + }, + 'request_id': 'request_5', + 'requesting_device_id': 'OTHERDEVICE', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // unknwon session FakeMatrixApi.calledEndpoints.clear(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.room_key_request', - content: { - 'action': 'request', - 'body': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'sender_key': validSenderKey, - 'session_id': 'invalid', - }, - 'request_id': 'request_6', - 'requesting_device_id': 'OTHERDEVICE', - }); + sender: '@alice:example.com', + type: 'm.room_key_request', + content: { + 'action': 'request', + 'body': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'sender_key': validSenderKey, + 'session_id': 'invalid', + }, + 'request_id': 'request_6', + 'requesting_device_id': 'OTHERDEVICE', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); FakeMatrixApi.calledEndpoints.clear(); await matrix.dispose(closeDatabase: true); @@ -274,8 +301,11 @@ void main() { final matrix = await getClient(); final requestRoom = matrix.getRoomById('!726s6s6q:example.com')!; await matrix.encryption!.keyManager.request( - requestRoom, validSessionId, validSenderKey, - tryOnlineBackup: false); + requestRoom, + validSessionId, + validSenderKey, + tryOnlineBackup: false, + ); final session = (await matrix.encryption!.keyManager .loadInboundGroupSession(requestRoom.id, validSessionId))!; @@ -283,123 +313,135 @@ void main() { .export_session(session.inboundGroupSession!.first_known_index()); matrix.encryption!.keyManager.clearInboundGroupSessions(); var event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.forwarded_room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - 'sender_key': validSenderKey, - 'forwarding_curve25519_key_chain': [], - 'sender_claimed_ed25519_key': - 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }, - encryptedContent: { - 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }); + sender: '@alice:example.com', + type: 'm.forwarded_room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + 'sender_key': validSenderKey, + 'forwarding_curve25519_key_chain': [], + 'sender_claimed_ed25519_key': + 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + encryptedContent: { + 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - matrix.encryption!.keyManager - .getInboundGroupSession(requestRoom.id, validSessionId) != - null, - true); + matrix.encryption!.keyManager + .getInboundGroupSession(requestRoom.id, validSessionId) != + null, + true, + ); // test ToDeviceEvent without sender_key in content event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.forwarded_room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - 'forwarding_curve25519_key_chain': [], - 'sender_claimed_ed25519_key': - 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }, - encryptedContent: { - 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }); + sender: '@alice:example.com', + type: 'm.forwarded_room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + 'forwarding_curve25519_key_chain': [], + 'sender_claimed_ed25519_key': + 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + encryptedContent: { + 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + ); // now test a few invalid scenarios // request not found matrix.encryption!.keyManager.clearInboundGroupSessions(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.forwarded_room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - 'sender_key': validSenderKey, - 'forwarding_curve25519_key_chain': [], - 'sender_claimed_ed25519_key': - 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }, - encryptedContent: { - 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }); + sender: '@alice:example.com', + type: 'm.forwarded_room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + 'sender_key': validSenderKey, + 'forwarding_curve25519_key_chain': [], + 'sender_claimed_ed25519_key': + 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + encryptedContent: { + 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - matrix.encryption!.keyManager - .getInboundGroupSession(requestRoom.id, validSessionId) != - null, - false); + matrix.encryption!.keyManager + .getInboundGroupSession(requestRoom.id, validSessionId) != + null, + false, + ); // unknown device await matrix.encryption!.keyManager .request(requestRoom, validSessionId, null, tryOnlineBackup: false); matrix.encryption!.keyManager.clearInboundGroupSessions(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.forwarded_room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - 'sender_key': validSenderKey, - 'forwarding_curve25519_key_chain': [], - 'sender_claimed_ed25519_key': - 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }, - encryptedContent: { - 'sender_key': 'invalid', - }); + sender: '@alice:example.com', + type: 'm.forwarded_room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + 'sender_key': validSenderKey, + 'forwarding_curve25519_key_chain': [], + 'sender_claimed_ed25519_key': + 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + encryptedContent: { + 'sender_key': 'invalid', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - matrix.encryption!.keyManager - .getInboundGroupSession(requestRoom.id, validSessionId) != - null, - false); + matrix.encryption!.keyManager + .getInboundGroupSession(requestRoom.id, validSessionId) != + null, + false, + ); // no encrypted content await matrix.encryption!.keyManager.request( - requestRoom, validSessionId, validSenderKey, - tryOnlineBackup: false); + requestRoom, + validSessionId, + validSenderKey, + tryOnlineBackup: false, + ); matrix.encryption!.keyManager.clearInboundGroupSessions(); event = ToDeviceEvent( - sender: '@alice:example.com', - type: 'm.forwarded_room_key', - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'room_id': '!726s6s6q:example.com', - 'session_id': validSessionId, - 'session_key': sessionKey, - 'sender_key': validSenderKey, - 'forwarding_curve25519_key_chain': [], - 'sender_claimed_ed25519_key': - 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', - }); + sender: '@alice:example.com', + type: 'm.forwarded_room_key', + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'room_id': '!726s6s6q:example.com', + 'session_id': validSessionId, + 'session_key': sessionKey, + 'sender_key': validSenderKey, + 'forwarding_curve25519_key_chain': [], + 'sender_claimed_ed25519_key': + 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', + }, + ); await matrix.encryption!.keyManager.handleToDeviceEvent(event); expect( - matrix.encryption!.keyManager - .getInboundGroupSession(requestRoom.id, validSessionId) != - null, - false); + matrix.encryption!.keyManager + .getInboundGroupSession(requestRoom.id, validSessionId) != + null, + false, + ); // There is a non awaiting setInboundGroupSession call on the database await Future.delayed(Duration(seconds: 1)); diff --git a/test/encryption/key_verification_test.dart b/test/encryption/key_verification_test.dart index 76676203a..7fd8edaf6 100644 --- a/test/encryption/key_verification_test.dart +++ b/test/encryption/key_verification_test.dart @@ -70,8 +70,10 @@ void main() async { httpClient: FakeMatrixApi.currentApi!, databaseBuilder: getDatabase, ); - await client2.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client2.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); await client2.init( newToken: 'abc', newUserID: '@othertest:fakeServer.notExisting', @@ -86,13 +88,13 @@ void main() async { KeyVerificationMethod.numbers, KeyVerificationMethod.qrScan, KeyVerificationMethod.qrShow, - KeyVerificationMethod.reciprocate + KeyVerificationMethod.reciprocate, }; client2.verificationMethods = { KeyVerificationMethod.emoji, KeyVerificationMethod.numbers, KeyVerificationMethod.qrShow, - KeyVerificationMethod.reciprocate + KeyVerificationMethod.reciprocate, }; // cancel sync to reduce background load and prevent sync overwriting which keys are tracked. @@ -120,8 +122,11 @@ void main() async { await client1.userDeviceKeys[client2.userID]!.startVerification( newDirectChatEnableEncryption: false, ); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -134,14 +139,18 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); @@ -155,29 +164,41 @@ void main() async { expect(req1.state, KeyVerificationState.waitingAccept); // no need for start (continueVerification) because sas only mode override already sent it after ready - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start', + ), + ); evt = getLastSentEvent(req1); // send accept FakeMatrixApi.calledEndpoints.clear(); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.accept')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.accept', + ), + ); evt = getLastSentEvent(req2); // send key FakeMatrixApi.calledEndpoints.clear(); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key', + ), + ); evt = getLastSentEvent(req1); // send key FakeMatrixApi.calledEndpoints.clear(); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key', + ), + ); evt = getLastSentEvent(req2); // receive last key @@ -212,33 +233,47 @@ void main() async { await req1.acceptSas(); evt = getLastSentEvent(req1); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac', + ), + ); expect(req1.state, KeyVerificationState.waitingSas); // send mac FakeMatrixApi.calledEndpoints.clear(); await req2.acceptSas(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac')); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac', + ), + ); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done', + ), + ); evt = getLastSentEvent(req2); FakeMatrixApi.calledEndpoints.clear(); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done', + ), + ); expect(req1.state, KeyVerificationState.done); expect(req2.state, KeyVerificationState.done); expect( - client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] - ?.directVerified, - true); + client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] + ?.directVerified, + true, + ); expect( - client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] - ?.directVerified, - true); + client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] + ?.directVerified, + true, + ); await client1.encryption!.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup(); }); @@ -251,8 +286,11 @@ void main() async { .startVerification(newDirectChatEnableEncryption: false); expect(req1.state, KeyVerificationState.askSSSS); await req1.openSSSS(recoveryKey: ssssKey); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); expect(req1.state, KeyVerificationState.waitingAccept); await req1.cancel(); @@ -269,8 +307,11 @@ void main() async { .setDirectVerified(true); final req1 = await client1.userDeviceKeys[client2.userID]! .startVerification(newDirectChatEnableEncryption: false); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -285,8 +326,11 @@ void main() async { // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); FakeMatrixApi.calledEndpoints.clear(); @@ -298,29 +342,41 @@ void main() async { expect(req1.state, KeyVerificationState.waitingAccept); // no need for start (continueVerification) because sas only mode override already sent it after ready - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start', + ), + ); evt = getLastSentEvent(req1); // send accept FakeMatrixApi.calledEndpoints.clear(); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.accept')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.accept', + ), + ); evt = getLastSentEvent(req2); // send key FakeMatrixApi.calledEndpoints.clear(); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key', + ), + ); evt = getLastSentEvent(req1); // send key FakeMatrixApi.calledEndpoints.clear(); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key', + ), + ); evt = getLastSentEvent(req2); // receive last key @@ -354,8 +410,11 @@ void main() async { await req1.acceptSas(); evt = getLastSentEvent(req1); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac', + ), + ); expect(req1.state, KeyVerificationState.waitingSas); // send mac @@ -363,10 +422,16 @@ void main() async { await req2.acceptSas(); evt = getLastSentEvent(req2); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac')); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac', + ), + ); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done', + ), + ); FakeMatrixApi.calledEndpoints.clear(); expect(req1.state, KeyVerificationState.askSSSS); @@ -388,8 +453,11 @@ void main() async { .setDirectVerified(false); final req1 = await client1.userDeviceKeys[client2.userID]! .startVerification(newDirectChatEnableEncryption: false); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -403,8 +471,11 @@ void main() async { FakeMatrixApi.calledEndpoints.clear(); await req2.rejectVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.cancel')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.cancel', + ), + ); evt = getLastSentEvent(req2); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); expect(req1.state, KeyVerificationState.error); @@ -421,8 +492,11 @@ void main() async { .setDirectVerified(false); final req1 = await client1.userDeviceKeys[client2.userID]! .startVerification(newDirectChatEnableEncryption: false); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -437,8 +511,11 @@ void main() async { // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); FakeMatrixApi.calledEndpoints.clear(); @@ -451,29 +528,41 @@ void main() async { expect(req1.state, KeyVerificationState.waitingAccept); // no need for start (continueVerification) because sas only mode override already sent it after ready - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start', + ), + ); evt = getLastSentEvent(req1); // send accept FakeMatrixApi.calledEndpoints.clear(); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.accept')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.accept', + ), + ); evt = getLastSentEvent(req2); // send key FakeMatrixApi.calledEndpoints.clear(); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key', + ), + ); evt = getLastSentEvent(req1); // send key FakeMatrixApi.calledEndpoints.clear(); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.key', + ), + ); evt = getLastSentEvent(req2); // receive last key @@ -481,12 +570,18 @@ void main() async { await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); await req1.acceptSas(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.mac', + ), + ); FakeMatrixApi.calledEndpoints.clear(); await req2.rejectSas(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.cancel')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.cancel', + ), + ); evt = getLastSentEvent(req2); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); expect(req1.state, KeyVerificationState.error); @@ -503,8 +598,11 @@ void main() async { .setDirectVerified(false); final req1 = await client1.userDeviceKeys[client2.userID]! .startVerification(newDirectChatEnableEncryption: false); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); final evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -516,39 +614,47 @@ void main() async { final req2 = await comp.future; await sub.cancel(); - await client2.encryption!.keyVerificationManager - .handleEventUpdate(EventUpdate( - content: { - 'event_id': req2.transactionId, - 'type': EventTypes.KeyVerificationReady, - 'content': { - 'methods': [EventTypes.Sas], - 'from_device': 'SOMEOTHERDEVICE', - 'm.relates_to': { - 'rel_type': 'm.reference', - 'event_id': req2.transactionId, + await client2.encryption!.keyVerificationManager.handleEventUpdate( + EventUpdate( + content: { + 'event_id': req2.transactionId, + 'type': EventTypes.KeyVerificationReady, + 'content': { + 'methods': [EventTypes.Sas], + 'from_device': 'SOMEOTHERDEVICE', + 'm.relates_to': { + 'rel_type': 'm.reference', + 'event_id': req2.transactionId, + }, }, + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + 'sender': client2.userID, }, - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - 'sender': client2.userID, - }, - type: EventUpdateType.timeline, - roomID: req2.room!.id, - )); + type: EventUpdateType.timeline, + roomID: req2.room!.id, + ), + ); expect(req2.state, KeyVerificationState.error); await req2.cancel(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.cancel')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.cancel', + ), + ); await client1.encryption!.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup(); }); test('Run qr verification mode 0, ssss start', () async { - expect(client1.userDeviceKeys[client2.userID]?.masterKey!.directVerified, - false); - expect(client2.userDeviceKeys[client1.userID]?.masterKey!.directVerified, - false); + expect( + client1.userDeviceKeys[client2.userID]?.masterKey!.directVerified, + false, + ); + expect( + client2.userDeviceKeys[client1.userID]?.masterKey!.directVerified, + false, + ); // for a full run we test in-room verification in a cleartext room // because then we can easily intercept the payloads and inject in the other client FakeMatrixApi.calledEndpoints.clear(); @@ -567,8 +673,11 @@ void main() async { expect(req1.state, KeyVerificationState.askSSSS); await req1.openSSSS(recoveryKey: ssssKey); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -582,33 +691,45 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); expect(req1.state, KeyVerificationState.askChoice); - expect(req1.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan]); - expect(req2.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow]); + expect( + req1.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan], + ); + expect( + req2.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow], + ); // send start FakeMatrixApi.calledEndpoints.clear(); - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req2.qrCode!.randomSharedSecret, req1.randomSharedSecretForQRCode); expect(req1.state, KeyVerificationState.showQRSuccess); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.start', + ), + ); evt = getLastSentEvent(req1); // send done @@ -616,8 +737,11 @@ void main() async { await client2.encryption!.keyVerificationManager.handleEventUpdate(evt); expect(req2.state, KeyVerificationState.confirmQRScan); await req2.acceptQRScanConfirmation(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.done', + ), + ); evt = getLastSentEvent(req2); FakeMatrixApi.calledEndpoints.clear(); @@ -626,10 +750,14 @@ void main() async { expect(req1.state, KeyVerificationState.done); expect(req2.state, KeyVerificationState.done); - expect(client1.userDeviceKeys[client2.userID]?.masterKey!.directVerified, - true); - expect(client2.userDeviceKeys[client1.userID]?.masterKey!.directVerified, - true); + expect( + client1.userDeviceKeys[client2.userID]?.masterKey!.directVerified, + true, + ); + expect( + client2.userDeviceKeys[client1.userID]?.masterKey!.directVerified, + true, + ); await client1.encryption!.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup(); @@ -648,8 +776,11 @@ void main() async { await client1.userDeviceKeys[client2.userID]!.startVerification( newDirectChatEnableEncryption: false, ); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -663,14 +794,18 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); @@ -683,9 +818,10 @@ void main() async { // send start FakeMatrixApi.calledEndpoints.clear(); // qrCode will be null here anyway because masterKey not signed - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req1.state, KeyVerificationState.error); await client1.encryption!.keyVerificationManager.cleanup(); @@ -705,8 +841,11 @@ void main() async { await client1.userDeviceKeys[client2.userID]!.startVerification( newDirectChatEnableEncryption: false, ); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -720,14 +859,18 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); @@ -738,9 +881,10 @@ void main() async { expect(req1.state, KeyVerificationState.waitingAccept); FakeMatrixApi.calledEndpoints.clear(); // qrCode will be null here anyway because masterKey not signed - await req2.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req2.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req2.state, KeyVerificationState.error); await client1.encryption!.keyVerificationManager.cleanup(); @@ -752,11 +896,11 @@ void main() async { () async { client1.verificationMethods = { KeyVerificationMethod.emoji, - KeyVerificationMethod.numbers + KeyVerificationMethod.numbers, }; client2.verificationMethods = { KeyVerificationMethod.emoji, - KeyVerificationMethod.numbers + KeyVerificationMethod.numbers, }; // for a full run we test in-room verification in a cleartext room @@ -770,8 +914,11 @@ void main() async { await client1.userDeviceKeys[client2.userID]!.startVerification( newDirectChatEnableEncryption: false, ); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.room.message', + ), + ); var evt = getLastSentEvent(req1); expect(req1.state, KeyVerificationState.waitingAccept); @@ -785,14 +932,18 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); // send ready FakeMatrixApi.calledEndpoints.clear(); await req2.acceptVerification(); - await FakeMatrixApi.firstWhere((e) => e.startsWith( - '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready')); + await FakeMatrixApi.firstWhere( + (e) => e.startsWith( + '/client/v3/rooms/!1234%3AfakeServer.notExisting/send/m.key.verification.ready', + ), + ); evt = getLastSentEvent(req2); expect(req2.state, KeyVerificationState.askChoice); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt); @@ -804,9 +955,10 @@ void main() async { FakeMatrixApi.calledEndpoints.clear(); // qrCode will be null here anyway because qr isn't supported - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req1.state, KeyVerificationState.error); await client1.encryption!.keyVerificationManager.cleanup(); diff --git a/test/encryption/olm_manager_test.dart b/test/encryption/olm_manager_test.dart index c6fa20390..984283e86 100644 --- a/test/encryption/olm_manager_test.dart +++ b/test/encryption/olm_manager_test.dart @@ -43,9 +43,13 @@ void main() { }; final signedPayload = client.encryption!.olmManager.signJson(payload); expect( - signedPayload.checkJsonSignature( - client.fingerprintKey, client.userID!, client.deviceID!), - true); + signedPayload.checkJsonSignature( + client.fingerprintKey, + client.userID!, + client.deviceID!, + ), + true, + ); }); test('uploadKeys', () async { @@ -54,7 +58,8 @@ void main() { .uploadKeys(uploadDeviceKeys: true); expect(res, true); var sent = json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/keys/upload']!.first); + FakeMatrixApi.calledEndpoints['/client/v3/keys/upload']!.first, + ); expect(sent['device_keys'] != null, true); expect(sent['one_time_keys'] != null, true); expect(sent['one_time_keys'].keys.length, 66); @@ -63,14 +68,16 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.olmManager.uploadKeys(); sent = json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/keys/upload']!.first); + FakeMatrixApi.calledEndpoints['/client/v3/keys/upload']!.first, + ); expect(sent['device_keys'] != null, false); expect(sent['fallback_keys'].keys.length, 1); FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.olmManager .uploadKeys(oldKeyCount: 20, unusedFallbackKey: true); sent = json.decode( - FakeMatrixApi.calledEndpoints['/client/v3/keys/upload']!.first); + FakeMatrixApi.calledEndpoints['/client/v3/keys/upload']!.first, + ); expect(sent['one_time_keys'].keys.length, 46); expect(sent['fallback_keys'].keys.length, 0); }); @@ -81,8 +88,9 @@ void main() { .handleDeviceOneTimeKeysCount({'signed_curve25519': 20}, null); await FakeMatrixApi.firstWhereValue('/client/v3/keys/upload'); expect( - FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), - true); + FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), + true, + ); FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.olmManager @@ -90,16 +98,18 @@ void main() { await FakeMatrixApi.firstWhereValue('/client/v3/keys/upload') .timeout(Duration(milliseconds: 50), onTimeout: () => ''); expect( - FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), - false); + FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), + false, + ); FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.olmManager .handleDeviceOneTimeKeysCount(null, []); await FakeMatrixApi.firstWhereValue('/client/v3/keys/upload'); expect( - FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), - true); + FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), + true, + ); // this will upload keys because we assume the key count is 0, if the server doesn't send one FakeMatrixApi.calledEndpoints.clear(); @@ -107,8 +117,9 @@ void main() { .handleDeviceOneTimeKeysCount(null, ['signed_curve25519']); await FakeMatrixApi.firstWhereValue('/client/v3/keys/upload'); expect( - FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), - true); + FakeMatrixApi.calledEndpoints.containsKey('/client/v3/keys/upload'), + true, + ); }); test('restoreOlmSession', () async { @@ -132,12 +143,13 @@ void main() { // start an olm session.....with ourself! client.encryption!.olmManager.olmSessions.clear(); await client.encryption!.olmManager.startOutgoingOlmSessions([ - client.userDeviceKeys[client.userID!]!.deviceKeys[client.deviceID]! + client.userDeviceKeys[client.userID!]!.deviceKeys[client.deviceID]!, ]); expect( - client.encryption!.olmManager.olmSessions - .containsKey(client.identityKey), - true); + client.encryption!.olmManager.olmSessions + .containsKey(client.identityKey), + true, + ); }); test('replay to_device events', () async { @@ -146,14 +158,15 @@ void main() { final senderKey = 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'; FakeMatrixApi.calledEndpoints.clear(); await client.database!.setLastSentMessageUserDeviceKey( - json.encode({ - 'type': 'm.foxies', - 'content': { - 'floof': 'foxhole', - }, - }), - userId, - deviceId); + json.encode({ + 'type': 'm.foxies', + 'content': { + 'floof': 'foxhole', + }, + }), + userId, + deviceId, + ); var event = ToDeviceEvent( sender: userId, type: 'm.dummy', @@ -164,23 +177,26 @@ void main() { ); await client.encryption!.olmManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - true); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + true, + ); // fail scenarios // not encrypted FakeMatrixApi.calledEndpoints.clear(); await client.database!.setLastSentMessageUserDeviceKey( - json.encode({ - 'type': 'm.foxies', - 'content': { - 'floof': 'foxhole', - }, - }), - userId, - deviceId); + json.encode({ + 'type': 'm.foxies', + 'content': { + 'floof': 'foxhole', + }, + }), + userId, + deviceId, + ); event = ToDeviceEvent( sender: userId, type: 'm.dummy', @@ -189,21 +205,24 @@ void main() { ); await client.encryption!.olmManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // device not found FakeMatrixApi.calledEndpoints.clear(); await client.database!.setLastSentMessageUserDeviceKey( - json.encode({ - 'type': 'm.foxies', - 'content': { - 'floof': 'foxhole', - }, - }), - userId, - deviceId); + json.encode({ + 'type': 'm.foxies', + 'content': { + 'floof': 'foxhole', + }, + }), + userId, + deviceId, + ); event = ToDeviceEvent( sender: userId, type: 'm.dummy', @@ -214,19 +233,22 @@ void main() { ); await client.encryption!.olmManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // don't replay if the last event is m.dummy itself FakeMatrixApi.calledEndpoints.clear(); await client.database!.setLastSentMessageUserDeviceKey( - json.encode({ - 'type': 'm.dummy', - 'content': {}, - }), - userId, - deviceId); + json.encode({ + 'type': 'm.dummy', + 'content': {}, + }), + userId, + deviceId, + ); event = ToDeviceEvent( sender: userId, type: 'm.dummy', @@ -237,9 +259,11 @@ void main() { ); await client.encryption!.olmManager.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); }); test('dispose client', () async { diff --git a/test/encryption/online_key_backup_test.dart b/test/encryption/online_key_backup_test.dart index cbabe3162..fffb8df94 100644 --- a/test/encryption/online_key_backup_test.dart +++ b/test/encryption/online_key_backup_test.dart @@ -54,10 +54,11 @@ void main() { await client.encryption!.keyManager .request(client.getRoomById(roomId)!, sessionId, senderKey); expect( - client.encryption!.keyManager - .getInboundGroupSession(roomId, sessionId) - ?.sessionId, - sessionId); + client.encryption!.keyManager + .getInboundGroupSession(roomId, sessionId) + ?.sessionId, + sessionId, + ); }); test('Load all Room Keys', () async { @@ -104,13 +105,18 @@ void main() { }; FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.keyManager.setInboundGroupSession( - roomId, sessionId, senderKey, sessionPayload, - forwarded: true); + roomId, + sessionId, + senderKey, + sessionPayload, + forwarded: true, + ); var dbSessions = await client.database!.getInboundGroupSessionsToUpload(); expect(dbSessions.isNotEmpty, true); await client.encryption!.keyManager.uploadInboundGroupSessions(); await FakeMatrixApi.firstWhereValue( - '/client/v3/room_keys/keys?version=5'); + '/client/v3/room_keys/keys?version=5', + ); final payload = FakeMatrixApi .calledEndpoints['/client/v3/room_keys/keys?version=5']!.first; dbSessions = await client.database!.getInboundGroupSessionsToUpload(); diff --git a/test/encryption/qr_verification_self_test.dart b/test/encryption/qr_verification_self_test.dart index 1e46b3486..43ff4752c 100644 --- a/test/encryption/qr_verification_self_test.dart +++ b/test/encryption/qr_verification_self_test.dart @@ -28,7 +28,9 @@ import '../fake_client.dart'; void main() async { // need to mock to pass correct data to handleToDeviceEvent Future ingestCorrectReadyEvent( - KeyVerification req1, KeyVerification req2) async { + KeyVerification req1, + KeyVerification req2, + ) async { final copyKnownVerificationMethods = List.from(req2.knownVerificationMethods); @@ -55,7 +57,7 @@ void main() async { content: { 'from_device': req2.client.deviceID, 'methods': copyKnownVerificationMethods, - 'transaction_id': req2.transactionId + 'transaction_id': req2.transactionId, }, ), ); @@ -76,13 +78,13 @@ void main() async { KeyVerificationMethod.emoji, KeyVerificationMethod.numbers, KeyVerificationMethod.qrScan, - KeyVerificationMethod.reciprocate + KeyVerificationMethod.reciprocate, }; client2.verificationMethods = { KeyVerificationMethod.emoji, KeyVerificationMethod.numbers, KeyVerificationMethod.qrShow, - KeyVerificationMethod.reciprocate + KeyVerificationMethod.reciprocate, }; }); tearDown(() async { @@ -92,17 +94,23 @@ void main() async { test('Run qr verification mode 1', () async { expect( - client1.userDeviceKeys[client2.userID]?.masterKey!.verified, false); + client1.userDeviceKeys[client2.userID]?.masterKey!.verified, + false, + ); expect( - client2.userDeviceKeys[client1.userID]?.masterKey!.verified, false); + client2.userDeviceKeys[client1.userID]?.masterKey!.verified, + false, + ); expect( - client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] - ?.verified, - false); + client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] + ?.verified, + false, + ); expect( - client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] - ?.verified, - false); + client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] + ?.verified, + false, + ); // make sure our master key is *not* verified to not triger SSSS for now client1.userDeviceKeys[client1.userID]!.masterKey! .setDirectVerified(true); @@ -130,7 +138,7 @@ void main() async { 'from_device': req1.client.deviceID, 'methods': req1.knownVerificationMethods, 'timestamp': DateTime.now().millisecondsSinceEpoch, - 'transaction_id': req1.transactionId + 'transaction_id': req1.transactionId, }, ), ); @@ -139,19 +147,24 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); expect(req1.possibleMethods, []); await req2.acceptVerification(); expect(req2.state, KeyVerificationState.askChoice); await ingestCorrectReadyEvent(req1, req2); - expect(req1.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan]); - expect(req2.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow]); + expect( + req1.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan], + ); + expect( + req2.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow], + ); expect(req1.state, KeyVerificationState.askChoice); @@ -159,9 +172,10 @@ void main() async { expect(req2.getOurQRMode(), QRMode.verifySelfUntrusted); // send start - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req2.qrCode!.randomSharedSecret, req1.randomSharedSecretForQRCode); expect(req1.state, KeyVerificationState.showQRSuccess); @@ -174,7 +188,7 @@ void main() async { 'from_device': req1.client.deviceID, 'm.relates_to': { 'event_id': req1.transactionId, - 'rel_type': 'm.reference' + 'rel_type': 'm.reference', }, 'method': EventTypes.Reciprocate, 'secret': req1.randomSharedSecretForQRCode, @@ -204,13 +218,15 @@ void main() async { expect(client2.userDeviceKeys[client1.userID]?.masterKey!.verified, true); expect( - client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] - ?.verified, - true); + client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] + ?.verified, + true, + ); expect( - client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] - ?.verified, - true); + client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] + ?.verified, + true, + ); // let any background box usage from ssss signing finish await Future.delayed(Duration(seconds: 1)); @@ -220,17 +236,23 @@ void main() async { test('Run qr verification mode 2', () async { expect( - client1.userDeviceKeys[client2.userID]?.masterKey!.verified, false); + client1.userDeviceKeys[client2.userID]?.masterKey!.verified, + false, + ); expect( - client2.userDeviceKeys[client1.userID]?.masterKey!.verified, false); + client2.userDeviceKeys[client1.userID]?.masterKey!.verified, + false, + ); expect( - client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] - ?.verified, - false); + client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] + ?.verified, + false, + ); expect( - client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] - ?.verified, - false); + client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] + ?.verified, + false, + ); // make sure our master key is *not* verified to not triger SSSS for now client1.userDeviceKeys[client1.userID]!.masterKey! .setDirectVerified(false); @@ -256,7 +278,7 @@ void main() async { 'from_device': req1.client.deviceID, 'methods': req1.knownVerificationMethods, 'timestamp': DateTime.now().millisecondsSinceEpoch, - 'transaction_id': req1.transactionId + 'transaction_id': req1.transactionId, }, ), ); @@ -265,9 +287,10 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); expect(req1.possibleMethods, []); await req2.acceptVerification(); @@ -275,10 +298,14 @@ void main() async { expect(req2.state, KeyVerificationState.askChoice); await ingestCorrectReadyEvent(req1, req2); - expect(req1.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan]); - expect(req2.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow]); + expect( + req1.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan], + ); + expect( + req2.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow], + ); expect(req1.state, KeyVerificationState.askChoice); @@ -286,9 +313,10 @@ void main() async { expect(req2.getOurQRMode(), QRMode.verifySelfTrusted); // send start - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req2.qrCode!.randomSharedSecret, req1.randomSharedSecretForQRCode); expect(req1.state, KeyVerificationState.showQRSuccess); @@ -301,7 +329,7 @@ void main() async { 'from_device': req1.client.deviceID, 'm.relates_to': { 'event_id': req1.transactionId, - 'rel_type': 'm.reference' + 'rel_type': 'm.reference', }, 'method': EventTypes.Reciprocate, 'secret': req1.randomSharedSecretForQRCode, @@ -333,13 +361,15 @@ void main() async { expect(client2.userDeviceKeys[client1.userID]?.masterKey!.verified, true); expect( - client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] - ?.verified, - true); + client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID] + ?.verified, + true, + ); expect( - client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] - ?.verified, - true); + client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID] + ?.verified, + true, + ); await client1.encryption!.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup(); @@ -376,7 +406,7 @@ void main() async { 'from_device': req1.client.deviceID, 'methods': req1.knownVerificationMethods, 'timestamp': DateTime.now().millisecondsSinceEpoch, - 'transaction_id': req1.transactionId + 'transaction_id': req1.transactionId, }, ), ); @@ -385,21 +415,26 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); expect(req1.possibleMethods, []); await req2.acceptVerification(); - expect(req2.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow]); + expect( + req2.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRShow], + ); expect(req2.state, KeyVerificationState.askChoice); await ingestCorrectReadyEvent(req1, req2); - expect(req1.possibleMethods, - [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan]); + expect( + req1.possibleMethods, + [EventTypes.Sas, EventTypes.Reciprocate, EventTypes.QRScan], + ); expect(req1.state, KeyVerificationState.askChoice); @@ -407,9 +442,10 @@ void main() async { expect(req2.getOurQRMode(), QRMode.verifySelfUntrusted); // send start - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req2.qrCode!.randomSharedSecret, req1.randomSharedSecretForQRCode); expect(req1.state, KeyVerificationState.showQRSuccess); @@ -422,7 +458,7 @@ void main() async { 'from_device': req1.client.deviceID, 'm.relates_to': { 'event_id': req1.transactionId, - 'rel_type': 'm.reference' + 'rel_type': 'm.reference', }, 'method': EventTypes.Reciprocate, 'secret': 'fake_secret', @@ -463,7 +499,7 @@ void main() async { 'from_device': req1.client.deviceID, 'methods': req1.knownVerificationMethods, 'timestamp': DateTime.now().millisecondsSinceEpoch, - 'transaction_id': req1.transactionId + 'transaction_id': req1.transactionId, }, ), ); @@ -472,9 +508,10 @@ void main() async { await sub.cancel(); expect( - client2.encryption!.keyVerificationManager - .getRequest(req2.transactionId!), - req2); + client2.encryption!.keyVerificationManager + .getRequest(req2.transactionId!), + req2, + ); await req2.acceptVerification(); expect(req2.possibleMethods, [EventTypes.Sas]); @@ -490,9 +527,10 @@ void main() async { expect(req2.getOurQRMode(), QRMode.verifySelfUntrusted); // send start - await req1.continueVerification(EventTypes.Reciprocate, - qrDataRawBytes: - Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? [])); + await req1.continueVerification( + EventTypes.Reciprocate, + qrDataRawBytes: Uint8List.fromList(req2.qrCode?.qrDataRawBytes ?? []), + ); expect(req1.state, KeyVerificationState.error); @@ -504,7 +542,7 @@ void main() async { 'from_device': req1.client.deviceID, 'm.relates_to': { 'event_id': req1.transactionId, - 'rel_type': 'm.reference' + 'rel_type': 'm.reference', }, 'method': EventTypes.Reciprocate, 'secret': 'stub_incorrect_secret_here', diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index 7141b4e46..c9670caa9 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -60,8 +60,10 @@ void main() { }); test('basic things', () async { - expect(client.encryption!.ssss.defaultKeyId, - '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'); + expect( + client.encryption!.ssss.defaultKeyId, + '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3', + ); }); test('encrypt / decrypt', () async { @@ -131,31 +133,36 @@ void main() { client.encryption!.ssss.open(EventTypes.CrossSigningSelfSigning); await handle.unlock(recoveryKey: ssssKey, postUnlock: false); expect( - (await client.encryption!.ssss - .getCached(EventTypes.CrossSigningSelfSigning)) != - null, - false); + (await client.encryption!.ssss + .getCached(EventTypes.CrossSigningSelfSigning)) != + null, + false, + ); expect( - (await client.encryption!.ssss - .getCached(EventTypes.CrossSigningUserSigning)) != - null, - false); + (await client.encryption!.ssss + .getCached(EventTypes.CrossSigningUserSigning)) != + null, + false, + ); await handle.getStored(EventTypes.CrossSigningSelfSigning); expect( - (await client.encryption!.ssss - .getCached(EventTypes.CrossSigningSelfSigning)) != - null, - true); + (await client.encryption!.ssss + .getCached(EventTypes.CrossSigningSelfSigning)) != + null, + true, + ); await handle.maybeCacheAll(); expect( - (await client.encryption!.ssss - .getCached(EventTypes.CrossSigningUserSigning)) != - null, - true); + (await client.encryption!.ssss + .getCached(EventTypes.CrossSigningUserSigning)) != + null, + true, + ); expect( - (await client.encryption!.ssss.getCached(EventTypes.MegolmBackup)) != - null, - true); + (await client.encryption!.ssss.getCached(EventTypes.MegolmBackup)) != + null, + true, + ); }); test('postUnlock', () async { @@ -166,21 +173,26 @@ void main() { client.encryption!.ssss.open(EventTypes.CrossSigningSelfSigning); await handle.unlock(recoveryKey: ssssKey); expect( - (await client.encryption!.ssss - .getCached(EventTypes.CrossSigningSelfSigning)) != - null, - true); + (await client.encryption!.ssss + .getCached(EventTypes.CrossSigningSelfSigning)) != + null, + true, + ); + expect( + (await client.encryption!.ssss + .getCached(EventTypes.CrossSigningUserSigning)) != + null, + true, + ); expect( - (await client.encryption!.ssss - .getCached(EventTypes.CrossSigningUserSigning)) != - null, - true); + (await client.encryption!.ssss.getCached(EventTypes.MegolmBackup)) != + null, + true, + ); expect( - (await client.encryption!.ssss.getCached(EventTypes.MegolmBackup)) != - null, - true); - expect(client.userDeviceKeys[client.userID!]!.masterKey!.directVerified, - true); + client.userDeviceKeys[client.userID!]!.masterKey!.directVerified, + true, + ); }); test('make share requests', () async { @@ -190,9 +202,11 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.ssss.request('some.type', [key]); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - true); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + true, + ); }); test('answer to share requests', () async { @@ -209,9 +223,11 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.ssss.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - true); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + true, + ); // now test some fail scenarios @@ -229,9 +245,11 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.ssss.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // secret not cached event = ToDeviceEvent( @@ -247,9 +265,11 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.ssss.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // is a cancelation event = ToDeviceEvent( @@ -265,9 +285,11 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.ssss.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); // device not verified final key = @@ -288,9 +310,11 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); await client.encryption!.ssss.handleToDeviceEvent(event); expect( - FakeMatrixApi.calledEndpoints.keys.any( - (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted')), - false); + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/v3/sendToDevice/m.room.encrypted'), + ), + false, + ); key.setDirectVerified(true); }); @@ -323,7 +347,7 @@ void main() { for (final type in [ EventTypes.CrossSigningSelfSigning, EventTypes.CrossSigningUserSigning, - EventTypes.MegolmBackup + EventTypes.MegolmBackup, ]) { final secret = await handle.getStored(type); await client.encryption!.ssss.clearCache(); @@ -432,8 +456,10 @@ void main() { }, ); await client.encryption!.ssss.handleToDeviceEvent(event); - expect(await client.encryption!.ssss.getCached(EventTypes.MegolmBackup), - null); + expect( + await client.encryption!.ssss.getCached(EventTypes.MegolmBackup), + null, + ); }); test('request all', () async { diff --git a/test/encryption/utils_test.dart b/test/encryption/utils_test.dart index 59e79a581..17c425471 100644 --- a/test/encryption/utils_test.dart +++ b/test/encryption/utils_test.dart @@ -56,22 +56,32 @@ void main() { ); expect(image.width, 220, reason: 'Unexpected image width'); expect(image.height, 220, reason: 'Unexpected image heigth'); - expect(image.blurhash, 'L75NyU5krSbx=zAF#kSNZxOZ%4NE', - reason: 'Unexpected image blur'); + expect( + image.blurhash, + 'L75NyU5krSbx=zAF#kSNZxOZ%4NE', + reason: 'Unexpected image blur', + ); final thumbnail = await image.generateThumbnail(dimension: 64); expect(thumbnail!.height, 64, reason: 'Unexpected thumbnail height'); final shrinkedImage = await MatrixImageFile.shrink( - bytes: data, - name: 'bomb.png', - mimeType: 'image/png', - maxDimension: 150); + bytes: data, + name: 'bomb.png', + mimeType: 'image/png', + maxDimension: 150, + ); expect(shrinkedImage.width, 150, reason: 'Unexpected scaled image width'); - expect(shrinkedImage.height, 150, - reason: 'Unexpected scaled image heigth'); - expect(shrinkedImage.blurhash, 'L75NyU5kvvbx^7AF#kSgZxOZ%5NE', - reason: 'Unexpected scaled image blur'); + expect( + shrinkedImage.height, + 150, + reason: 'Unexpected scaled image heigth', + ); + expect( + shrinkedImage.blurhash, + 'L75NyU5kvvbx^7AF#kSgZxOZ%5NE', + reason: 'Unexpected scaled image blur', + ); }); }); } diff --git a/test/event_test.dart b/test/event_test.dart index 7e06e8102..cfe02d455 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -54,7 +54,9 @@ void main() { final client = Client('testclient', httpClient: FakeMatrixApi()); final room = Room(id: '!testroom:example.abc', client: client); final event = Event.fromJson( - jsonObj, Room(id: '!testroom:example.abc', client: client)); + jsonObj, + Room(id: '!testroom:example.abc', client: client), + ); tearDownAll(() async => client.dispose()); @@ -258,14 +260,20 @@ void main() { test('sendAgain', () async { final matrix = Client('testclient', httpClient: FakeMatrixApi()); - await matrix.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); - await matrix.login(LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: 'test'), - password: '1234'); + await matrix.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); + await matrix.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: 'test'), + password: '1234', + ); final event = Event.fromJson( - jsonObj, Room(id: '!1234:example.com', client: matrix)); + jsonObj, + Room(id: '!1234:example.com', client: matrix), + ); final resp1 = await event.sendAgain(); event.status = EventStatus.error; final resp2 = await event.sendAgain(txid: '1234'); @@ -277,14 +285,20 @@ void main() { test('requestKey', tags: 'olm', () async { final matrix = Client('testclient', httpClient: FakeMatrixApi()); - await matrix.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); - await matrix.login(LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: 'test'), - password: '1234'); + await matrix.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); + await matrix.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: 'test'), + password: '1234', + ); final event = Event.fromJson( - jsonObj, Room(id: '!1234:example.com', client: matrix)); + jsonObj, + Room(id: '!1234:example.com', client: matrix), + ); String? exception; try { await event.requestKey(); @@ -293,24 +307,27 @@ void main() { } expect(exception, 'Session key not requestable'); - final event2 = Event.fromJson({ - 'event_id': id, - 'sender': senderID, - 'origin_server_ts': timestamp, - 'type': 'm.room.encrypted', - 'room_id': '1234', - 'status': EventStatus.synced.intValue, - 'content': json.encode({ - 'msgtype': 'm.bad.encrypted', - 'body': DecryptException.unknownSession, - 'can_request_session': true, - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...', - 'device_id': 'RJYKSTBOIE', - 'sender_key': 'IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA', - 'session_id': 'X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ' - }), - }, Room(id: '!1234:example.com', client: matrix)); + final event2 = Event.fromJson( + { + 'event_id': id, + 'sender': senderID, + 'origin_server_ts': timestamp, + 'type': 'm.room.encrypted', + 'room_id': '1234', + 'status': EventStatus.synced.intValue, + 'content': json.encode({ + 'msgtype': 'm.bad.encrypted', + 'body': DecryptException.unknownSession, + 'can_request_session': true, + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...', + 'device_id': 'RJYKSTBOIE', + 'sender_key': 'IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA', + 'session_id': 'X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ', + }), + }, + Room(id: '!1234:example.com', client: matrix), + ); await event2.requestKey(); @@ -319,7 +336,9 @@ void main() { test('requestKey', tags: 'olm', () async { jsonObj['state_key'] = '@alice:example.com'; final event = Event.fromJson( - jsonObj, Room(id: '!localpart:server.abc', client: client)); + jsonObj, + Room(id: '!localpart:server.abc', client: client), + ); expect(event.stateKeyUser?.id, '@alice:example.com'); }); test('canRedact', () async { @@ -339,799 +358,1020 @@ void main() { test('getLocalizedBody, isEventKnown', () async { final matrix = Client('testclient', httpClient: FakeMatrixApi()); final room = Room(id: '!1234:example.com', client: matrix); - var event = Event.fromJson({ - 'content': { - 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid', - 'membership': 'join' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'age': 1234, - 'redacted_because': { - 'content': {'reason': 'Spamming'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'redacts': '\$143273582443PhrSn:example.org', - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.redaction', - 'unsigned': {'age': 1234} - } - } - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Removed by Example'); + var event = Event.fromJson( + { + 'content': { + 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', + 'displayname': 'Alice Margatroid', + 'membership': 'join', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'age': 1234, + 'redacted_because': { + 'content': {'reason': 'Spamming'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'redacts': '\$143273582443PhrSn:example.org', + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.redaction', + 'unsigned': {'age': 1234}, + }, + }, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Removed by Example', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'avatar_url': - 'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek', - 'displayname': [ - [ - [[]] - ] - ], - 'membership': 'join' - }, - 'origin_server_ts': 1636487843183, - 'room_id': '!watercooler-v9:maunium.net', - 'sender': '@nyaaori:pixelthefox.net', - 'state_key': '@nyaaori:pixelthefox.net', - 'type': 'm.room.member', - 'unsigned': { + event = Event.fromJson( + { + 'content': { + 'avatar_url': + 'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek', + 'displayname': [ + [ + [[]], + ] + ], + 'membership': 'join', + }, + 'origin_server_ts': 1636487843183, + 'room_id': '!watercooler-v9:maunium.net', + 'sender': '@nyaaori:pixelthefox.net', + 'state_key': '@nyaaori:pixelthefox.net', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': { + 'avatar_url': + 'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek', + 'displayname': 1, + 'membership': 'join', + }, + 'prev_sender': '@nyaaori:pixelthefox.net', + 'replaces_state': '\$kcqn2k6kXQKOM45t_p8OA03PQRR3KB2N_PN4HUq1GiY', + }, + 'event_id': '\$21DJjleMGcviLoT4L9wvxawMlOXSQ9yW6R8mrhlbhfU', + 'user_id': '@nyaaori:pixelthefox.net', + 'replaces_state': '\$kcqn2k6kXQKOM45t_p8OA03PQRR3KB2N_PN4HUq1GiY', 'prev_content': { 'avatar_url': 'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek', 'displayname': 1, - 'membership': 'join' - }, - 'prev_sender': '@nyaaori:pixelthefox.net', - 'replaces_state': '\$kcqn2k6kXQKOM45t_p8OA03PQRR3KB2N_PN4HUq1GiY' - }, - 'event_id': '\$21DJjleMGcviLoT4L9wvxawMlOXSQ9yW6R8mrhlbhfU', - 'user_id': '@nyaaori:pixelthefox.net', - 'replaces_state': '\$kcqn2k6kXQKOM45t_p8OA03PQRR3KB2N_PN4HUq1GiY', - 'prev_content': { - 'avatar_url': - 'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek', - 'displayname': 1, - 'membership': 'join' - } - }, room); + 'membership': 'join', + }, + }, + room, + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'body': 'Landing', - 'info': { - 'h': 200, - 'mimetype': 'image/png', - 'size': 73602, - 'thumbnail_info': { + event = Event.fromJson( + { + 'content': { + 'body': 'Landing', + 'info': { 'h': 200, 'mimetype': 'image/png', 'size': 73602, - 'w': 140 + 'thumbnail_info': { + 'h': 200, + 'mimetype': 'image/png', + 'size': 73602, + 'w': 140, + }, + 'thumbnail_url': 'mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP', + 'w': 140, }, - 'thumbnail_url': 'mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP', - 'w': 140 - }, - 'url': 'mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.sticker', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example sent a sticker'); + 'url': 'mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.sticker', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example sent a sticker', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'reason': 'Spamming'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'redacts': '\$143273582443PhrSn:example.org', - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.redaction', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example redacted an event'); + event = Event.fromJson( + { + 'content': {'reason': 'Spamming'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'redacts': '\$143273582443PhrSn:example.org', + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.redaction', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example redacted an event', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'aliases': ['#somewhere:example.org', '#another:example.org'] - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': 'example.org', - 'type': 'm.room.aliases', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the room aliases'); + event = Event.fromJson( + { + 'content': { + 'aliases': ['#somewhere:example.org', '#another:example.org'], + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': 'example.org', + 'type': 'm.room.aliases', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the room aliases', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'aliases': ['#somewhere:example.org', '#another:example.org'] - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': 'example.org', - 'type': 'm.room.aliases', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the room aliases'); + event = Event.fromJson( + { + 'content': { + 'aliases': ['#somewhere:example.org', '#another:example.org'], + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': 'example.org', + 'type': 'm.room.aliases', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the room aliases', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'alias': '#somewhere:localhost'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.canonical_alias', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the room invitation link'); + event = Event.fromJson( + { + 'content': {'alias': '#somewhere:localhost'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.canonical_alias', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the room invitation link', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'creator': '@example:example.org', - 'm.federate': true, - 'predecessor': { - 'event_id': '\$something:example.org', - 'room_id': '!oldroom:example.org' - }, - 'room_version': '1' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.create', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example created the chat'); + event = Event.fromJson( + { + 'content': { + 'creator': '@example:example.org', + 'm.federate': true, + 'predecessor': { + 'event_id': '\$something:example.org', + 'room_id': '!oldroom:example.org', + }, + 'room_version': '1', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.create', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example created the chat', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'body': 'This room has been replaced', - 'replacement_room': '!newroom:example.org' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.tombstone', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Room has been upgraded'); + event = Event.fromJson( + { + 'content': { + 'body': 'This room has been replaced', + 'replacement_room': '!newroom:example.org', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.tombstone', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Room has been upgraded', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'join_rule': 'public'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.join_rules', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the join rules to Anyone can join'); + event = Event.fromJson( + { + 'content': {'join_rule': 'public'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.join_rules', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the join rules to Anyone can join', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': { - 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', - 'displayname': 'Alice Margatroid', - 'membership': 'join' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': {'age': 1234} - }, room); + event = Event.fromJson( + { + 'content': { + 'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF', + 'displayname': 'Alice Margatroid', + 'membership': 'join', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': {'age': 1234}, + }, + room, + ); expect(event.roomMemberChangeType, RoomMemberChangeType.join); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Alice joined the chat'); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Alice joined the chat', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'membership': 'invite'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member' - }, room); + event = Event.fromJson( + { + 'content': {'membership': 'invite'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + }, + room, + ); expect(event.roomMemberChangeType, RoomMemberChangeType.invite); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example has invited Alice'); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example has invited Alice', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'membership': 'leave'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'prev_content': {'membership': 'join'}, - } - }, room); + event = Event.fromJson( + { + 'content': {'membership': 'leave'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': {'membership': 'join'}, + }, + }, + room, + ); expect(event.roomMemberChangeType, RoomMemberChangeType.kick); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example kicked Alice'); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example kicked Alice', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'membership': 'ban'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'prev_content': {'membership': 'join'}, - } - }, room); + event = Event.fromJson( + { + 'content': {'membership': 'ban'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': {'membership': 'join'}, + }, + }, + room, + ); expect(event.roomMemberChangeType, RoomMemberChangeType.ban); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example banned Alice'); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example banned Alice', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'membership': 'join'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'prev_content': {'membership': 'invite'}, - } - }, room); + event = Event.fromJson( + { + 'content': {'membership': 'join'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': {'membership': 'invite'}, + }, + }, + room, + ); expect(event.roomMemberChangeType, RoomMemberChangeType.acceptInvite); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Alice accepted the invitation'); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Alice accepted the invitation', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'membership': 'invite'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'prev_content': {'membership': 'join'}, - } - }, room); + event = Event.fromJson( + { + 'content': {'membership': 'invite'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': {'membership': 'join'}, + }, + }, + room, + ); expect(event.roomMemberChangeType, RoomMemberChangeType.invite); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example has invited Alice'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': {'membership': 'leave'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'prev_content': {'membership': 'invite'}, - } - }, room); expect( - event.roomMemberChangeType, RoomMemberChangeType.withdrawInvitation); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example has withdrawn the invitation for Alice'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': {'membership': 'leave'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@alice:example.org', - 'state_key': '@alice:example.org', - 'type': 'm.room.member', - 'unsigned': { - 'prev_content': {'membership': 'invite'}, - } - }, room); - expect(event.roomMemberChangeType, RoomMemberChangeType.rejectInvite); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Alice rejected the invitation'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'ban': 50, - 'events': {'m.room.name': 100, 'm.room.power_levels': 100}, - 'events_default': 0, - 'invite': 50, - 'kick': 50, - 'notifications': {'room': 20}, - 'redact': 50, - 'state_default': 50, - 'users': {'@example:localhost': 100}, - 'users_default': 0 - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.power_levels', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the chat permissions'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': {'name': 'The room name'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.name', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the chat name to The room name'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': {'topic': 'A room topic'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.topic', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the chat description to A room topic'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'info': {'h': 398, 'mimetype': 'image/jpeg', 'size': 31037, 'w': 394}, - 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.avatar', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the chat avatar'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': {'history_visibility': 'shared'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.history_visibility', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example changed the history visibility to Visible for all participants'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'rotation_period_ms': 604800000, - 'rotation_period_msgs': 100 - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.encryption', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example activated end to end encryption. Need pantalaimon'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'This is an example text message', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message', - 'msgtype': 'm.text' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'This is an example text message'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'thinks this is an example emote', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'thinks this is an example emote', - 'msgtype': 'm.emote' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - '* thinks this is an example emote'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'This is an example notice', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example notice', - 'msgtype': 'm.notice' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'This is an example notice'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'filename.jpg', - 'info': {'h': 398, 'mimetype': 'image/jpeg', 'size': 31037, 'w': 394}, - 'msgtype': 'm.image', - 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example sent a picture'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'something-important.doc', - 'filename': 'something-important.doc', - 'info': {'mimetype': 'application/msword', 'size': 46144}, - 'msgtype': 'm.file', - 'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example sent a file'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'Bee Gees - Stayin Alive', - 'info': { - 'duration': 2140786, - 'mimetype': 'audio/mpeg', - 'size': 1563685 - }, - 'msgtype': 'm.audio', - 'url': 'mxc://example.org/ffed755USFFxlgbQYZGtryd' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example sent an audio'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'Big Ben, London, UK', - 'geo_uri': 'geo:51.5008,0.1247', - 'info': { - 'thumbnail_info': { - 'h': 300, - 'mimetype': 'image/jpeg', - 'size': 46144, - 'w': 300 - }, - 'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe' - }, - 'msgtype': 'm.location' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example shared the location'); - expect(event.isEventTypeKnown, true); - - event = Event.fromJson({ - 'content': { - 'body': 'Gangnam Style', - 'info': { - 'duration': 2140786, - 'h': 320, - 'mimetype': 'video/mp4', - 'size': 1563685, - 'thumbnail_info': { - 'h': 300, - 'mimetype': 'image/jpeg', - 'size': 46144, - 'w': 300 - }, - 'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', - 'w': 480 - }, - 'msgtype': 'm.video', - 'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example sent a video'); + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example has invited Alice', + ); expect(event.isEventTypeKnown, true); - event = Event.fromJson({ - 'content': {'beep': 'boop'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'unknown.event.type', - 'unsigned': {'age': 1234} - }, room); - expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Unknown event unknown.event.type'); - expect(event.isEventTypeKnown, false); - - await matrix.dispose(closeDatabase: true); - }); - - test('getLocalizedBody, parameters', () async { - final matrix = Client('testclient', httpClient: FakeMatrixApi()); - final room = Room(id: '!1234:example.com', client: matrix); - var event = Event.fromJson({ - 'content': { - 'body': 'This is an example text message', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message', - 'msgtype': 'm.text' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - plaintextBody: true), - '**This is an example text message**'); - - event = Event.fromJson({ - 'content': { - 'body': '* This is an example text message', - 'format': 'org.matrix.custom.html', - 'formatted_body': '* This is an example text message', - 'msgtype': 'm.text', - 'm.relates_to': { - 'rel_type': 'm.replace', - 'event_id': '\$some_event', - }, - 'm.new_content': { - 'body': 'This is an example text message', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message', - 'msgtype': 'm.text' - }, - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - hideEdit: true), - 'This is an example text message'); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - hideEdit: true, plaintextBody: true), - '**This is an example text message**'); - - event = Event.fromJson({ - 'content': { - 'body': '> <@user:example.org> beep\n\nhmm, fox', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'beephmm, fox', - 'msgtype': 'm.text' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - hideReply: true), - 'hmm, fox'); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - hideReply: true, plaintextBody: true), - 'hmm, *fox*'); - - event = Event.fromJson({ - 'content': { - 'body': - '# Title\nsome text and [link](https://example.com)\nokay and this is **important**', - 'format': 'org.matrix.custom.html', - 'formatted_body': - '

Title

\n

some text and link
okay and this is important

\n', - 'msgtype': 'm.text' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - removeMarkdown: true), - 'Title\nsome text and link\nokay and this is important'); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - removeMarkdown: true, plaintextBody: true), - 'Title\nsome text and 🔗link\nokay and this is important'); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - removeMarkdown: true, withSenderNamePrefix: true), - 'Example: Title\nsome text and link\nokay and this is important'); - expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations(), - removeMarkdown: true, - plaintextBody: true, - withSenderNamePrefix: true), - 'Example: Title\nsome text and 🔗link\nokay and this is important'); - - event = Event.fromJson({ - 'content': { - 'body': - 'Alice is requesting to verify your device, but your client does not support verification, so you may need to use a different verification method.', - 'from_device': 'AliceDevice2', - 'methods': ['m.sas.v1'], - 'msgtype': 'm.key.verification.request', - 'to': '@bob:example.org' - }, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} - }, room); + event = Event.fromJson( + { + 'content': {'membership': 'leave'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': {'membership': 'invite'}, + }, + }, + room, + ); expect( - await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example requested key verification', + event.roomMemberChangeType, + RoomMemberChangeType.withdrawInvitation, ); - - event.content['msgtype'] = 'm.key.verification.ready'; expect( await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example is ready for key verification', + 'Example has withdrawn the invitation for Alice', ); + expect(event.isEventTypeKnown, true); - event.content['msgtype'] = 'm.key.verification.start'; + event = Event.fromJson( + { + 'content': {'membership': 'leave'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@alice:example.org', + 'state_key': '@alice:example.org', + 'type': 'm.room.member', + 'unsigned': { + 'prev_content': {'membership': 'invite'}, + }, + }, + room, + ); + expect(event.roomMemberChangeType, RoomMemberChangeType.rejectInvite); expect( await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example started key verification', + 'Alice rejected the invitation', ); + expect(event.isEventTypeKnown, true); - event.content['msgtype'] = 'm.key.verification.cancel'; + event = Event.fromJson( + { + 'content': { + 'ban': 50, + 'events': {'m.room.name': 100, 'm.room.power_levels': 100}, + 'events_default': 0, + 'invite': 50, + 'kick': 50, + 'notifications': {'room': 20}, + 'redact': 50, + 'state_default': 50, + 'users': {'@example:localhost': 100}, + 'users_default': 0, + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.power_levels', + 'unsigned': {'age': 1234}, + }, + room, + ); expect( await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example canceled key verification', + 'Example changed the chat permissions', ); + expect(event.isEventTypeKnown, true); - event.content['msgtype'] = 'm.key.verification.done'; + event = Event.fromJson( + { + 'content': {'name': 'The room name'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.name', + 'unsigned': {'age': 1234}, + }, + room, + ); expect( await event.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Example completed key verification', + 'Example changed the chat name to The room name', ); + expect(event.isEventTypeKnown, true); - event.content['msgtype'] = 'm.key.verification.accept'; + event = Event.fromJson( + { + 'content': {'topic': 'A room topic'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.topic', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the chat description to A room topic', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'info': { + 'h': 398, + 'mimetype': 'image/jpeg', + 'size': 31037, + 'w': 394, + }, + 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.avatar', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the chat avatar', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': {'history_visibility': 'shared'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.history_visibility', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example changed the history visibility to Visible for all participants', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'rotation_period_ms': 604800000, + 'rotation_period_msgs': 100, + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.encryption', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example activated end to end encryption. Need pantalaimon', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'This is an example text message', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'This is an example text message', + 'msgtype': 'm.text', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'This is an example text message', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'thinks this is an example emote', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'thinks this is an example emote', + 'msgtype': 'm.emote', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + '* thinks this is an example emote', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'This is an example notice', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'This is an example notice', + 'msgtype': 'm.notice', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'This is an example notice', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'filename.jpg', + 'info': { + 'h': 398, + 'mimetype': 'image/jpeg', + 'size': 31037, + 'w': 394, + }, + 'msgtype': 'm.image', + 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example sent a picture', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'something-important.doc', + 'filename': 'something-important.doc', + 'info': {'mimetype': 'application/msword', 'size': 46144}, + 'msgtype': 'm.file', + 'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example sent a file', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'Bee Gees - Stayin Alive', + 'info': { + 'duration': 2140786, + 'mimetype': 'audio/mpeg', + 'size': 1563685, + }, + 'msgtype': 'm.audio', + 'url': 'mxc://example.org/ffed755USFFxlgbQYZGtryd', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example sent an audio', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'Big Ben, London, UK', + 'geo_uri': 'geo:51.5008,0.1247', + 'info': { + 'thumbnail_info': { + 'h': 300, + 'mimetype': 'image/jpeg', + 'size': 46144, + 'w': 300, + }, + 'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', + }, + 'msgtype': 'm.location', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example shared the location', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': { + 'body': 'Gangnam Style', + 'info': { + 'duration': 2140786, + 'h': 320, + 'mimetype': 'video/mp4', + 'size': 1563685, + 'thumbnail_info': { + 'h': 300, + 'mimetype': 'image/jpeg', + 'size': 46144, + 'w': 300, + }, + 'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', + 'w': 480, + }, + 'msgtype': 'm.video', + 'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example sent a video', + ); + expect(event.isEventTypeKnown, true); + + event = Event.fromJson( + { + 'content': {'beep': 'boop'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'unknown.event.type', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Unknown event unknown.event.type', + ); + expect(event.isEventTypeKnown, false); + + await matrix.dispose(closeDatabase: true); + }); + + test('getLocalizedBody, parameters', () async { + final matrix = Client('testclient', httpClient: FakeMatrixApi()); + final room = Room(id: '!1234:example.com', client: matrix); + var event = Event.fromJson( + { + 'content': { + 'body': 'This is an example text message', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'This is an example text message', + 'msgtype': 'm.text', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + plaintextBody: true, + ), + '**This is an example text message**', + ); + + event = Event.fromJson( + { + 'content': { + 'body': '* This is an example text message', + 'format': 'org.matrix.custom.html', + 'formatted_body': '* This is an example text message', + 'msgtype': 'm.text', + 'm.relates_to': { + 'rel_type': 'm.replace', + 'event_id': '\$some_event', + }, + 'm.new_content': { + 'body': 'This is an example text message', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'This is an example text message', + 'msgtype': 'm.text', + }, + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + hideEdit: true, + ), + 'This is an example text message', + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + hideEdit: true, + plaintextBody: true, + ), + '**This is an example text message**', + ); + + event = Event.fromJson( + { + 'content': { + 'body': '> <@user:example.org> beep\n\nhmm, fox', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'beephmm, fox', + 'msgtype': 'm.text', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + hideReply: true, + ), + 'hmm, fox', + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + hideReply: true, + plaintextBody: true, + ), + 'hmm, *fox*', + ); + + event = Event.fromJson( + { + 'content': { + 'body': + '# Title\nsome text and [link](https://example.com)\nokay and this is **important**', + 'format': 'org.matrix.custom.html', + 'formatted_body': + '

Title

\n

some text and link
okay and this is important

\n', + 'msgtype': 'm.text', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + removeMarkdown: true, + ), + 'Title\nsome text and link\nokay and this is important', + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + removeMarkdown: true, + plaintextBody: true, + ), + 'Title\nsome text and 🔗link\nokay and this is important', + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + removeMarkdown: true, + withSenderNamePrefix: true, + ), + 'Example: Title\nsome text and link\nokay and this is important', + ); + expect( + await event.calcLocalizedBody( + MatrixDefaultLocalizations(), + removeMarkdown: true, + plaintextBody: true, + withSenderNamePrefix: true, + ), + 'Example: Title\nsome text and 🔗link\nokay and this is important', + ); + + event = Event.fromJson( + { + 'content': { + 'body': + 'Alice is requesting to verify your device, but your client does not support verification, so you may need to use a different verification method.', + 'from_device': 'AliceDevice2', + 'methods': ['m.sas.v1'], + 'msgtype': 'm.key.verification.request', + 'to': '@bob:example.org', + }, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ); + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example requested key verification', + ); + + event.content['msgtype'] = 'm.key.verification.ready'; + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example is ready for key verification', + ); + + event.content['msgtype'] = 'm.key.verification.start'; + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example started key verification', + ); + + event.content['msgtype'] = 'm.key.verification.cancel'; + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example canceled key verification', + ); + + event.content['msgtype'] = 'm.key.verification.done'; + expect( + await event.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Example completed key verification', + ); + + event.content['msgtype'] = 'm.key.verification.accept'; expect( await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Example accepted key verification request', @@ -1142,102 +1382,131 @@ void main() { test('aggregations', () { final room = Room(id: '!1234', client: client); - final event = Event.fromJson({ - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - }, - 'type': 'm.room.message', - 'sender': '@example:example.org', - 'event_id': '\$source', - }, room); - final edit1 = Event.fromJson({ - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - 'm.relates_to': { - 'event_id': '\$source', - 'rel_type': RelationshipTypes.edit, - }, - }, - 'type': 'm.room.message', - 'sender': '@example:example.org', - 'event_id': '\$edit1', - }, room); - final edit2 = Event.fromJson({ - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - 'm.relates_to': { - 'event_id': '\$source', - 'rel_type': RelationshipTypes.edit, - }, - }, - 'type': 'm.room.message', - 'sender': '@example:example.org', - 'event_id': '\$edit2', - }, room); + final event = Event.fromJson( + { + 'content': { + 'body': 'blah', + 'msgtype': 'm.text', + }, + 'type': 'm.room.message', + 'sender': '@example:example.org', + 'event_id': '\$source', + }, + room, + ); + final edit1 = Event.fromJson( + { + 'content': { + 'body': 'blah', + 'msgtype': 'm.text', + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, + }, + 'type': 'm.room.message', + 'sender': '@example:example.org', + 'event_id': '\$edit1', + }, + room, + ); + final edit2 = Event.fromJson( + { + 'content': { + 'body': 'blah', + 'msgtype': 'm.text', + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, + }, + 'type': 'm.room.message', + 'sender': '@example:example.org', + 'event_id': '\$edit2', + }, + room, + ); final timeline = Timeline( - chunk: TimelineChunk(events: [event, edit1, edit2]), - room: room); + chunk: TimelineChunk(events: [event, edit1, edit2]), + room: room, + ); expect(event.hasAggregatedEvents(timeline, RelationshipTypes.edit), true); - expect(event.aggregatedEvents(timeline, RelationshipTypes.edit), - {edit1, edit2}); - expect(event.aggregatedEvents(timeline, RelationshipTypes.reaction), - {}); - expect(event.hasAggregatedEvents(timeline, RelationshipTypes.reaction), - false); + expect( + event.aggregatedEvents(timeline, RelationshipTypes.edit), + {edit1, edit2}, + ); + expect( + event.aggregatedEvents(timeline, RelationshipTypes.reaction), + {}, + ); + expect( + event.hasAggregatedEvents(timeline, RelationshipTypes.reaction), + false, + ); timeline.removeAggregatedEvent(edit2); expect(event.aggregatedEvents(timeline, RelationshipTypes.edit), {edit1}); timeline.addAggregatedEvent(edit2); - expect(event.aggregatedEvents(timeline, RelationshipTypes.edit), - {edit1, edit2}); + expect( + event.aggregatedEvents(timeline, RelationshipTypes.edit), + {edit1, edit2}, + ); timeline.removeAggregatedEvent(event); expect( - event.aggregatedEvents(timeline, RelationshipTypes.edit), {}); + event.aggregatedEvents(timeline, RelationshipTypes.edit), + {}, + ); }); test('plaintextBody', () { - final event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'blah', - }, - 'event_id': '\$source', - 'sender': '@alice:example.org', - }, room); + final event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'blah', + 'msgtype': 'm.text', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'blah', + }, + 'event_id': '\$source', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.plaintextBody, '**blah**'); }); test('body', () { - final event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'blub', - }, - 'event_id': '\$source', - 'sender': '@alice:example.org', - }, room); + final event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'blah', + 'msgtype': 'm.text', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'blub', + }, + 'event_id': '\$source', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.body, 'blah'); - final event2 = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': '', - 'msgtype': 'm.text', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'blub', - }, - 'event_id': '\$source', - 'sender': '@alice:example.org', - }, room); + final event2 = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': '', + 'msgtype': 'm.text', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'blub', + }, + 'event_id': '\$source', + 'sender': '@alice:example.org', + }, + room, + ); expect(event2.body, 'm.room.message'); }); @@ -1257,33 +1526,39 @@ void main() { }) { i += 1; test('$i', () { - final event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - if (body != null) 'body': body, - if (formattedBody != null) 'formatted_body': formattedBody, - if (html) 'format': 'org.matrix.custom.html', - if (isEdit) ...{ - 'm.new_content': { - if (editBody != null) 'body': editBody, - if (editFormattedBody != null) - 'formatted_body': editFormattedBody, - if (editHtml) 'format': 'org.matrix.custom.html', - }, - 'm.relates_to': { - 'event_id': '\$source2', - 'rel_type': RelationshipTypes.edit, + final event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + if (body != null) 'body': body, + if (formattedBody != null) 'formatted_body': formattedBody, + if (html) 'format': 'org.matrix.custom.html', + if (isEdit) ...{ + 'm.new_content': { + if (editBody != null) 'body': editBody, + if (editFormattedBody != null) + 'formatted_body': editFormattedBody, + if (editHtml) 'format': 'org.matrix.custom.html', + }, + 'm.relates_to': { + 'event_id': '\$source2', + 'rel_type': RelationshipTypes.edit, + }, }, }, + 'event_id': '\$source', + 'sender': '@alice:example.org', }, - 'event_id': '\$source', - 'sender': '@alice:example.org', - }, room); + room, + ); expect( event.calcUnlocalizedBody( - hideReply: true, hideEdit: true, plaintextBody: plaintextBody), + hideReply: true, + hideEdit: true, + plaintextBody: plaintextBody, + ), expectation, reason: 'event was ${event.toJson()} and plaintextBody ${plaintextBody ? "was" : "was not"} set', @@ -1789,107 +2064,140 @@ void main() { test('getDisplayEvent', () { final room = Room(id: '!1234', client: client); - var event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - }, - 'event_id': '\$source', - 'sender': '@alice:example.org', - }, room); - final edit1 = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': '* edit 1', - 'msgtype': 'm.text', - 'm.new_content': { - 'body': 'edit 1', + var event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'blah', 'msgtype': 'm.text', }, - 'm.relates_to': { - 'event_id': '\$source', - 'rel_type': RelationshipTypes.edit, - }, - }, - 'event_id': '\$edit1', - 'sender': '@alice:example.org', - }, room); - final edit2 = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': '* edit 2', - 'msgtype': 'm.text', - 'm.new_content': { - 'body': 'edit 2', + 'event_id': '\$source', + 'sender': '@alice:example.org', + }, + room, + ); + final edit1 = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': '* edit 1', 'msgtype': 'm.text', + 'm.new_content': { + 'body': 'edit 1', + 'msgtype': 'm.text', + }, + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, }, - 'm.relates_to': { - 'event_id': '\$source', - 'rel_type': RelationshipTypes.edit, - }, - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); - final edit3 = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': '* edit 3', - 'msgtype': 'm.text', - 'm.new_content': { - 'body': 'edit 3', + 'event_id': '\$edit1', + 'sender': '@alice:example.org', + }, + room, + ); + final edit2 = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': '* edit 2', 'msgtype': 'm.text', + 'm.new_content': { + 'body': 'edit 2', + 'msgtype': 'm.text', + }, + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, }, - 'm.relates_to': { - 'event_id': '\$source', - 'rel_type': RelationshipTypes.edit, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); + final edit3 = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': '* edit 3', + 'msgtype': 'm.text', + 'm.new_content': { + 'body': 'edit 3', + 'msgtype': 'm.text', + }, + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, }, + 'event_id': '\$edit3', + 'sender': '@bob:example.org', }, - 'event_id': '\$edit3', - 'sender': '@bob:example.org', - }, room); + room, + ); // no edits var displayEvent = event.getDisplayEvent( - Timeline(chunk: TimelineChunk(events: [event]), room: room)); + Timeline(chunk: TimelineChunk(events: [event]), room: room), + ); expect(displayEvent.body, 'blah'); // one edit - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit1]), room: room)); + displayEvent = event.getDisplayEvent( + Timeline( + chunk: TimelineChunk(events: [event, edit1]), + room: room, + ), + ); expect(displayEvent.body, 'edit 1'); // two edits - displayEvent = event.getDisplayEvent(Timeline( + displayEvent = event.getDisplayEvent( + Timeline( chunk: TimelineChunk(events: [event, edit1, edit2]), - room: room)); + room: room, + ), + ); expect(displayEvent.body, 'edit 2'); // foreign edit - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit3]), room: room)); + displayEvent = event.getDisplayEvent( + Timeline( + chunk: TimelineChunk(events: [event, edit3]), + room: room, + ), + ); expect(displayEvent.body, 'blah'); // mixed foreign and non-foreign - displayEvent = event.getDisplayEvent(Timeline( + displayEvent = event.getDisplayEvent( + Timeline( chunk: TimelineChunk(events: [event, edit1, edit2, edit3]), - room: room)); + room: room, + ), + ); expect(displayEvent.body, 'edit 2'); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'blah', - 'msgtype': 'm.text', - }, - 'event_id': '\$source', - 'sender': '@alice:example.org', - 'unsigned': { - 'redacted_because': { - 'event_id': '\$redact', - 'sender': '@alice:example.org', - 'type': 'm.room.redaction', + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'blah', + 'msgtype': 'm.text', + }, + 'event_id': '\$source', + 'sender': '@alice:example.org', + 'unsigned': { + 'redacted_because': { + 'event_id': '\$redact', + 'sender': '@alice:example.org', + 'type': 'm.room.redaction', + }, }, }, - }, room); - displayEvent = event.getDisplayEvent(Timeline( + room, + ); + displayEvent = event.getDisplayEvent( + Timeline( chunk: TimelineChunk(events: [event, edit1, edit2, edit3]), - room: room)); + room: room, + ), + ); expect(displayEvent.body, 'Redacted'); }); test('attachments', () async { @@ -1902,45 +2210,58 @@ void main() { }[uri.path]!; } - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final room = Room(id: '!localpart:server.abc', client: client); - var event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'url': 'mxc://example.org/file', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + var event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/file', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); var buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); + downloadCallback: downloadCallback, + ); expect(buffer.bytes, FILE_BUFF); - expect(event.attachmentOrThumbnailMxcUrl().toString(), - 'mxc://example.org/file'); - expect(event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(), - 'mxc://example.org/file'); - - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'url': 'mxc://example.org/file', - 'info': { - 'size': 8000000, - 'thumbnail_url': 'mxc://example.org/thumb', - 'thumbnail_info': { - 'mimetype': 'thumbnail/mimetype', + expect( + event.attachmentOrThumbnailMxcUrl().toString(), + 'mxc://example.org/file', + ); + expect( + event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(), + 'mxc://example.org/file', + ); + + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/file', + 'info': { + 'size': 8000000, + 'thumbnail_url': 'mxc://example.org/thumb', + 'thumbnail_info': { + 'mimetype': 'thumbnail/mimetype', + }, + 'mimetype': 'application/octet-stream', }, - 'mimetype': 'application/octet-stream', }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect(event.hasAttachment, true); expect(event.hasThumbnail, true); expect(event.isAttachmentEncrypted, false); @@ -1949,34 +2270,52 @@ void main() { expect(event.thumbnailMimetype, 'thumbnail/mimetype'); expect(event.attachmentMxcUrl.toString(), 'mxc://example.org/file'); expect(event.thumbnailMxcUrl.toString(), 'mxc://example.org/thumb'); - expect(event.attachmentOrThumbnailMxcUrl().toString(), - 'mxc://example.org/file'); - expect(event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(), - 'mxc://example.org/thumb'); - expect((await event.getAttachmentUri()).toString(), - 'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/file'); - expect((await event.getAttachmentUri(getThumbnail: true)).toString(), - 'https://fakeserver.notexisting/_matrix/client/v1/media/thumbnail/example.org/file?width=800&height=800&method=scale&animated=false'); - expect( - (await event.getAttachmentUri(useThumbnailMxcUrl: true)).toString(), - 'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/thumb'); - expect( - (await event.getAttachmentUri( - getThumbnail: true, useThumbnailMxcUrl: true)) - .toString(), - 'https://fakeserver.notexisting/_matrix/client/v1/media/thumbnail/example.org/thumb?width=800&height=800&method=scale&animated=false'); - expect( - (await event.getAttachmentUri( - getThumbnail: true, minNoThumbSize: 9000000)) - .toString(), - 'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/file'); + expect( + event.attachmentOrThumbnailMxcUrl().toString(), + 'mxc://example.org/file', + ); + expect( + event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(), + 'mxc://example.org/thumb', + ); + expect( + (await event.getAttachmentUri()).toString(), + 'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/file', + ); + expect( + (await event.getAttachmentUri(getThumbnail: true)).toString(), + 'https://fakeserver.notexisting/_matrix/client/v1/media/thumbnail/example.org/file?width=800&height=800&method=scale&animated=false', + ); + expect( + (await event.getAttachmentUri(useThumbnailMxcUrl: true)).toString(), + 'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/thumb', + ); + expect( + (await event.getAttachmentUri( + getThumbnail: true, + useThumbnailMxcUrl: true, + )) + .toString(), + 'https://fakeserver.notexisting/_matrix/client/v1/media/thumbnail/example.org/thumb?width=800&height=800&method=scale&animated=false', + ); + expect( + (await event.getAttachmentUri( + getThumbnail: true, + minNoThumbSize: 9000000, + )) + .toString(), + 'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/file', + ); buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); + downloadCallback: downloadCallback, + ); expect(buffer.bytes, FILE_BUFF); buffer = await event.downloadAndDecryptAttachment( - getThumbnail: true, downloadCallback: downloadCallback); + getThumbnail: true, + downloadCallback: downloadCallback, + ); expect(buffer.bytes, THUMBNAIL_BUFF); }); test( @@ -2001,78 +2340,85 @@ void main() { final room = Room(id: '!localpart:server.abc', client: await getClient()); - var event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'file': { - 'v': 'v2', - 'key': { - 'alg': 'A256CTR', - 'ext': true, - 'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc', - 'key_ops': ['encrypt', 'decrypt'], - 'kty': 'oct' - }, - 'iv': 'Wdsf+tnOHIoAAAAAAAAAAA', - 'hashes': { - 'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE' + var event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'file': { + 'v': 'v2', + 'key': { + 'alg': 'A256CTR', + 'ext': true, + 'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc', + 'key_ops': ['encrypt', 'decrypt'], + 'kty': 'oct', + }, + 'iv': 'Wdsf+tnOHIoAAAAAAAAAAA', + 'hashes': { + 'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE', + }, + 'url': 'mxc://example.com/file', + 'mimetype': 'text/plain', }, - 'url': 'mxc://example.com/file', - 'mimetype': 'text/plain' }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); var buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); + downloadCallback: downloadCallback, + ); expect(buffer.bytes, FILE_BUFF_DEC); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'file': { - 'v': 'v2', - 'key': { - 'alg': 'A256CTR', - 'ext': true, - 'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc', - 'key_ops': ['encrypt', 'decrypt'], - 'kty': 'oct' - }, - 'iv': 'Wdsf+tnOHIoAAAAAAAAAAA', - 'hashes': { - 'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE' - }, - 'url': 'mxc://example.com/file', - 'mimetype': 'text/plain' - }, - 'info': { - 'thumbnail_file': { + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'file': { 'v': 'v2', 'key': { 'alg': 'A256CTR', 'ext': true, - 'k': 'TmF-rZYetZbxpL5yjDPE21UALQJcpEE6X-nvUDD5rA0', + 'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc', 'key_ops': ['encrypt', 'decrypt'], - 'kty': 'oct' + 'kty': 'oct', }, - 'iv': '41ZqNRZSLFUAAAAAAAAAAA', + 'iv': 'Wdsf+tnOHIoAAAAAAAAAAA', 'hashes': { - 'sha256': 'zccOwXiOTAYhGXyk0Fra7CRreBF6itjiCKdd+ov8mO4' + 'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE', + }, + 'url': 'mxc://example.com/file', + 'mimetype': 'text/plain', + }, + 'info': { + 'thumbnail_file': { + 'v': 'v2', + 'key': { + 'alg': 'A256CTR', + 'ext': true, + 'k': 'TmF-rZYetZbxpL5yjDPE21UALQJcpEE6X-nvUDD5rA0', + 'key_ops': ['encrypt', 'decrypt'], + 'kty': 'oct', + }, + 'iv': '41ZqNRZSLFUAAAAAAAAAAA', + 'hashes': { + 'sha256': 'zccOwXiOTAYhGXyk0Fra7CRreBF6itjiCKdd+ov8mO4', + }, + 'url': 'mxc://example.com/thumb', + 'mimetype': 'text/plain', }, - 'url': 'mxc://example.com/thumb', - 'mimetype': 'text/plain' - } + }, }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect(event.hasAttachment, true); expect(event.hasThumbnail, true); expect(event.isAttachmentEncrypted, true); @@ -2082,11 +2428,14 @@ void main() { expect(event.attachmentMxcUrl.toString(), 'mxc://example.com/file'); expect(event.thumbnailMxcUrl.toString(), 'mxc://example.com/thumb'); buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); + downloadCallback: downloadCallback, + ); expect(buffer.bytes, FILE_BUFF_DEC); buffer = await event.downloadAndDecryptAttachment( - getThumbnail: true, downloadCallback: downloadCallback); + getThumbnail: true, + downloadCallback: downloadCallback, + ); expect(buffer.bytes, THUMB_BUFF_DEC); await room.client.dispose(closeDatabase: true); @@ -2102,34 +2451,45 @@ void main() { }[uri.path]!; } - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final room = Room(id: '!localpart:server.abc', client: await getClient()); - final event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'url': 'mxc://example.org/newfile', - 'info': { - 'size': 5, - }, - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + final event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/newfile', + 'info': { + 'size': 5, + }, + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(await event.isAttachmentInLocalStore(), false); var buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); - expect(await event.isAttachmentInLocalStore(), - event.room.client.database?.supportsFileStoring); + downloadCallback: downloadCallback, + ); + expect( + await event.isAttachmentInLocalStore(), + event.room.client.database?.supportsFileStoring, + ); expect(buffer.bytes, FILE_BUFF); expect(serverHits, 1); buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); + downloadCallback: downloadCallback, + ); expect(buffer.bytes, FILE_BUFF); expect( - serverHits, event.room.client.database!.supportsFileStoring ? 1 : 2); + serverHits, + event.room.client.database!.supportsFileStoring ? 1 : 2, + ); await room.client.dispose(closeDatabase: true); }); @@ -2144,39 +2504,52 @@ void main() { }[uri.path]!; } - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final room = Room(id: '!localpart:server.abc', client: await getClient()); - final event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'url': 'mxc://example.org/newfile', - 'info': { - 'size': 5, + final event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/newfile', + 'info': { + 'size': 5, + }, }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); var buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback); - expect(await event.isAttachmentInLocalStore(), - event.room.client.database?.supportsFileStoring); + downloadCallback: downloadCallback, + ); + expect( + await event.isAttachmentInLocalStore(), + event.room.client.database?.supportsFileStoring, + ); expect(buffer.bytes, FILE_BUFF); expect(serverHits, 1); if (event.room.client.database?.supportsFileStoring == true) { buffer = await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback, fromLocalStoreOnly: true); + downloadCallback: downloadCallback, + fromLocalStoreOnly: true, + ); expect(buffer.bytes, FILE_BUFF); } else { expect( - () async => await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback, fromLocalStoreOnly: true), - throwsA(anything)); + () async => await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback, + fromLocalStoreOnly: true, + ), + throwsA(anything), + ); } expect(serverHits, 1); @@ -2194,27 +2567,35 @@ void main() { }[uri.path]!; } - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final room = Room(id: '!localpart:server.abc', client: await getClient()); - final event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'body': 'image', - 'msgtype': 'm.image', - 'url': 'mxc://example.org/newfile', - 'info': { - 'size': 5, + final event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/newfile', + 'info': { + 'size': 5, + }, }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect( - () async => await event.downloadAndDecryptAttachment( - downloadCallback: downloadCallback, fromLocalStoreOnly: true), - throwsA(anything)); + () async => await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback, + fromLocalStoreOnly: true, + ), + throwsA(anything), + ); expect(serverHits, 0); @@ -2222,187 +2603,230 @@ void main() { }); test('emote detection', () async { - var event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': 'normal message', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + var event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': 'normal message', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, false); expect(event.numberEmotes, 0); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': 'normal message\n\nvery normal', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': 'normal message\n\nvery normal', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, false); expect(event.numberEmotes, 0); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': 'normal message with emoji 🦊', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': 'normal message with emoji 🦊', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, false); expect(event.numberEmotes, 1); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '🦊', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '🦊', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 1); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '🦊🦊 🦊\n🦊🦊', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '🦊🦊 🦊\n🦊🦊', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 5); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': 'rich message', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'rich message' - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': 'rich message', + 'format': 'org.matrix.custom.html', + 'formatted_body': 'rich message', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, false); expect(event.numberEmotes, 0); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '🦊', - 'format': 'org.matrix.custom.html', - 'formatted_body': '🦊' - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '🦊', + 'format': 'org.matrix.custom.html', + 'formatted_body': '🦊', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 1); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': ':blah:', - 'format': 'org.matrix.custom.html', - 'formatted_body': '' - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': ':blah:', + 'format': 'org.matrix.custom.html', + 'formatted_body': '', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 1); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '🦊 :blah:', - 'format': 'org.matrix.custom.html', - 'formatted_body': '🦊 ' - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '🦊 :blah:', + 'format': 'org.matrix.custom.html', + 'formatted_body': + '🦊 ', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 2); // with variant selector - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '❤️', - }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '❤️', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 1); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '''> <@alice:example.org> 😒😒 + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '''> <@alice:example.org> 😒😒 ❤❤❤''', - 'format': 'org.matrix.custom.html', - 'formatted_body': - '
In reply to @alice:example.org
😒😒
❤❤❤' + 'format': 'org.matrix.custom.html', + 'formatted_body': + '
In reply to @alice:example.org
😒😒
❤❤❤', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 3); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '''> <@alice:example.org> A 😒 + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '''> <@alice:example.org> A 😒 ❤❤''', - 'format': 'org.matrix.custom.html', - 'formatted_body': - '
In reply to @alice:example.org
A 😒
❤❤' + 'format': 'org.matrix.custom.html', + 'formatted_body': + '
In reply to @alice:example.org
A 😒
❤❤', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect(event.onlyEmotes, true); expect(event.numberEmotes, 2); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '''> <@alice:example.org> 😒😒😒 + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '''> <@alice:example.org> 😒😒😒 ❤A❤''', - 'format': 'org.matrix.custom.html', - 'formatted_body': - '
In reply to @alice:example.org
😒😒😒
❤A❤' + 'format': 'org.matrix.custom.html', + 'formatted_body': + '
In reply to @alice:example.org
😒😒😒
❤A❤', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect(event.onlyEmotes, false); expect(event.numberEmotes, 2); - event = Event.fromJson({ - 'type': EventTypes.Message, - 'content': { - 'msgtype': 'm.text', - 'body': '''> <@alice:example.org> A😒 + event = Event.fromJson( + { + 'type': EventTypes.Message, + 'content': { + 'msgtype': 'm.text', + 'body': '''> <@alice:example.org> A😒 ❤A❤''', - 'format': 'org.matrix.custom.html', - 'formatted_body': - '
In reply to @alice:example.org
A😒
❤A❤' + 'format': 'org.matrix.custom.html', + 'formatted_body': + '
In reply to @alice:example.org
A😒
❤A❤', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', }, - 'event_id': '\$edit2', - 'sender': '@alice:example.org', - }, room); + room, + ); expect(event.onlyEmotes, false); expect(event.numberEmotes, 2); }); diff --git a/test/fake_client.dart b/test/fake_client.dart index fcf46da4f..352874e23 100644 --- a/test/fake_client.dart +++ b/test/fake_client.dart @@ -41,8 +41,10 @@ Future getClient({ sendTimelineEventTimeout: sendTimelineEventTimeout, ); FakeMatrixApi.client = client; - await client.checkHomeserver(Uri.parse('https://fakeServer.notExisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeServer.notExisting'), + checkWellKnown: false, + ); await client.init( newToken: 'abcd', newRefreshToken: 'refresh_abcd', @@ -64,8 +66,10 @@ Future getOtherClient() async { databaseBuilder: getDatabase, ); FakeMatrixApi.client = client; - await client.checkHomeserver(Uri.parse('https://fakeServer.notExisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeServer.notExisting'), + checkWellKnown: false, + ); await client.init( newToken: '1234', newUserID: '@test:fakeServer.notExisting', diff --git a/test/fake_database.dart b/test/fake_database.dart index 055e824ea..c0cdcdfd0 100644 --- a/test/fake_database.dart +++ b/test/fake_database.dart @@ -45,8 +45,10 @@ Future getHiveCollectionsDatabase(Client? c) async { } // ignore: deprecated_member_use_from_same_package -Future getMatrixSdkDatabase(Client? c, - {String? path}) async { +Future getMatrixSdkDatabase( + Client? c, { + String? path, +}) async { final database = await databaseFactoryFfi.openDatabase( path ?? ':memory:', options: OpenDatabaseOptions(singleInstance: false), diff --git a/test/image_pack_test.dart b/test/image_pack_test.dart index ed1503cea..08a2d2167 100644 --- a/test/image_pack_test.dart +++ b/test/image_pack_test.dart @@ -31,137 +31,155 @@ void main() { client = await getClient(); room = Room(id: '!1234:fakeServer.notExisting', client: client); room2 = Room(id: '!abcd:fakeServer.notExisting', client: client); - room.setState(Event( - type: 'm.room.power_levels', - content: {}, - room: room, - stateKey: '', - senderId: client.userID!, - eventId: '\$fakeid1:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); - room.setState(Event( - type: 'm.room.member', - content: {'membership': 'join'}, - room: room, - stateKey: client.userID, - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid2:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); - room2.setState(Event( - type: 'm.room.power_levels', - content: {}, - room: room2, - stateKey: '', - senderId: client.userID!, - eventId: '\$fakeid3:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); - room2.setState(Event( - type: 'm.room.member', - content: {'membership': 'join'}, - room: room2, - stateKey: client.userID, - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid4:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + room.setState( + Event( + type: 'm.room.power_levels', + content: {}, + room: room, + stateKey: '', + senderId: client.userID!, + eventId: '\$fakeid1:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); + room.setState( + Event( + type: 'm.room.member', + content: {'membership': 'join'}, + room: room, + stateKey: client.userID, + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid2:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); + room2.setState( + Event( + type: 'm.room.power_levels', + content: {}, + room: room2, + stateKey: '', + senderId: client.userID!, + eventId: '\$fakeid3:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); + room2.setState( + Event( + type: 'm.room.member', + content: {'membership': 'join'}, + room: room2, + stateKey: client.userID, + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid4:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); client.rooms.add(room); client.rooms.add(room2); }); test('Single room', () async { - room.setState(Event( - type: 'im.ponies.room_emotes', - content: { - 'images': { - 'room_plain': {'url': 'mxc://room_plain'} - } - }, - room: room, - stateKey: '', - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid5:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + room.setState( + Event( + type: 'im.ponies.room_emotes', + content: { + 'images': { + 'room_plain': {'url': 'mxc://room_plain'}, + }, + }, + room: room, + stateKey: '', + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid5:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); final packs = room.getImagePacks(); expect(packs.length, 1); expect(packs['room']?.images.length, 1); - expect(packs['room']?.images['room_plain']?.url.toString(), - 'mxc://room_plain'); + expect( + packs['room']?.images['room_plain']?.url.toString(), + 'mxc://room_plain', + ); var packsFlat = room.getImagePacksFlat(); expect(packsFlat, { - 'room': {'room_plain': 'mxc://room_plain'} + 'room': {'room_plain': 'mxc://room_plain'}, }); - room.setState(Event( - type: 'im.ponies.room_emotes', - content: { - 'images': { - 'emote': { - 'url': 'mxc://emote', - 'usage': ['emoticon'] - }, - 'sticker': { - 'url': 'mxc://sticker', - 'usage': ['sticker'] + room.setState( + Event( + type: 'im.ponies.room_emotes', + content: { + 'images': { + 'emote': { + 'url': 'mxc://emote', + 'usage': ['emoticon'], + }, + 'sticker': { + 'url': 'mxc://sticker', + 'usage': ['sticker'], + }, }, - } - }, - room: room, - stateKey: '', - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid6:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + }, + room: room, + stateKey: '', + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid6:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon); expect(packsFlat, { - 'room': {'emote': 'mxc://emote'} + 'room': {'emote': 'mxc://emote'}, }); packsFlat = room.getImagePacksFlat(ImagePackUsage.sticker); expect(packsFlat, { - 'room': {'sticker': 'mxc://sticker'} + 'room': {'sticker': 'mxc://sticker'}, }); - room.setState(Event( - type: 'im.ponies.room_emotes', - content: { - 'images': { - 'emote': {'url': 'mxc://emote'}, - 'sticker': {'url': 'mxc://sticker'}, + room.setState( + Event( + type: 'im.ponies.room_emotes', + content: { + 'images': { + 'emote': {'url': 'mxc://emote'}, + 'sticker': {'url': 'mxc://sticker'}, + }, + 'pack': { + 'usage': ['emoticon'], + }, }, - 'pack': { - 'usage': ['emoticon'], - } - }, - room: room, - stateKey: '', - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid7:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + room: room, + stateKey: '', + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid7:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon); expect(packsFlat, { - 'room': {'emote': 'mxc://emote', 'sticker': 'mxc://sticker'} + 'room': {'emote': 'mxc://emote', 'sticker': 'mxc://sticker'}, }); packsFlat = room.getImagePacksFlat(ImagePackUsage.sticker); expect(packsFlat, {}); - room.setState(Event( - type: 'im.ponies.room_emotes', - content: { - 'images': { - 'fox': {'url': 'mxc://fox'}, + room.setState( + Event( + type: 'im.ponies.room_emotes', + content: { + 'images': { + 'fox': {'url': 'mxc://fox'}, + }, + 'pack': { + 'usage': ['emoticon'], + }, }, - 'pack': { - 'usage': ['emoticon'], - } - }, - room: room, - stateKey: 'fox', - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid8:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + room: room, + stateKey: 'fox', + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid8:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon); expect(packsFlat, { 'room': {'emote': 'mxc://emote', 'sticker': 'mxc://sticker'}, @@ -176,7 +194,7 @@ void main() { 'images': { 'user': { 'url': 'mxc://user', - } + }, }, }, }); @@ -189,22 +207,24 @@ void main() { }); test('other rooms', () async { - room2.setState(Event( - type: 'im.ponies.room_emotes', - content: { - 'images': { - 'other_room_emote': {'url': 'mxc://other_room_emote'}, + room2.setState( + Event( + type: 'im.ponies.room_emotes', + content: { + 'images': { + 'other_room_emote': {'url': 'mxc://other_room_emote'}, + }, + 'pack': { + 'usage': ['emoticon'], + }, }, - 'pack': { - 'usage': ['emoticon'], - } - }, - room: room2, - stateKey: '', - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid9:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + room: room2, + stateKey: '', + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid9:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); client.accountData['im.ponies.emote_rooms'] = BasicEvent.fromJson({ 'type': 'im.ponies.emote_rooms', 'content': { @@ -219,25 +239,27 @@ void main() { 'fox': {'fox': 'mxc://fox'}, 'user': {'user': 'mxc://user'}, 'empty-chat-abcdfakeservernotexisting': { - 'other_room_emote': 'mxc://other_room_emote' + 'other_room_emote': 'mxc://other_room_emote', }, }); - room2.setState(Event( - type: 'im.ponies.room_emotes', - content: { - 'images': { - 'other_fox': {'url': 'mxc://other_fox'}, + room2.setState( + Event( + type: 'im.ponies.room_emotes', + content: { + 'images': { + 'other_fox': {'url': 'mxc://other_fox'}, + }, + 'pack': { + 'usage': ['emoticon'], + }, }, - 'pack': { - 'usage': ['emoticon'], - } - }, - room: room2, - stateKey: 'fox', - senderId: '@fakeuser:fakeServer.notExisting', - eventId: '\$fakeid10:fakeServer.notExisting', - originServerTs: DateTime.now(), - )); + room: room2, + stateKey: 'fox', + senderId: '@fakeuser:fakeServer.notExisting', + eventId: '\$fakeid10:fakeServer.notExisting', + originServerTs: DateTime.now(), + ), + ); client.accountData['im.ponies.emote_rooms'] = BasicEvent.fromJson({ 'type': 'im.ponies.emote_rooms', 'content': { @@ -252,10 +274,10 @@ void main() { 'fox': {'fox': 'mxc://fox'}, 'user': {'user': 'mxc://user'}, 'empty-chat-abcdfakeservernotexisting': { - 'other_room_emote': 'mxc://other_room_emote' + 'other_room_emote': 'mxc://other_room_emote', }, 'empty-chat-fox-abcdfakeservernotexisting': { - 'other_fox': 'mxc://other_fox' + 'other_fox': 'mxc://other_fox', }, }); }); diff --git a/test/markdown_test.dart b/test/markdown_test.dart index cd2ef2a09..d685f62de 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart @@ -42,21 +42,33 @@ void main() { }; String? getMention(mention) => mentionMap[mention]; test('simple markdown', () { - expect(markdown('hey *there* how are **you** doing?'), - 'hey there how are you doing?'); + expect( + markdown('hey *there* how are **you** doing?'), + 'hey there how are you doing?', + ); expect(markdown('wha ~~strike~~ works!'), 'wha strike works!'); }); test('spoilers', () { - expect(markdown('Snape killed ||Dumbledoor||'), - 'Snape killed Dumbledoor'); - expect(markdown('Snape killed ||Story|Dumbledoor||'), - 'Snape killed Dumbledoor'); - expect(markdown('Snape killed ||Some dumb loser|Dumbledoor||'), - 'Snape killed Dumbledoor'); - expect(markdown('Snape killed ||Some dumb loser|Dumbledoor **bold**||'), - 'Snape killed Dumbledoor bold'); - expect(markdown('Snape killed ||Dumbledoor **bold**||'), - 'Snape killed Dumbledoor bold'); + expect( + markdown('Snape killed ||Dumbledoor||'), + 'Snape killed Dumbledoor', + ); + expect( + markdown('Snape killed ||Story|Dumbledoor||'), + 'Snape killed Dumbledoor', + ); + expect( + markdown('Snape killed ||Some dumb loser|Dumbledoor||'), + 'Snape killed Dumbledoor', + ); + expect( + markdown('Snape killed ||Some dumb loser|Dumbledoor **bold**||'), + 'Snape killed Dumbledoor bold', + ); + expect( + markdown('Snape killed ||Dumbledoor **bold**||'), + 'Snape killed Dumbledoor bold', + ); }); test('multiple paragraphs', () { expect(markdown('Heya!\n\nBeep'), '

Heya!

Beep

'); @@ -74,60 +86,108 @@ void main() { ); }); test('emotes', () { - expect(markdown(':fox:', getEmotePacks: () => emotePacks), - ':fox:'); - expect(markdown(':user~fox:', getEmotePacks: () => emotePacks), - ':fox:'); - expect(markdown(':raccoon:', getEmotePacks: () => emotePacks), - ':raccoon:'); - expect( - markdown(':invalid:', getEmotePacks: () => emotePacks), ':invalid:'); - expect(markdown(':invalid:?!', getEmotePacks: () => emotePacks), - ':invalid:?!'); - expect(markdown(':room~invalid:', getEmotePacks: () => emotePacks), - ':room~invalid:'); + expect( + markdown(':fox:', getEmotePacks: () => emotePacks), + ':fox:', + ); + expect( + markdown(':user~fox:', getEmotePacks: () => emotePacks), + ':fox:', + ); + expect( + markdown(':raccoon:', getEmotePacks: () => emotePacks), + ':raccoon:', + ); + expect( + markdown(':invalid:', getEmotePacks: () => emotePacks), + ':invalid:', + ); + expect( + markdown(':invalid:?!', getEmotePacks: () => emotePacks), + ':invalid:?!', + ); + expect( + markdown(':room~invalid:', getEmotePacks: () => emotePacks), + ':room~invalid:', + ); }); test('pills', () { - expect(markdown('Hey @sorunome:sorunome.de!'), - 'Hey @sorunome:sorunome.de!'); - expect(markdown('#fox:sorunome.de: you all are awesome'), - '#fox:sorunome.de: you all are awesome'); - expect(markdown('!blah:example.org'), - '!blah:example.org'); - expect(markdown('https://matrix.to/#/#fox:sorunome.de'), - 'https://matrix.to/#/#fox:sorunome.de'); - expect(markdown('Hey @sorunome:sorunome.de:1234!'), - 'Hey @sorunome:sorunome.de:1234!'); - expect(markdown('Hey @sorunome:127.0.0.1!'), - 'Hey @sorunome:127.0.0.1!'); - expect(markdown('Hey @sorunome:[::1]!'), - 'Hey @sorunome:[::1]!'); + expect( + markdown('Hey @sorunome:sorunome.de!'), + 'Hey @sorunome:sorunome.de!', + ); + expect( + markdown('#fox:sorunome.de: you all are awesome'), + '#fox:sorunome.de: you all are awesome', + ); + expect( + markdown('!blah:example.org'), + '!blah:example.org', + ); + expect( + markdown('https://matrix.to/#/#fox:sorunome.de'), + 'https://matrix.to/#/#fox:sorunome.de', + ); + expect( + markdown('Hey @sorunome:sorunome.de:1234!'), + 'Hey @sorunome:sorunome.de:1234!', + ); + expect( + markdown('Hey @sorunome:127.0.0.1!'), + 'Hey @sorunome:127.0.0.1!', + ); + expect( + markdown('Hey @sorunome:[::1]!'), + 'Hey @sorunome:[::1]!', + ); }); test('mentions', () { - expect(markdown('Hey @Bob!', getMention: getMention), - 'Hey @Bob!'); - expect(markdown('How is @[Bob Ross] doing?', getMention: getMention), - 'How is @[Bob Ross] doing?'); - expect( - markdown('Hey @invalid!', getMention: getMention), 'Hey @invalid!'); - expect(markdown('Hey @Fox#123!', getMention: getMention), - 'Hey @Fox#123!'); - expect(markdown('Hey @[Fast Fox]#123!', getMention: getMention), - 'Hey @[Fast Fox]#123!'); - expect(markdown('Hey @[">]!', getMention: getMention), - 'Hey @[">]!'); + expect( + markdown('Hey @Bob!', getMention: getMention), + 'Hey @Bob!', + ); + expect( + markdown('How is @[Bob Ross] doing?', getMention: getMention), + 'How is @[Bob Ross] doing?', + ); + expect( + markdown('Hey @invalid!', getMention: getMention), + 'Hey @invalid!', + ); + expect( + markdown('Hey @Fox#123!', getMention: getMention), + 'Hey @Fox#123!', + ); + expect( + markdown('Hey @[Fast Fox]#123!', getMention: getMention), + 'Hey @[Fast Fox]#123!', + ); + expect( + markdown('Hey @[">]!', getMention: getMention), + 'Hey @[">]!', + ); }); test('latex', () { - expect(markdown('meep \$\\frac{2}{3}\$'), - 'meep \\frac{2}{3}'); - expect(markdown('meep \$hmm *yay*\$'), - 'meep hmm *yay*'); - expect(markdown('you have \$somevar and \$someothervar'), - 'you have \$somevar and \$someothervar'); - expect(markdown('meep ||\$\\frac{2}{3}\$||'), - 'meep \\frac{2}{3}'); - expect(markdown('meep `\$\\frac{2}{3}\$`'), - 'meep \$\\frac{2}{3}\$'); + expect( + markdown('meep \$\\frac{2}{3}\$'), + 'meep \\frac{2}{3}', + ); + expect( + markdown('meep \$hmm *yay*\$'), + 'meep hmm *yay*', + ); + expect( + markdown('you have \$somevar and \$someothervar'), + 'you have \$somevar and \$someothervar', + ); + expect( + markdown('meep ||\$\\frac{2}{3}\$||'), + 'meep \\frac{2}{3}', + ); + expect( + markdown('meep `\$\\frac{2}{3}\$`'), + 'meep \$\\frac{2}{3}\$', + ); }); test('Code blocks', () { expect( diff --git a/test/matrix_api/try_get_map_extension_test.dart b/test/matrix_api/try_get_map_extension_test.dart index dc5f7843b..0c2cd7e63 100644 --- a/test/matrix_api/try_get_map_extension_test.dart +++ b/test/matrix_api/try_get_map_extension_test.dart @@ -35,12 +35,18 @@ void main() { expect(data.tryGet('str'), null); expect(data.tryGet('int'), 42); expect(data.tryGet('list'), [2, 3, 4]); - expect(data.tryGet>('map')?.tryGet('beep'), - 'boop'); - expect(data.tryGet>('map')?.tryGet('meep'), - null); - expect(data.tryGet>('pam')?.tryGet('beep'), - null); + expect( + data.tryGet>('map')?.tryGet('beep'), + 'boop', + ); + expect( + data.tryGet>('map')?.tryGet('meep'), + null, + ); + expect( + data.tryGet>('pam')?.tryGet('beep'), + null, + ); }); }); } diff --git a/test/matrix_exception_test.dart b/test/matrix_exception_test.dart index 76a79f74d..b094b288c 100644 --- a/test/matrix_exception_test.dart +++ b/test/matrix_exception_test.dart @@ -42,8 +42,10 @@ void main() { {'example_key': 'foobar'}, ); expect(matrixException.completedAuthenticationFlows.length, 1); - expect(matrixException.completedAuthenticationFlows.first, - 'example.type.foo'); + expect( + matrixException.completedAuthenticationFlows.first, + 'example.type.foo', + ); expect(matrixException.session, 'xxxxxxyz'); }); test('Unknown Exception', () async { diff --git a/test/matrix_file_test.dart b/test/matrix_file_test.dart index ff9d1a7c4..4dc116683 100644 --- a/test/matrix_file_test.dart +++ b/test/matrix_file_test.dart @@ -39,8 +39,11 @@ void main() { }); test('Shrink', () async { - final resp = await http.get(Uri.parse( - 'https://upload.wikimedia.org/wikipedia/commons/5/5f/Salagou_Lake%2C_Celles_cf01.jpg')); + final resp = await http.get( + Uri.parse( + 'https://upload.wikimedia.org/wikipedia/commons/5/5f/Salagou_Lake%2C_Celles_cf01.jpg', + ), + ); if (resp.statusCode == 200) { final file = MatrixImageFile( diff --git a/test/matrix_localizations_test.dart b/test/matrix_localizations_test.dart index 4c72451b5..9a0049803 100644 --- a/test/matrix_localizations_test.dart +++ b/test/matrix_localizations_test.dart @@ -25,36 +25,49 @@ void main() { group('Matrix Localizations', () { test('Matrix Localizations', () { expect( - HistoryVisibility.invited - .getLocalizedString(MatrixDefaultLocalizations()), - 'From the invitation'); - expect( - HistoryVisibility.joined - .getLocalizedString(MatrixDefaultLocalizations()), - 'From joining'); - expect( - HistoryVisibility.shared - .getLocalizedString(MatrixDefaultLocalizations()), - 'Visible for all participants'); - expect( - HistoryVisibility.worldReadable - .getLocalizedString(MatrixDefaultLocalizations()), - 'Visible for everyone'); - expect( - GuestAccess.canJoin.getLocalizedString(MatrixDefaultLocalizations()), - 'Guests can join'); - expect( - GuestAccess.forbidden - .getLocalizedString(MatrixDefaultLocalizations()), - 'Guests are forbidden'); - expect(JoinRules.invite.getLocalizedString(MatrixDefaultLocalizations()), - 'Invited users only'); - expect(JoinRules.public.getLocalizedString(MatrixDefaultLocalizations()), - 'Anyone can join'); - expect(JoinRules.private.getLocalizedString(MatrixDefaultLocalizations()), - 'private'); - expect(JoinRules.knock.getLocalizedString(MatrixDefaultLocalizations()), - 'knock'); + HistoryVisibility.invited + .getLocalizedString(MatrixDefaultLocalizations()), + 'From the invitation', + ); + expect( + HistoryVisibility.joined + .getLocalizedString(MatrixDefaultLocalizations()), + 'From joining', + ); + expect( + HistoryVisibility.shared + .getLocalizedString(MatrixDefaultLocalizations()), + 'Visible for all participants', + ); + expect( + HistoryVisibility.worldReadable + .getLocalizedString(MatrixDefaultLocalizations()), + 'Visible for everyone', + ); + expect( + GuestAccess.canJoin.getLocalizedString(MatrixDefaultLocalizations()), + 'Guests can join', + ); + expect( + GuestAccess.forbidden.getLocalizedString(MatrixDefaultLocalizations()), + 'Guests are forbidden', + ); + expect( + JoinRules.invite.getLocalizedString(MatrixDefaultLocalizations()), + 'Invited users only', + ); + expect( + JoinRules.public.getLocalizedString(MatrixDefaultLocalizations()), + 'Anyone can join', + ); + expect( + JoinRules.private.getLocalizedString(MatrixDefaultLocalizations()), + 'private', + ); + expect( + JoinRules.knock.getLocalizedString(MatrixDefaultLocalizations()), + 'knock', + ); }); }); } diff --git a/test/msc_extensions/msc_3814_dehydrated_devices_test.dart b/test/msc_extensions/msc_3814_dehydrated_devices_test.dart index a0c6b9dac..a13049559 100644 --- a/test/msc_extensions/msc_3814_dehydrated_devices_test.dart +++ b/test/msc_extensions/msc_3814_dehydrated_devices_test.dart @@ -34,18 +34,23 @@ void main() { final client = await getClient(); final ret = await client.uploadDehydratedDevice( - deviceId: 'DEHYDDEV', - initialDeviceDisplayName: 'DehydratedDevice', - deviceData: {'algorithm': 'some.famedly.proprietary.algorith'}); + deviceId: 'DEHYDDEV', + initialDeviceDisplayName: 'DehydratedDevice', + deviceData: {'algorithm': 'some.famedly.proprietary.algorith'}, + ); expect( - FakeMatrixApi.calledEndpoints.containsKey( - '/client/unstable/org.matrix.msc3814.v1/dehydrated_device'), - true); + FakeMatrixApi.calledEndpoints.containsKey( + '/client/unstable/org.matrix.msc3814.v1/dehydrated_device', + ), + true, + ); expect(ret.isNotEmpty, true); final device = await client.getDehydratedDevice(); expect(device.deviceId, 'DEHYDDEV'); - expect(device.deviceData?['algorithm'], - 'some.famedly.proprietary.algorithm'); + expect( + device.deviceData?['algorithm'], + 'some.famedly.proprietary.algorithm', + ); final events = await client.getDehydratedDeviceEvents(device.deviceId); expect(events.events?.length, 1); diff --git a/test/mxc_uri_extension_test.dart b/test/mxc_uri_extension_test.dart index 39e058319..541027d0d 100644 --- a/test/mxc_uri_extension_test.dart +++ b/test/mxc_uri_extension_test.dart @@ -26,70 +26,93 @@ void main() { Logs().level = Level.error; test('Formatting', () async { final client = Client('testclient', httpClient: FakeMatrixApi()); - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final mxc = 'mxc://exampleserver.abc/abcdefghijklmn'; final content = Uri.parse(mxc); expect(content.isScheme('mxc'), true); - expect((await content.getDownloadUri(client)).toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/download/exampleserver.abc/abcdefghijklmn'); expect( - (await content.getThumbnailUri(client, width: 50, height: 50)) - .toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop&animated=false'); + (await content.getDownloadUri(client)).toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/download/exampleserver.abc/abcdefghijklmn', + ); + expect( + (await content.getThumbnailUri(client, width: 50, height: 50)) + .toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop&animated=false', + ); expect( - (await content.getThumbnailUri(client, - width: 50, - height: 50, - method: ThumbnailMethod.scale, - animated: true)) - .toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale&animated=true'); + (await content.getThumbnailUri( + client, + width: 50, + height: 50, + method: ThumbnailMethod.scale, + animated: true, + )) + .toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale&animated=true', + ); }); test('other port', () async { final client = Client('testclient', httpClient: FakeMatrixApi()); - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); client.homeserver = Uri.parse('https://fakeserver.notexisting:1337'); final mxc = 'mxc://exampleserver.abc/abcdefghijklmn'; final content = Uri.parse(mxc); expect(content.isScheme('mxc'), true); - expect((await content.getDownloadUri(client)).toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/download/exampleserver.abc/abcdefghijklmn'); expect( - (await content.getThumbnailUri(client, width: 50, height: 50)) - .toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop&animated=false'); + (await content.getDownloadUri(client)).toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/download/exampleserver.abc/abcdefghijklmn', + ); + expect( + (await content.getThumbnailUri(client, width: 50, height: 50)) + .toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop&animated=false', + ); expect( - (await content.getThumbnailUri(client, - width: 50, - height: 50, - method: ThumbnailMethod.scale, - animated: true)) - .toString(), - 'https://fakeserver.notexisting:1337/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale&animated=true'); + (await content.getThumbnailUri( + client, + width: 50, + height: 50, + method: ThumbnailMethod.scale, + animated: true, + )) + .toString(), + 'https://fakeserver.notexisting:1337/_matrix/client/v1/media/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale&animated=true', + ); }); test('other remote port', () async { final client = Client('testclient', httpClient: FakeMatrixApi()); - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final mxc = 'mxc://exampleserver.abc:1234/abcdefghijklmn'; final content = Uri.parse(mxc); expect(content.isScheme('mxc'), true); - expect((await content.getDownloadUri(client)).toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/download/exampleserver.abc:1234/abcdefghijklmn'); expect( - (await content.getThumbnailUri(client, width: 50, height: 50)) - .toString(), - '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc:1234/abcdefghijklmn?width=50&height=50&method=crop&animated=false'); + (await content.getDownloadUri(client)).toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/download/exampleserver.abc:1234/abcdefghijklmn', + ); + expect( + (await content.getThumbnailUri(client, width: 50, height: 50)) + .toString(), + '${client.homeserver.toString()}/_matrix/client/v1/media/thumbnail/exampleserver.abc:1234/abcdefghijklmn?width=50&height=50&method=crop&animated=false', + ); }); test('Wrong scheme throw exception', () async { final client = Client('testclient', httpClient: FakeMatrixApi()); - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); final mxc = Uri.parse('https://wrong-scheme.com'); expect((await mxc.getDownloadUri(client)).toString(), ''); expect((await mxc.getThumbnailUri(client)).toString(), ''); @@ -98,20 +121,24 @@ void main() { test('auth media fallback', () async { final client = Client('testclient', httpClient: FakeMatrixApi()); await client.checkHomeserver( - Uri.parse('https://fakeserverpriortoauthmedia.notexisting'), - checkWellKnown: false); + Uri.parse('https://fakeserverpriortoauthmedia.notexisting'), + checkWellKnown: false, + ); expect(await client.authenticatedMediaSupported(), false); final mxc = 'mxc://exampleserver.abc:1234/abcdefghijklmn'; final content = Uri.parse(mxc); expect(content.isScheme('mxc'), true); - expect((await content.getDownloadUri(client)).toString(), - '${client.homeserver.toString()}/_matrix/media/v3/download/exampleserver.abc:1234/abcdefghijklmn'); expect( - (await content.getThumbnailUri(client, width: 50, height: 50)) - .toString(), - '${client.homeserver.toString()}/_matrix/media/v3/thumbnail/exampleserver.abc:1234/abcdefghijklmn?width=50&height=50&method=crop&animated=false'); + (await content.getDownloadUri(client)).toString(), + '${client.homeserver.toString()}/_matrix/media/v3/download/exampleserver.abc:1234/abcdefghijklmn', + ); + expect( + (await content.getThumbnailUri(client, width: 50, height: 50)) + .toString(), + '${client.homeserver.toString()}/_matrix/media/v3/thumbnail/exampleserver.abc:1234/abcdefghijklmn?width=50&height=50&method=crop&animated=false', + ); }); }); } diff --git a/test/push_notification.dart b/test/push_notification.dart index f35220da0..d13b28991 100644 --- a/test/push_notification.dart +++ b/test/push_notification.dart @@ -11,7 +11,7 @@ void main() { const json = { 'content': { 'body': "I'm floating in a most peculiar way.", - 'msgtype': 'm.text' + 'msgtype': 'm.text', }, 'counts': {'missed_calls': 1, 'unread': 2}, 'devices': [ @@ -20,7 +20,7 @@ void main() { 'data': {}, 'pushkey': 'V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/', 'pushkey_ts': 12345678, - 'tweaks': {'sound': 'bing'} + 'tweaks': {'sound': 'bing'}, } ], 'event_id': '\$3957tyerfgewrf384', @@ -30,7 +30,7 @@ void main() { 'room_name': 'Mission Control', 'sender': '@exampleuser:matrix.org', 'sender_display_name': 'Major Tom', - 'type': 'm.room.message' + 'type': 'm.room.message', }; test('fromJson and toJson', () async { diff --git a/test/pushevaluator_test.dart b/test/pushevaluator_test.dart index e293d7a59..6bace3743 100644 --- a/test/pushevaluator_test.dart +++ b/test/pushevaluator_test.dart @@ -59,63 +59,91 @@ void main() { test('event_match rule', () async { final event = Event.fromJson(jsonObj, room); - final override_ruleset = PushRuleSet(override: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition( - kind: 'event_match', pattern: 'fox', key: 'content.body'), - ]) - ]); - final underride_ruleset = PushRuleSet(underride: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition( - kind: 'event_match', pattern: 'fox', key: 'content.body'), - ]) - ]); - final content_ruleset = PushRuleSet(content: [ - PushRule( - ruleId: 'my.rule', - default$: false, - enabled: true, - actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], - pattern: 'fox', - ) - ]); - final room_ruleset = PushRuleSet(room: [ - PushRule( - ruleId: room.id, - default$: false, - enabled: true, - actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], - ) - ]); - final sender_ruleset = PushRuleSet(sender: [ - PushRule( - ruleId: senderID, - default$: false, - enabled: true, - actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], - ) - ]); + final override_ruleset = PushRuleSet( + override: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition( + kind: 'event_match', + pattern: 'fox', + key: 'content.body', + ), + ], + ), + ], + ); + final underride_ruleset = PushRuleSet( + underride: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition( + kind: 'event_match', + pattern: 'fox', + key: 'content.body', + ), + ], + ), + ], + ); + final content_ruleset = PushRuleSet( + content: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + pattern: 'fox', + ), + ], + ); + final room_ruleset = PushRuleSet( + room: [ + PushRule( + ruleId: room.id, + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + ), + ], + ); + final sender_ruleset = PushRuleSet( + sender: [ + PushRule( + ruleId: senderID, + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + ), + ], + ); void testMatch(PushRuleSet ruleset, Event event) { final evaluator = PushruleEvaluator.fromRuleset(ruleset); @@ -174,15 +202,27 @@ void main() { testMatch(room_ruleset, event); testMatch(sender_ruleset, event); - final override_ruleset2 = PushRuleSet(override: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition(kind: 'event_match', pattern: senderID, key: 'sender'), - ]) - ]); + final override_ruleset2 = PushRuleSet( + override: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition( + kind: 'event_match', + pattern: senderID, + key: 'sender', + ), + ], + ), + ], + ); testMatch(override_ruleset2, event); event.senderId = '@nope:server.tld'; @@ -199,19 +239,32 @@ void main() { }); test('invalid push condition', () async { - final invalid_ruleset = PushRuleSet(override: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition( - kind: 'invalidcondition', pattern: 'fox', key: 'content.body'), - ]) - ]); - - expect(() => PushruleEvaluator.fromRuleset(invalid_ruleset), - returnsNormally); + final invalid_ruleset = PushRuleSet( + override: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition( + kind: 'invalidcondition', + pattern: 'fox', + key: 'content.body', + ), + ], + ), + ], + ); + + expect( + () => PushruleEvaluator.fromRuleset(invalid_ruleset), + returnsNormally, + ); final evaluator = PushruleEvaluator.fromRuleset(invalid_ruleset); final event = Event.fromJson(jsonObj, room); @@ -224,24 +277,35 @@ void main() { test('match_display_name rule', () async { final event = Event.fromJson(jsonObj, room); (event.room.states[EventTypes.RoomMember] ??= {})[client.userID!] = - Event.fromJson({ - 'type': EventTypes.RoomMember, - 'sender': senderID, - 'state_key': 'client.senderID', - 'content': {'displayname': 'Nico', 'membership': 'join'}, - 'room_id': room.id, - 'origin_server_ts': 5, - }, room); - - final ruleset = PushRuleSet(override: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition(kind: 'contains_display_name'), - ]) - ]); + Event.fromJson( + { + 'type': EventTypes.RoomMember, + 'sender': senderID, + 'state_key': 'client.senderID', + 'content': {'displayname': 'Nico', 'membership': 'join'}, + 'room_id': room.id, + 'origin_server_ts': 5, + }, + room, + ); + + final ruleset = PushRuleSet( + override: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition(kind: 'contains_display_name'), + ], + ), + ], + ); event.content['body'] = 'äNicoü'; final evaluator = PushruleEvaluator.fromRuleset(ruleset); @@ -258,24 +322,35 @@ void main() { test('member_count rule', () async { final event = Event.fromJson(jsonObj, room); (event.room.states[EventTypes.RoomMember] ??= {})[client.userID!] = - Event.fromJson({ - 'type': EventTypes.RoomMember, - 'sender': senderID, - 'state_key': 'client.senderID', - 'content': {'displayname': 'Nico', 'membership': 'join'}, - 'room_id': room.id, - 'origin_server_ts': 5, - }, room); - - final ruleset = PushRuleSet(override: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition(kind: 'room_member_count', is$: '<5'), - ]) - ]); + Event.fromJson( + { + 'type': EventTypes.RoomMember, + 'sender': senderID, + 'state_key': 'client.senderID', + 'content': {'displayname': 'Nico', 'membership': 'join'}, + 'room_id': room.id, + 'origin_server_ts': 5, + }, + room, + ); + + final ruleset = PushRuleSet( + override: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition(kind: 'room_member_count', is$: '<5'), + ], + ), + ], + ); event.content['body'] = 'äNicoü'; var evaluator = PushruleEvaluator.fromRuleset(ruleset); @@ -309,27 +384,41 @@ void main() { test('notification permissions rule', () async { final event = Event.fromJson(jsonObj, room); (event.room.states[EventTypes.RoomPowerLevels] ??= {})[''] = - Event.fromJson({ - 'type': EventTypes.RoomMember, - 'sender': senderID, - 'state_key': 'client.senderID', - 'content': { - 'notifications': {'broom': 20}, - 'users': {senderID: 20}, + Event.fromJson( + { + 'type': EventTypes.RoomMember, + 'sender': senderID, + 'state_key': 'client.senderID', + 'content': { + 'notifications': {'broom': 20}, + 'users': {senderID: 20}, + }, + 'room_id': room.id, + 'origin_server_ts': 5, }, - 'room_id': room.id, - 'origin_server_ts': 5, - }, room); - - final ruleset = PushRuleSet(override: [ - PushRule(ruleId: 'my.rule', default$: false, enabled: true, actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], conditions: [ - PushCondition(kind: 'sender_notification_permission', key: 'broom'), - ]) - ]); + room, + ); + + final ruleset = PushRuleSet( + override: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + conditions: [ + PushCondition( + kind: 'sender_notification_permission', + key: 'broom', + ), + ], + ), + ], + ); final evaluator = PushruleEvaluator.fromRuleset(ruleset); expect(evaluator.match(event).notify, true); @@ -339,24 +428,29 @@ void main() { }); test('invalid content rule', () async { - final invalid_content_ruleset = PushRuleSet(content: [ - PushRule( - ruleId: 'my.rule', - default$: false, - enabled: true, - actions: [ - 'notify', - {'set_tweak': 'highlight', 'value': true}, - {'set_tweak': 'sound', 'value': 'goose.wav'}, - ], - // pattern: 'fox', <- no pattern! - ) - ]); - - expect(() => PushruleEvaluator.fromRuleset(invalid_content_ruleset), - returnsNormally); - - final dendriteRuleset = PushRuleSet.fromJson(json.decode('''{ + final invalid_content_ruleset = PushRuleSet( + content: [ + PushRule( + ruleId: 'my.rule', + default$: false, + enabled: true, + actions: [ + 'notify', + {'set_tweak': 'highlight', 'value': true}, + {'set_tweak': 'sound', 'value': 'goose.wav'}, + ], + // pattern: 'fox', <- no pattern! + ), + ], + ); + + expect( + () => PushruleEvaluator.fromRuleset(invalid_content_ruleset), + returnsNormally, + ); + + final dendriteRuleset = PushRuleSet.fromJson( + json.decode('''{ "global": { "override": [ { @@ -634,9 +728,12 @@ void main() { ] } } -''')); - expect(() => PushruleEvaluator.fromRuleset(dendriteRuleset), - returnsNormally); +'''), + ); + expect( + () => PushruleEvaluator.fromRuleset(dendriteRuleset), + returnsNormally, + ); }); }); } diff --git a/test/room_archived_test.dart b/test/room_archived_test.dart index 22fc0816f..98b506f2b 100644 --- a/test/room_archived_test.dart +++ b/test/room_archived_test.dart @@ -56,8 +56,10 @@ void main() async { expect(archive[0].room.id, '!5345234234:example.com'); expect(archive[0].room.membership, Membership.leave); expect(archive[0].room.name, 'The room name'); - expect(archive[0].room.lastEvent?.body, - 'This is a second text example message'); + expect( + archive[0].room.lastEvent?.body, + 'This is a second text example message', + ); expect(archive[0].room.roomAccountData.length, 1); expect(archive[1].room.id, '!5345234235:example.com'); expect(archive[1].room.membership, Membership.leave); @@ -106,74 +108,92 @@ void main() async { test('discard room from archives when membership change', () async { await client.loadArchiveWithTimeline(); - expect(client.getArchiveRoomFromCache('!5345234235:example.com') != null, - true); - await client.handleSync(SyncUpdate( + expect( + client.getArchiveRoomFromCache('!5345234235:example.com') != null, + true, + ); + await client.handleSync( + SyncUpdate( nextBatch: 't_456', rooms: RoomsUpdate( - invite: {'!5345234235:example.com': InvitedRoomUpdate()}))); + invite: {'!5345234235:example.com': InvitedRoomUpdate()}, + ), + ), + ); expect(client.getArchiveRoomFromCache('!5345234235:example.com'), null); }); - test("assert that key updates don't change membership", () async { - const roomid = '!5345234235:example.com'; + test( + "assert that key updates don't change membership", + () async { + const roomid = '!5345234235:example.com'; - // prep work to be able to set a last event that would trigger the (fixed) bug - await client.loadArchiveWithTimeline(); - expect(client.getArchiveRoomFromCache(roomid) != null, true); - expect(client.getRoomById(roomid)?.membership, Membership.leave); - - final outboundSession = await client.encryption?.keyManager - .createOutboundGroupSession(roomid); - final inboundSession = client.encryption!.keyManager - .getInboundGroupSession( - roomid, outboundSession!.outboundGroupSession!.session_id())!; - - // ensure encryption is "enabled" - client.getRoomById(roomid)?.setState(StrippedStateEvent( - type: EventTypes.Encryption, - content: {'algorithm': AlgorithmTypes.megolmV1AesSha2}, - senderId: client.userID!, - stateKey: '', - )); - final encryptedEvent = await client.encryption! - .encryptGroupMessagePayload( - roomid, {'msgtype': 'm.room.text', 'body': 'empty'}); - - // reset client - await client.dispose().onError((e, s) {}); - client = await getClient( - sendTimelineEventTimeout: const Duration(seconds: 5), - ); + // prep work to be able to set a last event that would trigger the (fixed) bug + await client.loadArchiveWithTimeline(); + expect(client.getArchiveRoomFromCache(roomid) != null, true); + expect(client.getRoomById(roomid)?.membership, Membership.leave); - await client.abortSync(); - insertList.clear(); - - // now do our tests - await client.loadArchiveWithTimeline(); - expect(client.getArchiveRoomFromCache(roomid) != null, true); - expect(client.getRoomById(roomid)?.membership, Membership.leave); - - // set the last event - final room = client.getRoomById(roomid)!; - room.lastEvent = Event( + final outboundSession = await client.encryption?.keyManager + .createOutboundGroupSession(roomid); + final inboundSession = + client.encryption!.keyManager.getInboundGroupSession( + roomid, + outboundSession!.outboundGroupSession!.session_id(), + )!; + + // ensure encryption is "enabled" + client.getRoomById(roomid)?.setState( + StrippedStateEvent( + type: EventTypes.Encryption, + content: {'algorithm': AlgorithmTypes.megolmV1AesSha2}, + senderId: client.userID!, + stateKey: '', + ), + ); + final encryptedEvent = + await client.encryption!.encryptGroupMessagePayload( + roomid, + {'msgtype': 'm.room.text', 'body': 'empty'}, + ); + + // reset client + await client.dispose().onError((e, s) {}); + client = await getClient( + sendTimelineEventTimeout: const Duration(seconds: 5), + ); + + await client.abortSync(); + insertList.clear(); + + // now do our tests + await client.loadArchiveWithTimeline(); + expect(client.getArchiveRoomFromCache(roomid) != null, true); + expect(client.getRoomById(roomid)?.membership, Membership.leave); + + // set the last event + final room = client.getRoomById(roomid)!; + room.lastEvent = Event( type: EventTypes.Encrypted, content: encryptedEvent, senderId: client.userID!, eventId: '\$archivedencr', room: room, - originServerTs: DateTime.now()); + originServerTs: DateTime.now(), + ); - // import the inbound session - await client.encryption!.keyManager.setInboundGroupSession( + // import the inbound session + await client.encryption!.keyManager.setInboundGroupSession( roomid, inboundSession.sessionId, inboundSession.senderKey, - inboundSession.content); - - expect(client.getArchiveRoomFromCache(roomid) != null, true); - expect(client.getRoomById(roomid)?.membership, Membership.leave); - }, tags: 'olm'); + inboundSession.content, + ); + + expect(client.getArchiveRoomFromCache(roomid) != null, true); + expect(client.getRoomById(roomid)?.membership, Membership.leave); + }, + tags: 'olm', + ); test('clear archive', () async { await client.loadArchiveWithTimeline(); diff --git a/test/room_test.dart b/test/room_test.dart index 6edbdda54..37c01c9ff 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -66,7 +66,7 @@ void main() { final heroes = [ '@alice:matrix.org', '@bob:example.com', - '@charley:example.org' + '@charley:example.org', ]; room = Room( @@ -93,56 +93,66 @@ void main() { }, ); - room.setState(Event( - room: room, - eventId: '143273582443PhrSn:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - senderId: '@example:example.org', - type: 'm.room.join_rules', - unsigned: {'age': 1234}, - content: {'join_rule': 'public'}, - stateKey: '', - )); - room.setState(Event( - room: room, - eventId: '143273582443PhrSnY:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - senderId: matrix.userID!, - type: 'm.room.member', - unsigned: {'age': 1234}, - content: {'membership': 'join', 'displayname': 'YOU'}, - stateKey: matrix.userID!, - )); - room.setState(Event( - room: room, - eventId: '143273582443PhrSnA:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - senderId: '@alice:matrix.org', - type: 'm.room.member', - unsigned: {'age': 1234}, - content: {'membership': 'join', 'displayname': 'Alice Margatroid'}, - stateKey: '@alice:matrix.org', - )); - room.setState(Event( - room: room, - eventId: '143273582443PhrSnB:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - senderId: '@bob:example.com', - type: 'm.room.member', - unsigned: {'age': 1234}, - content: {'membership': 'invite', 'displayname': 'Bob'}, - stateKey: '@bob:example.com', - )); - room.setState(Event( - room: room, - eventId: '143273582443PhrSnC:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - senderId: '@charley:example.org', - type: 'm.room.member', - unsigned: {'age': 1234}, - content: {'membership': 'invite', 'displayname': 'Charley'}, - stateKey: '@charley:example.org', - )); + room.setState( + Event( + room: room, + eventId: '143273582443PhrSn:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + senderId: '@example:example.org', + type: 'm.room.join_rules', + unsigned: {'age': 1234}, + content: {'join_rule': 'public'}, + stateKey: '', + ), + ); + room.setState( + Event( + room: room, + eventId: '143273582443PhrSnY:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + senderId: matrix.userID!, + type: 'm.room.member', + unsigned: {'age': 1234}, + content: {'membership': 'join', 'displayname': 'YOU'}, + stateKey: matrix.userID!, + ), + ); + room.setState( + Event( + room: room, + eventId: '143273582443PhrSnA:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + senderId: '@alice:matrix.org', + type: 'm.room.member', + unsigned: {'age': 1234}, + content: {'membership': 'join', 'displayname': 'Alice Margatroid'}, + stateKey: '@alice:matrix.org', + ), + ); + room.setState( + Event( + room: room, + eventId: '143273582443PhrSnB:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + senderId: '@bob:example.com', + type: 'm.room.member', + unsigned: {'age': 1234}, + content: {'membership': 'invite', 'displayname': 'Bob'}, + stateKey: '@bob:example.com', + ), + ); + room.setState( + Event( + room: room, + eventId: '143273582443PhrSnC:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + senderId: '@charley:example.org', + type: 'm.room.member', + unsigned: {'age': 1234}, + content: {'membership': 'invite', 'displayname': 'Charley'}, + stateKey: '@charley:example.org', + ), + ); final heroUsers = await room.loadHeroUsers(); expect(heroUsers.length, 3); @@ -154,61 +164,69 @@ void main() { expect(room.summary.mJoinedMemberCount, notificationCount); expect(room.summary.mInvitedMemberCount, 2); expect(room.summary.mHeroes, heroes); - expect(room.getLocalizedDisplayname(), - 'Group with Alice Margatroid, Bob, Charley'); expect( - room.getState('m.room.join_rules')?.content['join_rule'], 'public'); + room.getLocalizedDisplayname(), + 'Group with Alice Margatroid, Bob, Charley', + ); + expect( + room.getState('m.room.join_rules')?.content['join_rule'], + 'public', + ); expect(room.roomAccountData['com.test.foo']?.content['foo'], 'bar'); expect(room.fullyRead, '\$event_id:example.com'); room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.canonical_alias', - room: room, - eventId: '123', - content: {'alias': '#testalias:example.com'}, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.canonical_alias', + room: room, + eventId: '123', + content: {'alias': '#testalias:example.com'}, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.getLocalizedDisplayname(), 'testalias'); expect(room.canonicalAlias, '#testalias:example.com'); room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.name', - room: room, - eventId: '123', - content: {'name': 'testname'}, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.name', + room: room, + eventId: '123', + content: {'name': 'testname'}, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.getLocalizedDisplayname(), 'testname'); expect(room.topic, ''); room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.topic', - room: room, - eventId: '123', - content: {'topic': 'testtopic'}, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.topic', + room: room, + eventId: '123', + content: {'topic': 'testtopic'}, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.topic, 'testtopic'); expect(room.avatar, null); room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.avatar', - room: room, - eventId: '123', - content: {'url': 'mxc://testurl'}, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.avatar', + room: room, + eventId: '123', + content: {'url': 'mxc://testurl'}, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.avatar.toString(), 'mxc://testurl'); @@ -220,7 +238,7 @@ void main() { room: room, eventId: '123', content: { - 'pinned': ['1234'] + 'pinned': ['1234'], }, originServerTs: DateTime.now(), stateKey: '', @@ -332,7 +350,7 @@ void main() { 'body': 'edited cdc just because', 'm.new_content': { 'msgtype': 'm.text', - 'body': 'edited cdc just because' + 'body': 'edited cdc just because', }, 'm.relates_to': {'rel_type': 'm.replace', 'event_id': '2'}, }, @@ -364,7 +382,7 @@ void main() { 'body': 'edited cdc is back!', 'm.new_content': { 'msgtype': 'm.text', - 'body': 'edited cdc is back!' + 'body': 'edited cdc is back!', }, 'm.relates_to': {'rel_type': 'm.replace', 'event_id': '2'}, }, @@ -389,7 +407,7 @@ void main() { 'rel_type': RelationshipTypes.reaction, 'event_id': '1234', 'key': ':-)', - } + }, }, ), ); @@ -410,7 +428,7 @@ void main() { content: { 'body': 'A', 'm.mentions': {}, - 'msgtype': 'm.text' + 'msgtype': 'm.text', }, type: 'm.room.message', eventId: 'testLastEventBeforeEdit', @@ -443,13 +461,13 @@ void main() { 'm.new_content': { 'body': 'A-edited', 'm.mentions': {}, - 'msgtype': 'm.text' + 'msgtype': 'm.text', }, 'm.relates_to': { 'event_id': 'testLastEventBeforeEdit', - 'rel_type': 'm.replace' + 'rel_type': 'm.replace', }, - 'msgtype': 'm.text' + 'msgtype': 'm.text', }, type: 'm.room.message', eventId: 'testLastEventAfterEdit', @@ -518,7 +536,7 @@ void main() { content: { 'msgtype': 'm.text', 'body': 'B', - 'm.relates_to': {'rel_type': 'm.in_reply_to', 'event_id': '5'} + 'm.relates_to': {'rel_type': 'm.in_reply_to', 'event_id': '5'}, }, ), ); @@ -565,7 +583,7 @@ void main() { content: { 'msgtype': 'm.text', 'body': 'B', - 'm.relates_to': {'rel_type': 'm.in_reply_to', 'event_id': '8'} + 'm.relates_to': {'rel_type': 'm.in_reply_to', 'event_id': '8'}, }, stateKey: '', ), @@ -686,24 +704,25 @@ void main() { test('PowerLevels', () async { room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.power_levels', - room: room, - eventId: '123', - content: { - 'ban': 50, - 'events': {'m.room.name': 100, 'm.room.power_levels': 100}, - 'events_default': 0, - 'invite': 50, - 'kick': 50, - 'notifications': {'room': 20}, - 'redact': 50, - 'state_default': 50, - 'users': {'@test:fakeServer.notExisting': 100}, - 'users_default': 10 - }, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123', + content: { + 'ban': 50, + 'events': {'m.room.name': 100, 'm.room.power_levels': 100}, + 'events_default': 0, + 'invite': 50, + 'kick': 50, + 'notifications': {'room': 20}, + 'redact': 50, + 'state_default': 50, + 'users': {'@test:fakeServer.notExisting': 100}, + 'users_default': 10, + }, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.ownPowerLevel, 100); expect(room.getPowerLevelByUserId(matrix.userID!), room.ownPowerLevel); @@ -720,27 +739,28 @@ void main() { expect(room.canSendEvent('m.room.member'), true); room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.power_levels', - room: room, - eventId: '123', - content: { - 'ban': 50, - 'events': { - 'm.room.name': 'lannaForcedMeToTestThis', - 'm.room.power_levels': 100, - }, - 'events_default': 0, - 'invite': 50, - 'kick': 50, - 'notifications': {'room': 20}, - 'redact': 50, - 'state_default': 60, - 'users': {'@test:fakeServer.notExisting': 100}, - 'users_default': 10 + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123', + content: { + 'ban': 50, + 'events': { + 'm.room.name': 'lannaForcedMeToTestThis', + 'm.room.power_levels': 100, }, - originServerTs: DateTime.now(), - stateKey: ''), + 'events_default': 0, + 'invite': 50, + 'kick': 50, + 'notifications': {'room': 20}, + 'redact': 50, + 'state_default': 60, + 'users': {'@test:fakeServer.notExisting': 100}, + 'users_default': 10, + }, + originServerTs: DateTime.now(), + stateKey: '', + ), ); expect(room.powerForChangingStateEvent('m.room.name'), 60); expect(room.powerForChangingStateEvent('m.room.power_levels'), 100); @@ -762,7 +782,7 @@ void main() { 'redact': 50, 'state_default': 50, 'users': {}, - 'users_default': 0 + 'users_default': 0, }, originServerTs: DateTime.now(), stateKey: '', @@ -805,14 +825,17 @@ void main() { var userList = room.getParticipants(); expect(userList.length, 4); // add new user - room.setState(Event( + room.setState( + Event( senderId: '@alice:test.abc', type: 'm.room.member', room: room, eventId: '12345', originServerTs: DateTime.now(), content: {'displayname': 'alice'}, - stateKey: '@alice:test.abc')); + stateKey: '@alice:test.abc', + ), + ); userList = room.getParticipants(); expect(userList.length, 5); expect(userList[4].displayName, 'alice'); @@ -854,16 +877,18 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); final user = await room.requestUser('@getme:example.com'); expect(FakeMatrixApi.calledEndpoints.keys, [ - '/client/v3/rooms/!localpart%3Aserver.abc/state/m.room.member/%40getme%3Aexample.com' + '/client/v3/rooms/!localpart%3Aserver.abc/state/m.room.member/%40getme%3Aexample.com', ]); expect(user?.stateKey, '@getme:example.com'); expect(user?.calcDisplayname(), 'You got me'); expect(user?.membership, Membership.knock); // Yield for the onUpdate - await Future.delayed(Duration( - milliseconds: 1, - )); + await Future.delayed( + Duration( + milliseconds: 1, + ), + ); expect(called.length, 1); expect(called2.length, 1); @@ -871,16 +896,18 @@ void main() { final user2 = await room.requestUser('@getmeprofile:example.com'); expect(FakeMatrixApi.calledEndpoints.keys, [ '/client/v3/rooms/!localpart%3Aserver.abc/state/m.room.member/%40getmeprofile%3Aexample.com', - '/client/v3/profile/%40getmeprofile%3Aexample.com' + '/client/v3/profile/%40getmeprofile%3Aexample.com', ]); expect(user2?.stateKey, '@getmeprofile:example.com'); expect(user2?.calcDisplayname(), 'You got me (profile)'); expect(user2?.membership, Membership.leave); // Yield for the onUpdate - await Future.delayed(Duration( - milliseconds: 1, - )); + await Future.delayed( + Duration( + milliseconds: 1, + ), + ); expect(called.length, 2); expect(called2.length, 2); @@ -892,27 +919,34 @@ void main() { expect(userAgain?.membership, Membership.knock); // Yield for the onUpdate - await Future.delayed(Duration( - milliseconds: 1, - )); + await Future.delayed( + Duration( + milliseconds: 1, + ), + ); expect(called.length, 2, reason: 'onUpdate should not have been called.'); - expect(called2.length, 2, - reason: 'onRoomState should not have been called.'); + expect( + called2.length, + 2, + reason: 'onRoomState should not have been called.', + ); FakeMatrixApi.calledEndpoints.clear(); final user3 = await room.requestUser('@getmeempty:example.com'); expect(FakeMatrixApi.calledEndpoints.keys, [ '/client/v3/rooms/!localpart%3Aserver.abc/state/m.room.member/%40getmeempty%3Aexample.com', - '/client/v3/profile/%40getmeempty%3Aexample.com' + '/client/v3/profile/%40getmeempty%3Aexample.com', ]); expect(user3?.stateKey, '@getmeempty:example.com'); expect(user3?.calcDisplayname(), 'You got me (empty)'); expect(user3?.membership, Membership.leave); // Yield for the onUpdate - await Future.delayed(Duration( - milliseconds: 1, - )); + await Future.delayed( + Duration( + milliseconds: 1, + ), + ); expect(called.length, 3); expect(called2.length, 3); @@ -928,8 +962,9 @@ void main() { test('sendEvent', () async { final dynamic resp = await room.sendEvent( - {'msgtype': 'm.text', 'body': 'hello world'}, - txid: 'testtxid'); + {'msgtype': 'm.text', 'body': 'hello world'}, + txid: 'testtxid', + ); expect(resp?.startsWith('\$event'), true); }); @@ -949,8 +984,11 @@ void main() { test('send edit', () async { FakeMatrixApi.calledEndpoints.clear(); - final dynamic resp = await room.sendTextEvent('Hello world', - txid: 'testtxid', editEventId: '\$otherEvent'); + final dynamic resp = await room.sendTextEvent( + 'Hello world', + txid: 'testtxid', + editEventId: '\$otherEvent', + ); expect(resp?.startsWith('\$event'), true); final entry = FakeMatrixApi.calledEndpoints.entries .firstWhere((p) => p.key.contains('/send/m.room.message/')); @@ -970,18 +1008,24 @@ void main() { }); test('send reply', () async { - var event = Event.fromJson({ - 'event_id': '\$replyEvent', - 'content': { - 'body': 'Blah', - 'msgtype': 'm.text', + var event = Event.fromJson( + { + 'event_id': '\$replyEvent', + 'content': { + 'body': 'Blah', + 'msgtype': 'm.text', + }, + 'type': 'm.room.message', + 'sender': '@alice:example.org', }, - 'type': 'm.room.message', - 'sender': '@alice:example.org', - }, room); + room, + ); FakeMatrixApi.calledEndpoints.clear(); - var resp = await room.sendTextEvent('Hello world', - txid: 'testtxid', inReplyTo: event); + var resp = await room.sendTextEvent( + 'Hello world', + txid: 'testtxid', + inReplyTo: event, + ); expect(resp?.startsWith('\$event'), true); var entry = FakeMatrixApi.calledEndpoints.entries .firstWhere((p) => p.key.contains('/send/m.room.message/')); @@ -999,18 +1043,24 @@ void main() { }, }); - event = Event.fromJson({ - 'event_id': '\$replyEvent', - 'content': { - 'body': 'Blah\nbeep', - 'msgtype': 'm.text', + event = Event.fromJson( + { + 'event_id': '\$replyEvent', + 'content': { + 'body': 'Blah\nbeep', + 'msgtype': 'm.text', + }, + 'type': 'm.room.message', + 'sender': '@alice:example.org', }, - 'type': 'm.room.message', - 'sender': '@alice:example.org', - }, room); + room, + ); FakeMatrixApi.calledEndpoints.clear(); - resp = await room.sendTextEvent('Hello world\nfox', - txid: 'testtxid', inReplyTo: event); + resp = await room.sendTextEvent( + 'Hello world\nfox', + txid: 'testtxid', + inReplyTo: event, + ); expect(resp?.startsWith('\$event'), true); entry = FakeMatrixApi.calledEndpoints.entries .firstWhere((p) => p.key.contains('/send/m.room.message/')); @@ -1029,20 +1079,26 @@ void main() { }, }); - event = Event.fromJson({ - 'event_id': '\$replyEvent', - 'content': { - 'format': 'org.matrix.custom.html', - 'formatted_body': 'heyameow', - 'body': 'plaintext meow', - 'msgtype': 'm.text', + event = Event.fromJson( + { + 'event_id': '\$replyEvent', + 'content': { + 'format': 'org.matrix.custom.html', + 'formatted_body': 'heyameow', + 'body': 'plaintext meow', + 'msgtype': 'm.text', + }, + 'type': 'm.room.message', + 'sender': '@alice:example.org', }, - 'type': 'm.room.message', - 'sender': '@alice:example.org', - }, room); + room, + ); FakeMatrixApi.calledEndpoints.clear(); - resp = await room.sendTextEvent('Hello world', - txid: 'testtxid', inReplyTo: event); + resp = await room.sendTextEvent( + 'Hello world', + txid: 'testtxid', + inReplyTo: event, + ); expect(resp?.startsWith('\$event'), true); entry = FakeMatrixApi.calledEndpoints.entries .firstWhere((p) => p.key.contains('/send/m.room.message/')); @@ -1060,18 +1116,24 @@ void main() { }, }); - event = Event.fromJson({ - 'event_id': '\$replyEvent', - 'content': { - 'body': 'Hey @room', - 'msgtype': 'm.text', + event = Event.fromJson( + { + 'event_id': '\$replyEvent', + 'content': { + 'body': 'Hey @room', + 'msgtype': 'm.text', + }, + 'type': 'm.room.message', + 'sender': '@alice:example.org', }, - 'type': 'm.room.message', - 'sender': '@alice:example.org', - }, room); + room, + ); FakeMatrixApi.calledEndpoints.clear(); - resp = await room.sendTextEvent('Hello world', - txid: 'testtxid', inReplyTo: event); + resp = await room.sendTextEvent( + 'Hello world', + txid: 'testtxid', + inReplyTo: event, + ); expect(resp?.startsWith('\$event'), true); entry = FakeMatrixApi.calledEndpoints.entries .firstWhere((p) => p.key.contains('/send/m.room.message/')); @@ -1090,23 +1152,26 @@ void main() { }); // Reply to a reply - event = Event.fromJson({ - 'event_id': '\$replyEvent', - 'content': { - 'body': '> <@alice:example.org> Hey\n\nHello world', - 'msgtype': 'm.text', - 'format': 'org.matrix.custom.html', - 'formatted_body': - '
In reply to @alice:example.org
Hey
Hello world', - 'm.relates_to': { - 'm.in_reply_to': { - 'event_id': '\$replyEvent', + event = Event.fromJson( + { + 'event_id': '\$replyEvent', + 'content': { + 'body': '> <@alice:example.org> Hey\n\nHello world', + 'msgtype': 'm.text', + 'format': 'org.matrix.custom.html', + 'formatted_body': + '
In reply to @alice:example.org
Hey
Hello world', + 'm.relates_to': { + 'm.in_reply_to': { + 'event_id': '\$replyEvent', + }, }, }, + 'type': 'm.room.message', + 'sender': '@alice:example.org', }, - 'type': 'm.room.message', - 'sender': '@alice:example.org', - }, room); + room, + ); FakeMatrixApi.calledEndpoints.clear(); resp = await room.sendTextEvent('Fox', txid: 'testtxid', inReplyTo: event); @@ -1182,8 +1247,10 @@ void main() { expect(room.pushRuleState, PushRuleState.mentionsOnly); ((matrix.accountData['m.push_rules']?.content['global'] as Map)['override'] as List) - .add(((matrix.accountData['m.push_rules']?.content['global'] - as Map)['room'] as List)[0]); + .add( + ((matrix.accountData['m.push_rules']?.content['global'] + as Map)['room'] as List)[0], + ); expect(room.pushRuleState, PushRuleState.dontNotify); }); @@ -1194,17 +1261,18 @@ void main() { test('Enable encryption', () async { room.setState( Event( - senderId: '@alice:test.abc', - type: 'm.room.encryption', - room: room, - eventId: '12345', - originServerTs: DateTime.now(), - content: { - 'algorithm': AlgorithmTypes.megolmV1AesSha2, - 'rotation_period_ms': 604800000, - 'rotation_period_msgs': 100 - }, - stateKey: ''), + senderId: '@alice:test.abc', + type: 'm.room.encryption', + room: room, + eventId: '12345', + originServerTs: DateTime.now(), + content: { + 'algorithm': AlgorithmTypes.megolmV1AesSha2, + 'rotation_period_ms': 604800000, + 'rotation_period_msgs': 100, + }, + stateKey: '', + ), ); expect(room.encrypted, true); expect(room.encryptionAlgorithm, AlgorithmTypes.megolmV1AesSha2); @@ -1226,9 +1294,9 @@ void main() { 'tags': { 'm.favourite': {'order': 0.1}, 'm.wrong': {'order': 0.2}, - } + }, }, - 'type': 'm.tag' + 'type': 'm.tag', }); expect(room.tags.length, 1); expect(room.tags[TagType.favourite]?.order, 0.1); @@ -1242,7 +1310,7 @@ void main() { expect(room.markedUnread, false); room.roomAccountData['m.marked_unread'] = BasicRoomEvent.fromJson({ 'content': {'unread': true}, - 'type': 'm.marked_unread' + 'type': 'm.marked_unread', }); expect(room.markedUnread, true); }); @@ -1250,19 +1318,21 @@ void main() { test('joinRules', () async { expect(room.canChangeJoinRules, false); expect(room.joinRules, JoinRules.public); - room.setState(Event.fromJson( - { - 'content': {'join_rule': 'invite'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.join_rules', - 'unsigned': {'age': 1234} - }, - room, - )); + room.setState( + Event.fromJson( + { + 'content': {'join_rule': 'invite'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.join_rules', + 'unsigned': {'age': 1234}, + }, + room, + ), + ); expect(room.joinRules, JoinRules.invite); await room.setJoinRules(JoinRules.invite); }); @@ -1270,19 +1340,21 @@ void main() { test('guestAccess', () async { expect(room.canChangeGuestAccess, false); expect(room.guestAccess, GuestAccess.forbidden); - room.setState(Event.fromJson( - { - 'content': {'guest_access': 'can_join'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.guest_access', - 'unsigned': {'age': 1234} - }, - room, - )); + room.setState( + Event.fromJson( + { + 'content': {'guest_access': 'can_join'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'state_key': '', + 'type': 'm.room.guest_access', + 'unsigned': {'age': 1234}, + }, + room, + ), + ); expect(room.guestAccess, GuestAccess.canJoin); await room.setGuestAccess(GuestAccess.canJoin); }); @@ -1290,71 +1362,79 @@ void main() { test('historyVisibility', () async { expect(room.canChangeHistoryVisibility, false); expect(room.historyVisibility, null); - room.setState(Event.fromJson( - { - 'content': {'history_visibility': 'shared'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.history_visibility', - 'unsigned': {'age': 1234} - }, - room, - )); - expect(room.historyVisibility, HistoryVisibility.shared); - await room.setHistoryVisibility(HistoryVisibility.joined); - }); - - test('setState', () async { - // not set non-state-events - try { - room.setState(Event.fromJson( + room.setState( + Event.fromJson( { 'content': {'history_visibility': 'shared'}, 'event_id': '\$143273582443PhrSn:example.org', 'origin_server_ts': 1432735824653, 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', 'sender': '@example:example.org', - 'type': 'm.custom', + 'state_key': '', + 'type': 'm.room.history_visibility', 'unsigned': {'age': 1234}, }, room, - )); + ), + ); + expect(room.historyVisibility, HistoryVisibility.shared); + await room.setHistoryVisibility(HistoryVisibility.joined); + }); + + test('setState', () async { + // not set non-state-events + try { + room.setState( + Event.fromJson( + { + 'content': {'history_visibility': 'shared'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.custom', + 'unsigned': {'age': 1234}, + }, + room, + ), + ); } catch (_) {} expect(room.getState('m.custom') != null, false); // set state events - room.setState(Event.fromJson( - { - 'content': {'history_visibility': 'shared'}, - 'event_id': '\$143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.custom', - 'unsigned': {'age': 1234} - }, - room, - )); - expect(room.getState('m.custom') != null, true); - - // sets messages as state events - try { - room.setState(Event.fromJson( + room.setState( + Event.fromJson( { 'content': {'history_visibility': 'shared'}, 'event_id': '\$143273582443PhrSn:example.org', 'origin_server_ts': 1432735824653, 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', 'sender': '@example:example.org', - 'type': 'm.room.message', - 'unsigned': {'age': 1234} + 'state_key': '', + 'type': 'm.custom', + 'unsigned': {'age': 1234}, }, room, - )); + ), + ); + expect(room.getState('m.custom') != null, true); + + // sets messages as state events + try { + room.setState( + Event.fromJson( + { + 'content': {'history_visibility': 'shared'}, + 'event_id': '\$143273582443PhrSn:example.org', + 'origin_server_ts': 1432735824653, + 'room_id': '!jEsUZKDJdhlrceRyVU:example.org', + 'sender': '@example:example.org', + 'type': 'm.room.message', + 'unsigned': {'age': 1234}, + }, + room, + ), + ); } catch (_) {} expect(room.getState('m.room.message') == null, true); }); @@ -1362,59 +1442,68 @@ void main() { test('Widgets', () { expect(room.widgets.isEmpty, true); room.states['m.widget'] = { - 'test': Event.fromJson({ - 'content': { - 'creatorUserId': '@rxl881:matrix.org', - 'data': {'title': 'Bridges Dashboard', 'dateRange': '1y'}, - 'id': 'grafana_@rxl881:matrix.org_1514573757015', - 'name': 'Grafana', - 'type': 'm.grafana', - 'url': 'https://matrix.org/grafana/whatever', - 'waitForIframeLoad': true + 'test': Event.fromJson( + { + 'content': { + 'creatorUserId': '@rxl881:matrix.org', + 'data': {'title': 'Bridges Dashboard', 'dateRange': '1y'}, + 'id': 'grafana_@rxl881:matrix.org_1514573757015', + 'name': 'Grafana', + 'type': 'm.grafana', + 'url': 'https://matrix.org/grafana/whatever', + 'waitForIframeLoad': true, + }, + 'room_id': '!foo:bar', + 'event_id': '\$15104760642668662QICBu:matrix.org', + 'sender': '@rxl881:matrix.org', + 'state_key': 'test', + 'origin_server_ts': 1432735824653, + 'type': 'm.widget', }, - 'room_id': '!foo:bar', - 'event_id': '\$15104760642668662QICBu:matrix.org', - 'sender': '@rxl881:matrix.org', - 'state_key': 'test', - 'origin_server_ts': 1432735824653, - 'type': 'm.widget' - }, room), + room, + ), }; expect(room.widgets.length, 1); room.states['m.widget'] = { - 'test2': Event.fromJson({ - 'content': { - 'creatorUserId': '@rxl881:matrix.org', - 'data': {'title': 'Bridges Dashboard', 'dateRange': '1y'}, - 'id': 'grafana_@rxl881:matrix.org_1514573757016', - 'type': 'm.grafana', - 'url': 'https://matrix.org/grafana/whatever', - 'waitForIframeLoad': true + 'test2': Event.fromJson( + { + 'content': { + 'creatorUserId': '@rxl881:matrix.org', + 'data': {'title': 'Bridges Dashboard', 'dateRange': '1y'}, + 'id': 'grafana_@rxl881:matrix.org_1514573757016', + 'type': 'm.grafana', + 'url': 'https://matrix.org/grafana/whatever', + 'waitForIframeLoad': true, + }, + 'room_id': '!foo:bar', + 'event_id': '\$15104760642668663QICBu:matrix.org', + 'sender': '@rxl881:matrix.org', + 'state_key': 'test2', + 'origin_server_ts': 1432735824653, + 'type': 'm.widget', }, - 'room_id': '!foo:bar', - 'event_id': '\$15104760642668663QICBu:matrix.org', - 'sender': '@rxl881:matrix.org', - 'state_key': 'test2', - 'origin_server_ts': 1432735824653, - 'type': 'm.widget' - }, room), + room, + ), }; expect(room.widgets.length, 1); room.states['m.widget'] = { - 'test3': Event.fromJson({ - 'content': { - 'creatorUserId': '@rxl881:matrix.org', - 'data': {'title': 'Bridges Dashboard', 'dateRange': '1y'}, - 'type': 'm.grafana', - 'waitForIframeLoad': true + 'test3': Event.fromJson( + { + 'content': { + 'creatorUserId': '@rxl881:matrix.org', + 'data': {'title': 'Bridges Dashboard', 'dateRange': '1y'}, + 'type': 'm.grafana', + 'waitForIframeLoad': true, + }, + 'room_id': '!foo:bar', + 'event_id': '\$15104760642668662QICBu:matrix.org', + 'sender': '@rxl881:matrix.org', + 'state_key': 'test3', + 'origin_server_ts': 1432735824655, + 'type': 'm.widget', }, - 'room_id': '!foo:bar', - 'event_id': '\$15104760642668662QICBu:matrix.org', - 'sender': '@rxl881:matrix.org', - 'state_key': 'test3', - 'origin_server_ts': 1432735824655, - 'type': 'm.widget' - }, room), + room, + ), }; expect(room.widgets.length, 0); }); @@ -1443,7 +1532,7 @@ void main() { '!1234:example.invalid': Event.fromJson( { 'content': { - 'via': ['example.invalid'] + 'via': ['example.invalid'], }, 'event_id': '\$143273582443PhrSn:example.org', 'origin_server_ts': 1432735824653, @@ -1551,23 +1640,28 @@ void main() { room.summary.mJoinedMemberCount = 3; var matrixToLink = await room.matrixToInviteLink(); - expect(matrixToLink.toString(), - 'https://matrix.to/#/%23testalias%3Aexample.com'); + expect( + matrixToLink.toString(), + 'https://matrix.to/#/%23testalias%3Aexample.com', + ); room.setState( Event( - senderId: '@test:example.com', - type: 'm.room.canonical_alias', - room: room, - eventId: '123', - content: {'alias': ''}, - originServerTs: DateTime.now(), - stateKey: ''), + senderId: '@test:example.com', + type: 'm.room.canonical_alias', + room: room, + eventId: '123', + content: {'alias': ''}, + originServerTs: DateTime.now(), + stateKey: '', + ), ); matrixToLink = await room.matrixToInviteLink(); - expect(matrixToLink.toString(), - 'https://matrix.to/#/!localpart%3Aserver.abc?via=fakeServer.notExisting&via=matrix.org&via=test.abc'); + expect( + matrixToLink.toString(), + 'https://matrix.to/#/!localpart%3Aserver.abc?via=fakeServer.notExisting&via=matrix.org&via=test.abc', + ); }); test('cancelSend because EventTooLarge in postLoaded room', () async { @@ -1593,10 +1687,12 @@ void main() { try { await room.sendTextEvent( - txid: 'event_too_large', - // data just bigger than maxBodySize - base64Encode( - List.generate(60001, (i) => Random().nextInt(256)))); + txid: 'event_too_large', + // data just bigger than maxBodySize + base64Encode( + List.generate(60001, (i) => Random().nextInt(256)), + ), + ); } catch (e) { expect(e.runtimeType, EventTooLarge); expect(room.lastEvent?.eventId, 'event_too_large'); @@ -1612,8 +1708,9 @@ void main() { // work in postLoaded room expect(room.lastEvent?.eventId, 'event_too_large'); expect( - await room.lastEvent?.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Cancelled sending message'); + await room.lastEvent?.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Cancelled sending message', + ); // check if persisted in db final lastEventFromDB = @@ -1628,8 +1725,9 @@ void main() { expect(roomFromDB?.lastEvent?.eventId, 'event_too_large'); expect( - await room.lastEvent?.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Cancelled sending message'); + await room.lastEvent?.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Cancelled sending message', + ); roomFromDB = await matrix.database?.getSingleRoom(matrix, room.id); @@ -1638,8 +1736,9 @@ void main() { expect(roomFromDB?.lastEvent?.eventId, 'event_too_large'); expect( - await room.lastEvent?.calcLocalizedBody(MatrixDefaultLocalizations()), - 'Cancelled sending message'); + await room.lastEvent?.calcLocalizedBody(MatrixDefaultLocalizations()), + 'Cancelled sending message', + ); }); test('logout', () async { diff --git a/test/sync_filter_test.dart b/test/sync_filter_test.dart index 72ab33d2f..a2faea49a 100644 --- a/test/sync_filter_test.dart +++ b/test/sync_filter_test.dart @@ -48,7 +48,7 @@ const updates = { 'last_active_ago': 2478593, 'presence': 'online', 'currently_active': false, - 'status_msg': 'Making cupcakes' + 'status_msg': 'Making cupcakes', }, 'type': 'm.presence', 'sender': '@example:localhost', diff --git a/test/timeline_context_test.dart b/test/timeline_context_test.dart index 5b25fc6ea..ae27e0905 100644 --- a/test/timeline_context_test.dart +++ b/test/timeline_context_test.dart @@ -50,12 +50,15 @@ void main() { } }); - return completer.future.timeout(Duration(seconds: 1), - onTimeout: () async { - throw TimeoutException( + return completer.future.timeout( + Duration(seconds: 1), + onTimeout: () async { + throw TimeoutException( 'Failed to wait for updateCount == $count, current == $updateCount', - Duration(seconds: 1)); - }); + Duration(seconds: 1), + ); + }, + ); } late Client client; @@ -67,7 +70,11 @@ void main() { ); room = Room( - id: roomID, client: client, prev_batch: 't123', roomAccountData: {}); + id: roomID, + client: client, + prev_batch: 't123', + roomAccountData: {}, + ); timeline = Timeline( room: room, chunk: TimelineChunk(events: [], nextBatch: 't456', prevBatch: 't123'), @@ -91,7 +98,8 @@ void main() { }); tearDown( - () async => client.dispose(closeDatabase: true).onError((e, s) {})); + () async => client.dispose(closeDatabase: true).onError((e, s) {}), + ); test('Request future', () async { timeline.events.clear(); @@ -99,8 +107,11 @@ void main() { await timeline.requestFuture(); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/messages?from=t456&dir=f')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/messages?from=t456&dir=f', + ), + ); expect(updateCount, 3); expect(insertList, [0, 1, 2]); @@ -117,33 +128,42 @@ void main() { /// We send a message in a fragmented timeline, it didn't reached the end so we shouldn't be displayed. test('Send message not displayed', () async { await room.sendTextEvent('test', txid: '1234'); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/1234')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/1234', + ), + ); expect(updateCount, 0); expect(insertList, []); - expect(insertList.length, - timeline.events.length); // expect no new events to have been added + expect( + insertList.length, + timeline.events.length, + ); // expect no new events to have been added final eventId = '1844295642248BcDkn:example.org'; - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'test'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': eventId, - 'unsigned': {'transaction_id': '1234'}, - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch - }, - )); // just assume that it was on the server for this call but not for the following. + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'test'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': eventId, + 'unsigned': {'transaction_id': '1234'}, + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); // just assume that it was on the server for this call but not for the following. expect(updateCount, 0); expect(insertList, []); - expect(timeline.events.length, - 0); // we still expect the timeline to contain the same numbre of elements + expect( + timeline.events.length, + 0, + ); // we still expect the timeline to contain the same numbre of elements }); test('Request future end of timeline', () async { @@ -151,8 +171,11 @@ void main() { await timeline.requestFuture(); await timeline.requestFuture(); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/messages?from=t789&dir=f')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/messages?from=t789&dir=f', + ), + ); expect(updateCount, 6); expect(insertList, [0, 1, 2]); @@ -172,8 +195,11 @@ void main() { await timeline.requestFuture(); await room.sendTextEvent('test', txid: '1234'); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/1234')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/1234', + ), + ); expect(updateCount, 8); expect(insertList, [0, 1, 2, 0]); @@ -182,19 +208,21 @@ void main() { expect(eventId.startsWith('\$event'), true); expect(timeline.events[0].status, EventStatus.sent); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'test'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': eventId, - 'unsigned': {'transaction_id': '1234'}, - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch - }, - )); + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'test'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': eventId, + 'unsigned': {'transaction_id': '1234'}, + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await waitForCount(9); @@ -210,18 +238,20 @@ void main() { await timeline.requestFuture(); await waitForCount(6); - 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.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); + 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.sending.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await waitForCount(7); @@ -231,17 +261,26 @@ void main() { await room.sendTextEvent('test', txid: 'errortxid'); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/errortxid')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/errortxid', + ), + ); await waitForCount(9); expect(updateCount, 9); await room.sendTextEvent('test', txid: 'errortxid2'); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/errortxid2')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/errortxid2', + ), + ); await room.sendTextEvent('test', txid: 'errortxid3'); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/errortxid3')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/errortxid3', + ), + ); expect(updateCount, 13); expect(insertList, [0, 1, 2, 0, 0, 1, 2]); @@ -257,18 +296,20 @@ void main() { await timeline.requestFuture(); await timeline.requestFuture(); // send a failed message - 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.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); + 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.sending.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await waitForCount(7); await timeline.events[0].cancelSend(); @@ -286,18 +327,20 @@ void main() { test('getEventById', () async { await timeline.requestFuture(); await timeline.requestFuture(); - 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.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); + 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.sending.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await waitForCount(7); var event = await timeline.getEventById('abc'); expect(event?.content, {'msgtype': 'm.text', 'body': 'Testcase'}); @@ -317,19 +360,21 @@ void main() { timeline.events.clear(); await timeline.requestFuture(); await timeline.requestFuture(); - 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.error.intValue, - 'event_id': 'new-test-event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'newresend'}, - }, - )); + 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.error.intValue, + 'event_id': 'new-test-event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'newresend'}, + }, + ), + ); await waitForCount(7); expect(timeline.events[0].status, EventStatus.error); @@ -337,8 +382,11 @@ void main() { await timeline.events[0].sendAgain(); - await FakeMatrixApi.firstWhere((a) => a.startsWith( - '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/newresend')); + await FakeMatrixApi.firstWhere( + (a) => a.startsWith( + '/client/v3/rooms/!1234%3Aexample.com/send/m.room.message/newresend', + ), + ); expect(updateCount, 9); @@ -359,17 +407,20 @@ void main() { rooms: RoomsUpdate( join: { roomID: JoinedRoomUpdate( - timeline: - TimelineUpdate(limited: true, prevBatch: 'blah', events: [ - MatrixEvent( - eventId: '\$somerandomfox', - type: 'm.room.message', - content: {'msgtype': 'm.text', 'body': 'Testcase'}, - senderId: '@alice:example.com', - originServerTs: - DateTime.fromMillisecondsSinceEpoch(testTimeStamp), - ), - ]), + timeline: TimelineUpdate( + limited: true, + prevBatch: 'blah', + events: [ + MatrixEvent( + eventId: '\$somerandomfox', + type: 'm.room.message', + content: {'msgtype': 'm.text', 'body': 'Testcase'}, + senderId: '@alice:example.com', + originServerTs: + DateTime.fromMillisecondsSinceEpoch(testTimeStamp), + ), + ], + ), unreadNotifications: UnreadNotificationCounts( highlightCount: 0, notificationCount: 0, @@ -387,30 +438,34 @@ void main() { timeline.events.clear(); await timeline.requestFuture(); await timeline.requestFuture(); - 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.error.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); - 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': 'def', - 'origin_server_ts': testTimeStamp + 5 - }, - )); + 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.error.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); + 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': 'def', + 'origin_server_ts': testTimeStamp + 5, + }, + ), + ); await waitForCount(8); expect(timeline.events[0].status, EventStatus.error); expect(timeline.events[1].status, EventStatus.synced); @@ -420,33 +475,37 @@ void main() { timeline.events.clear(); await timeline.requestFuture(); await timeline.requestFuture(); - 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.sending.intValue, - 'event_id': 'will-fail', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'will-fail', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await waitForCount(7); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 4); - 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.error.intValue, - 'event_id': 'will-fail', - 'origin_server_ts': testTimeStamp - }, - )); + 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.error.intValue, + 'event_id': 'will-fail', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await waitForCount(8); expect(timeline.events[0].status, EventStatus.error); expect(timeline.events.length, 4); @@ -454,18 +513,20 @@ void main() { test('setReadMarker', () async { await timeline.requestFuture(); await timeline.requestFuture(); - 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, - }, - )); + 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 waitForCount(7); room.notificationCount = 1; @@ -478,50 +539,56 @@ void main() { await timeline.requestFuture(); await timeline.requestFuture(); - 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.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await waitForCount(7); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 4); - 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.sent.intValue, - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'} - }, - )); + 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.sent.intValue, + 'event_id': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await waitForCount(8); expect(timeline.events[0].status, EventStatus.sent); expect(timeline.events.length, 4); - 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': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'} - }, - )); + 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': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await waitForCount(9); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 4); @@ -532,57 +599,63 @@ void main() { await timeline.requestFuture(); await timeline.requestFuture(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - 'unsigned': { - messageSendingStatusKey: EventStatus.sending.intValue, - 'transaction_id': 'transaction', + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + 'unsigned': { + messageSendingStatusKey: EventStatus.sending.intValue, + 'transaction_id': 'transaction', + }, }, - }, - )); + ), + ); await waitForCount(7); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 4); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': { - 'transaction_id': 'transaction', - messageSendingStatusKey: EventStatus.synced.intValue, + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'event_id': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': { + 'transaction_id': 'transaction', + messageSendingStatusKey: EventStatus.synced.intValue, + }, }, - }, - )); + ), + ); await waitForCount(8); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 4); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': { - 'transaction_id': 'transaction', - messageSendingStatusKey: EventStatus.sent.intValue, + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'event_id': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': { + 'transaction_id': 'transaction', + messageSendingStatusKey: EventStatus.sent.intValue, + }, }, - }, - )); + ), + ); await waitForCount(9); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 4); @@ -592,49 +665,55 @@ void main() { await timeline.requestFuture(); await timeline.requestFuture(); - 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.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await waitForCount(7); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 4); - 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.error.intValue, - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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.error.intValue, + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await waitForCount(8); expect(timeline.events[0].status, EventStatus.error); expect(timeline.events.length, 4); - 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': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await waitForCount(9); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 4); @@ -644,49 +723,55 @@ void main() { await timeline.requestFuture(); await timeline.requestFuture(); - 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.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await waitForCount(7); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 4); - 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': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await waitForCount(8); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 4); - 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.error.intValue, - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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.error.intValue, + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await waitForCount(9); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 4); diff --git a/test/timeline_test.dart b/test/timeline_test.dart index e87a1144c..d12f8fabd 100644 --- a/test/timeline_test.dart +++ b/test/timeline_test.dart @@ -52,12 +52,15 @@ void main() { } }); - return completer.future.timeout(Duration(seconds: 1), - onTimeout: () async { - throw TimeoutException( + return completer.future.timeout( + Duration(seconds: 1), + onTimeout: () async { + throw TimeoutException( 'Failed to wait for updateCount == $count, current == $updateCount', - Duration(seconds: 1)); - }); + Duration(seconds: 1), + ); + }, + ); } late Client client; @@ -73,7 +76,11 @@ void main() { currentPoison = poison; room = Room( - id: roomID, client: client, prev_batch: '1234', roomAccountData: {}); + id: roomID, + client: client, + prev_batch: '1234', + roomAccountData: {}, + ); timeline = Timeline( room: room, chunk: TimelineChunk(events: []), @@ -88,8 +95,10 @@ void main() { ); client.rooms.add(room); - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); await client.abortSync(); updateCount = 0; @@ -102,33 +111,38 @@ void main() { }); tearDown( - () async => client.dispose(closeDatabase: true).onError((e, s) {})); + () async => client.dispose(closeDatabase: true).onError((e, s) {}), + ); test('Create', () 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': '2', - 'origin_server_ts': testTimeStamp - 1000 - }, - )); - 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': '\$1', - 'origin_server_ts': testTimeStamp - }, - )); + 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': '2', + 'origin_server_ts': testTimeStamp - 1000, + }, + ), + ); + 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': '\$1', + 'origin_server_ts': testTimeStamp, + }, + ), + ); expect(timeline.sub != null, true); @@ -141,53 +155,67 @@ void main() { expect(removeList, []); expect(timeline.events.length, 2); expect(timeline.events[0].eventId, '\$1'); - expect(timeline.events[0].senderFromMemoryOrFallback.id, - '@alice:example.com'); - expect(timeline.events[0].originServerTs.millisecondsSinceEpoch, - testTimeStamp); + expect( + timeline.events[0].senderFromMemoryOrFallback.id, + '@alice:example.com', + ); + expect( + timeline.events[0].originServerTs.millisecondsSinceEpoch, + testTimeStamp, + ); expect(timeline.events[0].body, 'Testcase'); expect( - timeline.events[0].originServerTs.millisecondsSinceEpoch > - timeline.events[1].originServerTs.millisecondsSinceEpoch, - true); + timeline.events[0].originServerTs.millisecondsSinceEpoch > + timeline.events[1].originServerTs.millisecondsSinceEpoch, + true, + ); expect(timeline.events[0].receipts, []); - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - timeline.events.first.eventId: { - 'm.read': { - '@alice:example.com': { - 'ts': 1436451550453, - } + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + timeline.events.first.eventId: { + 'm.read': { + '@alice:example.com': { + 'ts': 1436451550453, + }, + }, + }, }, - }, - }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].receipts.length, 1); expect(timeline.events[0].receipts[0].user.id, '@alice:example.com'); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.redaction', - 'content': {'reason': 'spamming'}, - 'sender': '@alice:example.com', - 'redacts': '2', - 'event_id': '3', - 'origin_server_ts': testTimeStamp + 1000 - }, - )); + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.redaction', + 'content': {'reason': 'spamming'}, + 'sender': '@alice:example.com', + 'redacts': '2', + 'event_id': '3', + 'origin_server_ts': testTimeStamp + 1000, + }, + ), + ); await waitForCount(3); @@ -201,37 +229,45 @@ void main() { }); test('Receipt updates', () async { - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent.fromJson({ - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$2', - 'origin_server_ts': testTimeStamp - 1000, - }), - MatrixEvent.fromJson({ - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$1', - 'origin_server_ts': testTimeStamp, - }), - MatrixEvent.fromJson({ - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@bob:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$0', - 'origin_server_ts': testTimeStamp + 50, - }), - ])) - }))); + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': '\$2', + 'origin_server_ts': testTimeStamp - 1000, + }), + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': '\$1', + 'origin_server_ts': testTimeStamp, + }), + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@bob:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': '\$0', + 'origin_server_ts': testTimeStamp + 50, + }), + ], + ), + ), + }, + ), + ), + ); expect(timeline.sub != null, true); @@ -240,198 +276,261 @@ void main() { expect(updateCount, 3); expect(insertList, [0, 0, 0]); expect(insertList.length, timeline.events.length); - expect(timeline.events[1].senderFromMemoryOrFallback.id, - '@alice:example.com'); expect( - timeline.events[0].senderFromMemoryOrFallback.id, '@bob:example.com'); + timeline.events[1].senderFromMemoryOrFallback.id, + '@alice:example.com', + ); + expect( + timeline.events[0].senderFromMemoryOrFallback.id, + '@bob:example.com', + ); expect(timeline.events[0].receipts, []); expect(timeline.events[1].receipts, []); expect(timeline.events[2].receipts, []); - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - '\$2': { - 'm.read': { - '@alice:example.com': { - 'ts': 1436451550453, - } + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + '\$2': { + 'm.read': { + '@alice:example.com': { + 'ts': 1436451550453, + }, + }, + }, }, - }, - }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); expect(room.receiptState.global.latestOwnReceipt?.eventId, null); - expect(room.receiptState.global.otherUsers['@alice:example.com']?.eventId, - '\$2'); + expect( + room.receiptState.global.otherUsers['@alice:example.com']?.eventId, + '\$2', + ); expect(timeline.events[2].receipts.length, 1); expect(timeline.events[2].receipts[0].user.id, '@alice:example.com'); - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something2', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - '\$2': { - 'm.read': { - client.userID: { - 'ts': 1436451550453, - }, - '@bob:example.com': { - 'ts': 1436451550453, + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + '\$2': { + 'm.read': { + client.userID: { + 'ts': 1436451550453, + }, + '@bob:example.com': { + 'ts': 1436451550453, + }, + }, }, }, - }, - }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); expect(room.receiptState.global.latestOwnReceipt?.eventId, '\$2'); expect(room.receiptState.global.ownPublic?.eventId, '\$2'); expect(room.receiptState.global.ownPrivate?.eventId, null); - expect(room.receiptState.global.otherUsers['@alice:example.com']?.eventId, - '\$2'); - expect(room.receiptState.global.otherUsers['@bob:example.com']?.eventId, - '\$2'); + expect( + room.receiptState.global.otherUsers['@alice:example.com']?.eventId, + '\$2', + ); + expect( + room.receiptState.global.otherUsers['@bob:example.com']?.eventId, + '\$2', + ); expect(timeline.events[2].receipts.length, 3); expect(timeline.events[2].receipts[0].user.id, '@alice:example.com'); - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something3', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - '\$2': { - 'm.read.private': { - client.userID: { - 'ts': 1436451550453, - }, - '@alice:example.com': { - 'ts': 1436451550453, - }, - }, - 'm.read': { - '@bob:example.com': { - 'ts': 1436451550453, - 'thread_id': '\$734' + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + '\$2': { + 'm.read.private': { + client.userID: { + 'ts': 1436451550453, + }, + '@alice:example.com': { + 'ts': 1436451550453, + }, + }, + 'm.read': { + '@bob:example.com': { + 'ts': 1436451550453, + 'thread_id': '\$734', + }, + }, }, }, - }, - }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); expect(room.receiptState.global.latestOwnReceipt?.eventId, '\$2'); expect(room.receiptState.global.ownPublic?.eventId, '\$2'); expect(room.receiptState.global.ownPrivate?.eventId, '\$2'); - expect(room.receiptState.global.otherUsers['@alice:example.com']?.eventId, - '\$2'); - expect(room.receiptState.global.otherUsers['@bob:example.com']?.eventId, - '\$2'); + expect( + room.receiptState.global.otherUsers['@alice:example.com']?.eventId, + '\$2', + ); + expect( + room.receiptState.global.otherUsers['@bob:example.com']?.eventId, + '\$2', + ); expect(room.receiptState.byThread.length, 1); expect(timeline.events[2].receipts.length, 3); expect(timeline.events[2].receipts[0].user.id, '@alice:example.com'); - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something4', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - '\$1': { - 'm.read.private': { - client.userID: { - 'ts': 1436451550453, - }, - '@bob:example.com': { - 'ts': 1436451550453, + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + '\$1': { + 'm.read.private': { + client.userID: { + 'ts': 1436451550453, + }, + '@bob:example.com': { + 'ts': 1436451550453, + }, + }, }, }, - }, - }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); expect(room.receiptState.global.latestOwnReceipt?.eventId, '\$1'); expect(room.receiptState.global.ownPublic?.eventId, '\$2'); expect(room.receiptState.global.ownPrivate?.eventId, '\$1'); - expect(room.receiptState.global.otherUsers['@alice:example.com']?.eventId, - '\$2'); - expect(room.receiptState.global.otherUsers['@bob:example.com']?.eventId, - '\$1'); + expect( + room.receiptState.global.otherUsers['@alice:example.com']?.eventId, + '\$2', + ); + expect( + room.receiptState.global.otherUsers['@bob:example.com']?.eventId, + '\$1', + ); expect(room.receiptState.byThread.length, 1); expect(timeline.events[1].receipts.length, 2); expect(timeline.events[1].receipts[0].user.id, '@bob:example.com'); // test receipt only on main thread expect(timeline.events[2].receipts.length, 1); - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something5', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - '\$2': { - 'm.read': { - '@eve:example.com': { - 'ts': 1436451550453, - 'thread_id': 'main' - }, - '@john:example.com': { - 'ts': 1436451550453, - 'thread_id': 'main' + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + '\$2': { + 'm.read': { + '@eve:example.com': { + 'ts': 1436451550453, + 'thread_id': 'main', + }, + '@john:example.com': { + 'ts': 1436451550453, + 'thread_id': 'main', + }, + }, }, }, - }, - }, - }) - ]) - }))); - - expect(room.receiptState.global.otherUsers['@eve:example.com']?.eventId, - null); + }), + ], + ), + }, + ), + ), + ); + expect( - room.receiptState.mainThread?.otherUsers['@eve:example.com']?.eventId, - '\$2'); + room.receiptState.global.otherUsers['@eve:example.com']?.eventId, + null, + ); + expect( + room.receiptState.mainThread?.otherUsers['@eve:example.com']?.eventId, + '\$2', + ); expect(timeline.events[1].receipts.length, 2); expect(timeline.events[2].receipts.length, 3); // test own receipt on main thread - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something6', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate(ephemeral: [ - BasicRoomEvent.fromJson({ - 'type': 'm.receipt', - 'content': { - '\$2': { - 'm.read': { - client.userID: { - 'ts': 1436451550453, - 'thread_id': 'main', + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + ephemeral: [ + BasicRoomEvent.fromJson({ + 'type': 'm.receipt', + 'content': { + '\$2': { + 'm.read': { + client.userID: { + 'ts': 1436451550453, + 'thread_id': 'main', + }, + }, }, }, - }, - }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); expect(room.receiptState.global.latestOwnReceipt?.eventId, '\$1'); expect(room.receiptState.mainThread?.latestOwnReceipt?.eventId, '\$2'); expect(room.receiptState.global.ownPublic?.eventId, '\$2'); @@ -442,20 +541,24 @@ void main() { test('Sending both receipts at the same time sets the latest receipt', () async { - await client.handleSync(SyncUpdate( + await client.handleSync( + SyncUpdate( nextBatch: 'something', - rooms: RoomsUpdate(join: { - timeline.room.id: JoinedRoomUpdate( - timeline: TimelineUpdate(events: [ - MatrixEvent.fromJson({ - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$2', - 'origin_server_ts': testTimeStamp - 1000, - }), - ]), + rooms: RoomsUpdate( + join: { + timeline.room.id: JoinedRoomUpdate( + timeline: TimelineUpdate( + events: [ + MatrixEvent.fromJson({ + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': '\$2', + 'origin_server_ts': testTimeStamp - 1000, + }), + ], + ), ephemeral: [ BasicRoomEvent.fromJson({ 'type': 'm.receipt', @@ -473,9 +576,13 @@ void main() { }, }, }, - }) - ]) - }))); + }), + ], + ), + }, + ), + ), + ); expect(timeline.sub != null, true); @@ -497,19 +604,21 @@ void main() { expect(eventId.startsWith('\$event'), true); expect(timeline.events[0].status, EventStatus.sent); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'test'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': eventId, - 'unsigned': {'transaction_id': '1234'}, - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch - }, - )); + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'test'}, + 'sender': '@alice:example.com', + 'status': EventStatus.synced.intValue, + 'event_id': eventId, + 'unsigned': {'transaction_id': '1234'}, + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await waitForCount(3); expect(updateCount, 3); @@ -520,21 +629,23 @@ void main() { }); test('Send message with error', () async { - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': { - 'msgtype': 'm.text', - 'body': 'Testcase should not show up in Sync' + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': { + 'msgtype': 'm.text', + 'body': 'Testcase should not show up in Sync', + }, + 'sender': '@alice:example.com', + 'status': EventStatus.sending.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, }, - 'sender': '@alice:example.com', - 'status': EventStatus.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); + ), + ); await waitForCount(1); await room.sendTextEvent('test', txid: 'errortxid'); @@ -557,18 +668,20 @@ void main() { test('Remove message', () async { // send a failed message - 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.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); + 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.sending.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await waitForCount(1); await timeline.events[0].cancelSend(); @@ -582,18 +695,20 @@ void main() { }); test('getEventById', () 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.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); + 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.sending.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await waitForCount(1); var event = await timeline.getEventById('abc'); expect(event?.content, {'msgtype': 'm.text', 'body': 'Testcase'}); @@ -611,19 +726,21 @@ void main() { test('Resend message', () async { timeline.events.clear(); - 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.error.intValue, - 'event_id': 'new-test-event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'newresend'}, - }, - )); + 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.error.intValue, + 'event_id': 'new-test-event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'newresend'}, + }, + ), + ); await waitForCount(1); expect(timeline.events[0].status, EventStatus.error); await timeline.events[0].sendAgain(); @@ -682,30 +799,34 @@ void main() { test('sort errors on top', () async { timeline.events.clear(); - 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.error.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); - 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': 'def', - 'origin_server_ts': testTimeStamp + 5 - }, - )); + 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.error.intValue, + 'event_id': 'abc', + 'origin_server_ts': testTimeStamp, + }, + ), + ); + 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': 'def', + 'origin_server_ts': testTimeStamp + 5, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.error); expect(timeline.events[1].status, EventStatus.synced); @@ -713,50 +834,56 @@ void main() { test('sending event to failed update', () async { timeline.events.clear(); - 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.sending.intValue, - 'event_id': 'will-fail', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'will-fail', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 1); - 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.error.intValue, - 'event_id': 'will-fail', - 'origin_server_ts': testTimeStamp - }, - )); + 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.error.intValue, + 'event_id': 'will-fail', + 'origin_server_ts': testTimeStamp, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); 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, - }, - )); + 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(); @@ -765,50 +892,56 @@ void main() { test('sending an event and the http request finishes first, 0 -> 1 -> 2', () async { timeline.events.clear(); - 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.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 1); - 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.sent.intValue, - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'} - }, - )); + 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.sent.intValue, + 'event_id': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.sent); expect(timeline.events.length, 1); - 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': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'} - }, - )); + 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': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 1); @@ -816,155 +949,173 @@ void main() { test('sending an event where the sync reply arrives first, 0 -> 2 -> 1', () async { timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - 'unsigned': { - messageSendingStatusKey: EventStatus.sending.intValue, - 'transaction_id': 'transaction', + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + 'unsigned': { + messageSendingStatusKey: EventStatus.sending.intValue, + 'transaction_id': 'transaction', + }, }, - }, - )); + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': { - 'transaction_id': 'transaction', - messageSendingStatusKey: EventStatus.synced.intValue, + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'event_id': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': { + 'transaction_id': 'transaction', + messageSendingStatusKey: EventStatus.synced.intValue, + }, }, - }, - )); + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': { - 'transaction_id': 'transaction', - messageSendingStatusKey: EventStatus.sent.intValue, + client.onEvent.add( + EventUpdate( + type: EventUpdateType.timeline, + roomID: roomID, + content: { + 'type': 'm.room.message', + 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, + 'sender': '@alice:example.com', + 'event_id': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': { + 'transaction_id': 'transaction', + messageSendingStatusKey: EventStatus.sent.intValue, + }, }, - }, - )); + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 1); }); test('sending an event 0 -> -1 -> 2', () async { timeline.events.clear(); - 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.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 1); - 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.error.intValue, - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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.error.intValue, + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.error); expect(timeline.events.length, 1); - 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': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 1); }); test('sending an event 0 -> 2 -> -1', () async { timeline.events.clear(); - 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.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); + 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.sending.intValue, + 'event_id': 'transaction', + 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.sending); expect(timeline.events.length, 1); - 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': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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': '\$event', + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 1); - 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.error.intValue, - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); + 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.error.intValue, + 'origin_server_ts': testTimeStamp, + 'unsigned': {'transaction_id': 'transaction'}, + }, + ), + ); await Future.delayed(Duration(milliseconds: 50)); expect(timeline.events[0].status, EventStatus.synced); expect(timeline.events.length, 1); diff --git a/test/user_test.dart b/test/user_test.dart index 85d51f908..cc2e4ce55 100644 --- a/test/user_test.dart +++ b/test/user_test.dart @@ -43,11 +43,15 @@ void main() { room.setState(user1); room.setState(user2); setUp(() async { - await client.checkHomeserver(Uri.parse('https://fakeserver.notexisting'), - checkWellKnown: false); - await client.login(LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: 'test'), - password: '1234'); + await client.checkHomeserver( + Uri.parse('https://fakeserver.notexisting'), + checkWellKnown: false, + ); + await client.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: 'test'), + password: '1234', + ); await client.abortSync(); }); tearDown(() async { @@ -71,7 +75,7 @@ void main() { 'content': { 'membership': 'join', 'avatar_url': avatarUrl, - 'displayname': displayName + 'displayname': displayName, }, 'type': 'm.room.member', 'event_id': '143273582443PhrSn:example.org', @@ -79,7 +83,7 @@ void main() { 'sender': id, 'origin_server_ts': 1432735824653, 'unsigned': {'age': 1234}, - 'state_key': id + 'state_key': id, }; final user = Event.fromJson(jsonObj, room).asUser; @@ -100,7 +104,9 @@ void main() { expect(user3.calcDisplayname(), 'Alice Mep'); expect(user3.calcDisplayname(formatLocalpart: false), 'alice_mep'); expect( - user3.calcDisplayname(mxidLocalPartFallback: false), 'Unknown user'); + user3.calcDisplayname(mxidLocalPartFallback: false), + 'Unknown user', + ); }); test('kick', () async { await user1.kick(); @@ -118,20 +124,24 @@ void main() { await user1.startDirectChat(waitForSync: false); }); test('getPresence', () async { - await client.handleSync(SyncUpdate.fromJson({ - 'next_batch': 'fake', - 'presence': { - 'events': [ - { - 'sender': '@alice:example.com', - 'type': 'm.presence', - 'content': {'presence': 'online'} - } - ] - } - })); + await client.handleSync( + SyncUpdate.fromJson({ + 'next_batch': 'fake', + 'presence': { + 'events': [ + { + 'sender': '@alice:example.com', + 'type': 'm.presence', + 'content': {'presence': 'online'}, + } + ], + }, + }), + ); expect( - (await user1.fetchCurrentPresence()).presence, PresenceType.online); + (await user1.fetchCurrentPresence()).presence, + PresenceType.online, + ); }); test('canBan', () async { expect(user1.canBan, false); diff --git a/test/webrtc_stub.dart b/test/webrtc_stub.dart index 18c3d0b94..c0f30631a 100644 --- a/test/webrtc_stub.dart +++ b/test/webrtc_stub.dart @@ -63,7 +63,10 @@ class MockWebRTCDelegate implements WebRTCDelegate { class MockEncryptionKeyProvider implements EncryptionKeyProvider { @override Future onSetEncryptionKey( - CallParticipant participant, Uint8List key, int index) async { + CallParticipant participant, + Uint8List key, + int index, + ) async { Logs().i('Mock onSetEncryptionKey called for ${participant.id} '); } @@ -104,7 +107,8 @@ class MockMediaDevices implements MediaDevices { @override Future getUserMedia( - Map mediaConstraints) async { + Map mediaConstraints, + ) async { return MockMediaStream('', ''); } @@ -203,16 +207,18 @@ class MockRTCPeerConnection implements RTCPeerConnection { } @override - Future createOffer( - [Map? constraints]) { + Future createOffer([ + Map? constraints, + ]) { // Mock implementation for creating an offer Logs().i('Mock: Creating offer'); return Future.value(RTCSessionDescription('', '')); } @override - Future createAnswer( - [Map? constraints]) { + Future createAnswer([ + Map? constraints, + ]) { // Mock implementation for creating an answer Logs().i('Mock: Creating answer'); return Future.value(RTCSessionDescription('', '')); @@ -285,7 +291,9 @@ class MockRTCPeerConnection implements RTCPeerConnection { @override Future createDataChannel( - String label, RTCDataChannelInit dataChannelDict) async { + String label, + RTCDataChannelInit dataChannelDict, + ) async { // Mock implementation for creating a data channel Logs().i('Mock: Creating data channel'); return MockRTCDataChannel(); @@ -332,8 +340,10 @@ class MockRTCPeerConnection implements RTCPeerConnection { } @override - Future addTrack(MediaStreamTrack track, - [MediaStream? stream]) async { + Future addTrack( + MediaStreamTrack track, [ + MediaStream? stream, + ]) async { // Mock implementation for adding a track Logs().i('Mock: Adding track'); return MockRTCRtpSender(); @@ -347,10 +357,11 @@ class MockRTCPeerConnection implements RTCPeerConnection { } @override - Future addTransceiver( - {MediaStreamTrack? track, - RTCRtpMediaType? kind, - RTCRtpTransceiverInit? init}) async { + Future addTransceiver({ + MediaStreamTrack? track, + RTCRtpMediaType? kind, + RTCRtpTransceiverInit? init, + }) async { // Mock implementation for adding a transceiver Logs().i('Mock: Adding transceiver'); return MockRTCRtpTransceiver(); @@ -418,7 +429,8 @@ class MockRTCRtpTransceiver implements RTCRtpTransceiver { TransceiverDirection get currentDirection { // Deprecated method, should be replaced with `await getCurrentDirection` throw UnimplementedError( - 'Need to be call asynchronously from native SDK, so the method is deprecated'); + 'Need to be call asynchronously from native SDK, so the method is deprecated', + ); } @override @@ -598,17 +610,24 @@ class MockMediaStreamTrack implements MediaStreamTrack { class MockRTCDTMFSender implements RTCDTMFSender { @override - Future insertDTMF(String tones, - {int duration = 100, int interToneGap = 70}) async { + Future insertDTMF( + String tones, { + int duration = 100, + int interToneGap = 70, + }) async { // Mock implementation for inserting DTMF tones Logs().i( - 'Mock: Inserting DTMF tones: $tones, Duration: $duration, InterToneGap: $interToneGap'); + 'Mock: Inserting DTMF tones: $tones, Duration: $duration, InterToneGap: $interToneGap', + ); } @override @Deprecated('Use method insertDTMF instead') - Future sendDtmf(String tones, - {int duration = 100, int interToneGap = 70}) async { + Future sendDtmf( + String tones, { + int duration = 100, + int interToneGap = 70, + }) async { return insertDTMF(tones, duration: duration, interToneGap: interToneGap); } @@ -696,16 +715,20 @@ class MockMediaStream implements MediaStream { } @override - Future addTrack(MediaStreamTrack track, - {bool addToNative = true}) async { + Future addTrack( + MediaStreamTrack track, { + bool addToNative = true, + }) async { // Mock implementation for adding a track Logs().i('Mock: Adding track to MediaStream: $track'); onAddTrack?.call(track); } @override - Future removeTrack(MediaStreamTrack track, - {bool removeFromNative = true}) async { + Future removeTrack( + MediaStreamTrack track, { + bool removeFromNative = true, + }) async { // Mock implementation for removing a track Logs().i('Mock: Removing track from MediaStream: $track'); onRemoveTrack?.call(track); diff --git a/test_driver/matrixsdk_test.dart b/test_driver/matrixsdk_test.dart index a70c38c32..2f29919e2 100644 --- a/test_driver/matrixsdk_test.dart +++ b/test_driver/matrixsdk_test.dart @@ -33,262 +33,300 @@ const String testMessage4 = 'Hello star'; const String testMessage5 = 'Hello earth'; const String testMessage6 = 'Hello mars'; -void main() => group('Integration tests', () { - test('E2EE', () async { - Client? testClientA, testClientB; - - try { - Hive.init(null); - - await olm.init(); - olm.Account(); - Logs().i('[LibOlm] Enabled'); - - final homeserverUri = Uri.parse(homeserver); - Logs().i('++++ Using homeserver $homeserverUri ++++'); - - Logs().i('++++ Login Alice at ++++'); - testClientA = Client('TestClientA', databaseBuilder: getDatabase); - await testClientA.checkHomeserver(homeserverUri); - await testClientA.login( - LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: Users.user1.name), - password: Users.user1.password, - ); - expect(testClientA.encryptionEnabled, true); - - Logs().i('++++ Login Bob ++++'); - testClientB = Client('TestClientB', databaseBuilder: getDatabase); - await testClientB.checkHomeserver(homeserverUri); - await testClientB.login( - LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: Users.user2.name), - password: Users.user2.password, - ); - expect(testClientB.encryptionEnabled, true); - - Logs().i('++++ (Alice) Leave all rooms ++++'); - while (testClientA.rooms.isNotEmpty) { - final room = testClientA.rooms.first; - if (room.canonicalAlias.isNotEmpty) { - break; - } - try { - await room.leave(); - await room.forget(); - } catch (_) {} - } - - Logs().i('++++ (Bob) Leave all rooms ++++'); - for (var i = 0; i < 3; i++) { - if (testClientB.rooms.isNotEmpty) { - final room = testClientB.rooms.first; +void main() => group( + 'Integration tests', + () { + test('E2EE', () async { + Client? testClientA, testClientB; + + try { + Hive.init(null); + + await olm.init(); + olm.Account(); + Logs().i('[LibOlm] Enabled'); + + final homeserverUri = Uri.parse(homeserver); + Logs().i('++++ Using homeserver $homeserverUri ++++'); + + Logs().i('++++ Login Alice at ++++'); + testClientA = Client('TestClientA', databaseBuilder: getDatabase); + await testClientA.checkHomeserver(homeserverUri); + await testClientA.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: Users.user1.name), + password: Users.user1.password, + ); + expect(testClientA.encryptionEnabled, true); + + Logs().i('++++ Login Bob ++++'); + testClientB = Client('TestClientB', databaseBuilder: getDatabase); + await testClientB.checkHomeserver(homeserverUri); + await testClientB.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: Users.user2.name), + password: Users.user2.password, + ); + expect(testClientB.encryptionEnabled, true); + + Logs().i('++++ (Alice) Leave all rooms ++++'); + while (testClientA.rooms.isNotEmpty) { + final room = testClientA.rooms.first; + if (room.canonicalAlias.isNotEmpty) { + break; + } try { await room.leave(); await room.forget(); } catch (_) {} } - } - Logs().i('++++ Check if own olm device is verified by default ++++'); - expect(testClientA.userDeviceKeys, contains(testClientA.userID)); - expect(testClientA.userDeviceKeys[testClientA.userID]!.deviceKeys, - contains(testClientA.deviceID)); - expect( + Logs().i('++++ (Bob) Leave all rooms ++++'); + for (var i = 0; i < 3; i++) { + if (testClientB.rooms.isNotEmpty) { + final room = testClientB.rooms.first; + try { + await room.leave(); + await room.forget(); + } catch (_) {} + } + } + + Logs() + .i('++++ Check if own olm device is verified by default ++++'); + expect(testClientA.userDeviceKeys, contains(testClientA.userID)); + expect( + testClientA.userDeviceKeys[testClientA.userID]!.deviceKeys, + contains(testClientA.deviceID), + ); + expect( testClientA.userDeviceKeys[testClientA.userID]! .deviceKeys[testClientA.deviceID!]!.verified, - isTrue); - expect( + isTrue, + ); + expect( !testClientA.userDeviceKeys[testClientA.userID]! .deviceKeys[testClientA.deviceID!]!.blocked, - isTrue); - expect(testClientB.userDeviceKeys, contains(testClientB.userID)); - expect(testClientB.userDeviceKeys[testClientB.userID]!.deviceKeys, - contains(testClientB.deviceID)); - expect( + isTrue, + ); + expect(testClientB.userDeviceKeys, contains(testClientB.userID)); + expect( + testClientB.userDeviceKeys[testClientB.userID]!.deviceKeys, + contains(testClientB.deviceID), + ); + expect( testClientB.userDeviceKeys[testClientB.userID]! .deviceKeys[testClientB.deviceID!]!.verified, - isTrue); - expect( + isTrue, + ); + expect( !testClientB.userDeviceKeys[testClientB.userID]! .deviceKeys[testClientB.deviceID!]!.blocked, - isTrue); - - Logs().i('++++ (Alice) Create room and invite Bob ++++'); - await testClientA.startDirectChat( - testClientB.userID!, - enableEncryption: false, - ); - await Future.delayed(Duration(seconds: 1)); - final room = testClientA.rooms.first; - final roomId = room.id; - - Logs().i('++++ (Bob) Join room ++++'); - final inviteRoom = testClientB.getRoomById(roomId)!; - await inviteRoom.join(); - await Future.delayed(Duration(seconds: 1)); - expect(inviteRoom.membership, Membership.join); - - Logs().i('++++ (Alice) Enable encryption ++++'); - expect(room.encrypted, false); - await room.enableEncryption(); - var waitSeconds = 0; - while (!room.encrypted) { + isTrue, + ); + + Logs().i('++++ (Alice) Create room and invite Bob ++++'); + await testClientA.startDirectChat( + testClientB.userID!, + enableEncryption: false, + ); await Future.delayed(Duration(seconds: 1)); - waitSeconds++; - if (waitSeconds >= 60) { - throw Exception('Unable to enable encryption'); + final room = testClientA.rooms.first; + final roomId = room.id; + + Logs().i('++++ (Bob) Join room ++++'); + final inviteRoom = testClientB.getRoomById(roomId)!; + await inviteRoom.join(); + await Future.delayed(Duration(seconds: 1)); + expect(inviteRoom.membership, Membership.join); + + Logs().i('++++ (Alice) Enable encryption ++++'); + expect(room.encrypted, false); + await room.enableEncryption(); + var waitSeconds = 0; + while (!room.encrypted) { + await Future.delayed(Duration(seconds: 1)); + waitSeconds++; + if (waitSeconds >= 60) { + throw Exception('Unable to enable encryption'); + } } - } - expect(room.encrypted, isTrue); - expect( + expect(room.encrypted, isTrue); + expect( room.client.encryption!.keyManager .getOutboundGroupSession(room.id), - isNull); + isNull, + ); - Logs().i('++++ (Alice) Check known olm devices ++++'); - expect(testClientA.userDeviceKeys, contains(testClientB.userID)); - expect(testClientA.userDeviceKeys[testClientB.userID]!.deviceKeys, - contains(testClientB.deviceID)); - expect( + Logs().i('++++ (Alice) Check known olm devices ++++'); + expect(testClientA.userDeviceKeys, contains(testClientB.userID)); + expect( + testClientA.userDeviceKeys[testClientB.userID]!.deviceKeys, + contains(testClientB.deviceID), + ); + expect( testClientA.userDeviceKeys[testClientB.userID]! .deviceKeys[testClientB.deviceID!]!.verified, - isFalse); - expect( + isFalse, + ); + expect( testClientA.userDeviceKeys[testClientB.userID]! .deviceKeys[testClientB.deviceID!]!.blocked, - isFalse); - expect(testClientB.userDeviceKeys, contains(testClientA.userID)); - expect(testClientB.userDeviceKeys[testClientA.userID]!.deviceKeys, - contains(testClientA.deviceID)); - expect( + isFalse, + ); + expect(testClientB.userDeviceKeys, contains(testClientA.userID)); + expect( + testClientB.userDeviceKeys[testClientA.userID]!.deviceKeys, + contains(testClientA.deviceID), + ); + expect( testClientB.userDeviceKeys[testClientA.userID]! .deviceKeys[testClientA.deviceID!]!.verified, - isFalse); - expect( + isFalse, + ); + expect( testClientB.userDeviceKeys[testClientA.userID]! .deviceKeys[testClientA.deviceID!]!.blocked, - isFalse); - await Future.wait([ - testClientA.updateUserDeviceKeys(), - testClientB.updateUserDeviceKeys(), - ]); - await testClientA.userDeviceKeys[testClientB.userID]! - .deviceKeys[testClientB.deviceID!]! - .setVerified(true); - - Logs().i('++++ Check if own olm device is verified by default ++++'); - expect(testClientA.userDeviceKeys, contains(testClientA.userID)); - expect(testClientA.userDeviceKeys[testClientA.userID]!.deviceKeys, - contains(testClientA.deviceID)); - expect( + isFalse, + ); + await Future.wait([ + testClientA.updateUserDeviceKeys(), + testClientB.updateUserDeviceKeys(), + ]); + await testClientA.userDeviceKeys[testClientB.userID]! + .deviceKeys[testClientB.deviceID!]! + .setVerified(true); + + Logs() + .i('++++ Check if own olm device is verified by default ++++'); + expect(testClientA.userDeviceKeys, contains(testClientA.userID)); + expect( + testClientA.userDeviceKeys[testClientA.userID]!.deviceKeys, + contains(testClientA.deviceID), + ); + expect( testClientA.userDeviceKeys[testClientA.userID]! .deviceKeys[testClientA.deviceID!]!.verified, - isTrue); - expect(testClientB.userDeviceKeys, contains(testClientB.userID)); - expect(testClientB.userDeviceKeys[testClientB.userID]!.deviceKeys, - contains(testClientB.deviceID)); - expect( + isTrue, + ); + expect(testClientB.userDeviceKeys, contains(testClientB.userID)); + expect( + testClientB.userDeviceKeys[testClientB.userID]!.deviceKeys, + contains(testClientB.deviceID), + ); + expect( testClientB.userDeviceKeys[testClientB.userID]! .deviceKeys[testClientB.deviceID!]!.verified, - isTrue); + isTrue, + ); - Logs().i("++++ (Alice) Send encrypted message: '$testMessage' ++++"); - await room.sendTextEvent(testMessage); - await Future.delayed(Duration(seconds: 5)); - expect( + Logs() + .i("++++ (Alice) Send encrypted message: '$testMessage' ++++"); + await room.sendTextEvent(testMessage); + await Future.delayed(Duration(seconds: 5)); + expect( room.client.encryption!.keyManager .getOutboundGroupSession(room.id), - isNotNull); - var currentSessionIdA = room.client.encryption!.keyManager - .getOutboundGroupSession(room.id)! - .outboundGroupSession! - .session_id(); - /*expect(room.client.encryption.keyManager + isNotNull, + ); + var currentSessionIdA = room.client.encryption!.keyManager + .getOutboundGroupSession(room.id)! + .outboundGroupSession! + .session_id(); + /*expect(room.client.encryption.keyManager .getInboundGroupSession(room.id, currentSessionIdA, '') != null);*/ - expect( - testClientA.encryption!.olmManager - .olmSessions[testClientB.identityKey]!.length, - olmLengthMatcher, - ); - expect( - testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.length, - olmLengthMatcher, - ); - expect( + expect( + testClientA.encryption!.olmManager + .olmSessions[testClientB.identityKey]!.length, + olmLengthMatcher, + ); + expect( + testClientB.encryption!.olmManager + .olmSessions[testClientA.identityKey]!.length, + olmLengthMatcher, + ); + expect( testClientA.encryption!.olmManager .olmSessions[testClientB.identityKey]!.first.sessionId, testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.first.sessionId); - /*expect(inviteRoom.client.encryption.keyManager + .olmSessions[testClientA.identityKey]!.first.sessionId, + ); + /*expect(inviteRoom.client.encryption.keyManager .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != null);*/ - expect(room.lastEvent!.body, testMessage); - expect(inviteRoom.lastEvent!.body, testMessage); - Logs().i( - "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++"); - - Logs().i( - "++++ (Alice) Send again encrypted message: '$testMessage2' ++++"); - await room.sendTextEvent(testMessage2); - await Future.delayed(Duration(seconds: 5)); - expect( - testClientA.encryption!.olmManager - .olmSessions[testClientB.identityKey]!.length, - olmLengthMatcher, - ); - expect( - testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.length, - olmLengthMatcher, - ); - expect( + expect(room.lastEvent!.body, testMessage); + expect(inviteRoom.lastEvent!.body, testMessage); + Logs().i( + "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++", + ); + + Logs().i( + "++++ (Alice) Send again encrypted message: '$testMessage2' ++++", + ); + await room.sendTextEvent(testMessage2); + await Future.delayed(Duration(seconds: 5)); + expect( + testClientA.encryption!.olmManager + .olmSessions[testClientB.identityKey]!.length, + olmLengthMatcher, + ); + expect( + testClientB.encryption!.olmManager + .olmSessions[testClientA.identityKey]!.length, + olmLengthMatcher, + ); + expect( testClientA.encryption!.olmManager .olmSessions[testClientB.identityKey]!.first.sessionId, testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.first.sessionId); + .olmSessions[testClientA.identityKey]!.first.sessionId, + ); - expect( + expect( room.client.encryption!.keyManager .getOutboundGroupSession(room.id)! .outboundGroupSession! .session_id(), - currentSessionIdA); - /*expect(room.client.encryption.keyManager + currentSessionIdA, + ); + /*expect(room.client.encryption.keyManager .getInboundGroupSession(room.id, currentSessionIdA, '') != null);*/ - expect(room.lastEvent!.body, testMessage2); - expect(inviteRoom.lastEvent!.body, testMessage2); - Logs().i( - "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++"); - - Logs().i( - "++++ (Bob) Send again encrypted message: '$testMessage3' ++++"); - await inviteRoom.sendTextEvent(testMessage3); - await Future.delayed(Duration(seconds: 5)); - expect( - testClientA.encryption!.olmManager - .olmSessions[testClientB.identityKey]!.length, - olmLengthMatcher, - ); - expect( + expect(room.lastEvent!.body, testMessage2); + expect(inviteRoom.lastEvent!.body, testMessage2); + Logs().i( + "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++", + ); + + Logs().i( + "++++ (Bob) Send again encrypted message: '$testMessage3' ++++", + ); + await inviteRoom.sendTextEvent(testMessage3); + await Future.delayed(Duration(seconds: 5)); + expect( + testClientA.encryption!.olmManager + .olmSessions[testClientB.identityKey]!.length, + olmLengthMatcher, + ); + expect( testClientB.encryption!.olmManager .olmSessions[testClientA.identityKey]!.length, - olmLengthMatcher); - expect( + olmLengthMatcher, + ); + expect( room.client.encryption!.keyManager .getOutboundGroupSession(room.id)! .outboundGroupSession! .session_id(), - currentSessionIdA); - final inviteRoomOutboundGroupSession = inviteRoom - .client.encryption!.keyManager - .getOutboundGroupSession(inviteRoom.id)!; + currentSessionIdA, + ); + final inviteRoomOutboundGroupSession = inviteRoom + .client.encryption!.keyManager + .getOutboundGroupSession(inviteRoom.id)!; - expect(inviteRoomOutboundGroupSession.isValid, isTrue); - /*expect(inviteRoom.client.encryption.keyManager.getInboundGroupSession( + expect(inviteRoomOutboundGroupSession.isValid, isTrue); + /*expect(inviteRoom.client.encryption.keyManager.getInboundGroupSession( inviteRoom.id, inviteRoomOutboundGroupSession.outboundGroupSession.session_id(), '') != @@ -298,228 +336,262 @@ void main() => group('Integration tests', () { inviteRoomOutboundGroupSession.outboundGroupSession.session_id(), '') != null);*/ - expect(inviteRoom.lastEvent!.body, testMessage3); - expect(room.lastEvent!.body, testMessage3); - Logs().i( - "++++ (Alice) Received decrypted message: '${room.lastEvent!.body}' ++++"); - - Logs().i('++++ Login Bob in another client ++++'); - final testClientC = - Client('TestClientC', databaseBuilder: getDatabase); - await testClientC.checkHomeserver(homeserverUri); - // We can't sign in using the displayname, since that breaks e2ee on dendrite: https://github.com/matrix-org/dendrite/issues/2914 - await testClientC.login( - LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: Users.user2.name), - password: Users.user2.password, - ); - await Future.delayed(Duration(seconds: 3)); - - Logs().i( - "++++ (Alice) Send again encrypted message: '$testMessage4' ++++"); - await room.sendTextEvent(testMessage4); - await Future.delayed(Duration(seconds: 7)); - expect( - testClientA.encryption!.olmManager - .olmSessions[testClientB.identityKey]!.length, - olmLengthMatcher, - ); - expect( - testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.length, - olmLengthMatcher, - ); - expect( + expect(inviteRoom.lastEvent!.body, testMessage3); + expect(room.lastEvent!.body, testMessage3); + Logs().i( + "++++ (Alice) Received decrypted message: '${room.lastEvent!.body}' ++++", + ); + + Logs().i('++++ Login Bob in another client ++++'); + final testClientC = + Client('TestClientC', databaseBuilder: getDatabase); + await testClientC.checkHomeserver(homeserverUri); + // We can't sign in using the displayname, since that breaks e2ee on dendrite: https://github.com/matrix-org/dendrite/issues/2914 + await testClientC.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: Users.user2.name), + password: Users.user2.password, + ); + await Future.delayed(Duration(seconds: 3)); + + Logs().i( + "++++ (Alice) Send again encrypted message: '$testMessage4' ++++", + ); + await room.sendTextEvent(testMessage4); + await Future.delayed(Duration(seconds: 7)); + expect( + testClientA.encryption!.olmManager + .olmSessions[testClientB.identityKey]!.length, + olmLengthMatcher, + ); + expect( + testClientB.encryption!.olmManager + .olmSessions[testClientA.identityKey]!.length, + olmLengthMatcher, + ); + expect( testClientA.encryption!.olmManager .olmSessions[testClientB.identityKey]!.first.sessionId, testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.first.sessionId); - expect( + .olmSessions[testClientA.identityKey]!.first.sessionId, + ); + expect( testClientA.encryption!.olmManager .olmSessions[testClientC.identityKey]!.length, - olmLengthMatcher); - expect( + olmLengthMatcher, + ); + expect( testClientC.encryption!.olmManager .olmSessions[testClientA.identityKey]!.length, - olmLengthMatcher); - expect( + olmLengthMatcher, + ); + expect( testClientA.encryption!.olmManager .olmSessions[testClientC.identityKey]!.first.sessionId, testClientC.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.first.sessionId); - expect( + .olmSessions[testClientA.identityKey]!.first.sessionId, + ); + expect( room.client.encryption!.keyManager .getOutboundGroupSession(room.id)! .outboundGroupSession! .session_id(), - currentSessionIdA); - /*expect(inviteRoom.client.encryption.keyManager + currentSessionIdA, + ); + /*expect(inviteRoom.client.encryption.keyManager .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != null);*/ - expect(room.lastEvent!.body, testMessage4); - expect(inviteRoom.lastEvent!.body, testMessage4); - Logs().i( - "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++"); - - Logs() - .i('++++ Logout Bob another client ${testClientC.deviceID} ++++'); - await testClientC.dispose(closeDatabase: false); - await testClientC.logout(); - await Future.delayed(Duration(seconds: 5)); - - Logs().i( - "++++ (Alice) Send again encrypted message: '$testMessage6' ++++"); - await room.sendTextEvent(testMessage6); - await Future.delayed(Duration(seconds: 5)); - expect( - testClientA.encryption!.olmManager - .olmSessions[testClientB.identityKey]!.length, - olmLengthMatcher, - ); - expect( - testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.length, - olmLengthMatcher, - ); - expect( + expect(room.lastEvent!.body, testMessage4); + expect(inviteRoom.lastEvent!.body, testMessage4); + Logs().i( + "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++", + ); + + Logs().i( + '++++ Logout Bob another client ${testClientC.deviceID} ++++', + ); + await testClientC.dispose(closeDatabase: false); + await testClientC.logout(); + await Future.delayed(Duration(seconds: 5)); + + Logs().i( + "++++ (Alice) Send again encrypted message: '$testMessage6' ++++", + ); + await room.sendTextEvent(testMessage6); + await Future.delayed(Duration(seconds: 5)); + expect( + testClientA.encryption!.olmManager + .olmSessions[testClientB.identityKey]!.length, + olmLengthMatcher, + ); + expect( + testClientB.encryption!.olmManager + .olmSessions[testClientA.identityKey]!.length, + olmLengthMatcher, + ); + expect( testClientA.encryption!.olmManager .olmSessions[testClientB.identityKey]!.first.sessionId, testClientB.encryption!.olmManager - .olmSessions[testClientA.identityKey]!.first.sessionId); + .olmSessions[testClientA.identityKey]!.first.sessionId, + ); - // This does not work on conduit because of a server bug: https://gitlab.com/famedly/conduit/-/issues/325 - if (Platform.environment['HOMESERVER_IMPLEMENTATION'] != 'conduit') { - expect( + // This does not work on conduit because of a server bug: https://gitlab.com/famedly/conduit/-/issues/325 + if (Platform.environment['HOMESERVER_IMPLEMENTATION'] != + 'conduit') { + expect( room.client.encryption!.keyManager .getOutboundGroupSession(room.id)! .outboundGroupSession! .session_id(), - isNot(currentSessionIdA)); - } - currentSessionIdA = room.client.encryption!.keyManager - .getOutboundGroupSession(room.id)! - .outboundGroupSession! - .session_id(); - /*expect(inviteRoom.client.encryption.keyManager + isNot(currentSessionIdA), + ); + } + currentSessionIdA = room.client.encryption!.keyManager + .getOutboundGroupSession(room.id)! + .outboundGroupSession! + .session_id(); + /*expect(inviteRoom.client.encryption.keyManager .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != null);*/ - expect(room.lastEvent!.body, testMessage6); - expect(inviteRoom.lastEvent!.body, testMessage6); - Logs().i( - "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++"); - - await room.leave(); - await room.forget(); - await inviteRoom.leave(); - await inviteRoom.forget(); - await Future.delayed(Duration(seconds: 1)); - } catch (e, s) { - Logs().e('Test failed', e, s); - rethrow; - } finally { - Logs().i('++++ Logout Alice and Bob ++++'); - if (testClientA?.isLogged() ?? false) await testClientA!.logoutAll(); - if (testClientA?.isLogged() ?? false) await testClientB!.logoutAll(); - await testClientA?.dispose(closeDatabase: false); - await testClientB?.dispose(closeDatabase: false); - testClientA = null; - testClientB = null; - } - return; - }); - - test('dm creation', () async { - Client? testClientA, testClientB; - - try { - Hive.init(null); - - await olm.init(); - olm.Account(); - Logs().i('[LibOlm] Enabled'); - - final homeserverUri = Uri.parse(homeserver); - Logs().i('++++ Using homeserver $homeserverUri ++++'); - - Logs().i('++++ Login Alice at ++++'); - testClientA = Client('TestClientA', databaseBuilder: getDatabase); - await testClientA.checkHomeserver(homeserverUri); - await testClientA.login( - LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: Users.user1.name), - password: Users.user1.password, - ); - expect(testClientA.encryptionEnabled, true); - - Logs().i('++++ Login Bob ++++'); - testClientB = Client('TestClientB', databaseBuilder: getDatabase); - await testClientB.checkHomeserver(homeserverUri); - await testClientB.login( - LoginType.mLoginPassword, - identifier: AuthenticationUserIdentifier(user: Users.user2.name), - password: Users.user2.password, - ); - expect(testClientB.encryptionEnabled, true); - - Logs().i('++++ (Alice) Leave all rooms ++++'); - while (testClientA.rooms.isNotEmpty) { - final room = testClientA.rooms.first; - if (room.canonicalAlias.isNotEmpty) { - break; + expect(room.lastEvent!.body, testMessage6); + expect(inviteRoom.lastEvent!.body, testMessage6); + Logs().i( + "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++", + ); + + await room.leave(); + await room.forget(); + await inviteRoom.leave(); + await inviteRoom.forget(); + await Future.delayed(Duration(seconds: 1)); + } catch (e, s) { + Logs().e('Test failed', e, s); + rethrow; + } finally { + Logs().i('++++ Logout Alice and Bob ++++'); + if (testClientA?.isLogged() ?? false) { + await testClientA!.logoutAll(); + } + if (testClientA?.isLogged() ?? false) { + await testClientB!.logoutAll(); } - try { - await room.leave(); - await room.forget(); - } catch (_) {} + await testClientA?.dispose(closeDatabase: false); + await testClientB?.dispose(closeDatabase: false); + testClientA = null; + testClientB = null; } + return; + }); + + test('dm creation', () async { + Client? testClientA, testClientB; + + try { + Hive.init(null); - Logs().i('++++ (Bob) Leave all rooms ++++'); - for (var i = 0; i < 3; i++) { - if (testClientB.rooms.isNotEmpty) { - final room = testClientB.rooms.first; + await olm.init(); + olm.Account(); + Logs().i('[LibOlm] Enabled'); + + final homeserverUri = Uri.parse(homeserver); + Logs().i('++++ Using homeserver $homeserverUri ++++'); + + Logs().i('++++ Login Alice at ++++'); + testClientA = Client('TestClientA', databaseBuilder: getDatabase); + await testClientA.checkHomeserver(homeserverUri); + await testClientA.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: Users.user1.name), + password: Users.user1.password, + ); + expect(testClientA.encryptionEnabled, true); + + Logs().i('++++ Login Bob ++++'); + testClientB = Client('TestClientB', databaseBuilder: getDatabase); + await testClientB.checkHomeserver(homeserverUri); + await testClientB.login( + LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: Users.user2.name), + password: Users.user2.password, + ); + expect(testClientB.encryptionEnabled, true); + + Logs().i('++++ (Alice) Leave all rooms ++++'); + while (testClientA.rooms.isNotEmpty) { + final room = testClientA.rooms.first; + if (room.canonicalAlias.isNotEmpty) { + break; + } try { await room.leave(); await room.forget(); } catch (_) {} } - } - Logs().i('++++ (Alice) Create DM ++++'); - final dmRoom = await testClientA.startDirectChat(testClientB.userID!); + Logs().i('++++ (Bob) Leave all rooms ++++'); + for (var i = 0; i < 3; i++) { + if (testClientB.rooms.isNotEmpty) { + final room = testClientB.rooms.first; + try { + await room.leave(); + await room.forget(); + } catch (_) {} + } + } - if (testClientB.getRoomById(dmRoom) == null) { - await testClientB.waitForRoomInSync(dmRoom, invite: true); - } - // Wait at least for one additional sync to make sure the invite landed - // correctly. Workaround for synapse CI job failing. - await testClientB.onSync.stream.first; + Logs().i('++++ (Alice) Create DM ++++'); + final dmRoom = + await testClientA.startDirectChat(testClientB.userID!); + + if (testClientB.getRoomById(dmRoom) == null) { + await testClientB.waitForRoomInSync(dmRoom, invite: true); + } + // Wait at least for one additional sync to make sure the invite landed + // correctly. Workaround for synapse CI job failing. + await testClientB.onSync.stream.first; - Logs().i('++++ (Bob) Create DM ++++'); - final dmRoomFromB = - await testClientB.startDirectChat(testClientA.userID!); + Logs().i('++++ (Bob) Create DM ++++'); + final dmRoomFromB = + await testClientB.startDirectChat(testClientA.userID!); - expect(dmRoom, dmRoomFromB, + expect( + dmRoom, + dmRoomFromB, reason: - "Bob should join alice's DM room instead of creating a new one"); - expect(testClientB.getRoomById(dmRoom)?.membership, Membership.join, - reason: 'Room should actually be in the join state now.'); - expect(testClientA.getRoomById(dmRoom)?.membership, Membership.join, - reason: 'Room should actually be in the join state now.'); - } catch (e, s) { - Logs().e('Test failed', e, s); - rethrow; - } finally { - Logs().i('++++ Logout Alice and Bob ++++'); - if (testClientA?.isLogged() ?? false) await testClientA!.logoutAll(); - if (testClientA?.isLogged() ?? false) await testClientB!.logoutAll(); - await testClientA?.dispose(closeDatabase: false); - await testClientB?.dispose(closeDatabase: false); - testClientA = null; - testClientB = null; - } - return; - }); - }, timeout: Timeout(Duration(minutes: 6))); + "Bob should join alice's DM room instead of creating a new one", + ); + expect( + testClientB.getRoomById(dmRoom)?.membership, + Membership.join, + reason: 'Room should actually be in the join state now.', + ); + expect( + testClientA.getRoomById(dmRoom)?.membership, + Membership.join, + reason: 'Room should actually be in the join state now.', + ); + } catch (e, s) { + Logs().e('Test failed', e, s); + rethrow; + } finally { + Logs().i('++++ Logout Alice and Bob ++++'); + if (testClientA?.isLogged() ?? false) { + await testClientA!.logoutAll(); + } + if (testClientA?.isLogged() ?? false) { + await testClientB!.logoutAll(); + } + await testClientA?.dispose(closeDatabase: false); + await testClientB?.dispose(closeDatabase: false); + testClientA = null; + testClientB = null; + } + return; + }); + }, + timeout: Timeout(Duration(minutes: 6)), + ); Object get olmLengthMatcher { return