From e5eaea74c1b20f7382e21f55b698c1dd1d292a4f Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Mon, 30 May 2022 13:44:05 +0200 Subject: [PATCH] refactor: Switch to Hive Collections DB --- lib/pages/search/search_view.dart | 6 +- lib/utils/client_manager.dart | 6 +- .../fluffybox_database.dart | 3 + ...=> flutter_hive_collections_database.dart} | 92 ++++++++++++------- lib/widgets/public_room_bottom_sheet.dart | 4 +- pubspec.lock | 15 ++- pubspec.yaml | 2 +- test/utils/test_client.dart | 4 +- 8 files changed, 84 insertions(+), 48 deletions(-) rename lib/utils/matrix_sdk_extensions.dart/{flutter_matrix_hive_database.dart => flutter_hive_collections_database.dart} (50%) diff --git a/lib/pages/search/search_view.dart b/lib/pages/search/search_view.dart index c75c8fffe..0bf6f95ad 100644 --- a/lib/pages/search/search_view.dart +++ b/lib/pages/search/search_view.dart @@ -46,13 +46,11 @@ class SearchView extends StatelessWidget { }).then((QueryPublicRoomsResponse res) { final genericSearchTerm = controller.genericSearchTerm; if (genericSearchTerm != null && - !res.chunk.any((room) => - (room.aliases?.contains(controller.genericSearchTerm) ?? false) || - room.canonicalAlias == controller.genericSearchTerm)) { + !res.chunk.any( + (room) => room.canonicalAlias == controller.genericSearchTerm)) { // we have to tack on the original alias res.chunk.add( PublicRoomsChunk( - aliases: [genericSearchTerm], name: genericSearchTerm, numJoinedMembers: 0, roomId: '!unknown', diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index 60a966da1..44047d9a4 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -8,10 +8,10 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:fluffychat/utils/custom_image_resizer.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'famedlysdk_store.dart'; import 'matrix_sdk_extensions.dart/fluffybox_database.dart'; -import 'matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart'; abstract class ClientManager { static const String clientNamespace = 'im.fluffychat.store.clients'; @@ -95,8 +95,8 @@ abstract class ClientManager { // To check which story room we can post in EventTypes.RoomPowerLevels, }, - databaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder, - legacyDatabaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder, + databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder, + legacyDatabaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder, supportedLoginTypes: { AuthenticationTypes.password, if (PlatformInfos.isMobile || diff --git a/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart b/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart index ef7bd00ab..c2e938fd3 100644 --- a/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart +++ b/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart @@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart'; import '../client_manager.dart'; import '../famedlysdk_store.dart'; +// ignore: deprecated_member_use class FlutterFluffyBoxDatabase extends FluffyBoxDatabase { FlutterFluffyBoxDatabase( String name, @@ -27,6 +28,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase { static const String _cipherStorageKey = 'database_encryption_key'; + // ignore: deprecated_member_use static Future databaseBuilder(Client client) async { Logs().d('Open FluffyBox...'); fluffybox.HiveAesCipher? hiverCipher; @@ -59,6 +61,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase { rethrow; } + // ignore: deprecated_member_use final db = FluffyBoxDatabase( 'fluffybox_${client.clientName.replaceAll(' ', '_').toLowerCase()}', await _findDatabasePath(client), diff --git a/lib/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart b/lib/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart similarity index 50% rename from lib/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart rename to lib/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart index 9879ff9c4..0f7756727 100644 --- a/lib/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart +++ b/lib/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart @@ -2,78 +2,108 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/foundation.dart' hide Key; import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; -import 'package:hive_flutter/hive_flutter.dart'; import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; -import '../platform_infos.dart'; - -class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase { - FlutterMatrixHiveStore(String name, {HiveCipher? encryptionCipher}) - : super( +class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase { + FlutterHiveCollectionsDatabase( + String name, + String path, { + HiveCipher? key, + }) : super( name, - encryptionCipher: encryptionCipher, + path, + key: key, ); - static bool _hiveInitialized = false; - static const String _hiveCipherStorageKey = 'hive_encryption_key'; + static const String _cipherStorageKey = 'database_encryption_key'; - static Future hiveDatabaseBuilder( + static Future databaseBuilder( Client client) async { - if (!kIsWeb && !_hiveInitialized) { - _hiveInitialized = true; - } - HiveCipher? hiverCipher; + Logs().d('Open Hive...'); + HiveAesCipher? hiverCipher; try { // Workaround for secure storage is calling Platform.operatingSystem on web - if (kIsWeb || Platform.isLinux) throw MissingPluginException(); + if (kIsWeb) throw MissingPluginException(); const secureStorage = FlutterSecureStorage(); final containsEncryptionKey = - await secureStorage.containsKey(key: _hiveCipherStorageKey); + await secureStorage.containsKey(key: _cipherStorageKey); if (!containsEncryptionKey) { + // do not try to create a buggy secure storage for new Linux users + if (Platform.isLinux) throw MissingPluginException(); final key = Hive.generateSecureKey(); await secureStorage.write( - key: _hiveCipherStorageKey, + key: _cipherStorageKey, value: base64UrlEncode(key), ); } // workaround for if we just wrote to the key and it still doesn't exist - final rawEncryptionKey = - await secureStorage.read(key: _hiveCipherStorageKey); + final rawEncryptionKey = await secureStorage.read(key: _cipherStorageKey); if (rawEncryptionKey == null) throw MissingPluginException(); - final encryptionKey = base64Url.decode(rawEncryptionKey); - hiverCipher = HiveAesCipher(encryptionKey); + hiverCipher = HiveAesCipher(base64Url.decode(rawEncryptionKey)); } on MissingPluginException catch (_) { Logs().i('Hive encryption is not supported on this platform'); + } catch (_) { + const FlutterSecureStorage().delete(key: _cipherStorageKey); + rethrow; } - final db = FlutterMatrixHiveStore( - client.clientName, - encryptionCipher: hiverCipher, + + final db = FlutterHiveCollectionsDatabase( + 'hive_collections_${client.clientName.replaceAll(' ', '_').toLowerCase()}', + await _findDatabasePath(client), + key: hiverCipher, ); try { await db.open(); - } catch (e, s) { - Logs().e('Unable to open Hive. Delete and try again...', e, s); + } catch (_) { + Logs().w('Unable to open Hive. Delete database and storage key...'); + const FlutterSecureStorage().delete(key: _cipherStorageKey); await db.clear(); - await db.open(); + rethrow; } + Logs().d('Hive is ready'); return db; } + static Future _findDatabasePath(Client client) async { + String path = client.clientName; + if (!kIsWeb) { + Directory directory; + try { + if (Platform.isLinux) { + directory = await getApplicationSupportDirectory(); + } else { + directory = await getApplicationDocumentsDirectory(); + } + } catch (_) { + try { + directory = await getLibraryDirectory(); + } catch (_) { + directory = Directory.current; + } + } + // do not destroy your stable FluffyChat in debug mode + if (kDebugMode) { + directory = Directory(directory.uri.resolve('debug').toFilePath()); + directory.create(recursive: true); + } + path = directory.path; + } + return path; + } + @override int get maxFileSize => supportsFileStoring ? 100 * 1024 * 1024 : 0; @override - bool get supportsFileStoring => (PlatformInfos.isIOS || - PlatformInfos.isAndroid || - PlatformInfos.isDesktop); + bool get supportsFileStoring => !kIsWeb; Future _getFileStoreDirectory() async { try { diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index 1aedfe26a..414702dc1 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -41,9 +41,7 @@ class PublicRoomBottomSheet extends StatelessWidget { } } - bool _testRoom(PublicRoomsChunk r) => - r.canonicalAlias == roomAlias || - (r.aliases?.contains(roomAlias) ?? false); + bool _testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias; Future _search(BuildContext context) async { final chunk = this.chunk; diff --git a/pubspec.lock b/pubspec.lock index fefd972f5..fe0c79e97 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -395,6 +395,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.1" + enhanced_enum: + dependency: transitive + description: + name: enhanced_enum + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" fake_async: dependency: transitive description: @@ -788,7 +795,7 @@ packages: name: hive url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.1" hive_flutter: dependency: "direct main" description: @@ -1012,21 +1019,21 @@ packages: name: matrix url: "https://pub.dartlang.org" source: hosted - version: "0.9.6" + version: "0.9.9" matrix_api_lite: dependency: transitive description: name: matrix_api_lite url: "https://pub.dartlang.org" source: hosted - version: "0.5.3" + version: "1.0.0" matrix_homeserver_recommendations: dependency: "direct main" description: name: matrix_homeserver_recommendations url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.1" matrix_link_text: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e2f8c479d..131958fc3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: keyboard_shortcuts: ^0.1.4 localstorage: ^4.0.0+1 lottie: ^1.2.2 - matrix: ^0.9.4 + matrix: ^0.9.9 matrix_homeserver_recommendations: ^0.2.0 matrix_link_text: ^1.0.2 native_imaging: diff --git a/test/utils/test_client.dart b/test/utils/test_client.dart index 7cbf23394..ac9bacf4a 100644 --- a/test/utils/test_client.dart +++ b/test/utils/test_client.dart @@ -2,7 +2,7 @@ import 'package:matrix/encryption/utils/key_verification.dart'; import 'package:matrix/matrix.dart'; import 'package:matrix_api_lite/fake_matrix_api.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart'; Future prepareTestClient({ bool loggedIn = false, @@ -20,7 +20,7 @@ Future prepareTestClient({ importantStateEvents: { 'im.ponies.room_emotes', // we want emotes to work properly }, - databaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder, + databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder, supportedLoginTypes: { AuthenticationTypes.password, AuthenticationTypes.sso