diff --git a/.gitignore b/.gitignore index 8b85f543..15c900d7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,9 @@ app.*.symbols # Obfuscation related app.*.map.json + +# Flatpak +.flatpak-builder/ +flatpak-build/ +flatpak-repo/ +*.flatpak diff --git a/flatpak.yml b/flatpak.yml index 6ab28b3d..65e5d245 100644 --- a/flatpak.yml +++ b/flatpak.yml @@ -1,6 +1,6 @@ app-id: org.selfprivacy.app runtime: org.freedesktop.Platform -runtime-version: '22.08' +runtime-version: '23.08' sdk: org.freedesktop.Sdk command: selfprivacy finish-args: @@ -11,6 +11,7 @@ finish-args: - "--share=network" - "--own-name=org.selfprivacy.app" - "--device=dri" + - "--talk-name=org.freedesktop.secrets" modules: - name: selfprivacy buildsystem: simple @@ -35,7 +36,7 @@ modules: sources: - type: git url: https://gitlab.gnome.org/GNOME/libsecret.git - tag: 0.20.5 + tag: 0.21.4 - name: libjsoncpp buildsystem: meson config-opts: diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 11672859..85a86583 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; @@ -28,33 +28,47 @@ class HiveConfig { await Hive.openBox(BNames.appSettingsBox); - final HiveAesCipher cipher = HiveAesCipher( - await getEncryptedKey(BNames.serverInstallationEncryptionKey), - ); + try { + final HiveAesCipher cipher = HiveAesCipher( + await getEncryptedKey(BNames.serverInstallationEncryptionKey), + ); - await Hive.openBox(BNames.usersDeprecated); - await Hive.openBox(BNames.usersBox, encryptionCipher: cipher); + await Hive.openBox(BNames.usersDeprecated); + await Hive.openBox(BNames.usersBox, encryptionCipher: cipher); - final Box deprecatedUsers = Hive.box(BNames.usersDeprecated); - if (deprecatedUsers.isNotEmpty) { - final Box users = Hive.box(BNames.usersBox); - await users.addAll(deprecatedUsers.values.toList()); - await deprecatedUsers.clear(); + final Box deprecatedUsers = Hive.box(BNames.usersDeprecated); + if (deprecatedUsers.isNotEmpty) { + final Box users = Hive.box(BNames.usersBox); + await users.addAll(deprecatedUsers.values.toList()); + await deprecatedUsers.clear(); + } + + await Hive.openBox( + BNames.serverInstallationBox, + encryptionCipher: cipher, + ); + } on PlatformException catch (e) { + print('HiveConfig: Error while opening boxes: $e'); + rethrow; } - - await Hive.openBox(BNames.serverInstallationBox, encryptionCipher: cipher); } static Future getEncryptedKey(final String encKey) async { const FlutterSecureStorage secureStorage = FlutterSecureStorage(); - final bool hasEncryptionKey = await secureStorage.containsKey(key: encKey); - if (!hasEncryptionKey) { - final List key = Hive.generateSecureKey(); - await secureStorage.write(key: encKey, value: base64UrlEncode(key)); - } + try { + final bool hasEncryptionKey = + await secureStorage.containsKey(key: encKey); + if (!hasEncryptionKey) { + final List key = Hive.generateSecureKey(); + await secureStorage.write(key: encKey, value: base64UrlEncode(key)); + } - final String? string = await secureStorage.read(key: encKey); - return base64Url.decode(string!); + final String? string = await secureStorage.read(key: encKey); + return base64Url.decode(string!); + } on PlatformException catch (e) { + print('HiveConfig: Error while getting encryption key: $e'); + rethrow; + } } } diff --git a/lib/main.dart b/lib/main.dart index 9acae644..5c2580fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/app_controller/inherited_app_controller.dart'; import 'package:selfprivacy/config/bloc_config.dart'; @@ -10,6 +11,7 @@ import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/config/localization.dart'; import 'package:selfprivacy/config/preferences_repository/datasources/preferences_hive_datasource.dart'; import 'package:selfprivacy/config/preferences_repository/inherited_preferences_repository.dart'; +import 'package:selfprivacy/ui/pages/errors/failed_to_init_secure_storage.dart'; import 'package:selfprivacy/ui/router/router.dart'; import 'package:timezone/data/latest.dart' as tz; @@ -24,13 +26,19 @@ void main() async { // print(e); // } - await Future.wait( - >[ - HiveConfig.init(), - EasyLocalization.ensureInitialized(), - ], - ); - await getItSetup(); + try { + await Future.wait( + >[ + HiveConfig.init(), + EasyLocalization.ensureInitialized(), + ], + ); + await getItSetup(); + } on PlatformException catch (e) { + runApp( + FailedToInitSecureStorageScreen(e: e), + ); + } tz.initializeTimeZones(); diff --git a/lib/ui/layouts/brand_hero_screen.dart b/lib/ui/layouts/brand_hero_screen.dart index 6286bd3c..a82ed720 100644 --- a/lib/ui/layouts/brand_hero_screen.dart +++ b/lib/ui/layouts/brand_hero_screen.dart @@ -127,7 +127,9 @@ class _HeroSliverAppBarState extends State { Widget build(final BuildContext context) { final isMobile = widget.ignoreBreakpoints ? true : Breakpoints.small.isActive(context); - final isJobsListEmpty = context.watch().state is JobsStateEmpty; + final isJobsListEmpty = widget.hasFlashButton + ? context.watch().state is JobsStateEmpty + : true; return SliverAppBar( expandedHeight: widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height, diff --git a/lib/ui/pages/errors/failed_to_init_secure_storage.dart b/lib/ui/pages/errors/failed_to_init_secure_storage.dart new file mode 100644 index 00000000..10d6e935 --- /dev/null +++ b/lib/ui/pages/errors/failed_to_init_secure_storage.dart @@ -0,0 +1,71 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:gap/gap.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/pages/more/about_application.dart'; + +class FailedToInitSecureStorageScreen extends StatelessWidget { + const FailedToInitSecureStorageScreen({ + required this.e, + super.key, + }); + + final PlatformException e; + + @override + Widget build(final BuildContext context) => MaterialApp( + home: BrandHeroScreen( + heroIcon: Icons.error_outline, + heroTitle: 'Failed to initialize secure storage', + hasBackButton: false, + children: [ + const Text( + 'SelfPrivacy requires a secure storage provided by your operating system to encrypt sensitive data, but it failed to initialize.', + ), + if (Platform.isLinux) + const Text( + 'Please make sure that the libsecret library is installed.', + ), + const Gap(16), + Text('Error: ${e.message}'), + const Gap(16), + const Divider(), + const Gap(16), + const LinkListTile( + title: 'Our website', + subtitle: 'selfprivacy.org', + uri: 'https://selfprivacy.org/', + icon: Icons.language_outlined, + ), + const LinkListTile( + title: 'Documentation', + subtitle: 'selfprivacy.org/docs', + uri: 'https://selfprivacy.org/docs/', + icon: Icons.library_books_outlined, + ), + const LinkListTile( + title: 'Privacy Policy', + subtitle: 'selfprivacy.org/privacy-policy', + uri: 'https://selfprivacy.org/privacy-policy/', + icon: Icons.policy_outlined, + ), + const LinkListTile( + title: 'Matrix support chat', + subtitle: '#chat:selfprivacy.org', + uri: 'https://matrix.to/#/#chat:selfprivacy.org', + icon: Icons.question_answer_outlined, + longPressText: '#chat:selfprivacy.org', + ), + const LinkListTile( + title: 'Telegram support chat', + subtitle: '@selfprivacy_chat', + uri: 'https://t.me/selfprivacy_chat', + icon: Icons.question_answer_outlined, + longPressText: '@selfprivacy_chat', + ), + ], + ), + ); +}