diff --git a/lib/components/shared/dialogs/prompt_dialog.dart b/lib/components/shared/dialogs/prompt_dialog.dart index 6fd54a986..30a63bcfd 100644 --- a/lib/components/shared/dialogs/prompt_dialog.dart +++ b/lib/components/shared/dialogs/prompt_dialog.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:spotube/extensions/context.dart'; Future showPromptDialog({ required BuildContext context, required String title, required String message, String okText = "Ok", - String cancelText = "Cancel", + String? cancelText = "Cancel", }) async { return showDialog( context: context, @@ -14,12 +15,15 @@ Future showPromptDialog({ title: Text(title), content: Text(message), actions: [ - OutlinedButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text(cancelText), - ), + if (cancelText != null) + OutlinedButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text( + cancelText == "Cancel" ? context.l10n.cancel : cancelText, + ), + ), FilledButton( - child: Text(okText), + child: Text(okText == "Ok" ? context.l10n.ok : okText), onPressed: () => Navigator.of(context).pop(true), ), ], diff --git a/lib/l10n/app_bn.arb b/lib/l10n/app_bn.arb index 106190246..73ab308f2 100644 --- a/lib/l10n/app_bn.arb +++ b/lib/l10n/app_bn.arb @@ -249,5 +249,8 @@ "developers": "ডেভেলপার", "not_logged_in": "আপনি লগইন করা নেই", "search_mode": "অনুসন্ধান মোড", - "youtube_api_type": "API প্রকার" + "youtube_api_type": "API প্রকার", + "ok": "ঠিক আছে", + "failed_to_encrypt": "এনক্রিপ্ট করা ব্যর্থ হয়েছে", + "encryption_failed_warning": "Spotube আপনার তথ্যগুলি নিরাপদভাবে স্টোর করতে এনক্রিপশন ব্যবহার করে। কিন্তু এটি ব্যর্থ হয়েছে। তাই এটি অনিরাপদ স্টোরে ফলফল হবে\nযদি আপনি Linux ব্যবহার করেন, তবে দয়া করে নিশ্চিত হউন যে আপনার কোনও সিক্রেট-সার্ভিস gnome-keyring, kde-wallet, keepassxc ইত্যাদি ইনস্টল করা আছে" } \ No newline at end of file diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 9f6d67f53..4d005ea32 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -249,5 +249,8 @@ "developers": "Entwickler", "not_logged_in": "Sie sind nicht angemeldet", "search_mode": "Suchmodus", - "youtube_api_type": "API-Typ" + "youtube_api_type": "API-Typ", + "ok": "OK", + "failed_to_encrypt": "Verschlüsselung fehlgeschlagen", + "encryption_failed_warning": "Spotube verwendet Verschlüsselung, um Ihre Daten sicher zu speichern. Dies ist jedoch fehlgeschlagen. Daher wird es auf unsichere Speicherung zurückgreifen\nWenn Sie Linux verwenden, stellen Sie bitte sicher, dass Sie Secret-Services wie gnome-keyring, kde-wallet und keepassxc installiert haben" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1ded2ac6a..9e919acd9 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -249,5 +249,8 @@ "developers": "Developers", "not_logged_in": "You're not logged in", "search_mode": "Search Mode", - "youtube_api_type": "API Type" -} + "youtube_api_type": "API Type", + "ok": "Ok", + "failed_to_encrypt": "Failed to encrypt", + "encryption_failed_warning": "Spotube uses encryption to securely store your data. But failed to do so. So it'll fallback to insecure storage\nIf you're using linux, please make sure you've any secret-service (gnome-keyring, kde-wallet, keepassxc etc) installed" +} \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index eb6a714a6..9a2285998 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -249,5 +249,8 @@ "developers": "Desarrolladores", "not_logged_in": "No has iniciado sesión", "search_mode": "Modo de búsqueda", - "youtube_api_type": "Tipo de API de YouTube" -} + "youtube_api_type": "Tipo de API de YouTube", + "ok": "OK", + "failed_to_encrypt": "Error al cifrar", + "encryption_failed_warning": "Spotube utiliza el cifrado para almacenar sus datos de forma segura. Pero ha fallado. Por lo tanto, volverá a un almacenamiento no seguro\nSi está utilizando Linux, asegúrese de tener instalados servicios secretos como gnome-keyring, kde-wallet y keepassxc" +} \ No newline at end of file diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 3036a9aae..62c62d7f1 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -249,5 +249,8 @@ "developers": "Développeurs", "not_logged_in": "Vous n'êtes pas connecté(e)", "search_mode": "Mode de recherche", - "youtube_api_type": "Type d'API" + "youtube_api_type": "Type d'API", + "ok": "OK", + "failed_to_encrypt": "Échec de la cryptage", + "encryption_failed_warning": "Spotube utilise le cryptage pour stocker vos données en toute sécurité. Mais cela a échoué. Il basculera donc vers un stockage non sécurisé\nSi vous utilisez Linux, assurez-vous d'avoir installé des services secrets tels que gnome-keyring, kde-wallet et keepassxc" } \ No newline at end of file diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index 675c97e68..e6963e4b8 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -249,5 +249,8 @@ "developers": "डेवलपर्स", "not_logged_in": "आप लॉग इन नहीं हैं", "search_mode": "खोज मोड", - "youtube_api_type": "API प्रकार" + "youtube_api_type": "API प्रकार", + "ok": "ठीक है", + "failed_to_encrypt": "एन्क्रिप्ट करने में विफल रहा", + "encryption_failed_warning": "Spotube आपके डेटा को सुरक्षित रूप से स्टोर करने के लिए एन्क्रिप्शन का उपयोग करता है। लेकिन इसमें विफल रहा। इसलिए, यह असुरक्षित स्टोरेज पर फॉलबैक करेगा\nयदि आप Linux का उपयोग कर रहे हैं, तो कृपया सुनिश्चित करें कि आपके पास gnome-keyring, kde-wallet, keepassxc आदि जैसी कोई सीक्रेट-सर्विस इंस्टॉल की गई है" } \ No newline at end of file diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 85172fd0d..121605267 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -249,5 +249,8 @@ "developers": "開発", "not_logged_in": "ログインしていません", "search_mode": "検索モード", - "youtube_api_type": "APIの種類" + "youtube_api_type": "APIの種類", + "ok": "分かりました", + "failed_to_encrypt": "暗号化に失敗しました", + "encryption_failed_warning": "Spotubeはデータを安全に保存するために暗号化を使用しています。しかし、失敗しました。したがって、安全でないストレージにフォールバックします\nLinuxを使用している場合は、gnome-keyring、kde-wallet、keepassxcなどのシークレットサービスがインストールされていることを確認してください" } \ No newline at end of file diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 9212ebff3..4a4c47054 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -249,5 +249,8 @@ "developers": "开发者", "not_logged_in": "你尚未登录", "search_mode": "搜索模式", - "youtube_api_type": "API 类型" -} + "youtube_api_type": "API 类型", + "ok": "确定", + "failed_to_encrypt": "加密失败", + "encryption_failed_warning": "Spotube使用加密来安全地存储您的数据。但是失败了。因此,它将回退到不安全的存储\n如果您使用Linux,请确保已安装gnome-keyring、kde-wallet和keepassxc等秘密服务" +} \ No newline at end of file diff --git a/lib/pages/root/root_app.dart b/lib/pages/root/root_app.dart index 930c7f55f..a9ef43dab 100644 --- a/lib/pages/root/root_app.dart +++ b/lib/pages/root/root_app.dart @@ -5,12 +5,14 @@ import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart'; import 'package:spotube/components/root/bottom_player.dart'; import 'package:spotube/components/root/sidebar.dart'; import 'package:spotube/components/root/spotube_navigation_bar.dart'; import 'package:spotube/hooks/use_update_checker.dart'; import 'package:spotube/provider/download_manager_provider.dart'; +import 'package:spotube/utils/persisted_state_notifier.dart'; const rootPaths = { 0: "/", @@ -33,6 +35,17 @@ class RootApp extends HookConsumerWidget { final showingDialogCompleter = useRef(Completer()..complete()); final downloader = ref.watch(downloadManagerProvider.notifier); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((_) async { + final sharedPreferences = await SharedPreferences.getInstance(); + + if (sharedPreferences.getBool(kIsUsingEncryption) == false && + context.mounted) { + await PersistedStateNotifier.showNoEncryptionDialog(context); + } + }); + }, []); + useEffect(() { downloader.onFileExists = (track) async { if (!isMounted()) return false; diff --git a/lib/utils/persisted_state_notifier.dart b/lib/utils/persisted_state_notifier.dart index 551d74380..c8deafab7 100644 --- a/lib/utils/persisted_state_notifier.dart +++ b/lib/utils/persisted_state_notifier.dart @@ -1,10 +1,14 @@ import 'dart:async'; import 'dart:convert'; +import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:spotube/collections/routes.dart'; +import 'package:spotube/components/shared/dialogs/prompt_dialog.dart'; +import 'package:spotube/extensions/context.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/primitive_utils.dart'; @@ -15,6 +19,8 @@ const secureStorage = FlutterSecureStorage( ); const kKeyBoxName = "spotube_box_name"; +const kNoEncryptionWarningShownKey = "showedNoEncryptionWarning"; +const kIsUsingEncryption = "isUsingEncryption"; String getBoxKey(String boxName) => "spotube_box_$boxName"; abstract class PersistedStateNotifier extends StateNotifier { @@ -34,12 +40,36 @@ abstract class PersistedStateNotifier extends StateNotifier { static late LazyBox _box; static late LazyBox _encryptedBox; + static Future showNoEncryptionDialog(BuildContext context) async { + final localStorage = await SharedPreferences.getInstance(); + final wasShownAlready = + localStorage.getBool(kNoEncryptionWarningShownKey) == true; + + if (wasShownAlready || !context.mounted) { + return; + } + + await showPromptDialog( + context: context, + title: context.l10n.failed_to_encrypt, + message: context.l10n.encryption_failed_warning, + cancelText: null, + ); + await localStorage.setBool(kNoEncryptionWarningShownKey, true); + } + static Future read(String key) async { final localStorage = await SharedPreferences.getInstance(); if (kIsMacOS || kIsIOS) { return localStorage.getString(key); } else { - return secureStorage.read(key: key); + try { + await localStorage.setBool(kIsUsingEncryption, true); + return await secureStorage.read(key: key); + } catch (e) { + await localStorage.setBool(kIsUsingEncryption, false); + return localStorage.getString(key); + } } } @@ -49,7 +79,13 @@ abstract class PersistedStateNotifier extends StateNotifier { await localStorage.setString(key, value); return; } else { - return secureStorage.write(key: key, value: value); + try { + await localStorage.setBool(kIsUsingEncryption, true); + await secureStorage.write(key: key, value: value); + } catch (e) { + await localStorage.setBool(kIsUsingEncryption, false); + await localStorage.setString(key, value); + } } }