From 5ec745915e23f37fd127c01adcefb3d2d76fbd75 Mon Sep 17 00:00:00 2001 From: Krille Date: Thu, 8 May 2025 14:23:29 +0200 Subject: [PATCH] refactor: (BREAKING) Remove hive database and hive dependencies --- lib/matrix.dart | 1 - .../database/hive_collections_database.dart | 1802 ----------------- lib/src/database/matrix_sdk_database.dart | 25 + pubspec.yaml | 1 - test/database_api_test.dart | 24 +- test/fake_database.dart | 21 - test_driver/matrixsdk_test.dart | 5 - 7 files changed, 33 insertions(+), 1846 deletions(-) delete mode 100644 lib/src/database/hive_collections_database.dart diff --git a/lib/matrix.dart b/lib/matrix.dart index bf31f0c1..81477825 100644 --- a/lib/matrix.dart +++ b/lib/matrix.dart @@ -26,7 +26,6 @@ export 'fake_matrix_api.dart' show FakeMatrixApi; export 'src/client.dart'; export 'src/database/database_api.dart'; export 'src/database/matrix_sdk_database.dart'; -export 'src/database/hive_collections_database.dart'; export 'src/database/sqflite_encryption_helper.dart'; export 'src/event.dart'; export 'src/presence.dart'; diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart deleted file mode 100644 index cdc57c4e..00000000 --- a/lib/src/database/hive_collections_database.dart +++ /dev/null @@ -1,1802 +0,0 @@ -/* - * Famedly Matrix SDK - * Copyright (C) 2019, 2020, 2021 Famedly GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import 'dart:async'; -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:collection/collection.dart'; -import 'package:hive/hive.dart'; - -import 'package:matrix/encryption/utils/olm_session.dart'; -import 'package:matrix/encryption/utils/outbound_group_session.dart'; -import 'package:matrix/encryption/utils/ssss_cache.dart'; -import 'package:matrix/encryption/utils/stored_inbound_group_session.dart'; -import 'package:matrix/matrix.dart'; -import 'package:matrix/src/utils/copy_map.dart'; -import 'package:matrix/src/utils/queued_to_device_event.dart'; -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!', -) -class HiveCollectionsDatabase extends DatabaseApi { - static const int version = 7; - final String name; - final String? path; - final HiveCipher? key; - final Future Function( - String name, - Set boxNames, { - String? path, - HiveCipher? key, - }) collectionFactory; - late BoxCollection _collection; - late CollectionBox _clientBox; - late CollectionBox _accountDataBox; - late CollectionBox _roomsBox; - late CollectionBox _toDeviceQueueBox; - - /// Key is a tuple as TupleKey(roomId, type) where stateKey can be - /// an empty string. - late CollectionBox _roomStateBox; - - /// Key is a tuple as TupleKey(roomId, userId) - late CollectionBox _roomMembersBox; - - /// Key is a tuple as TupleKey(roomId, type) - late CollectionBox _roomAccountDataBox; - late CollectionBox _inboundGroupSessionsBox; - late CollectionBox _outboundGroupSessionsBox; - late CollectionBox _olmSessionsBox; - - /// Key is a tuple as TupleKey(userId, deviceId) - late CollectionBox _userDeviceKeysBox; - - /// Key is the user ID as a String - late CollectionBox _userDeviceKeysOutdatedBox; - - /// Key is a tuple as TupleKey(userId, publicKey) - late CollectionBox _userCrossSigningKeysBox; - late CollectionBox _ssssCacheBox; - late CollectionBox _presencesBox; - - /// Key is a tuple as Multikey(roomId, fragmentId) while the default - /// fragmentId is an empty String - late CollectionBox _timelineFragmentsBox; - - /// Key is a tuple as TupleKey(roomId, eventId) - late CollectionBox _eventsBox; - - /// Key is a tuple as TupleKey(userId, deviceId) - late CollectionBox _seenDeviceIdsBox; - - late CollectionBox _seenDeviceKeysBox; - - String get _clientBoxName => 'box_client'; - - String get _accountDataBoxName => 'box_account_data'; - - String get _roomsBoxName => 'box_rooms'; - - String get _toDeviceQueueBoxName => 'box_to_device_queue'; - - String get _roomStateBoxName => 'box_room_states'; - - String get _roomMembersBoxName => 'box_room_members'; - - String get _roomAccountDataBoxName => 'box_room_account_data'; - - String get _inboundGroupSessionsBoxName => 'box_inbound_group_session'; - - String get _outboundGroupSessionsBoxName => 'box_outbound_group_session'; - - String get _olmSessionsBoxName => 'box_olm_session'; - - String get _userDeviceKeysBoxName => 'box_user_device_keys'; - - String get _userDeviceKeysOutdatedBoxName => 'box_user_device_keys_outdated'; - - String get _userCrossSigningKeysBoxName => 'box_cross_signing_keys'; - - String get _ssssCacheBoxName => 'box_ssss_cache'; - - String get _presencesBoxName => 'box_presences'; - - String get _timelineFragmentsBoxName => 'box_timeline_fragments'; - - String get _eventsBoxName => 'box_events'; - - String get _seenDeviceIdsBoxName => 'box_seen_device_ids'; - - String get _seenDeviceKeysBoxName => 'box_seen_device_keys'; - - HiveCollectionsDatabase( - this.name, - this.path, { - this.key, - this.collectionFactory = BoxCollection.open, - }); - - @override - int get maxFileSize => 0; - - Future open() async { - _collection = await collectionFactory( - name, - { - _clientBoxName, - _accountDataBoxName, - _roomsBoxName, - _toDeviceQueueBoxName, - _roomStateBoxName, - _roomMembersBoxName, - _roomAccountDataBoxName, - _inboundGroupSessionsBoxName, - _outboundGroupSessionsBoxName, - _olmSessionsBoxName, - _userDeviceKeysBoxName, - _userDeviceKeysOutdatedBoxName, - _userCrossSigningKeysBoxName, - _ssssCacheBoxName, - _presencesBoxName, - _timelineFragmentsBoxName, - _eventsBoxName, - _seenDeviceIdsBoxName, - _seenDeviceKeysBoxName, - }, - key: key, - path: path, - ); - _clientBox = await _collection.openBox( - _clientBoxName, - preload: true, - ); - _accountDataBox = await _collection.openBox( - _accountDataBoxName, - preload: true, - ); - _roomsBox = await _collection.openBox( - _roomsBoxName, - preload: true, - ); - _roomStateBox = await _collection.openBox( - _roomStateBoxName, - ); - _roomMembersBox = await _collection.openBox( - _roomMembersBoxName, - ); - _toDeviceQueueBox = await _collection.openBox( - _toDeviceQueueBoxName, - preload: true, - ); - _roomAccountDataBox = await _collection.openBox( - _roomAccountDataBoxName, - preload: true, - ); - _inboundGroupSessionsBox = await _collection.openBox( - _inboundGroupSessionsBoxName, - ); - _outboundGroupSessionsBox = await _collection.openBox( - _outboundGroupSessionsBoxName, - ); - _olmSessionsBox = await _collection.openBox( - _olmSessionsBoxName, - ); - _userDeviceKeysBox = await _collection.openBox( - _userDeviceKeysBoxName, - ); - _userDeviceKeysOutdatedBox = await _collection.openBox( - _userDeviceKeysOutdatedBoxName, - ); - _userCrossSigningKeysBox = await _collection.openBox( - _userCrossSigningKeysBoxName, - ); - _ssssCacheBox = await _collection.openBox( - _ssssCacheBoxName, - ); - _presencesBox = await _collection.openBox( - _presencesBoxName, - ); - _timelineFragmentsBox = await _collection.openBox( - _timelineFragmentsBoxName, - ); - _eventsBox = await _collection.openBox( - _eventsBoxName, - ); - _seenDeviceIdsBox = await _collection.openBox( - _seenDeviceIdsBoxName, - ); - _seenDeviceKeysBox = await _collection.openBox( - _seenDeviceKeysBoxName, - ); - - // Check version and check if we need a migration - final currentVersion = int.tryParse(await _clientBox.get('version') ?? ''); - if (currentVersion == null) { - await _clientBox.put('version', version.toString()); - } else if (currentVersion != version) { - await _migrateFromVersion(currentVersion); - } - - return; - } - - Future _migrateFromVersion(int currentVersion) async { - Logs().i('Migrate store database from version $currentVersion to $version'); - await clearCache(); - await _clientBox.put('version', version.toString()); - } - - @override - Future clear() => transaction(() async { - await _clientBox.clear(); - await _accountDataBox.clear(); - await _roomsBox.clear(); - await _roomStateBox.clear(); - await _roomMembersBox.clear(); - await _toDeviceQueueBox.clear(); - await _roomAccountDataBox.clear(); - await _inboundGroupSessionsBox.clear(); - await _outboundGroupSessionsBox.clear(); - await _olmSessionsBox.clear(); - await _userDeviceKeysBox.clear(); - await _userDeviceKeysOutdatedBox.clear(); - await _userCrossSigningKeysBox.clear(); - await _ssssCacheBox.clear(); - await _presencesBox.clear(); - await _timelineFragmentsBox.clear(); - await _eventsBox.clear(); - await _seenDeviceIdsBox.clear(); - await _seenDeviceKeysBox.clear(); - await _collection.deleteFromDisk(); - }); - - @override - Future clearCache() => transaction(() async { - await _roomsBox.clear(); - await _accountDataBox.clear(); - await _roomAccountDataBox.clear(); - await _roomStateBox.clear(); - await _roomMembersBox.clear(); - await _eventsBox.clear(); - await _timelineFragmentsBox.clear(); - await _outboundGroupSessionsBox.clear(); - await _presencesBox.clear(); - await _clientBox.delete('prev_batch'); - }); - - @override - Future clearSSSSCache() => _ssssCacheBox.clear(); - - @override - Future close() async => _collection.close(); - - @override - Future deleteFromToDeviceQueue(int id) async { - await _toDeviceQueueBox.delete(id.toString()); - return; - } - - @override - Future deleteOldFiles(int savedAt) async { - return; - } - - @override - Future forgetRoom(String roomId) => transaction(() async { - await _timelineFragmentsBox.delete(TupleKey(roomId, '').toString()); - final eventsBoxKeys = await _eventsBox.getAllKeys(); - for (final key in eventsBoxKeys) { - final multiKey = TupleKey.fromString(key); - if (multiKey.parts.first != roomId) continue; - await _eventsBox.delete(key); - } - final roomStateBoxKeys = await _roomStateBox.getAllKeys(); - for (final key in roomStateBoxKeys) { - final multiKey = TupleKey.fromString(key); - if (multiKey.parts.first != roomId) continue; - await _roomStateBox.delete(key); - } - final roomMembersBoxKeys = await _roomMembersBox.getAllKeys(); - for (final key in roomMembersBoxKeys) { - final multiKey = TupleKey.fromString(key); - if (multiKey.parts.first != roomId) continue; - await _roomMembersBox.delete(key); - } - final roomAccountDataBoxKeys = await _roomAccountDataBox.getAllKeys(); - for (final key in roomAccountDataBoxKeys) { - final multiKey = TupleKey.fromString(key); - if (multiKey.parts.first != roomId) continue; - await _roomAccountDataBox.delete(key); - } - await _roomsBox.delete(roomId); - }); - - @override - Future> getAccountData() => - runBenchmarked>('Get all account data from store', - () async { - final accountData = {}; - final raws = await _accountDataBox.getAllValues(); - for (final entry in raws.entries) { - accountData[entry.key] = BasicEvent( - type: entry.key, - content: copyMap(entry.value), - ); - } - return accountData; - }); - - @override - Future?> getClient(String name) => - runBenchmarked('Get Client from store', () async { - final map = {}; - final keys = await _clientBox.getAllKeys(); - for (final key in keys) { - if (key == 'version') continue; - final value = await _clientBox.get(key); - if (value != null) map[key] = value; - } - if (map.isEmpty) return null; - return map; - }); - - @override - Future getEventById(String eventId, Room room) async { - final raw = await _eventsBox.get(TupleKey(room.id, eventId).toString()); - if (raw == null) return null; - return Event.fromJson(copyMap(raw), room); - } - - /// Loads a whole list of events at once from the store for a specific room - Future> _getEventsByIds(List eventIds, Room room) async { - final keys = eventIds - .map( - (eventId) => TupleKey(room.id, eventId).toString(), - ) - .toList(); - final rawEvents = await _eventsBox.getAll(keys); - return rawEvents - .map( - (rawEvent) => - rawEvent != null ? Event.fromJson(copyMap(rawEvent), room) : null, - ) - .whereNotNull() - .toList(); - } - - @override - Future> getEventList( - Room room, { - int start = 0, - bool onlySending = false, - int? limit, - }) => - runBenchmarked>('Get event list', () async { - // Get the synced event IDs from the store - final timelineKey = TupleKey(room.id, '').toString(); - final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? [], - ); - - // Get the local stored SENDING events from the store - late final List sendingEventIds; - if (start != 0) { - sendingEventIds = []; - } else { - final sendingTimelineKey = TupleKey(room.id, 'SENDING').toString(); - sendingEventIds = List.from( - (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 eventIds = List.from([ - ...sendingEventIds, - ...(start < timelineEventIds.length && !onlySending - ? timelineEventIds.getRange(start, end).toList() - : []), - ]); - - return await _getEventsByIds(eventIds, room); - }); - - @override - Future> getEventIdList( - Room room, { - int start = 0, - bool includeSending = false, - int? limit, - }) => - runBenchmarked>('Get event id list', () async { - // Get the synced event IDs from the store - final timelineKey = TupleKey(room.id, '').toString(); - final timelineEventIds = List.from( - (await _timelineFragmentsBox.get(timelineKey)) ?? [], - ); - - // Get the local stored SENDING events from the store - late final List sendingEventIds; - if (!includeSending) { - sendingEventIds = []; - } else { - final sendingTimelineKey = TupleKey(room.id, 'SENDING').toString(); - sendingEventIds = List.from( - (await _timelineFragmentsBox.get(sendingTimelineKey)) ?? [], - ); - } - - // Combine those two lists while respecting the start and limit parameters. - final eventIds = sendingEventIds + timelineEventIds; - if (limit != null && eventIds.length > limit) { - eventIds.removeRange(limit, eventIds.length); - } - - return eventIds; - }); - - @override - Future getFile(Uri mxcUri) async { - return null; - } - - @override - Future deleteFile(Uri mxcUri) async { - return false; - } - - @override - Future getInboundGroupSession( - String roomId, - String sessionId, - ) async { - final raw = await _inboundGroupSessionsBox.get(sessionId); - if (raw == null) return null; - return StoredInboundGroupSession.fromJson(copyMap(raw)); - } - - @override - Future> - getInboundGroupSessionsToUpload() async { - final sessions = (await _inboundGroupSessionsBox.getAllValues()) - .values - .where((rawSession) => rawSession['uploaded'] == false) - .take(50) - .map( - (json) => StoredInboundGroupSession.fromJson( - copyMap(json), - ), - ) - .toList(); - return sessions; - } - - @override - Future> getLastSentMessageUserDeviceKey( - String userId, - String deviceId, - ) async { - final raw = - await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); - if (raw == null) return []; - return [raw['last_sent_message']]; - } - - @override - Future storeOlmSession( - String identityKey, - String sessionId, - String pickle, - int lastReceived, - ) async { - final rawSessions = (await _olmSessionsBox.get(identityKey)) ?? {}; - rawSessions[sessionId] = { - 'identity_key': identityKey, - 'pickle': pickle, - 'session_id': sessionId, - 'last_received': lastReceived, - }; - await _olmSessionsBox.put(identityKey, rawSessions); - return; - } - - @override - Future> getOlmSessions( - String identityKey, - String userId, - ) async { - final rawSessions = await _olmSessionsBox.get(identityKey); - if (rawSessions == null || rawSessions.isEmpty) return []; - return rawSessions.values - .map((json) => OlmSession.fromJson(copyMap(json), userId)) - .toList(); - } - - @override - Future> getAllOlmSessions() => - _olmSessionsBox.getAllValues(); - - @override - Future> getOlmSessionsForDevices( - List identityKeys, - String userId, - ) async { - final sessions = await Future.wait( - identityKeys.map((identityKey) => getOlmSessions(identityKey, userId)), - ); - return [for (final sublist in sessions) ...sublist]; - } - - @override - Future getOutboundGroupSession( - 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 { - // Get raw room from database: - final roomData = await _roomsBox.get(roomId); - if (roomData == null) return null; - final room = Room.fromJson(copyMap(roomData), client); - - // Get the room account data - final allKeys = await _roomAccountDataBox.getAllKeys(); - final roomAccountDataKeys = allKeys - .where((key) => TupleKey.fromString(key).parts.first == roomId) - .toList(); - final roomAccountDataList = - await _roomAccountDataBox.getAll(roomAccountDataKeys); - - for (final data in roomAccountDataList) { - if (data == null) continue; - final event = BasicEvent.fromJson(copyMap(data)); - room.roomAccountData[event.type] = event; - } - - // Get important states: - if (loadImportantStates) { - final dbKeys = client.importantStateEvents - .map((state) => TupleKey(roomId, state).toString()) - .toList(); - final rawStates = await _roomStateBox.getAll(dbKeys); - for (final rawState in rawStates) { - if (rawState == null || rawState[''] == null) continue; - room.setState(Event.fromJson(copyMap(rawState['']), room)); - } - } - - return room; - } - - @override - Future> getRoomList(Client client) => - runBenchmarked>('Get room list from store', () async { - final rooms = {}; - final userID = client.userID; - - final rawRooms = await _roomsBox.getAllValues(); - - final getRoomStateRequests = >{}; - final getRoomMembersRequests = >{}; - - for (final raw in rawRooms.values) { - // Get the room - final room = Room.fromJson(copyMap(raw), client); - // Get the "important" room states. All other states will be loaded once - // `getUnimportantRoomStates()` is called. - final dbKeys = client.importantStateEvents - .map((state) => TupleKey(room.id, state).toString()) - .toList(); - getRoomStateRequests[room.id] = _roomStateBox.getAll( - dbKeys, - ); - - // Add to the list and continue. - rooms[room.id] = room; - } - - for (final room in rooms.values) { - // Add states to the room - final statesList = await getRoomStateRequests[room.id]; - if (statesList != null) { - 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), - ) - .toList(); - for (final state in stateEvents) { - room.setState(state); - } - } - - // now that we have the state we can continue - 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 - if (room.lastEvent != null && !room.isDirectChat) { - membersToPostload.add(room.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 - final heroes = room.summary.mHeroes; - if (heroes != null) { - membersToPostload.addAll(heroes); - } - } - // Load members - final membersDbKeys = membersToPostload - .map((member) => TupleKey(room.id, member).toString()) - .toList(); - getRoomMembersRequests[room.id] = _roomMembersBox.getAll( - membersDbKeys, - ); - } - } - - for (final room in rooms.values) { - // Add members to the room - final members = await getRoomMembersRequests[room.id]; - 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), - ); - } - } - } - - // Get the room account data - final roomAccountDataRaws = await _roomAccountDataBox.getAllValues(); - for (final entry in roomAccountDataRaws.entries) { - final keys = TupleKey.fromString(entry.key); - final basicRoomEvent = BasicEvent.fromJson( - copyMap(entry.value), - ); - final roomId = keys.parts.first; - if (rooms.containsKey(roomId)) { - rooms[roomId]!.roomAccountData[basicRoomEvent.type] = - basicRoomEvent; - } else { - Logs().w( - 'Found account data for unknown room $roomId. Delete now...', - ); - await _roomAccountDataBox - .delete(TupleKey(roomId, basicRoomEvent.type).toString()); - } - } - - return rooms.values.toList(); - }); - - @override - Future getSSSSCache(String type) async { - final raw = await _ssssCacheBox.get(type); - if (raw == null) return null; - return SSSSCache.fromJson(copyMap(raw)); - } - - @override - Future> getToDeviceEventQueue() async { - final raws = await _toDeviceQueueBox.getAllValues(); - final copiedRaws = raws.entries.map((entry) { - final copiedRaw = copyMap(entry.value); - copiedRaw['id'] = int.parse(entry.key); - copiedRaw['content'] = jsonDecode(copiedRaw['content'] as String); - return copiedRaw; - }).toList(); - return copiedRaws.map((raw) => QueuedToDeviceEvent.fromJson(raw)).toList(); - } - - @override - Future> getUnimportantRoomEventStatesForRoom( - 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]); - }); - - final unimportantEvents = []; - for (final key in keys) { - final states = await _roomStateBox.get(key); - if (states == null) continue; - unimportantEvents.addAll( - states.values.map((raw) => Event.fromJson(copyMap(raw), room)), - ); - } - return unimportantEvents.where((event) => event.stateKey != null).toList(); - } - - @override - Future getUser(String userId, Room room) async { - final state = - await _roomMembersBox.get(TupleKey(room.id, userId).toString()); - if (state == null) return null; - return Event.fromJson(copyMap(state), room).asUser; - } - - @override - Future> getUserDeviceKeys(Client client) => - runBenchmarked>( - 'Get all user device keys from store', () async { - final deviceKeysOutdated = - await _userDeviceKeysOutdatedBox.getAllKeys(); - if (deviceKeysOutdated.isEmpty) { - return {}; - } - final res = {}; - final userDeviceKeysBoxKeys = await _userDeviceKeysBox.getAllKeys(); - final userCrossSigningKeysBoxKeys = - await _userCrossSigningKeysBox.getAllKeys(); - for (final userId in deviceKeysOutdated) { - final deviceKeysBoxKeys = userDeviceKeysBoxKeys.where((tuple) { - final tupleKey = TupleKey.fromString(tuple); - return tupleKey.parts.first == userId; - }); - final crossSigningKeysBoxKeys = - userCrossSigningKeysBoxKeys.where((tuple) { - final tupleKey = TupleKey.fromString(tuple); - return tupleKey.parts.first == userId; - }); - final childEntries = await Future.wait( - deviceKeysBoxKeys.map( - (key) async { - final userDeviceKey = await _userDeviceKeysBox.get(key); - if (userDeviceKey == null) return null; - return copyMap(userDeviceKey); - }, - ), - ); - final crossSigningEntries = await Future.wait( - crossSigningKeysBoxKeys.map( - (key) async { - final crossSigningKey = await _userCrossSigningKeysBox.get(key); - if (crossSigningKey == null) return null; - return copyMap(crossSigningKey); - }, - ), - ); - 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, - ); - } - return res; - }); - - @override - Future> getUsers(Room room) async { - final users = []; - final keys = (await _roomMembersBox.getAllKeys()) - .where((key) => TupleKey.fromString(key).parts.first == room.id) - .toList(); - final states = await _roomMembersBox.getAll(keys); - states.removeWhere((state) => state == null); - for (final state in states) { - users.add(Event.fromJson(copyMap(state!), room).asUser); - } - - return users; - } - - @override - Future insertClient( - 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); - await _clientBox.put('user_id', userId); - if (refreshToken == null) { - await _clientBox.delete('refresh_token'); - } else { - await _clientBox.put('refresh_token', refreshToken); - } - if (tokenExpiresAt == null) { - await _clientBox.delete('token_expires_at'); - } else { - await _clientBox.put( - 'token_expires_at', - tokenExpiresAt.millisecondsSinceEpoch.toString(), - ); - } - if (deviceId == null) { - await _clientBox.delete('device_id'); - } else { - await _clientBox.put('device_id', deviceId); - } - if (deviceName == null) { - await _clientBox.delete('device_name'); - } else { - await _clientBox.put('device_name', deviceName); - } - if (prevBatch == null) { - await _clientBox.delete('prev_batch'); - } else { - await _clientBox.put('prev_batch', prevBatch); - } - if (olmAccount == null) { - await _clientBox.delete('olm_account'); - } else { - await _clientBox.put('olm_account', olmAccount); - } - await _clientBox.delete('sync_filter_id'); - }); - return 0; - } - - @override - Future insertIntoToDeviceQueue( - String type, - String txnId, - String content, - ) async { - final id = DateTime.now().millisecondsSinceEpoch; - await _toDeviceQueueBox.put(id.toString(), { - 'type': type, - 'txn_id': txnId, - 'content': content, - }); - return id; - } - - @override - Future markInboundGroupSessionAsUploaded( - 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!', - ); - return; - } - raw['uploaded'] = true; - await _inboundGroupSessionsBox.put(sessionId, raw); - return; - } - - @override - Future markInboundGroupSessionsAsNeedingUpload() async { - final keys = await _inboundGroupSessionsBox.getAllKeys(); - for (final sessionId in keys) { - final raw = await _inboundGroupSessionsBox.get(sessionId); - if (raw == null) continue; - raw['uploaded'] = false; - await _inboundGroupSessionsBox.put(sessionId, raw); - } - return; - } - - @override - Future removeEvent(String eventId, String roomId) async { - await _eventsBox.delete(TupleKey(roomId, eventId).toString()); - final keys = await _timelineFragmentsBox.getAllKeys(); - for (final key in keys) { - final multiKey = TupleKey.fromString(key); - if (multiKey.parts.first != roomId) continue; - final eventIds = await _timelineFragmentsBox.get(key) ?? []; - final prevLength = eventIds.length; - eventIds.removeWhere((id) => id == eventId); - if (eventIds.length < prevLength) { - await _timelineFragmentsBox.put(key, eventIds); - } - } - return; - } - - @override - Future removeOutboundGroupSession(String roomId) async { - await _outboundGroupSessionsBox.delete(roomId); - return; - } - - @override - Future removeUserCrossSigningKey( - String userId, - String publicKey, - ) async { - await _userCrossSigningKeysBox - .delete(TupleKey(userId, publicKey).toString()); - return; - } - - @override - Future removeUserDeviceKey(String userId, String deviceId) async { - await _userDeviceKeysBox.delete(TupleKey(userId, deviceId).toString()); - return; - } - - @override - Future setBlockedUserCrossSigningKey( - bool blocked, - String userId, - String publicKey, - ) async { - final raw = await _userCrossSigningKeysBox - .get(TupleKey(userId, publicKey).toString()); - raw!['blocked'] = blocked; - await _userCrossSigningKeysBox.put( - TupleKey(userId, publicKey).toString(), - raw, - ); - return; - } - - @override - Future setBlockedUserDeviceKey( - bool blocked, - String userId, - String deviceId, - ) async { - final raw = - await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); - raw!['blocked'] = blocked; - await _userDeviceKeysBox.put( - TupleKey(userId, deviceId).toString(), - raw, - ); - return; - } - - @override - Future setLastActiveUserDeviceKey( - int lastActive, - String userId, - String deviceId, - ) async { - final raw = - await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); - raw!['last_active'] = lastActive; - await _userDeviceKeysBox.put( - TupleKey(userId, deviceId).toString(), - raw, - ); - } - - @override - Future setLastSentMessageUserDeviceKey( - String lastSentMessage, - String userId, - String deviceId, - ) async { - final raw = - await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); - raw!['last_sent_message'] = lastSentMessage; - await _userDeviceKeysBox.put( - TupleKey(userId, deviceId).toString(), - raw, - ); - } - - @override - Future setRoomPrevBatch( - String? prevBatch, - String roomId, - Client client, - ) async { - final raw = await _roomsBox.get(roomId); - if (raw == null) return; - final room = Room.fromJson(copyMap(raw), client); - room.prev_batch = prevBatch; - await _roomsBox.put(roomId, room.toJson()); - return; - } - - @override - Future setVerifiedUserCrossSigningKey( - bool verified, - String userId, - String publicKey, - ) async { - final raw = (await _userCrossSigningKeysBox - .get(TupleKey(userId, publicKey).toString())) ?? - {}; - raw['verified'] = verified; - await _userCrossSigningKeysBox.put( - TupleKey(userId, publicKey).toString(), - raw, - ); - return; - } - - @override - Future setVerifiedUserDeviceKey( - bool verified, - String userId, - String deviceId, - ) async { - final raw = - await _userDeviceKeysBox.get(TupleKey(userId, deviceId).toString()); - raw!['verified'] = verified; - await _userDeviceKeysBox.put( - TupleKey(userId, deviceId).toString(), - raw, - ); - return; - } - - @override - Future storeAccountData( - String type, - Map content, - ) async { - await _accountDataBox.put(type, copyMap(content)); - return; - } - - @override - Future storeRoomAccountData(String roomId, BasicEvent event) async { - await _roomAccountDataBox.put( - TupleKey(roomId, event.type).toString(), - copyMap(event.toJson()), - ); - return; - } - - @override - Future storeEventUpdate( - String roomId, - StrippedStateEvent event, - EventUpdateType type, - Client client, - ) async { - final eventUpdate = EventUpdate( - roomID: roomId, - content: event.toJson(), - type: type, - ); - final tmpRoom = client.getRoomById(eventUpdate.roomID) ?? - Room(id: eventUpdate.roomID, client: client); - - // In case of this is a redaction event - if (eventUpdate.content['type'] == EventTypes.Redaction) { - final eventId = eventUpdate.content.tryGet('redacts'); - final event = - eventId != null ? await getEventById(eventId, tmpRoom) : null; - if (event != null) { - event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom)); - await _eventsBox.put( - TupleKey(eventUpdate.roomID, event.eventId).toString(), - event.toJson(), - ); - - if (tmpRoom.lastEvent?.eventId == event.eventId) { - await _roomStateBox.put( - TupleKey(eventUpdate.roomID, event.type).toString(), - {'': event.toJson()}, - ); - } - } - } - - // Store a common message event - if ({ - EventUpdateType.timeline, - EventUpdateType.history, - EventUpdateType.decryptedTimelineQueue, - }.contains(eventUpdate.type)) { - final eventId = eventUpdate.content['event_id']; - // Is this ID already in the store? - final prevEvent = await _eventsBox - .get(TupleKey(eventUpdate.roomID, eventId).toString()); - final prevStatus = prevEvent == null - ? null - : () { - final json = copyMap(prevEvent); - final statusInt = json.tryGet('status') ?? - json - .tryGetMap('unsigned') - ?.tryGet(messageSendingStatusKey); - return statusInt == null ? null : eventStatusFromInt(statusInt); - }(); - - // calculate the status - final newStatus = eventStatusFromInt( - eventUpdate.content.tryGet('status') ?? - eventUpdate.content - .tryGetMap('unsigned') - ?.tryGet(messageSendingStatusKey) ?? - EventStatus.synced.intValue, - ); - - // Is this the response to a sending event which is already synced? Then - // there is nothing to do here. - if (!newStatus.isSynced && prevStatus != null && prevStatus.isSynced) { - return; - } - - final status = newStatus.isError || prevStatus == null - ? newStatus - : latestEventStatus( - prevStatus, - newStatus, - ); - - // Add the status and the sort order to the content so it get stored - eventUpdate.content['unsigned'] ??= {}; - eventUpdate.content['unsigned'][messageSendingStatusKey] = - eventUpdate.content['status'] = status.intValue; - - // In case this event has sent from this account we have a transaction ID - final transactionId = eventUpdate.content - .tryGetMap('unsigned') - ?.tryGet('transaction_id'); - await _eventsBox.put( - TupleKey(eventUpdate.roomID, eventId).toString(), - eventUpdate.content, - ); - - // Update timeline fragments - final key = TupleKey(eventUpdate.roomID, status.isSent ? '' : 'SENDING') - .toString(); - - final eventIds = - List.from(await _timelineFragmentsBox.get(key) ?? []); - - if (!eventIds.contains(eventId)) { - if (eventUpdate.type == EventUpdateType.history) { - eventIds.add(eventId); - } else { - eventIds.insert(0, eventId); - } - await _timelineFragmentsBox.put(key, eventIds); - } else if (status.isSynced && - prevStatus != null && - prevStatus.isSent && - eventUpdate.type != EventUpdateType.history) { - // Status changes from 1 -> 2? Make sure event is correctly sorted. - eventIds.remove(eventId); - eventIds.insert(0, eventId); - } - - // If event comes from server timeline, remove sending events with this ID - if (status.isSent) { - final key = TupleKey(eventUpdate.roomID, 'SENDING').toString(); - final eventIds = - List.from(await _timelineFragmentsBox.get(key) ?? []); - final i = eventIds.indexWhere((id) => id == eventId); - if (i != -1) { - await _timelineFragmentsBox.put(key, eventIds..removeAt(i)); - } - } - - // Is there a transaction id? Then delete the event with this id. - if (!status.isError && !status.isSending && transactionId != null) { - await removeEvent(transactionId, eventUpdate.roomID); - } - } - - final stateKey = eventUpdate.content['state_key']; - // Store a common state event - if (stateKey != null && - // Don't store events as state updates when paginating backwards. - (eventUpdate.type == EventUpdateType.timeline || - eventUpdate.type == EventUpdateType.state || - eventUpdate.type == EventUpdateType.inviteState)) { - if (eventUpdate.content['type'] == EventTypes.RoomMember) { - await _roomMembersBox.put( - TupleKey( - eventUpdate.roomID, - eventUpdate.content['state_key'], - ).toString(), - eventUpdate.content, - ); - } else { - final key = TupleKey( - eventUpdate.roomID, - eventUpdate.content['type'], - ).toString(); - final stateMap = copyMap(await _roomStateBox.get(key) ?? {}); - - stateMap[stateKey] = eventUpdate.content; - await _roomStateBox.put(key, stateMap); - } - } - } - - @override - Future storeFile(Uri mxcUri, Uint8List bytes, int time) async { - return; - } - - @override - Future storeInboundGroupSession( - 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(), - ); - return; - } - - @override - Future storeOutboundGroupSession( - String roomId, - String pickle, - String deviceIds, - int creationTime, - ) async { - await _outboundGroupSessionsBox.put(roomId, { - 'room_id': roomId, - 'pickle': pickle, - 'device_ids': deviceIds, - 'creation_time': creationTime, - }); - return; - } - - @override - Future storePrevBatch( - String prevBatch, - ) async { - if ((await _clientBox.getAllKeys()).isEmpty) return; - await _clientBox.put('prev_batch', prevBatch); - return; - } - - @override - Future storeRoomUpdate( - String roomId, - SyncRoomUpdate roomUpdate, - Event? lastEvent, - Client client, - ) async { - // Leave room if membership is leave - if (roomUpdate is LeftRoomUpdate) { - await forgetRoom(roomId); - return; - } - final membership = roomUpdate is LeftRoomUpdate - ? Membership.leave - : roomUpdate is InvitedRoomUpdate - ? Membership.invite - : Membership.join; - // Make sure room exists - 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(), - ); - } 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(), - ); - } - } - - @override - Future deleteTimelineForRoom(String roomId) => - _timelineFragmentsBox.delete(TupleKey(roomId, '').toString()); - - @override - Future storeSSSSCache( - String type, - String keyId, - String ciphertext, - String content, - ) async { - await _ssssCacheBox.put( - type, - SSSSCache( - type: type, - keyId: keyId, - ciphertext: ciphertext, - content: content, - ).toJson(), - ); - } - - @override - Future storeSyncFilterId( - String syncFilterId, - ) async { - await _clientBox.put('sync_filter_id', syncFilterId); - } - - @override - Future storeUserCrossSigningKey( - String userId, - String publicKey, - String content, - bool verified, - bool blocked, - ) async { - await _userCrossSigningKeysBox.put( - TupleKey(userId, publicKey).toString(), - { - 'user_id': userId, - 'public_key': publicKey, - 'content': content, - 'verified': verified, - 'blocked': blocked, - }, - ); - } - - @override - 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, - 'content': content, - 'verified': verified, - 'blocked': blocked, - 'last_active': lastActive, - 'last_sent_message': '', - }); - return; - } - - @override - Future storeUserDeviceKeysInfo(String userId, bool outdated) async { - await _userDeviceKeysOutdatedBox.put(userId, outdated); - return; - } - - @override - Future transaction(Future Function() action) => - _collection.transaction(action); - - @override - Future updateClient( - 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(), - ); - } - if (refreshToken == null) { - await _clientBox.delete('refresh_token'); - } else { - await _clientBox.put('refresh_token', refreshToken); - } - await _clientBox.put('user_id', userId); - if (deviceId == null) { - await _clientBox.delete('device_id'); - } else { - await _clientBox.put('device_id', deviceId); - } - if (deviceName == null) { - await _clientBox.delete('device_name'); - } else { - await _clientBox.put('device_name', deviceName); - } - if (prevBatch == null) { - await _clientBox.delete('prev_batch'); - } else { - await _clientBox.put('prev_batch', prevBatch); - } - if (olmAccount == null) { - await _clientBox.delete('olm_account'); - } else { - await _clientBox.put('olm_account', olmAccount); - } - }); - return; - } - - @override - Future updateClientKeys( - String olmAccount, - ) async { - await _clientBox.put('olm_account', olmAccount); - return; - } - - @override - Future updateInboundGroupSessionAllowedAtIndex( - 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!', - ); - return; - } - raw['allowed_at_index'] = allowedAtIndex; - await _inboundGroupSessionsBox.put(sessionId, raw); - return; - } - - @override - Future updateInboundGroupSessionIndexes( - 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!', - ); - return; - } - final json = copyMap(raw); - json['indexes'] = indexes; - await _inboundGroupSessionsBox.put(sessionId, json); - return; - } - - @override - Future> getAllInboundGroupSessions() async { - final rawSessions = await _inboundGroupSessionsBox.getAllValues(); - return rawSessions.values - .map((raw) => StoredInboundGroupSession.fromJson(copyMap(raw))) - .toList(); - } - - @override - Future addSeenDeviceId( - String userId, - String deviceId, - String publicKeys, - ) => - _seenDeviceIdsBox.put(TupleKey(userId, deviceId).toString(), publicKeys); - - @override - Future addSeenPublicKey( - String publicKey, - String deviceId, - ) => - _seenDeviceKeysBox.put(publicKey, deviceId); - - @override - Future deviceIdSeen(userId, deviceId) async { - final raw = - await _seenDeviceIdsBox.get(TupleKey(userId, deviceId).toString()); - if (raw == null) return null; - return raw; - } - - @override - Future publicKeySeen(String publicKey) async { - final raw = await _seenDeviceKeysBox.get(publicKey); - if (raw == null) return null; - return raw; - } - - @override - Future storePresence(String userId, CachedPresence presence) => - _presencesBox.put(userId, presence.toJson()); - - @override - Future getPresence(String userId) async { - final rawPresence = await _presencesBox.get(userId); - if (rawPresence == null) return null; - - return CachedPresence.fromJson(copyMap(rawPresence)); - } - - @override - Future exportDump() async { - final dataMap = { - _clientBoxName: await _clientBox.getAllValues(), - _accountDataBoxName: await _accountDataBox.getAllValues(), - _roomsBoxName: await _roomsBox.getAllValues(), - _roomStateBoxName: await _roomStateBox.getAllValues(), - _roomMembersBoxName: await _roomMembersBox.getAllValues(), - _toDeviceQueueBoxName: await _toDeviceQueueBox.getAllValues(), - _roomAccountDataBoxName: await _roomAccountDataBox.getAllValues(), - _inboundGroupSessionsBoxName: - await _inboundGroupSessionsBox.getAllValues(), - _outboundGroupSessionsBoxName: - await _outboundGroupSessionsBox.getAllValues(), - _olmSessionsBoxName: await _olmSessionsBox.getAllValues(), - _userDeviceKeysBoxName: await _userDeviceKeysBox.getAllValues(), - _userDeviceKeysOutdatedBoxName: - await _userDeviceKeysOutdatedBox.getAllValues(), - _userCrossSigningKeysBoxName: - await _userCrossSigningKeysBox.getAllValues(), - _ssssCacheBoxName: await _ssssCacheBox.getAllValues(), - _presencesBoxName: await _presencesBox.getAllValues(), - _timelineFragmentsBoxName: await _timelineFragmentsBox.getAllValues(), - _eventsBoxName: await _eventsBox.getAllValues(), - _seenDeviceIdsBoxName: await _seenDeviceIdsBox.getAllValues(), - _seenDeviceKeysBoxName: await _seenDeviceKeysBox.getAllValues(), - }; - final json = jsonEncode(dataMap); - await clear(); - return json; - } - - @override - Future importDump(String export) async { - try { - await clear(); - await open(); - final json = Map.from(jsonDecode(export)).cast(); - for (final key in json[_clientBoxName]!.keys) { - await _clientBox.put(key, json[_clientBoxName]![key]); - } - for (final key in json[_accountDataBoxName]!.keys) { - await _accountDataBox.put(key, json[_accountDataBoxName]![key]); - } - for (final key in json[_roomsBoxName]!.keys) { - await _roomsBox.put(key, json[_roomsBoxName]![key]); - } - for (final key in json[_roomStateBoxName]!.keys) { - await _roomStateBox.put(key, json[_roomStateBoxName]![key]); - } - for (final key in json[_roomMembersBoxName]!.keys) { - await _roomMembersBox.put(key, json[_roomMembersBoxName]![key]); - } - for (final key in json[_toDeviceQueueBoxName]!.keys) { - await _toDeviceQueueBox.put(key, json[_toDeviceQueueBoxName]![key]); - } - for (final key in json[_roomAccountDataBoxName]!.keys) { - await _roomAccountDataBox.put(key, json[_roomAccountDataBoxName]![key]); - } - for (final key in json[_inboundGroupSessionsBoxName]!.keys) { - await _inboundGroupSessionsBox.put( - key, - json[_inboundGroupSessionsBoxName]![key], - ); - } - for (final key in json[_outboundGroupSessionsBoxName]!.keys) { - await _outboundGroupSessionsBox.put( - key, - json[_outboundGroupSessionsBoxName]![key], - ); - } - for (final key in json[_olmSessionsBoxName]!.keys) { - await _olmSessionsBox.put(key, json[_olmSessionsBoxName]![key]); - } - for (final key in json[_userDeviceKeysBoxName]!.keys) { - await _userDeviceKeysBox.put(key, json[_userDeviceKeysBoxName]![key]); - } - for (final key in json[_userDeviceKeysOutdatedBoxName]!.keys) { - await _userDeviceKeysOutdatedBox.put( - key, - json[_userDeviceKeysOutdatedBoxName]![key], - ); - } - for (final key in json[_userCrossSigningKeysBoxName]!.keys) { - await _userCrossSigningKeysBox.put( - key, - json[_userCrossSigningKeysBoxName]![key], - ); - } - for (final key in json[_ssssCacheBoxName]!.keys) { - await _ssssCacheBox.put(key, json[_ssssCacheBoxName]![key]); - } - for (final key in json[_presencesBoxName]!.keys) { - await _presencesBox.put(key, json[_presencesBoxName]![key]); - } - for (final key in json[_timelineFragmentsBoxName]!.keys) { - await _timelineFragmentsBox.put( - key, - json[_timelineFragmentsBoxName]![key], - ); - } - for (final key in json[_seenDeviceIdsBoxName]!.keys) { - await _seenDeviceIdsBox.put(key, json[_seenDeviceIdsBoxName]![key]); - } - for (final key in json[_seenDeviceKeysBoxName]!.keys) { - await _seenDeviceKeysBox.put(key, json[_seenDeviceKeysBoxName]![key]); - } - return true; - } catch (e, s) { - Logs().e('Database import error: ', e, s); - return false; - } - } - - @override - Future storeWellKnown(DiscoveryInformation? discoveryInformation) { - if (discoveryInformation == null) { - return _clientBox.delete('discovery_information'); - } - return _clientBox.put( - 'discovery_information', - jsonEncode(discoveryInformation.toJson()), - ); - } - - @override - Future getWellKnown() async { - final rawDiscoveryInformation = - await _clientBox.get('discovery_information'); - if (rawDiscoveryInformation == null) return null; - return DiscoveryInformation.fromJson(jsonDecode(rawDiscoveryInformation)); - } - - @override - Future delete() => _collection.deleteFromDisk(); - - @override - Future markUserProfileAsOutdated(userId) async { - return; - } - - @override - Future getUserProfile(String userId) async { - return null; - } - - @override - Future storeUserProfile( - String userId, - CachedProfileInformation profile, - ) async { - return; - } -} - -class TupleKey { - final List parts; - - TupleKey(String key1, [String? key2, String? key3]) - : parts = [ - key1, - if (key2 != null) key2, - if (key3 != null) key3, - ]; - - const TupleKey.byParts(this.parts); - - TupleKey.fromString(String multiKeyString) - : parts = multiKeyString.split('|').toList(); - - @override - String toString() => parts.join('|'); - - @override - bool operator ==(other) => parts.toString() == other.toString(); - - @override - int get hashCode => Object.hashAll(parts); -} diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index 43965987..05cd9480 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -1816,3 +1816,28 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { profile.toJson(), ); } + +class TupleKey { + final List parts; + + TupleKey(String key1, [String? key2, String? key3]) + : parts = [ + key1, + if (key2 != null) key2, + if (key3 != null) key3, + ]; + + const TupleKey.byParts(this.parts); + + TupleKey.fromString(String multiKeyString) + : parts = multiKeyString.split('|').toList(); + + @override + String toString() => parts.join('|'); + + @override + bool operator ==(other) => parts.toString() == other.toString(); + + @override + int get hashCode => Object.hashAll(parts); +} diff --git a/pubspec.yaml b/pubspec.yaml index d6c2717b..42a6877d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,6 @@ dependencies: crypto: ^3.0.0 enhanced_enum: ^0.2.4 ffi: ^2.0.0 - hive: ^2.2.3 html: ^0.15.0 html_unescape: ^2.0.0 http: ">=0.13.0 <2.0.0" diff --git a/test/database_api_test.dart b/test/database_api_test.dart index 85d1e3ec..7a359da1 100644 --- a/test/database_api_test.dart +++ b/test/database_api_test.dart @@ -36,10 +36,7 @@ String createLargeString(String character, int desiredSize) { } void main() { - final databaseBuilders = { - 'Matrix SDK Database': getMatrixSdkDatabase, - 'Hive Collections Database': getHiveCollectionsDatabase, - }; + final databaseBuilders = {'Matrix SDK Database': getMatrixSdkDatabase}; for (final databaseBuilder in databaseBuilders.entries) { group('Test ${databaseBuilder.key}', tags: 'olm', () { @@ -673,19 +670,14 @@ void main() { updated: DateTime.now(), ), ); - // ignore: deprecated_member_use_from_same_package - if (database is! HiveCollectionsDatabase) { - final profile2 = - await database.getUserProfile('@alice:example.com'); - expect(profile2?.displayname, 'Alice M'); - expect(profile2?.outdated, false); - await database.markUserProfileAsOutdated('@alice:example.com'); + final profile2 = await database.getUserProfile('@alice:example.com'); + expect(profile2?.displayname, 'Alice M'); + expect(profile2?.outdated, false); + await database.markUserProfileAsOutdated('@alice:example.com'); - final profile3 = - await database.getUserProfile('@alice:example.com'); - expect(profile3?.displayname, 'Alice M'); - expect(profile3?.outdated, true); - } + final profile3 = await database.getUserProfile('@alice:example.com'); + expect(profile3?.displayname, 'Alice M'); + expect(profile3?.outdated, true); }, ); diff --git a/test/fake_database.dart b/test/fake_database.dart index 6ebe13e1..9f9023c8 100644 --- a/test/fake_database.dart +++ b/test/fake_database.dart @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -import 'package:file/local.dart'; -import 'package:hive/hive.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:matrix/matrix.dart'; @@ -25,25 +23,6 @@ import 'package:matrix/matrix.dart'; Future getDatabase(Client? c, {String? databasePath}) => getMatrixSdkDatabase(c, path: databasePath); -bool hiveInitialized = false; - -// ignore: deprecated_member_use_from_same_package -Future getHiveCollectionsDatabase(Client? c) async { - final testHivePath = await LocalFileSystem() - .systemTempDirectory - .createTemp('dart-sdk-tests-database'); - if (!hiveInitialized) { - Hive.init(testHivePath.path); - } - // ignore: deprecated_member_use_from_same_package - final db = HiveCollectionsDatabase( - 'unit_test.${c?.hashCode}', - testHivePath.path, - ); - await db.open(); - return db; -} - // ignore: deprecated_member_use_from_same_package Future getMatrixSdkDatabase( Client? c, { diff --git a/test_driver/matrixsdk_test.dart b/test_driver/matrixsdk_test.dart index 2f29919e..9c783984 100644 --- a/test_driver/matrixsdk_test.dart +++ b/test_driver/matrixsdk_test.dart @@ -18,7 +18,6 @@ import 'dart:io'; -import 'package:hive/hive.dart'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; @@ -40,8 +39,6 @@ void main() => group( Client? testClientA, testClientB; try { - Hive.init(null); - await olm.init(); olm.Account(); Logs().i('[LibOlm] Enabled'); @@ -488,8 +485,6 @@ void main() => group( Client? testClientA, testClientB; try { - Hive.init(null); - await olm.init(); olm.Account(); Logs().i('[LibOlm] Enabled');