Merge branch 'malin/add-type-casts-for-refactored-maps' into 'main'
Add type casts for refactored Maps See merge request famedly/company/frontend/famedlysdk!1313
This commit is contained in:
		
						commit
						69292b1fd1
					
				|  | @ -69,11 +69,14 @@ class KeyManager { | ||||||
|         if (lastEvent != null && |         if (lastEvent != null && | ||||||
|             lastEvent.type == EventTypes.Encrypted && |             lastEvent.type == EventTypes.Encrypted && | ||||||
|             lastEvent.content['can_request_session'] == true) { |             lastEvent.content['can_request_session'] == true) { | ||||||
|           try { |           final sessionId = lastEvent.content.tryGet<String>('session_id'); | ||||||
|             maybeAutoRequest(room.id, lastEvent.content['session_id'], |           final senderKey = lastEvent.content.tryGet<String>('sender_key'); | ||||||
|                 lastEvent.content['sender_key']); |           if (sessionId != null && senderKey != null) { | ||||||
|           } catch (_) { |             maybeAutoRequest( | ||||||
|             // dispose |               room.id, | ||||||
|  |               sessionId, | ||||||
|  |               senderKey, | ||||||
|  |             ); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | @ -638,23 +641,25 @@ class KeyManager { | ||||||
|           final sessionId = sessionEntry.key; |           final sessionId = sessionEntry.key; | ||||||
|           final session = sessionEntry.value; |           final session = sessionEntry.value; | ||||||
|           final sessionData = session.sessionData; |           final sessionData = session.sessionData; | ||||||
|           Map<String, dynamic>? decrypted; |           Map<String, Object?>? decrypted; | ||||||
|           try { |           try { | ||||||
|             decrypted = json.decode(decryption.decrypt(sessionData['ephemeral'], |             decrypted = json.decode(decryption.decrypt( | ||||||
|                 sessionData['mac'], sessionData['ciphertext'])); |                 sessionData['ephemeral'] as String, | ||||||
|  |                 sessionData['mac'] as String, | ||||||
|  |                 sessionData['ciphertext'] as String)); | ||||||
|           } catch (e, s) { |           } catch (e, s) { | ||||||
|             Logs().e('[LibOlm] Error decrypting room key', e, s); |             Logs().e('[LibOlm] Error decrypting room key', e, s); | ||||||
|           } |           } | ||||||
|           if (decrypted != null) { |           final senderKey = decrypted?.tryGet<String>('sender_key'); | ||||||
|  |           if (decrypted != null && senderKey != null) { | ||||||
|             decrypted['session_id'] = sessionId; |             decrypted['session_id'] = sessionId; | ||||||
|             decrypted['room_id'] = roomId; |             decrypted['room_id'] = roomId; | ||||||
|             await setInboundGroupSession( |             await setInboundGroupSession( | ||||||
|                 roomId, sessionId, decrypted['sender_key'], decrypted, |                 roomId, sessionId, senderKey, decrypted, | ||||||
|                 forwarded: true, |                 forwarded: true, | ||||||
|                 senderClaimedKeys: decrypted['sender_claimed_keys'] != null |                 senderClaimedKeys: decrypted | ||||||
|                     ? Map<String, String>.from( |                         .tryGetMap<String, String>('sender_claimed_keys') ?? | ||||||
|                         decrypted['sender_claimed_keys']!) |                     <String, String>{}, | ||||||
|                     : <String, String>{}, |  | ||||||
|                 uploaded: true); |                 uploaded: true); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  | @ -831,13 +836,24 @@ class KeyManager { | ||||||
|         Logs().i( |         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')) { |         if (!event.content.containsKey('body')) { | ||||||
|           Logs().i('[KeyManager] No body, doing nothing'); |           Logs().w('[KeyManager] No body, doing nothing'); | ||||||
|           return; // no body |           return; // no body | ||||||
|         } |         } | ||||||
|  |         final body = event.content.tryGetMap<String, Object?>('body'); | ||||||
|  |         if (body == null) { | ||||||
|  |           Logs().w('[KeyManager] Wrong type for body, doing nothing'); | ||||||
|  |           return; // wrong type for body | ||||||
|  |         } | ||||||
|  |         final roomId = body.tryGet<String>('room_id'); | ||||||
|  |         if (roomId == null) { | ||||||
|  |           Logs().w( | ||||||
|  |               '[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] |         final device = client.userDeviceKeys[event.sender] | ||||||
|             ?.deviceKeys[event.content['requesting_device_id']]; |             ?.deviceKeys[event.content['requesting_device_id']]; | ||||||
|         if (device == null) { |         if (device == null) { | ||||||
|           Logs().i('[KeyManager] Device not found, doing nothing'); |           Logs().w('[KeyManager] Device not found, doing nothing'); | ||||||
|           return; // device not found |           return; // device not found | ||||||
|         } |         } | ||||||
|         if (device.userId == client.userID && |         if (device.userId == client.userID && | ||||||
|  | @ -845,20 +861,30 @@ class KeyManager { | ||||||
|           Logs().i('[KeyManager] Request is by ourself, ignoring'); |           Logs().i('[KeyManager] Request is by ourself, ignoring'); | ||||||
|           return; // ignore requests by ourself |           return; // ignore requests by ourself | ||||||
|         } |         } | ||||||
|         final room = client.getRoomById(event.content['body']['room_id']); |         final room = client.getRoomById(roomId); | ||||||
|         if (room == null) { |         if (room == null) { | ||||||
|           Logs().i('[KeyManager] Unknown room, ignoring'); |           Logs().i('[KeyManager] Unknown room, ignoring'); | ||||||
|           return; // unknown room |           return; // unknown room | ||||||
|         } |         } | ||||||
|         final sessionId = event.content['body']['session_id']; |         final sessionId = body.tryGet<String>('session_id'); | ||||||
|  |         if (sessionId == null) { | ||||||
|  |           Logs().w( | ||||||
|  |               '[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 |         // okay, let's see if we have this session at all | ||||||
|         final session = await loadInboundGroupSession(room.id, sessionId); |         final session = await loadInboundGroupSession(room.id, sessionId); | ||||||
|         if (session == null) { |         if (session == null) { | ||||||
|           Logs().i('[KeyManager] Unknown session, ignoring'); |           Logs().i('[KeyManager] Unknown session, ignoring'); | ||||||
|           return; // we don't have this session anyways |           return; // we don't have this session anyways | ||||||
|         } |         } | ||||||
|  |         if (event.content['request_id'] is! String) { | ||||||
|  |           Logs().w( | ||||||
|  |               '[KeyManager] Wrong type for request_id or no request_id, doing nothing'); | ||||||
|  |           return; // wrong type for request_id | ||||||
|  |         } | ||||||
|         final request = KeyManagerKeyShareRequest( |         final request = KeyManagerKeyShareRequest( | ||||||
|           requestId: event.content['request_id'], |           requestId: event.content.tryGet<String>('request_id')!, | ||||||
|           devices: [device], |           devices: [device], | ||||||
|           room: room, |           room: room, | ||||||
|           sessionId: sessionId, |           sessionId: sessionId, | ||||||
|  | @ -878,7 +904,7 @@ class KeyManager { | ||||||
|           await roomKeyRequest.forwardKey(); |           await roomKeyRequest.forwardKey(); | ||||||
|         } else if (device.encryptToDevice && |         } else if (device.encryptToDevice && | ||||||
|             session.allowedAtIndex |             session.allowedAtIndex | ||||||
|                     .tryGet<Map<String, dynamic>>(device.userId) |                     .tryGet<Map<String, Object?>>(device.userId) | ||||||
|                     ?.tryGet(device.curve25519Key!) != |                     ?.tryGet(device.curve25519Key!) != | ||||||
|                 null) { |                 null) { | ||||||
|           // if we know the user may see the message, then we can just forward the key. |           // if we know the user may see the message, then we can just forward the key. | ||||||
|  | @ -931,15 +957,19 @@ class KeyManager { | ||||||
|       if (event.content['forwarding_curve25519_key_chain'] is! List) { |       if (event.content['forwarding_curve25519_key_chain'] is! List) { | ||||||
|         event.content['forwarding_curve25519_key_chain'] = <String>[]; |         event.content['forwarding_curve25519_key_chain'] = <String>[]; | ||||||
|       } |       } | ||||||
|       event.content['forwarding_curve25519_key_chain'] |       (event.content['forwarding_curve25519_key_chain'] as List) | ||||||
|           .add(encryptedContent['sender_key']); |           .add(encryptedContent['sender_key']); | ||||||
|  |       if (event.content['sender_claimed_ed25519_key'] is! String) { | ||||||
|  |         Logs().w('sender_claimed_ed255519_key has wrong type'); | ||||||
|  |         return; // wrong type | ||||||
|  |       } | ||||||
|       // TODO: verify that the keys work to decrypt a message |       // TODO: verify that the keys work to decrypt a message | ||||||
|       // alright, all checks out, let's go ahead and store this session |       // alright, all checks out, let's go ahead and store this session | ||||||
|       await setInboundGroupSession(request.room.id, request.sessionId, |       await setInboundGroupSession(request.room.id, request.sessionId, | ||||||
|           device.curve25519Key!, event.content, |           device.curve25519Key!, event.content, | ||||||
|           forwarded: true, |           forwarded: true, | ||||||
|           senderClaimedKeys: { |           senderClaimedKeys: { | ||||||
|             'ed25519': event.content['sender_claimed_ed25519_key'], |             'ed25519': event.content['sender_claimed_ed25519_key'] as String, | ||||||
|           }); |           }); | ||||||
|       request.devices.removeWhere( |       request.devices.removeWhere( | ||||||
|           (k) => k.userId == device.userId && k.deviceId == device.deviceId); |           (k) => k.userId == device.userId && k.deviceId == device.deviceId); | ||||||
|  | @ -972,8 +1002,13 @@ class KeyManager { | ||||||
|         Logs().v('[KeyManager] not encrypted, ignoring...'); |         Logs().v('[KeyManager] not encrypted, ignoring...'); | ||||||
|         return; // the event wasn't encrypted, this is a security risk; |         return; // the event wasn't encrypted, this is a security risk; | ||||||
|       } |       } | ||||||
|       final String roomId = event.content['room_id']; |       final roomId = event.content.tryGet<String>('room_id'); | ||||||
|       final String sessionId = event.content['session_id']; |       final sessionId = event.content.tryGet<String>('session_id'); | ||||||
|  |       if (roomId == null || sessionId == null) { | ||||||
|  |         Logs().w( | ||||||
|  |             'Either room_id or session_id are not the expected type or missing'); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|       final sender_ed25519 = client.userDeviceKeys[event.sender] |       final sender_ed25519 = client.userDeviceKeys[event.sender] | ||||||
|           ?.deviceKeys[event.content['requesting_device_id']]?.ed25519Key; |           ?.deviceKeys[event.content['requesting_device_id']]?.ed25519Key; | ||||||
|       if (sender_ed25519 != null) { |       if (sender_ed25519 != null) { | ||||||
|  |  | ||||||
|  | @ -597,11 +597,12 @@ class OlmManager { | ||||||
|             client.userDeviceKeys[userId]!.deviceKeys[deviceId]!.ed25519Key; |             client.userDeviceKeys[userId]!.deviceKeys[deviceId]!.ed25519Key; | ||||||
|         final identityKey = |         final identityKey = | ||||||
|             client.userDeviceKeys[userId]!.deviceKeys[deviceId]!.curve25519Key; |             client.userDeviceKeys[userId]!.deviceKeys[deviceId]!.curve25519Key; | ||||||
|         for (final Map<String, dynamic> deviceKey |         for (final deviceKey in deviceKeysEntry.value.values) { | ||||||
|             in deviceKeysEntry.value.values) { |  | ||||||
|           if (fingerprintKey == null || |           if (fingerprintKey == null || | ||||||
|               identityKey == null || |               identityKey == null || | ||||||
|               !deviceKey.checkJsonSignature(fingerprintKey, userId, deviceId)) { |               deviceKey is! Map<String, Object?> || | ||||||
|  |               !deviceKey.checkJsonSignature(fingerprintKey, userId, deviceId) || | ||||||
|  |               deviceKey['key'] is! String) { | ||||||
|             Logs().w( |             Logs().w( | ||||||
|               'Skipping invalid device key from $userId:$deviceId', |               'Skipping invalid device key from $userId:$deviceId', | ||||||
|               deviceKey, |               deviceKey, | ||||||
|  | @ -612,7 +613,7 @@ class OlmManager { | ||||||
|           final session = olm.Session(); |           final session = olm.Session(); | ||||||
|           try { |           try { | ||||||
|             session.create_outbound( |             session.create_outbound( | ||||||
|                 _olmAccount!, identityKey, deviceKey['key']); |                 _olmAccount!, identityKey, deviceKey.tryGet<String>('key')!); | ||||||
|             await storeOlmSession(OlmSession( |             await storeOlmSession(OlmSession( | ||||||
|               key: client.userID!, |               key: client.userID!, | ||||||
|               identityKey: identityKey, |               identityKey: identityKey, | ||||||
|  |  | ||||||
|  | @ -280,8 +280,7 @@ class SSSS { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   bool isSecret(String type) => |   bool isSecret(String type) => | ||||||
|       client.accountData[type] != null && |       client.accountData[type]?.content['encrypted'] is Map; | ||||||
|       client.accountData[type]!.content['encrypted'] is Map; |  | ||||||
| 
 | 
 | ||||||
|   Future<String?> getCached(String type) async { |   Future<String?> getCached(String type) async { | ||||||
|     if (client.database == null) { |     if (client.database == null) { | ||||||
|  | @ -295,8 +294,11 @@ class SSSS { | ||||||
|     bool isValid(SSSSCache dbEntry) => |     bool isValid(SSSSCache dbEntry) => | ||||||
|         keys.contains(dbEntry.keyId) && |         keys.contains(dbEntry.keyId) && | ||||||
|         dbEntry.ciphertext != null && |         dbEntry.ciphertext != null && | ||||||
|         client.accountData[type]?.content['encrypted'][dbEntry.keyId] |         dbEntry.keyId != null && | ||||||
|                 ['ciphertext'] == |         client.accountData[type]?.content | ||||||
|  |                 .tryGetMap<String, Object?>('encrypted') | ||||||
|  |                 ?.tryGetMap<String, Object?>(dbEntry.keyId!) | ||||||
|  |                 ?.tryGet<String>('ciphertext') == | ||||||
|             dbEntry.ciphertext; |             dbEntry.ciphertext; | ||||||
| 
 | 
 | ||||||
|     final fromCache = _cache[type]; |     final fromCache = _cache[type]; | ||||||
|  | @ -319,20 +321,31 @@ class SSSS { | ||||||
|     if (secretInfo == null) { |     if (secretInfo == null) { | ||||||
|       throw Exception('Not found'); |       throw Exception('Not found'); | ||||||
|     } |     } | ||||||
|     if (secretInfo.content['encrypted'] is! Map) { |     final encryptedContent = | ||||||
|  |         secretInfo.content.tryGetMap<String, Object?>('encrypted'); | ||||||
|  |     if (encryptedContent == null) { | ||||||
|       throw Exception('Content is not encrypted'); |       throw Exception('Content is not encrypted'); | ||||||
|     } |     } | ||||||
|     if (secretInfo.content['encrypted'][keyId] is! Map) { |     final enc = encryptedContent.tryGetMap<String, Object?>(keyId); | ||||||
|  |     if (enc == null) { | ||||||
|       throw Exception('Wrong / unknown key'); |       throw Exception('Wrong / unknown key'); | ||||||
|     } |     } | ||||||
|     final enc = secretInfo.content['encrypted'][keyId]; |     final ciphertext = enc.tryGet<String>('ciphertext'); | ||||||
|  |     final iv = enc.tryGet<String>('iv'); | ||||||
|  |     final mac = enc.tryGet<String>('mac'); | ||||||
|  |     if (ciphertext == null || iv == null || mac == null) { | ||||||
|  |       throw Exception('Wrong types for encrypted content or missing keys.'); | ||||||
|  |     } | ||||||
|     final encryptInfo = EncryptedContent( |     final encryptInfo = EncryptedContent( | ||||||
|         iv: enc['iv'], ciphertext: enc['ciphertext'], mac: enc['mac']); |       iv: iv, | ||||||
|  |       ciphertext: ciphertext, | ||||||
|  |       mac: mac, | ||||||
|  |     ); | ||||||
|     final decrypted = await decryptAes(encryptInfo, key, type); |     final decrypted = await decryptAes(encryptInfo, key, type); | ||||||
|     final db = client.database; |     final db = client.database; | ||||||
|     if (cacheTypes.contains(type) && db != null) { |     if (cacheTypes.contains(type) && db != null) { | ||||||
|       // cache the thing |       // cache the thing | ||||||
|       await db.storeSSSSCache(type, keyId, enc['ciphertext'], decrypted); |       await db.storeSSSSCache(type, keyId, ciphertext, decrypted); | ||||||
|       onSecretStored.add(keyId); |       onSecretStored.add(keyId); | ||||||
|       if (_cacheCallbacks.containsKey(type) && await getCached(type) == null) { |       if (_cacheCallbacks.containsKey(type) && await getCached(type) == null) { | ||||||
|         _cacheCallbacks[type]!(decrypted); |         _cacheCallbacks[type]!(decrypted); | ||||||
|  | @ -382,10 +395,14 @@ class SSSS { | ||||||
|     if (content == null) { |     if (content == null) { | ||||||
|       throw InvalidPassphraseException('Key has no content!'); |       throw InvalidPassphraseException('Key has no content!'); | ||||||
|     } |     } | ||||||
|  |     final encryptedContent = content.tryGetMap<String, Object?>('encrypted'); | ||||||
|  |     if (encryptedContent == null) { | ||||||
|  |       throw Exception('Wrong type for encrypted content!'); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     final otherKeys = |     final otherKeys = | ||||||
|         Set<String>.from(content['encrypted'].keys.where((k) => k != keyId)); |         Set<String>.from(encryptedContent.keys.where((k) => k != keyId)); | ||||||
|     content['encrypted'].removeWhere((k, v) => otherKeys.contains(k)); |     encryptedContent.removeWhere((k, v) => otherKeys.contains(k)); | ||||||
|     // yes, we are paranoid... |     // yes, we are paranoid... | ||||||
|     if (await getStored(type, keyId, key) != secret) { |     if (await getStored(type, keyId, key) != secret) { | ||||||
|       throw Exception('Secrets do not match up!'); |       throw Exception('Secrets do not match up!'); | ||||||
|  | @ -394,8 +411,13 @@ class SSSS { | ||||||
|     await client.setAccountData(client.userID!, type, content); |     await client.setAccountData(client.userID!, type, content); | ||||||
|     if (cacheTypes.contains(type)) { |     if (cacheTypes.contains(type)) { | ||||||
|       // cache the thing |       // cache the thing | ||||||
|       await client.database?.storeSSSSCache( |       final ciphertext = encryptedContent | ||||||
|           type, keyId, content['encrypted'][keyId]['ciphertext'], secret); |           .tryGetMap<String, Object?>(keyId) | ||||||
|  |           ?.tryGet<String>('ciphertext'); | ||||||
|  |       if (ciphertext == null) { | ||||||
|  |         throw Exception('Wrong type for ciphertext!'); | ||||||
|  |       } | ||||||
|  |       await client.database?.storeSSSSCache(type, keyId, ciphertext, secret); | ||||||
|       onSecretStored.add(keyId); |       onSecretStored.add(keyId); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -501,7 +523,11 @@ class SSSS { | ||||||
|         return; // nope....unknown or untrusted device |         return; // nope....unknown or untrusted device | ||||||
|       } |       } | ||||||
|       // alright, all seems fine...let's check if we actually have the secret they are asking for |       // alright, all seems fine...let's check if we actually have the secret they are asking for | ||||||
|       final type = event.content['name']; |       final type = event.content.tryGet<String>('name'); | ||||||
|  |       if (type == null) { | ||||||
|  |         Logs().i('[SSSS] Wrong data type for type param, ignoring'); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|       final secret = await getCached(type); |       final secret = await getCached(type); | ||||||
|       if (secret == null) { |       if (secret == null) { | ||||||
|         Logs() |         Logs() | ||||||
|  | @ -536,8 +562,8 @@ class SSSS { | ||||||
|         Logs().i('[SSSS] Someone else replied?'); |         Logs().i('[SSSS] Someone else replied?'); | ||||||
|         return; // someone replied whom we didn't send the share request to |         return; // someone replied whom we didn't send the share request to | ||||||
|       } |       } | ||||||
|       final secret = event.content['secret']; |       final secret = event.content.tryGet<String>('secret'); | ||||||
|       if (event.content['secret'] is! String) { |       if (secret == null) { | ||||||
|         Logs().i('[SSSS] Secret wasn\'t a string'); |         Logs().i('[SSSS] Secret wasn\'t a string'); | ||||||
|         return; // the secret wasn't a string....wut? |         return; // the secret wasn't a string....wut? | ||||||
|       } |       } | ||||||
|  | @ -557,8 +583,14 @@ class SSSS { | ||||||
|       if (db != null) { |       if (db != null) { | ||||||
|         final keyId = keyIdFromType(request.type); |         final keyId = keyIdFromType(request.type); | ||||||
|         if (keyId != null) { |         if (keyId != null) { | ||||||
|           final ciphertext = client.accountData[request.type]! |           final ciphertext = (client.accountData[request.type]!.content | ||||||
|               .content['encrypted'][keyId]['ciphertext']; |                   .tryGetMap<String, Object?>('encrypted')) | ||||||
|  |               ?.tryGetMap<String, Object?>(keyId) | ||||||
|  |               ?.tryGet<String>('ciphertext'); | ||||||
|  |           if (ciphertext == null) { | ||||||
|  |             Logs().i('[SSSS] Ciphertext is empty or not a String'); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|           await db.storeSSSSCache(request.type, keyId, ciphertext, secret); |           await db.storeSSSSCache(request.type, keyId, ciphertext, secret); | ||||||
|           if (_cacheCallbacks.containsKey(request.type)) { |           if (_cacheCallbacks.containsKey(request.type)) { | ||||||
|             _cacheCallbacks[request.type]!(secret); |             _cacheCallbacks[request.type]!(secret); | ||||||
|  | @ -574,8 +606,10 @@ class SSSS { | ||||||
|     if (data == null) { |     if (data == null) { | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
|     if (data.content['encrypted'] is Map) { |     final contentEncrypted = | ||||||
|       return data.content['encrypted'].keys.toSet(); |         data.content.tryGetMap<String, Object?>('encrypted'); | ||||||
|  |     if (contentEncrypted != null) { | ||||||
|  |       return contentEncrypted.keys.toSet(); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -104,12 +104,14 @@ class Bootstrap { | ||||||
|     for (final entry in client.accountData.entries) { |     for (final entry in client.accountData.entries) { | ||||||
|       final type = entry.key; |       final type = entry.key; | ||||||
|       final event = entry.value; |       final event = entry.value; | ||||||
|       if (event.content['encrypted'] is! Map) { |       final encryptedContent = | ||||||
|  |           event.content.tryGetMap<String, Object?>('encrypted'); | ||||||
|  |       if (encryptedContent == null) { | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
|       final validKeys = <String>{}; |       final validKeys = <String>{}; | ||||||
|       final invalidKeys = <String>{}; |       final invalidKeys = <String>{}; | ||||||
|       for (final keyEntry in event.content['encrypted'].entries) { |       for (final keyEntry in encryptedContent.entries) { | ||||||
|         final key = keyEntry.key; |         final key = keyEntry.key; | ||||||
|         final value = keyEntry.value; |         final value = keyEntry.value; | ||||||
|         if (value is! Map) { |         if (value is! Map) { | ||||||
|  |  | ||||||
|  | @ -47,7 +47,9 @@ extension DehydratedDeviceMatrixApi on MatrixApi { | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
|     return Map<String, int>.from(response['one_time_key_counts']); |     return Map<String, int>.from( | ||||||
|  |         response.tryGetMap<String, Object?>('one_time_key_counts') ?? | ||||||
|  |             <String, int>{}); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// uploads a dehydrated device. |   /// uploads a dehydrated device. | ||||||
|  |  | ||||||
|  | @ -298,7 +298,7 @@ class Client extends MatrixApi { | ||||||
|     final ruleset = TryGetPushRule.tryFromJson( |     final ruleset = TryGetPushRule.tryFromJson( | ||||||
|         _accountData[EventTypes.PushRules] |         _accountData[EventTypes.PushRules] | ||||||
|                 ?.content |                 ?.content | ||||||
|                 .tryGetMap<String, dynamic>('global') ?? |                 .tryGetMap<String, Object?>('global') ?? | ||||||
|             {}); |             {}); | ||||||
|     _pushruleEvaluator = PushruleEvaluator.fromRuleset(ruleset); |     _pushruleEvaluator = PushruleEvaluator.fromRuleset(ruleset); | ||||||
|   } |   } | ||||||
|  | @ -1068,7 +1068,7 @@ class Client extends MatrixApi { | ||||||
|   PushRuleSet? get globalPushRules { |   PushRuleSet? get globalPushRules { | ||||||
|     final pushrules = _accountData['m.push_rules'] |     final pushrules = _accountData['m.push_rules'] | ||||||
|         ?.content |         ?.content | ||||||
|         .tryGetMap<String, dynamic>('global'); |         .tryGetMap<String, Object?>('global'); | ||||||
|     return pushrules != null ? TryGetPushRule.tryFromJson(pushrules) : null; |     return pushrules != null ? TryGetPushRule.tryFromJson(pushrules) : null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1076,7 +1076,7 @@ class Client extends MatrixApi { | ||||||
|   PushRuleSet? get devicePushRules { |   PushRuleSet? get devicePushRules { | ||||||
|     final pushrules = _accountData['m.push_rules'] |     final pushrules = _accountData['m.push_rules'] | ||||||
|         ?.content |         ?.content | ||||||
|         .tryGetMap<String, dynamic>('device'); |         .tryGetMap<String, Object?>('device'); | ||||||
|     return pushrules != null ? TryGetPushRule.tryFromJson(pushrules) : null; |     return pushrules != null ? TryGetPushRule.tryFromJson(pushrules) : null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -2731,12 +2731,15 @@ class Client extends MatrixApi { | ||||||
|   /// Whether all push notifications are muted using the [.m.rule.master] |   /// Whether all push notifications are muted using the [.m.rule.master] | ||||||
|   /// rule of the push rules: https://matrix.org/docs/spec/client_server/r0.6.0#m-rule-master |   /// rule of the push rules: https://matrix.org/docs/spec/client_server/r0.6.0#m-rule-master | ||||||
|   bool get allPushNotificationsMuted { |   bool get allPushNotificationsMuted { | ||||||
|     final Map<String, dynamic>? globalPushRules = |     final Map<String, Object?>? globalPushRules = | ||||||
|         _accountData[EventTypes.PushRules]?.content['global']; |         _accountData[EventTypes.PushRules] | ||||||
|  |             ?.content | ||||||
|  |             .tryGetMap<String, Object?>('global'); | ||||||
|     if (globalPushRules == null) return false; |     if (globalPushRules == null) return false; | ||||||
| 
 | 
 | ||||||
|     if (globalPushRules['override'] is List) { |     final globalPushRulesOverride = globalPushRules.tryGetList('override'); | ||||||
|       for (final pushRule in globalPushRules['override']) { |     if (globalPushRulesOverride != null) { | ||||||
|  |       for (final pushRule in globalPushRulesOverride) { | ||||||
|         if (pushRule['rule_id'] == '.m.rule.master') { |         if (pushRule['rule_id'] == '.m.rule.master') { | ||||||
|           return pushRule['enabled']; |           return pushRule['enabled']; | ||||||
|         } |         } | ||||||
|  | @ -2813,12 +2816,12 @@ class Client extends MatrixApi { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// A list of mxids of users who are ignored. |   /// A list of mxids of users who are ignored. | ||||||
|   List<String> get ignoredUsers => (_accountData |   List<String> get ignoredUsers => | ||||||
|               .containsKey('m.ignored_user_list') && |       List<String>.from(_accountData['m.ignored_user_list'] | ||||||
|           _accountData['m.ignored_user_list']?.content['ignored_users'] is Map) |               ?.content | ||||||
|       ? List<String>.from( |               .tryGetMap<String, Object?>('ignored_users') | ||||||
|           _accountData['m.ignored_user_list']?.content['ignored_users'].keys) |               ?.keys ?? | ||||||
|       : []; |           <String>[]); | ||||||
| 
 | 
 | ||||||
|   /// Ignore another user. This will clear the local cached messages to |   /// Ignore another user. This will clear the local cached messages to | ||||||
|   /// hide all previous messages from this user. |   /// hide all previous messages from this user. | ||||||
|  |  | ||||||
|  | @ -259,7 +259,7 @@ class Event extends MatrixEvent { | ||||||
| 
 | 
 | ||||||
|   String get messageType => type == EventTypes.Sticker |   String get messageType => type == EventTypes.Sticker | ||||||
|       ? MessageTypes.Sticker |       ? MessageTypes.Sticker | ||||||
|       : (content['msgtype'] is String ? content['msgtype'] : MessageTypes.Text); |       : (content.tryGet<String>('msgtype') ?? MessageTypes.Text); | ||||||
| 
 | 
 | ||||||
|   void setRedactionEvent(Event redactedBecause) { |   void setRedactionEvent(Event redactedBecause) { | ||||||
|     unsigned = { |     unsigned = { | ||||||
|  | @ -301,11 +301,10 @@ class Event extends MatrixEvent { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Returns the body of this event if it has a body. |   /// Returns the body of this event if it has a body. | ||||||
|   String get text => content['body'] is String ? content['body'] : ''; |   String get text => content.tryGet<String>('body') ?? ''; | ||||||
| 
 | 
 | ||||||
|   /// Returns the formatted boy of this event if it has a formatted body. |   /// Returns the formatted boy of this event if it has a formatted body. | ||||||
|   String get formattedText => |   String get formattedText => content.tryGet<String>('formatted_body') ?? ''; | ||||||
|       content['formatted_body'] is String ? content['formatted_body'] : ''; |  | ||||||
| 
 | 
 | ||||||
|   /// Use this to get the body. |   /// Use this to get the body. | ||||||
|   String get body { |   String get body { | ||||||
|  | @ -402,7 +401,7 @@ class Event extends MatrixEvent { | ||||||
|     // in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2 |     // in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2 | ||||||
|     return await room.sendEvent( |     return await room.sendEvent( | ||||||
|       content, |       content, | ||||||
|       txid: txid ?? unsigned?['transaction_id'] ?? eventId, |       txid: txid ?? unsigned?.tryGet<String>('transaction_id') ?? eventId, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -432,13 +431,19 @@ class Event extends MatrixEvent { | ||||||
|         content['can_request_session'] != true) { |         content['can_request_session'] != true) { | ||||||
|       throw ('Session key not requestable'); |       throw ('Session key not requestable'); | ||||||
|     } |     } | ||||||
|     await room.requestSessionKey(content['session_id'], content['sender_key']); | 
 | ||||||
|  |     final sessionId = content.tryGet<String>('session_id'); | ||||||
|  |     final senderKey = content.tryGet<String>('sender_key'); | ||||||
|  |     if (sessionId == null || senderKey == null) { | ||||||
|  |       throw ('Unknown session_id or sender_key'); | ||||||
|  |     } | ||||||
|  |     await room.requestSessionKey(sessionId, senderKey); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Gets the info map of file events, or a blank map if none present |   /// Gets the info map of file events, or a blank map if none present | ||||||
|   Map get infoMap => |   Map get infoMap => | ||||||
|       content['info'] is Map ? content['info'] : <String, dynamic>{}; |       content.tryGetMap<String, Object?>('info') ?? <String, Object?>{}; | ||||||
| 
 | 
 | ||||||
|   /// Gets the thumbnail info map of file events, or a blank map if nonepresent |   /// Gets the thumbnail info map of file events, or a blank map if nonepresent | ||||||
|   Map get thumbnailInfoMap => infoMap['thumbnail_info'] is Map |   Map get thumbnailInfoMap => infoMap['thumbnail_info'] is Map | ||||||
|  | @ -461,9 +466,10 @@ class Event extends MatrixEvent { | ||||||
|   /// Gets the mimetype of the attachment of a file event, or a blank string if not present |   /// Gets the mimetype of the attachment of a file event, or a blank string if not present | ||||||
|   String get attachmentMimetype => infoMap['mimetype'] is String |   String get attachmentMimetype => infoMap['mimetype'] is String | ||||||
|       ? infoMap['mimetype'].toLowerCase() |       ? infoMap['mimetype'].toLowerCase() | ||||||
|       : (content['file'] is Map && content['file']['mimetype'] is String |       : (content | ||||||
|           ? content['file']['mimetype'] |               .tryGetMap<String, Object?>('file') | ||||||
|           : ''); |               ?.tryGet<String>('mimetype') ?? | ||||||
|  |           ''); | ||||||
| 
 | 
 | ||||||
|   /// Gets the mimetype of the thumbnail of a file event, or a blank string if not present |   /// Gets the mimetype of the thumbnail of a file event, or a blank string if not present | ||||||
|   String get thumbnailMimetype => thumbnailInfoMap['mimetype'] is String |   String get thumbnailMimetype => thumbnailInfoMap['mimetype'] is String | ||||||
|  | @ -475,7 +481,9 @@ class Event extends MatrixEvent { | ||||||
| 
 | 
 | ||||||
|   /// Gets the underlying mxc url of an attachment of a file event, or null if not present |   /// Gets the underlying mxc url of an attachment of a file event, or null if not present | ||||||
|   Uri? get attachmentMxcUrl { |   Uri? get attachmentMxcUrl { | ||||||
|     final url = isAttachmentEncrypted ? content['file']['url'] : content['url']; |     final url = isAttachmentEncrypted | ||||||
|  |         ? (content.tryGetMap<String, Object?>('file')?['url']) | ||||||
|  |         : content['url']; | ||||||
|     return url is String ? Uri.tryParse(url) : null; |     return url is String ? Uri.tryParse(url) : null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -759,21 +767,17 @@ class Event extends MatrixEvent { | ||||||
|     // if we need to strip the reply fallback. |     // if we need to strip the reply fallback. | ||||||
|     var htmlMessage = content['format'] != 'org.matrix.custom.html'; |     var htmlMessage = content['format'] != 'org.matrix.custom.html'; | ||||||
|     // If we have an edit, we want to operate on the new content |     // If we have an edit, we want to operate on the new content | ||||||
|  |     final newContent = content.tryGetMap<String, Object?>('m.new_content'); | ||||||
|     if (hideEdit && |     if (hideEdit && | ||||||
|         relationshipType == RelationshipTypes.edit && |         relationshipType == RelationshipTypes.edit && | ||||||
|         content.tryGet<Map<String, dynamic>>('m.new_content') != null) { |         newContent != null) { | ||||||
|       if (plaintextBody && |       if (plaintextBody && newContent['format'] == 'org.matrix.custom.html') { | ||||||
|           content['m.new_content']['format'] == 'org.matrix.custom.html') { |  | ||||||
|         htmlMessage = true; |         htmlMessage = true; | ||||||
|         body = HtmlToText.convert( |         body = HtmlToText.convert( | ||||||
|             (content['m.new_content'] as Map<String, dynamic>) |             newContent.tryGet<String>('formatted_body') ?? formattedText); | ||||||
|                     .tryGet<String>('formatted_body') ?? |  | ||||||
|                 formattedText); |  | ||||||
|       } else { |       } else { | ||||||
|         htmlMessage = false; |         htmlMessage = false; | ||||||
|         body = (content['m.new_content'] as Map<String, dynamic>) |         body = newContent.tryGet<String>('body') ?? body; | ||||||
|                 .tryGet<String>('body') ?? |  | ||||||
|             body; |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     // Hide reply fallback |     // Hide reply fallback | ||||||
|  | @ -815,31 +819,27 @@ class Event extends MatrixEvent { | ||||||
| 
 | 
 | ||||||
|   /// Get the relationship type of an event. `null` if there is none |   /// Get the relationship type of an event. `null` if there is none | ||||||
|   String? get relationshipType { |   String? get relationshipType { | ||||||
|     if (content.tryGet<Map<String, dynamic>>('m.relates_to') == null) { |     final mRelatesTo = content.tryGetMap<String, Object?>('m.relates_to'); | ||||||
|  |     if (mRelatesTo == null) { | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
|     if (content['m.relates_to'].containsKey('rel_type')) { |     final relType = mRelatesTo.tryGet<String>('rel_type'); | ||||||
|       if (content |     if (relType == RelationshipTypes.thread) { | ||||||
|               .tryGet<Map<String, dynamic>>('m.relates_to') |  | ||||||
|               ?.tryGet<String>('rel_type') == |  | ||||||
|           RelationshipTypes.thread) { |  | ||||||
|       return RelationshipTypes.thread; |       return RelationshipTypes.thread; | ||||||
|     } |     } | ||||||
|     } | 
 | ||||||
|     if (content['m.relates_to'].containsKey('m.in_reply_to')) { |     if (mRelatesTo.containsKey('m.in_reply_to')) { | ||||||
|       return RelationshipTypes.reply; |       return RelationshipTypes.reply; | ||||||
|     } |     } | ||||||
|     return content |     return relType; | ||||||
|         .tryGet<Map<String, dynamic>>('m.relates_to') |  | ||||||
|         ?.tryGet<String>('rel_type'); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Get the event ID that this relationship will reference. `null` if there is none |   /// Get the event ID that this relationship will reference. `null` if there is none | ||||||
|   String? get relationshipEventId { |   String? get relationshipEventId { | ||||||
|     final relatesToMap = content.tryGetMap<String, dynamic>('m.relates_to'); |     final relatesToMap = content.tryGetMap<String, Object?>('m.relates_to'); | ||||||
|     return relatesToMap?.tryGet<String>('event_id') ?? |     return relatesToMap?.tryGet<String>('event_id') ?? | ||||||
|         relatesToMap |         relatesToMap | ||||||
|             ?.tryGetMap<String, dynamic>('m.in_reply_to') |             ?.tryGetMap<String, Object?>('m.in_reply_to') | ||||||
|             ?.tryGet<String>('event_id'); |             ?.tryGet<String>('event_id'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -267,9 +267,10 @@ class Room { | ||||||
|     } |     } | ||||||
|     if (membership == Membership.leave) { |     if (membership == Membership.leave) { | ||||||
|       final invitation = getState(EventTypes.RoomMember, client.userID!); |       final invitation = getState(EventTypes.RoomMember, client.userID!); | ||||||
|       if (invitation != null && invitation.unsigned?['prev_sender'] != null) { |       if (invitation != null && | ||||||
|  |           invitation.unsigned?.tryGet<String>('prev_sender') != null) { | ||||||
|         final name = unsafeGetUserFromMemoryOrFallback( |         final name = unsafeGetUserFromMemoryOrFallback( | ||||||
|                 invitation.unsigned?['prev_sender']) |                 invitation.unsigned!.tryGet<String>('prev_sender')!) | ||||||
|             .calcDisplayname(i18n: i18n); |             .calcDisplayname(i18n: i18n); | ||||||
|         return i18n.wasDirectChatDisplayName(name); |         return i18n.wasDirectChatDisplayName(name); | ||||||
|       } |       } | ||||||
|  | @ -1315,8 +1316,9 @@ class Room { | ||||||
|   Future<void> removeFromDirectChat() async { |   Future<void> removeFromDirectChat() async { | ||||||
|     final directChats = client.directChats.copy(); |     final directChats = client.directChats.copy(); | ||||||
|     for (final k in directChats.keys) { |     for (final k in directChats.keys) { | ||||||
|       if (directChats[k] is List && directChats[k].contains(id)) { |       final directChat = directChats[k]; | ||||||
|         directChats[k].remove(id); |       if (directChat is List && directChat.contains(id)) { | ||||||
|  |         directChat.remove(id); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1747,7 +1749,7 @@ class Room { | ||||||
|       return getState(EventTypes.RoomCreate)?.senderId == userId ? 100 : 0; |       return getState(EventTypes.RoomCreate)?.senderId == userId ? 100 : 0; | ||||||
|     } |     } | ||||||
|     return powerLevelMap |     return powerLevelMap | ||||||
|             .tryGetMap<String, dynamic>('users') |             .tryGetMap<String, Object?>('users') | ||||||
|             ?.tryGet<int>(userId) ?? |             ?.tryGet<int>(userId) ?? | ||||||
|         powerLevelMap.tryGet<int>('users_default') ?? |         powerLevelMap.tryGet<int>('users_default') ?? | ||||||
|         0; |         0; | ||||||
|  | @ -1802,7 +1804,7 @@ class Room { | ||||||
|     final powerLevelMap = getState(EventTypes.RoomPowerLevels)?.content; |     final powerLevelMap = getState(EventTypes.RoomPowerLevels)?.content; | ||||||
|     if (powerLevelMap == null) return 0; |     if (powerLevelMap == null) return 0; | ||||||
|     return powerLevelMap |     return powerLevelMap | ||||||
|             .tryGetMap<String, dynamic>('events') |             .tryGetMap<String, Object?>('events') | ||||||
|             ?.tryGet<int>(action) ?? |             ?.tryGet<int>(action) ?? | ||||||
|         powerLevelMap.tryGet<int>('state_default') ?? |         powerLevelMap.tryGet<int>('state_default') ?? | ||||||
|         50; |         50; | ||||||
|  | @ -1833,7 +1835,8 @@ class Room { | ||||||
|     final currentPowerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; |     final currentPowerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; | ||||||
|     if (currentPowerLevelsMap != null) { |     if (currentPowerLevelsMap != null) { | ||||||
|       final newPowerLevelMap = currentPowerLevelsMap; |       final newPowerLevelMap = currentPowerLevelsMap; | ||||||
|       final eventsMap = newPowerLevelMap['events'] ?? {}; |       final eventsMap = newPowerLevelMap.tryGetMap<String, Object?>('events') ?? | ||||||
|  |           <String, Object?>{}; | ||||||
|       eventsMap.addAll({ |       eventsMap.addAll({ | ||||||
|         EventTypes.GroupCallPrefix: getDefaultPowerLevel(currentPowerLevelsMap), |         EventTypes.GroupCallPrefix: getDefaultPowerLevel(currentPowerLevelsMap), | ||||||
|         EventTypes.GroupCallMemberPrefix: |         EventTypes.GroupCallMemberPrefix: | ||||||
|  | @ -1901,7 +1904,7 @@ class Room { | ||||||
|     final powerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; |     final powerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; | ||||||
|     if (powerLevelsMap == null) return 0 <= ownPowerLevel; |     if (powerLevelsMap == null) return 0 <= ownPowerLevel; | ||||||
|     final pl = powerLevelsMap |     final pl = powerLevelsMap | ||||||
|             .tryGetMap<String, dynamic>('events') |             .tryGetMap<String, Object?>('events') | ||||||
|             ?.tryGet<int>(eventType) ?? |             ?.tryGet<int>(eventType) ?? | ||||||
|         powerLevelsMap.tryGet<int>('events_default') ?? |         powerLevelsMap.tryGet<int>('events_default') ?? | ||||||
|         0; |         0; | ||||||
|  | @ -1913,7 +1916,7 @@ class Room { | ||||||
|     final userLevel = getPowerLevelByUserId(userid); |     final userLevel = getPowerLevelByUserId(userid); | ||||||
|     final notificationLevel = getState(EventTypes.RoomPowerLevels) |     final notificationLevel = getState(EventTypes.RoomPowerLevels) | ||||||
|             ?.content |             ?.content | ||||||
|             .tryGetMap<String, dynamic>('notifications') |             .tryGetMap<String, Object?>('notifications') | ||||||
|             ?.tryGet<int>(notificationType) ?? |             ?.tryGet<int>(notificationType) ?? | ||||||
|         50; |         50; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -347,16 +347,16 @@ class Timeline { | ||||||
|       if (event.type == EventTypes.Encrypted && |       if (event.type == EventTypes.Encrypted && | ||||||
|           event.messageType == MessageTypes.BadEncrypted && |           event.messageType == MessageTypes.BadEncrypted && | ||||||
|           event.content['can_request_session'] == true) { |           event.content['can_request_session'] == true) { | ||||||
|         try { |         final sessionId = event.content.tryGet<String>('session_id'); | ||||||
|  |         final senderKey = event.content.tryGet<String>('sender_key'); | ||||||
|  |         if (sessionId != null && senderKey != null) { | ||||||
|           room.client.encryption?.keyManager.maybeAutoRequest( |           room.client.encryption?.keyManager.maybeAutoRequest( | ||||||
|             room.id, |             room.id, | ||||||
|             event.content['session_id'], |             sessionId, | ||||||
|             event.content['sender_key'], |             senderKey, | ||||||
|             tryOnlineBackup: tryOnlineBackup, |             tryOnlineBackup: tryOnlineBackup, | ||||||
|             onlineKeyBackupOnly: onlineKeyBackupOnly, |             onlineKeyBackupOnly: onlineKeyBackupOnly, | ||||||
|           ); |           ); | ||||||
|         } catch (_) { |  | ||||||
|           // dispose |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -387,7 +387,7 @@ class Timeline { | ||||||
|     for (i = 0; i < events.length; i++) { |     for (i = 0; i < events.length; i++) { | ||||||
|       final searchHaystack = <String>{events[i].eventId}; |       final searchHaystack = <String>{events[i].eventId}; | ||||||
| 
 | 
 | ||||||
|       final txnid = events[i].unsigned?['transaction_id']; |       final txnid = events[i].unsigned?.tryGet<String>('transaction_id'); | ||||||
|       if (txnid != null) { |       if (txnid != null) { | ||||||
|         searchHaystack.add(txnid); |         searchHaystack.add(txnid); | ||||||
|       } |       } | ||||||
|  | @ -401,8 +401,9 @@ class Timeline { | ||||||
|   void _removeEventFromSet(Set<Event> eventSet, Event event) { |   void _removeEventFromSet(Set<Event> eventSet, Event event) { | ||||||
|     eventSet.removeWhere((e) => |     eventSet.removeWhere((e) => | ||||||
|         e.matchesEventOrTransactionId(event.eventId) || |         e.matchesEventOrTransactionId(event.eventId) || | ||||||
|         (event.unsigned != null && |         event.unsigned != null && | ||||||
|             e.matchesEventOrTransactionId(event.unsigned?['transaction_id']))); |             e.matchesEventOrTransactionId( | ||||||
|  |                 event.unsigned?.tryGet<String>('transaction_id'))); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void addAggregatedEvent(Event event) { |   void addAggregatedEvent(Event event) { | ||||||
|  |  | ||||||
|  | @ -275,7 +275,7 @@ abstract class SignableKey extends MatrixSignableKey { | ||||||
|         var haveValidSignature = false; |         var haveValidSignature = false; | ||||||
|         var gotSignatureFromCache = false; |         var gotSignatureFromCache = false; | ||||||
|         final fullKeyIdBool = validSignatures |         final fullKeyIdBool = validSignatures | ||||||
|             ?.tryGetMap<String, dynamic>(otherUserId) |             ?.tryGetMap<String, Object?>(otherUserId) | ||||||
|             ?.tryGet<bool>(fullKeyId); |             ?.tryGet<bool>(fullKeyId); | ||||||
|         if (fullKeyIdBool == true) { |         if (fullKeyIdBool == true) { | ||||||
|           haveValidSignature = true; |           haveValidSignature = true; | ||||||
|  | @ -426,14 +426,15 @@ class DeviceKeys extends SignableKey { | ||||||
|   late DateTime lastActive; |   late DateTime lastActive; | ||||||
| 
 | 
 | ||||||
|   String? get curve25519Key => keys['curve25519:$deviceId']; |   String? get curve25519Key => keys['curve25519:$deviceId']; | ||||||
|   String? get deviceDisplayName => unsigned?['device_display_name']; |   String? get deviceDisplayName => | ||||||
|  |       unsigned?.tryGet<String>('device_display_name'); | ||||||
| 
 | 
 | ||||||
|   bool? _validSelfSignature; |   bool? _validSelfSignature; | ||||||
|   bool get selfSigned => |   bool get selfSigned => | ||||||
|       _validSelfSignature ?? |       _validSelfSignature ?? | ||||||
|       (_validSelfSignature = (deviceId != null && |       (_validSelfSignature = (deviceId != null && | ||||||
|               signatures |               signatures | ||||||
|                       ?.tryGetMap<String, dynamic>(userId) |                       ?.tryGetMap<String, Object?>(userId) | ||||||
|                       ?.tryGet<String>('ed25519:$deviceId') == |                       ?.tryGet<String>('ed25519:$deviceId') == | ||||||
|                   null |                   null | ||||||
|           ? false |           ? false | ||||||
|  |  | ||||||
|  | @ -204,11 +204,11 @@ abstract class EventLocalizations { | ||||||
|             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), |             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), | ||||||
|     EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo( |     EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo( | ||||||
|         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), |         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), | ||||||
|         event.content['name']), |         event.content.tryGet<String>('name') ?? ''), | ||||||
|     EventTypes.RoomTopic: (event, i18n, body) => |     EventTypes.RoomTopic: (event, i18n, body) => | ||||||
|         i18n.changedTheChatDescriptionTo( |         i18n.changedTheChatDescriptionTo( | ||||||
|             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), |             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), | ||||||
|             event.content['topic']), |             event.content.tryGet<String>('topic') ?? ''), | ||||||
|     EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar( |     EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar( | ||||||
|         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), |         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), | ||||||
|     EventTypes.GuestAccess: (event, i18n, body) { |     EventTypes.GuestAccess: (event, i18n, body) { | ||||||
|  | @ -260,7 +260,7 @@ abstract class EventLocalizations { | ||||||
|     EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction( |     EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction( | ||||||
|           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), |           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), | ||||||
|           event.content |           event.content | ||||||
|                   .tryGetMap<String, dynamic>('m.relates_to') |                   .tryGetMap<String, Object?>('m.relates_to') | ||||||
|                   ?.tryGet<String>('key') ?? |                   ?.tryGet<String>('key') ?? | ||||||
|               body, |               body, | ||||||
|         ), |         ), | ||||||
|  |  | ||||||
|  | @ -60,12 +60,14 @@ extension ImagePackRoomExtension on Room { | ||||||
|     addImagePack(client.accountData['im.ponies.user_emotes'], slug: 'user'); |     addImagePack(client.accountData['im.ponies.user_emotes'], slug: 'user'); | ||||||
|     // next we add all the external image packs |     // next we add all the external image packs | ||||||
|     final packRooms = client.accountData['im.ponies.emote_rooms']; |     final packRooms = client.accountData['im.ponies.emote_rooms']; | ||||||
|     if (packRooms != null && packRooms.content['rooms'] is Map) { |     final rooms = packRooms?.content.tryGetMap<String, Object?>('rooms'); | ||||||
|       for (final roomEntry in packRooms.content['rooms'].entries) { |     if (packRooms != null && rooms != null) { | ||||||
|  |       for (final roomEntry in rooms.entries) { | ||||||
|         final roomId = roomEntry.key; |         final roomId = roomEntry.key; | ||||||
|         final room = client.getRoomById(roomId); |         final room = client.getRoomById(roomId); | ||||||
|         if (room != null && roomEntry.value is Map) { |         final roomEntryValue = roomEntry.value; | ||||||
|           for (final stateKeyEntry in roomEntry.value.entries) { |         if (room != null && roomEntryValue is Map<String, Object?>) { | ||||||
|  |           for (final stateKeyEntry in roomEntryValue.entries) { | ||||||
|             final stateKey = stateKeyEntry.key; |             final stateKey = stateKeyEntry.key; | ||||||
|             final fallbackSlug = |             final fallbackSlug = | ||||||
|                 '${room.getLocalizedDisplayname()}-${stateKey.isNotEmpty ? '$stateKey-' : ''}${room.id}'; |                 '${room.getLocalizedDisplayname()}-${stateKey.isNotEmpty ? '$stateKey-' : ''}${room.id}'; | ||||||
|  |  | ||||||
|  | @ -704,16 +704,18 @@ class VoIP { | ||||||
| 
 | 
 | ||||||
|     final groupCallId = event.stateKey; |     final groupCallId = event.stateKey; | ||||||
| 
 | 
 | ||||||
|     final callType = content['m.type']; |     final callType = content.tryGet<String>('m.type'); | ||||||
| 
 | 
 | ||||||
|     if (callType != GroupCallType.Video && callType != GroupCallType.Voice) { |     if (callType == null || | ||||||
|  |         callType != GroupCallType.Video && callType != GroupCallType.Voice) { | ||||||
|       Logs().w('Received invalid group call type $callType for room $roomId.'); |       Logs().w('Received invalid group call type $callType for room $roomId.'); | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     final callIntent = content['m.intent']; |     final callIntent = content.tryGet<String>('m.intent'); | ||||||
| 
 | 
 | ||||||
|     if (callIntent != GroupCallIntent.Prompt && |     if (callIntent == null || | ||||||
|  |         callIntent != GroupCallIntent.Prompt && | ||||||
|             callIntent != GroupCallIntent.Room && |             callIntent != GroupCallIntent.Room && | ||||||
|             callIntent != GroupCallIntent.Ring) { |             callIntent != GroupCallIntent.Ring) { | ||||||
|       Logs() |       Logs() | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ dependencies: | ||||||
|   image: ^4.0.15 |   image: ^4.0.15 | ||||||
|   js: ^0.6.3 |   js: ^0.6.3 | ||||||
|   markdown: ^4.0.0 |   markdown: ^4.0.0 | ||||||
|   matrix_api_lite: ">=1.6.0 <1.7.0" # Pinned until compatible with 1.7.x |   matrix_api_lite: ^1.7.0 | ||||||
|   mime: ^1.0.0 |   mime: ^1.0.0 | ||||||
|   olm: ^2.0.2 |   olm: ^2.0.2 | ||||||
|   random_string: ^2.3.1 |   random_string: ^2.3.1 | ||||||
|  | @ -38,3 +38,7 @@ dev_dependencies: | ||||||
|   lints: ^2.0.0 |   lints: ^2.0.0 | ||||||
|   test: ^1.15.7 |   test: ^1.15.7 | ||||||
|   #flutter_test: {sdk: flutter} |   #flutter_test: {sdk: flutter} | ||||||
|  | 
 | ||||||
|  | #dependency_overrides: | ||||||
|  | #  matrix_api_lite: | ||||||
|  | #    path: ../matrix_api_lite | ||||||
|  |  | ||||||
|  | @ -1042,8 +1042,10 @@ void main() { | ||||||
| 
 | 
 | ||||||
|     test('pushRuleState', () async { |     test('pushRuleState', () async { | ||||||
|       expect(room.pushRuleState, PushRuleState.mentionsOnly); |       expect(room.pushRuleState, PushRuleState.mentionsOnly); | ||||||
|       matrix.accountData['m.push_rules']?.content['global']['override'].add( |       ((matrix.accountData['m.push_rules']?.content['global'] | ||||||
|           matrix.accountData['m.push_rules']?.content['global']['room'][0]); |               as Map<String, Object?>)['override'] as List) | ||||||
|  |           .add(((matrix.accountData['m.push_rules']?.content['global'] | ||||||
|  |               as Map<String, Object?>)['room'] as List)[0]); | ||||||
|       expect(room.pushRuleState, PushRuleState.dontNotify); |       expect(room.pushRuleState, PushRuleState.dontNotify); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue