diff --git a/assets/localization/en.json b/assets/localization/en.json index 4cbe05c9..c7df00d1 100644 --- a/assets/localization/en.json +++ b/assets/localization/en.json @@ -87,6 +87,7 @@ "lunasea.Settings": "Settings", "lunasea.Stable": "Stable", "lunasea.StartingView": "Starting View", + "lunasea.TransactionFailure": "Transaction Failure", "lunasea.TryAgain": "Try Again", "lunasea.Tweaks": "Tweaks", "lunasea.Unknown": "Unknown", @@ -280,6 +281,7 @@ "settings.AddModuleSuccess": "Module Added", "settings.AddProfile": "Add Profile", "settings.AddProfileDescription": "Add a New Profile", + "settings.AddedProfile": "Added Profile", "settings.AllFieldsAreRequired": "All fields are required", "settings.AmoledTheme": "AMOLED Theme", "settings.AmoledThemeBorders": "AMOLED Theme Borders", @@ -318,6 +320,7 @@ "settings.BuildChannels": "Build Channels", "settings.BuildChannelsDescription": "Learn About Additional Build Channels", "settings.CalendarSettings": "Calendar Settings", + "settings.ChangedProfile": "Changed Profile", "settings.Channel": "Channel", "settings.ClearConfiguration": "Clear Configuration", "settings.ClearConfigurationHint1": "Are you sure you want to clear your configuration?", @@ -354,6 +357,7 @@ "settings.DecryptBackupHint1": "Please enter the encryption key for this backup.", "settings.DebugMenu": "Debug Menu", "settings.DebugMenuDescription": "Debug and Development Utilities", + "settings.DeletedProfile": "Deleted Profile", "settings.DeleteAccount": "Delete Account", "settings.DeleteAccountDescription": "Permanently Delete Your Account", "settings.DeleteAccountHint1": "Are you sure you want to delete your LunaSea account?", @@ -444,9 +448,11 @@ "settings.NewPassword": "New Password", "settings.Notifications": "Notifications", "settings.NotificationsDescription": "Set up Webhooks for Push Notifications", + "settings.NoAdditionalProfilesAdded": "No additional profiles have been added", "settings.NoBackupsFound": "No Backups Found", "settings.NoExternalModulesFound": "No External Modules Found", "settings.NoHeadersAdded": "No Headers Added", + "settings.NoProfilesFound": "No Profiles Found", "settings.OpenLinksIn": "Open Links In…", "settings.Password": "Password", "settings.PasswordHint1": "If your password includes special characters, considering adding a basic authentication header with your username and password instead for better support", @@ -463,6 +469,7 @@ "settings.ProfileAlreadyExists": "Profile Already Exists", "settings.ProfileName": "Profile Name", "settings.ProfileNameRequired": "Profile Name Required", + "settings.ProfileToProfile": "\"{}\" to \"{}\"", "settings.QuickActions": "Quick Actions", "settings.QuickActionsDescription": "Quick Actions on the Home Screen", "settings.QuickActionsBannerLine1": "Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing LunaSea's icon.", @@ -471,6 +478,7 @@ "settings.Register": "Register", "settings.RegisteredFailure": "Failed to Register", "settings.RegisteredSuccess": "Registered", + "settings.RenamedProfile": "Renamed Profile", "settings.RenameProfile": "Rename Profile", "settings.RenameProfileDescription": "Rename an Existing Profile", "settings.ResetPassword": "Reset Password", diff --git a/lib/core.dart b/lib/core.dart index 77059d93..0cb31ef4 100644 --- a/lib/core.dart +++ b/lib/core.dart @@ -20,13 +20,13 @@ export 'core/router/router.dart'; export 'core/services/the_movie_db.dart'; export 'core/state/module_state.dart'; export 'core/state/state.dart'; -export 'core/system/profile.dart'; export 'core/utilities/changelog.dart'; export 'core/utilities/dialogs.dart'; export 'database/box.dart'; export 'database/tables/lunasea.dart'; export 'system/logger.dart'; +export 'system/profile.dart'; export 'widgets/ui.dart'; export 'modules.dart'; export 'vendor.dart' diff --git a/lib/core/state/state.dart b/lib/core/state/state.dart index 9f0d419c..364676e9 100644 --- a/lib/core/state/state.dart +++ b/lib/core/state/state.dart @@ -22,7 +22,8 @@ class LunaState { GlobalKey(); /// Calls `.reset()` on all states which extend [LunaModuleState]. - static void reset(BuildContext context) { + static void reset([BuildContext? ctx]) { + final context = ctx ?? navigatorKey.currentContext!; LunaModule.values.forEach((module) => module.state(context)?.reset()); } diff --git a/lib/core/system/profile.dart b/lib/core/system/profile.dart deleted file mode 100644 index 40adc5e2..00000000 --- a/lib/core/system/profile.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'package:lunasea/core.dart'; -import 'package:lunasea/modules/settings.dart'; - -class LunaProfile { - static const String DEFAULT_PROFILE = 'default'; - - /// Returns a list of the profiles, sorted by their lowercase key/display name. - List profilesList() { - return LunaBox.profiles.data.keys.map((p) => p.toString()).toList() - ..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); - } - - /// Safely change profiles. - /// - /// Does this safely by: - /// - Ensures that the passed in profile isn't already enabled - /// - Ensures that the profile exists - Future safelyChangeProfiles( - String profile, { - bool showSnackbar = true, - bool popToFirst = false, - }) async { - if (LunaSeaDatabase.ENABLED_PROFILE.read() == profile) return true; - if (LunaBox.profiles.contains(profile)) { - LunaSeaDatabase.ENABLED_PROFILE.update(profile); - LunaState.reset(LunaState.navigatorKey.currentContext!); - if (showSnackbar) - showLunaSuccessSnackBar( - title: 'Changed Profile', - message: profile, - ); - if (popToFirst) LunaState.navigatorKey.currentState!.lunaPopToFirst(); - return true; - } else { - LunaLogger().warning('LunaProfile', 'changeProfile', - 'Attempted to change profile to unknown profile: $profile'); - return false; - } - } - - /// Rename a profile. - /// - /// Shows a dialog with a list of profiles, on selection: - /// - Shows a input bar for a new name - /// - Updates the profile's key (which is also the display name) - Future renameProfile({bool showSnackbar = true}) async { - List profiles = profilesList(); - Tuple2 selectedProfile = await SettingsDialogs() - .renameProfile(LunaState.navigatorKey.currentContext!, profiles); - if (selectedProfile.item1) { - Tuple2 newProfile = await SettingsDialogs() - .renameProfileSelected( - LunaState.navigatorKey.currentContext!, profiles); - if (newProfile.item1) { - ProfileHiveObject oldProfileObject = - LunaBox.profiles.read(selectedProfile.item2)!; - LunaBox.profiles.update(newProfile.item2, - ProfileHiveObject.fromProfileHiveObject(oldProfileObject)); - if (LunaSeaDatabase.ENABLED_PROFILE.read() == selectedProfile.item2) - LunaProfile() - .safelyChangeProfiles(newProfile.item2, showSnackbar: false); - oldProfileObject.delete(); - if (showSnackbar) - showLunaSuccessSnackBar( - title: 'Renamed Profile', - message: - '"${selectedProfile.item2}" has been renamed to "${newProfile.item2}"'); - } - return true; - } - return false; - } - - /// Delete a profile. - /// - /// Show a dialog with a list of profiles, on selection: - /// - Validate that the profile is not currently enabled - Future deleteProfile({bool showSnackbar = true}) async { - List profiles = profilesList(); - profiles.remove(LunaSeaDatabase.ENABLED_PROFILE.read()); - if (profiles.isNotEmpty) { - Tuple2 selectedProfile = await SettingsDialogs() - .deleteProfile(LunaState.navigatorKey.currentContext!, profiles); - if (selectedProfile.item1) { - if (selectedProfile.item2 != LunaSeaDatabase.ENABLED_PROFILE.read()) { - LunaBox.profiles.delete(selectedProfile.item2); - if (showSnackbar) - showLunaSuccessSnackBar( - title: 'Deleted Profile', - message: '"${selectedProfile.item2}" has been deleted'); - return true; - } - if (showSnackbar) - showLunaErrorSnackBar( - title: 'Unable to Delete Profile', - message: 'Cannot delete the enabled profile'); - } - } else { - showLunaInfoSnackBar( - title: 'No Profiles to Delete', - message: 'No additional profiles have been added'); - } - return false; - } - - Future addProfile({bool showSnackbar = true}) async { - List profiles = profilesList(); - Tuple2 result = await SettingsDialogs() - .addProfile(LunaState.navigatorKey.currentContext!, profiles); - if (result.item1) { - await LunaBox.profiles.update(result.item2, ProfileHiveObject.empty()); - safelyChangeProfiles(result.item2); - if (showSnackbar) - showLunaSuccessSnackBar( - title: 'Profile Added', - message: result.item2, - ); - } - return false; - } - - static ProfileHiveObject get current { - const _db = LunaSeaDatabase.ENABLED_PROFILE; - String _name = LunaBox.lunasea.read(_db.key) ?? DEFAULT_PROFILE; - return LunaBox.profiles.read(_name) ?? ProfileHiveObject.empty(); - } -} diff --git a/lib/database/box.dart b/lib/database/box.dart index 19ba4a3d..dca69373 100644 --- a/lib/database/box.dart +++ b/lib/database/box.dart @@ -1,8 +1,10 @@ import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:lunasea/core/models/configuration/external_module.dart'; import 'package:lunasea/core/models/configuration/indexer.dart'; import 'package:lunasea/core/models/configuration/profile.dart'; import 'package:lunasea/core/models/logs/log.dart'; +import 'package:lunasea/database/table.dart'; import 'package:lunasea/system/logger.dart'; import 'package:lunasea/vendor.dart'; @@ -51,10 +53,6 @@ enum LunaBox { return _instance.delete(key); } - ValueListenable> listenable([List? keys]) { - return _instance.listenable(keys: keys); - } - Future clear() async { _instance.keys.forEach((k) async => await _instance.delete(k)); } @@ -62,6 +60,28 @@ enum LunaBox { Future> _open() async { return Hive.openBox(key); } + + ValueListenable> listenable([List? keys]) { + return _instance.listenable(keys: keys); + } + + ValueListenableBuilder watch({ + required Widget Function(BuildContext, Widget?) builder, + List? selectKeys, + List? selectItems, + Key? key, + Widget? child, + }) { + final items = selectItems?.map((item) => item.key).toList(); + final keys = [...?selectKeys, ...?items]; + + return ValueListenableBuilder( + key: key, + valueListenable: listenable(keys.isNotEmpty ? keys : null), + builder: (context, _, widget) => builder(context, widget), + child: child, + ); + } } extension LunaBoxExtension on LunaBox { diff --git a/lib/database/database.dart b/lib/database/database.dart index b52a8b79..5560f427 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -1,9 +1,9 @@ import 'package:lunasea/core/models/configuration/profile.dart'; -import 'package:lunasea/core/system/profile.dart'; import 'package:lunasea/database/box.dart'; import 'package:lunasea/database/table.dart'; import 'package:lunasea/database/tables/lunasea.dart'; import 'package:lunasea/system/platform.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/vendor.dart'; class LunaDatabase { diff --git a/lib/database/table.dart b/lib/database/table.dart index d4271e8b..ab76a4a1 100644 --- a/lib/database/table.dart +++ b/lib/database/table.dart @@ -90,15 +90,15 @@ mixin LunaTableMixin on Enum { return update(value); } - ValueListenableBuilder listen({ + ValueListenableBuilder watch({ required Widget Function(BuildContext, Widget?) builder, Key? key, Widget? child, }) { - return ValueListenableBuilder( + return box.watch( key: key, - valueListenable: box.listenable([this.key]), - builder: (context, _, widget) => builder(context, widget), + selectItems: [this], + builder: (context, widget) => builder(context, widget), child: child, ); } diff --git a/lib/firebase/messaging.dart b/lib/firebase/messaging.dart index 82a2295d..bd464e31 100644 --- a/lib/firebase/messaging.dart +++ b/lib/firebase/messaging.dart @@ -130,10 +130,7 @@ class LunaFirebaseMessaging { ); return; } - bool result = await LunaProfile().safelyChangeProfiles( - profile, - popToFirst: true, - ); + bool result = LunaProfile().changeTo(profile, popToRoot: true); if (result) { module.handleWebhook(message.data); } else { diff --git a/lib/main.dart b/lib/main.dart index 1f7bccd7..10aff9c2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -63,12 +63,12 @@ class LunaBIOS extends StatelessWidget { startLocale: LunaLocalization.fallbackLocale, useFallbackTranslations: true, child: LunaState.providers( - child: ValueListenableBuilder( - valueListenable: LunaBox.lunasea.listenable([ - LunaSeaDatabase.THEME_AMOLED.key, - LunaSeaDatabase.THEME_AMOLED_BORDER.key, - ]), - builder: (context, dynamic box, _) { + child: LunaBox.lunasea.watch( + selectItems: [ + LunaSeaDatabase.THEME_AMOLED, + LunaSeaDatabase.THEME_AMOLED_BORDER, + ], + builder: (context, _) { return MaterialApp( localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, diff --git a/lib/modules.dart b/lib/modules.dart index 0bf8d723..2e1e91fa 100644 --- a/lib/modules.dart +++ b/lib/modules.dart @@ -498,10 +498,10 @@ extension LunaModuleExtension on LunaModule { String key = 'LUNASEA_MODULE_INFORMATION_${this.key}'; void markSeen() => LunaBox.alerts.update(key, false); - return ValueListenableBuilder( - valueListenable: LunaBox.alerts.listenable([key]), - builder: (context, box, _) { - if (LunaBox.lunasea.read(key, fallback: true)) { + return LunaBox.alerts.watch( + selectKeys: [key], + builder: (context, _) { + if (LunaBox.alerts.read(key, fallback: true)) { return LunaBanner( dismissCallback: markSeen, headerText: this.title, diff --git a/lib/modules/dashboard/core/api/data/lidarr.dart b/lib/modules/dashboard/core/api/data/lidarr.dart index c01f5f39..cc4b9e33 100644 --- a/lib/modules/dashboard/core/api/data/lidarr.dart +++ b/lib/modules/dashboard/core/api/data/lidarr.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/modules/lidarr/core/api/api.dart'; import 'package:lunasea/modules/lidarr/routes/details_artist.dart'; diff --git a/lib/modules/dashboard/core/api/data/radarr.dart b/lib/modules/dashboard/core/api/data/radarr.dart index b7c873ca..5d35c0f2 100644 --- a/lib/modules/dashboard/core/api/data/radarr.dart +++ b/lib/modules/dashboard/core/api/data/radarr.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/core/extensions/int.dart'; import 'package:lunasea/extensions/string.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/vendor.dart'; import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/modules/radarr/core/api_helper.dart'; diff --git a/lib/modules/dashboard/core/api/data/sonarr.dart b/lib/modules/dashboard/core/api/data/sonarr.dart index c18313a4..056aec00 100644 --- a/lib/modules/dashboard/core/api/data/sonarr.dart +++ b/lib/modules/dashboard/core/api/data/sonarr.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/database/tables/lunasea.dart'; import 'package:lunasea/extensions/string.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/system/logger.dart'; import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/vendor.dart'; diff --git a/lib/modules/dashboard/core/state.dart b/lib/modules/dashboard/core/state.dart index 5167ac33..67cb6301 100644 --- a/lib/modules/dashboard/core/state.dart +++ b/lib/modules/dashboard/core/state.dart @@ -1,6 +1,6 @@ import 'package:lunasea/core/models/configuration/profile.dart'; import 'package:lunasea/core/state/module_state.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/database/tables/dashboard.dart'; import 'package:lunasea/modules/dashboard/core/adapters/calendar_starting_type.dart'; import 'package:lunasea/modules/dashboard/core/api/api.dart'; diff --git a/lib/modules/dashboard/routes/dashboard/pages/modules.dart b/lib/modules/dashboard/routes/dashboard/pages/modules.dart index 3ce18ec0..9e2d68dd 100644 --- a/lib/modules/dashboard/routes/dashboard/pages/modules.dart +++ b/lib/modules/dashboard/routes/dashboard/pages/modules.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lunasea/modules.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/database/tables/lunasea.dart'; import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/modules/wake_on_lan/api/wake_on_lan.dart'; diff --git a/lib/modules/dashboard/routes/dashboard/route.dart b/lib/modules/dashboard/routes/dashboard/route.dart index f70eeea6..0ba85937 100644 --- a/lib/modules/dashboard/routes/dashboard/route.dart +++ b/lib/modules/dashboard/routes/dashboard/route.dart @@ -64,7 +64,7 @@ class _State extends State { } Widget _body() { - return LunaSeaDatabase.ENABLED_PROFILE.listen( + return LunaSeaDatabase.ENABLED_PROFILE.watch( builder: (context, _) => LunaPageView( controller: _pageController, children: [ diff --git a/lib/modules/dashboard/routes/dashboard/widgets/calendar_view.dart b/lib/modules/dashboard/routes/dashboard/widgets/calendar_view.dart index d1971c7a..eec606bc 100644 --- a/lib/modules/dashboard/routes/dashboard/widgets/calendar_view.dart +++ b/lib/modules/dashboard/routes/dashboard/widgets/calendar_view.dart @@ -124,12 +124,12 @@ class _State extends State { } Widget _calendar() { - return ValueListenableBuilder( - valueListenable: LunaBox.lunasea.listenable([ - DashboardDatabase.CALENDAR_STARTING_DAY.key, - DashboardDatabase.CALENDAR_STARTING_SIZE.key, - ]), - builder: (context, dynamic box, _) { + return LunaBox.lunasea.watch( + selectItems: [ + DashboardDatabase.CALENDAR_STARTING_DAY, + DashboardDatabase.CALENDAR_STARTING_SIZE, + ], + builder: (context, _) { DateTime firstDay = context.watch().today!.subtract( Duration(days: DashboardDatabase.CALENDAR_DAYS_PAST.read()), ); diff --git a/lib/modules/dashboard/routes/dashboard/widgets/content_block.dart b/lib/modules/dashboard/routes/dashboard/widgets/content_block.dart index 02353296..d383b824 100644 --- a/lib/modules/dashboard/routes/dashboard/widgets/content_block.dart +++ b/lib/modules/dashboard/routes/dashboard/widgets/content_block.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/modules/dashboard/core/api/data/abstract.dart'; import 'package:lunasea/modules/dashboard/core/api/data/lidarr.dart'; diff --git a/lib/modules/lidarr/core/dialogs.dart b/lib/modules/lidarr/core/dialogs.dart index 8ba8b2f7..e5becec1 100644 --- a/lib/modules/lidarr/core/dialogs.dart +++ b/lib/modules/lidarr/core/dialogs.dart @@ -318,7 +318,7 @@ class LidarrDialogs { ], showCancelButton: false, content: [ - LidarrDatabase.ADD_ARTIST_SEARCH_FOR_MISSING.listen( + LidarrDatabase.ADD_ARTIST_SEARCH_FOR_MISSING.watch( builder: (context, _) => LunaDialog.checkbox( title: 'lidarr.StartSearchForMissingAlbums'.tr(), value: LidarrDatabase.ADD_ARTIST_SEARCH_FOR_MISSING.read(), diff --git a/lib/modules/lidarr/routes/add_details.dart b/lib/modules/lidarr/routes/add_details.dart index 4f50cfcd..84345e6f 100644 --- a/lib/modules/lidarr/routes/add_details.dart +++ b/lib/modules/lidarr/routes/add_details.dart @@ -164,7 +164,7 @@ class _State extends State with LunaScrollControllerMixin { _arguments!.data.discogsLink!.openLink(); }, ), - LidarrDatabase.ADD_ROOT_FOLDER.listen( + LidarrDatabase.ADD_ROOT_FOLDER.watch( builder: (context, _) { final _rootfolder = LidarrDatabase.ADD_ROOT_FOLDER.read(); return LunaBlock( @@ -182,7 +182,7 @@ class _State extends State with LunaScrollControllerMixin { ); }, ), - LidarrDatabase.ADD_MONITORED_STATUS.listen(builder: (context, _) { + LidarrDatabase.ADD_MONITORED_STATUS.watch(builder: (context, _) { const _db = LidarrDatabase.ADD_MONITORED_STATUS; final _status = LidarrMonitorStatus.ALL.fromKey(_db.read()) ?? LidarrMonitorStatus.ALL; @@ -198,7 +198,7 @@ class _State extends State with LunaScrollControllerMixin { }, ); }), - LidarrDatabase.ADD_QUALITY_PROFILE.listen( + LidarrDatabase.ADD_QUALITY_PROFILE.watch( builder: (context, _) { final _profile = LidarrDatabase.ADD_QUALITY_PROFILE.read(); return LunaBlock( @@ -216,7 +216,7 @@ class _State extends State with LunaScrollControllerMixin { ); }, ), - LidarrDatabase.ADD_METADATA_PROFILE.listen( + LidarrDatabase.ADD_METADATA_PROFILE.watch( builder: (context, _) { final _profile = LidarrDatabase.ADD_METADATA_PROFILE.read(); return LunaBlock( diff --git a/lib/modules/radarr/core/dialogs.dart b/lib/modules/radarr/core/dialogs.dart index 5a1fe84b..69474887 100644 --- a/lib/modules/radarr/core/dialogs.dart +++ b/lib/modules/radarr/core/dialogs.dart @@ -466,7 +466,7 @@ class RadarrDialogs { ), ], content: [ - RadarrDatabase.QUEUE_REMOVE_FROM_CLIENT.listen( + RadarrDatabase.QUEUE_REMOVE_FROM_CLIENT.watch( builder: (context, _) => LunaDialog.checkbox( title: 'Remove From Client', value: RadarrDatabase.QUEUE_REMOVE_FROM_CLIENT.read(), @@ -474,7 +474,7 @@ class RadarrDialogs { RadarrDatabase.QUEUE_REMOVE_FROM_CLIENT.update(selected!), ), ), - RadarrDatabase.QUEUE_BLACKLIST.listen( + RadarrDatabase.QUEUE_BLACKLIST.watch( builder: (context, _) => LunaDialog.checkbox( title: 'Blacklist Release', value: RadarrDatabase.QUEUE_BLACKLIST.read(), @@ -609,7 +609,7 @@ class RadarrDialogs { ), ], content: [ - RadarrDatabase.REMOVE_MOVIE_IMPORT_LIST.listen( + RadarrDatabase.REMOVE_MOVIE_IMPORT_LIST.watch( builder: (context, _) => LunaDialog.checkbox( title: 'Add to Exclusion List', value: RadarrDatabase.REMOVE_MOVIE_IMPORT_LIST.read(), @@ -617,7 +617,7 @@ class RadarrDialogs { RadarrDatabase.REMOVE_MOVIE_IMPORT_LIST.update(value!), ), ), - RadarrDatabase.REMOVE_MOVIE_DELETE_FILES.listen( + RadarrDatabase.REMOVE_MOVIE_DELETE_FILES.watch( builder: (context, _) => LunaDialog.checkbox( title: 'Delete Files', value: RadarrDatabase.REMOVE_MOVIE_DELETE_FILES.read(), @@ -689,7 +689,7 @@ class RadarrDialogs { ], showCancelButton: false, content: [ - RadarrDatabase.ADD_MOVIE_SEARCH_FOR_MISSING.listen( + RadarrDatabase.ADD_MOVIE_SEARCH_FOR_MISSING.watch( builder: (context, _) => LunaDialog.checkbox( title: 'radarr.StartSearchForMissingMovie'.tr(), value: RadarrDatabase.ADD_MOVIE_SEARCH_FOR_MISSING.read(), diff --git a/lib/modules/radarr/routes/manual_import_details/widgets/bottom_action_bar.dart b/lib/modules/radarr/routes/manual_import_details/widgets/bottom_action_bar.dart index 7feee927..60a1cb53 100644 --- a/lib/modules/radarr/routes/manual_import_details/widgets/bottom_action_bar.dart +++ b/lib/modules/radarr/routes/manual_import_details/widgets/bottom_action_bar.dart @@ -11,7 +11,7 @@ class RadarrManualImportDetailsBottomActionBar extends StatelessWidget { Widget build(BuildContext context) { return LunaBottomActionBar( actions: [ - RadarrDatabase.MANUAL_IMPORT_DEFAULT_MODE.listen( + RadarrDatabase.MANUAL_IMPORT_DEFAULT_MODE.watch( builder: (context, _) => LunaActionBarCard( title: 'radarr.ImportMode'.tr(), subtitle: RadarrImportMode.COPY diff --git a/lib/modules/search/core/dialogs.dart b/lib/modules/search/core/dialogs.dart index 5ee6889a..38d82e3e 100644 --- a/lib/modules/search/core/dialogs.dart +++ b/lib/modules/search/core/dialogs.dart @@ -18,7 +18,7 @@ class SearchDialogs { await LunaDialog.dialog( context: context, title: 'search.Download'.tr(), - customContent: LunaSeaDatabase.ENABLED_PROFILE.listen( + customContent: LunaSeaDatabase.ENABLED_PROFILE.watch( builder: (context, _) => LunaDialog.content( children: [ Padding( @@ -54,7 +54,7 @@ class SearchDialogs { ), onSelected: (result) { HapticFeedback.selectionClick(); - LunaProfile().safelyChangeProfiles(result); + LunaProfile().changeTo(result); }, itemBuilder: (context) { return >[ diff --git a/lib/modules/settings/core/banners.dart b/lib/modules/settings/core/banners.dart index 23715fb2..2c301a95 100644 --- a/lib/modules/settings/core/banners.dart +++ b/lib/modules/settings/core/banners.dart @@ -72,21 +72,22 @@ extension SettingsBannersExtension on SettingsBanners { ValueListenableBuilder banner({ Color headerColor = Colors.white, Color bodyColor = LunaColours.grey, - }) => - ValueListenableBuilder( - valueListenable: LunaBox.alerts.listenable([key]), - builder: (context, box, _) { - if (shouldShow!) - return LunaBanner( - dismissCallback: markSeen, - headerText: header, - bodyText: body, - icon: icon, - iconColor: iconColor, - headerColor: headerColor, - bodyColor: bodyColor, - ); - return const SizedBox(height: 0.0, width: double.infinity); - }, - ); + }) { + return LunaBox.alerts.watch( + selectKeys: [key], + builder: (context, _) { + if (shouldShow!) + return LunaBanner( + dismissCallback: markSeen, + headerText: header, + bodyText: body, + icon: icon, + iconColor: iconColor, + headerColor: headerColor, + bodyColor: bodyColor, + ); + return const SizedBox(height: 0.0, width: double.infinity); + }, + ); + } } diff --git a/lib/modules/settings/core/dialogs.dart b/lib/modules/settings/core/dialogs.dart index 011dcb2d..e5b62c02 100644 --- a/lib/modules/settings/core/dialogs.dart +++ b/lib/modules/settings/core/dialogs.dart @@ -722,14 +722,14 @@ class SettingsDialogs { return Tuple2(_flag, _controller.text); } - Future> renameProfile( + Future> renameProfile( BuildContext context, - List profiles, + List profiles, ) async { bool _flag = false; - String? _profile = ''; + String _profile = ''; - void _setValues(bool flag, String? profile) { + void _setValues(bool flag, String profile) { _flag = flag; _profile = profile; Navigator.of(context).pop(); @@ -743,7 +743,7 @@ class SettingsDialogs { (index) => LunaDialog.tile( icon: Icons.settings_rounded, iconColor: LunaColours().byListIndex(index), - text: profiles[index]!, + text: profiles[index], onTap: () => _setValues(true, profiles[index]), ), ), @@ -800,14 +800,14 @@ class SettingsDialogs { return Tuple2(_flag, _controller.text); } - Future> deleteProfile( + Future> deleteProfile( BuildContext context, - List profiles, + List profiles, ) async { bool _flag = false; - String? _profile = ''; + String _profile = ''; - void _setValues(bool flag, String? profile) { + void _setValues(bool flag, String profile) { _flag = flag; _profile = profile; Navigator.of(context).pop(); @@ -821,7 +821,7 @@ class SettingsDialogs { (index) => LunaDialog.tile( icon: Icons.settings_rounded, iconColor: LunaColours().byListIndex(index), - text: profiles[index]!, + text: profiles[index], onTap: () => _setValues(true, profiles[index]), ), ), @@ -830,14 +830,14 @@ class SettingsDialogs { return Tuple2(_flag, _profile); } - Future> enabledProfile( + Future> enabledProfile( BuildContext context, - List profiles, + List profiles, ) async { bool _flag = false; - String? _profile = ''; + String _profile = ''; - void _setValues(bool flag, String? profile) { + void _setValues(bool flag, String profile) { _flag = flag; _profile = profile; Navigator.of(context).pop(); @@ -851,7 +851,7 @@ class SettingsDialogs { (index) => LunaDialog.tile( icon: LunaIcons.USER, iconColor: LunaColours().byListIndex(index), - text: profiles[index]!, + text: profiles[index], onTap: () => _setValues(true, profiles[index]), ), ), diff --git a/lib/modules/settings/core/pages/headers.dart b/lib/modules/settings/core/pages/headers.dart index 156d27a6..fe147f28 100644 --- a/lib/modules/settings/core/pages/headers.dart +++ b/lib/modules/settings/core/pages/headers.dart @@ -53,9 +53,8 @@ class _State extends State with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic profile, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ if ((_headers() ?? {}).isEmpty) _noHeadersFound(), diff --git a/lib/modules/settings/routes/configuration/route.dart b/lib/modules/settings/routes/configuration/route.dart index 4e0ead35..9ae71a17 100644 --- a/lib/modules/settings/routes/configuration/route.dart +++ b/lib/modules/settings/routes/configuration/route.dart @@ -37,21 +37,38 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { return LunaAppBar( title: 'settings.Configuration'.tr(), scrollControllers: [scrollController], - actions: [ - LunaIconButton( + actions: [_enabledProfile()], + ); + } + + Widget _enabledProfile() { + return LunaBox.profiles.watch( + builder: (context, _) { + if (LunaBox.profiles.size < 2) return const SizedBox(); + return LunaIconButton( icon: Icons.switch_account_rounded, onPressed: () async { - Tuple2 results = - await SettingsDialogs().enabledProfile( - LunaState.navigatorKey.currentContext!, - LunaProfile().profilesList(), - ); - if (results.item1 && - results.item2 != LunaSeaDatabase.ENABLED_PROFILE.read()) - LunaProfile().safelyChangeProfiles(results.item2!); + final dialogs = SettingsDialogs(); + final enabledProfile = LunaSeaDatabase.ENABLED_PROFILE.read(); + final context = LunaState.navigatorKey.currentContext!; + final profiles = LunaProfile.list; + profiles.removeWhere((p) => p == enabledProfile); + + if (profiles.isEmpty) { + showLunaInfoSnackBar( + title: 'settings.NoProfilesFound'.tr(), + message: 'settings.NoAdditionalProfilesAdded'.tr(), + ); + return; + } + + final selected = await dialogs.enabledProfile(context, profiles); + if (selected.item1) { + LunaProfile().changeTo(selected.item2); + } }, - ), - ], + ); + }, ); } diff --git a/lib/modules/settings/routes/configuration_appearance/route.dart b/lib/modules/settings/routes/configuration_appearance/route.dart index 478fe23a..ce24152b 100644 --- a/lib/modules/settings/routes/configuration_appearance/route.dart +++ b/lib/modules/settings/routes/configuration_appearance/route.dart @@ -52,7 +52,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _amoledTheme() { const _db = LunaSeaDatabase.THEME_AMOLED; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.AmoledTheme'.tr(), body: [ @@ -70,12 +70,12 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _amoledThemeBorders() { - return ValueListenableBuilder( - valueListenable: LunaBox.lunasea.listenable([ - LunaSeaDatabase.THEME_AMOLED_BORDER.key, - LunaSeaDatabase.THEME_AMOLED.key, - ]), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.lunasea.watch( + selectItems: [ + LunaSeaDatabase.THEME_AMOLED_BORDER, + LunaSeaDatabase.THEME_AMOLED, + ], + builder: (context, _) => LunaBlock( title: 'settings.AmoledThemeBorders'.tr(), body: [ TextSpan(text: 'settings.AmoledThemeBordersDescription'.tr()), @@ -92,7 +92,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _imageBackgroundOpacity() { const _db = LunaSeaDatabase.THEME_IMAGE_BACKGROUND_OPACITY; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.BackgroundImageOpacity'.tr(), body: [ diff --git a/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart b/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart index bcd8b27d..ad517930 100644 --- a/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart +++ b/lib/modules/settings/routes/configuration_dashboard/pages/calendar_settings.dart @@ -71,7 +71,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _pastDaysInSchedule() { const _db = DashboardDatabase.CALENDAR_SHOW_PAST_DAYS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.PastDaysInScheduleView'.tr(), trailing: LunaSwitch( @@ -84,7 +84,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _pastDays() { const _db = DashboardDatabase.CALENDAR_DAYS_PAST; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.PastDays'.tr(), body: [ @@ -106,7 +106,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _futureDays() { const _db = DashboardDatabase.CALENDAR_DAYS_FUTURE; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.FutureDays'.tr(), body: [ @@ -128,7 +128,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _modulesLidarr() { const _db = DashboardDatabase.CALENDAR_ENABLE_LIDARR; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: LunaModule.LIDARR.title, body: [ @@ -148,7 +148,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _modulesRadarr() { const _db = DashboardDatabase.CALENDAR_ENABLE_RADARR; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: LunaModule.RADARR.title, body: [ @@ -168,7 +168,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _modulesSonarr() { const _db = DashboardDatabase.CALENDAR_ENABLE_SONARR; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: LunaModule.SONARR.title, body: [ @@ -188,7 +188,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _startingView() { const _db = DashboardDatabase.CALENDAR_STARTING_TYPE; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.StartingView'.tr(), body: [ @@ -206,7 +206,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _startingDay() { const _db = DashboardDatabase.CALENDAR_STARTING_DAY; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.StartingDay'.tr(), body: [ @@ -224,7 +224,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _startingSize() { const _db = DashboardDatabase.CALENDAR_STARTING_SIZE; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.StartingSize'.tr(), body: [ diff --git a/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart b/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart index 595da4d3..f2366bcd 100644 --- a/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_dashboard/pages/default_pages.dart @@ -55,7 +55,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = DashboardDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'lunasea.Home'.tr(), body: [TextSpan(text: HomeNavigationBar.titles[_db.read()])], diff --git a/lib/modules/settings/routes/configuration_drawer/route.dart b/lib/modules/settings/routes/configuration_drawer/route.dart index 9b8ba3f4..02b38014 100644 --- a/lib/modules/settings/routes/configuration_drawer/route.dart +++ b/lib/modules/settings/routes/configuration_drawer/route.dart @@ -54,7 +54,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { body: [ TextSpan(text: 'settings.AutomaticallyManageOrderDescription'.tr()), ], - trailing: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.listen( + trailing: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.watch( builder: (context, _) => LunaSwitch( value: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.read(), onChanged: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.update, @@ -84,7 +84,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _reorderableModuleTile(int index) { - return LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.listen( + return LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.watch( key: ObjectKey(_modules![index]), builder: (context, _) => LunaBlock( disabled: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.read(), diff --git a/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart b/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart index b7dd3a00..a134ffb9 100644 --- a/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart +++ b/lib/modules/settings/routes/configuration_externalmodules/pages/edit_module.dart @@ -104,9 +104,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.externalModules.listenable([widget.moduleId]), - builder: (context, dynamic _, __) { + return LunaBox.externalModules.watch( + selectKeys: [widget.moduleId], + builder: (context, dynamic _) { if (!LunaBox.externalModules.contains(widget.moduleId)) return Container(); _module = LunaBox.externalModules.read(widget.moduleId); diff --git a/lib/modules/settings/routes/configuration_externalmodules/route.dart b/lib/modules/settings/routes/configuration_externalmodules/route.dart index 7b6516fd..13e02c55 100644 --- a/lib/modules/settings/routes/configuration_externalmodules/route.dart +++ b/lib/modules/settings/routes/configuration_externalmodules/route.dart @@ -54,9 +54,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.externalModules.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.externalModules.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ LunaModule.EXTERNAL_MODULES.informationBanner(), diff --git a/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart index 9c2e8e39..b5344631 100644 --- a/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_lidarr/pages/connection_details.dart @@ -51,9 +51,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart b/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart index 8b747d26..fc79d9b7 100644 --- a/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_lidarr/pages/default_pages.dart @@ -51,7 +51,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = LidarrDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'lunasea.Home'.tr(), body: [TextSpan(text: LidarrNavigationBar.titles[_db.read()])], diff --git a/lib/modules/settings/routes/configuration_lidarr/route.dart b/lib/modules/settings/routes/configuration_lidarr/route.dart index d12ecebb..5f628a0c 100644 --- a/lib/modules/settings/routes/configuration_lidarr/route.dart +++ b/lib/modules/settings/routes/configuration_lidarr/route.dart @@ -54,9 +54,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'settings.EnableModule'.tr(args: [LunaModule.LIDARR.title]), trailing: LunaSwitch( value: LunaProfile.current.lidarrEnabled ?? false, diff --git a/lib/modules/settings/routes/configuration_localization/route.dart b/lib/modules/settings/routes/configuration_localization/route.dart index 26d4f350..12671fc9 100644 --- a/lib/modules/settings/routes/configuration_localization/route.dart +++ b/lib/modules/settings/routes/configuration_localization/route.dart @@ -69,7 +69,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _use24HourTime() { const _db = LunaSeaDatabase.USE_24_HOUR_TIME; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.Use24HourTime'.tr(), body: [TextSpan(text: 'settings.Use24HourTimeDescription'.tr())], diff --git a/lib/modules/settings/routes/configuration_network/route.dart b/lib/modules/settings/routes/configuration_network/route.dart index 49604d10..c02a608f 100644 --- a/lib/modules/settings/routes/configuration_network/route.dart +++ b/lib/modules/settings/routes/configuration_network/route.dart @@ -51,7 +51,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _useTLSValidation() { const _db = LunaSeaDatabase.NETWORKING_TLS_VALIDATION; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.TLSCertificateValidation'.tr(), body: [ diff --git a/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart b/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart index f296445d..bb1b90d2 100644 --- a/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_nzbget/pages/connection_details.dart @@ -51,9 +51,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart b/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart index f439a477..b29e1aba 100644 --- a/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_nzbget/pages/default_pages.dart @@ -52,7 +52,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = NZBGetDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'lunasea.Home'.tr(), body: [TextSpan(text: NZBGetNavigationBar.titles[_db.read()])], diff --git a/lib/modules/settings/routes/configuration_nzbget/route.dart b/lib/modules/settings/routes/configuration_nzbget/route.dart index 9486ccaa..846edfa1 100644 --- a/lib/modules/settings/routes/configuration_nzbget/route.dart +++ b/lib/modules/settings/routes/configuration_nzbget/route.dart @@ -54,9 +54,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'settings.EnableModule'.tr(args: [LunaModule.NZBGET.title]), trailing: LunaSwitch( value: LunaProfile.current.nzbgetEnabled ?? false, diff --git a/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart index 863f38df..c129d832 100644 --- a/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_overseerr/pages/connection_details.dart @@ -51,9 +51,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_overseerr/route.dart b/lib/modules/settings/routes/configuration_overseerr/route.dart index e680f5ae..93a03a44 100644 --- a/lib/modules/settings/routes/configuration_overseerr/route.dart +++ b/lib/modules/settings/routes/configuration_overseerr/route.dart @@ -52,9 +52,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'Enable ${LunaModule.OVERSEERR.title}', trailing: LunaSwitch( value: LunaProfile.current.overseerrEnabled ?? false, diff --git a/lib/modules/settings/routes/configuration_quickactions/route.dart b/lib/modules/settings/routes/configuration_quickactions/route.dart index 0e472dc1..2787e7b7 100644 --- a/lib/modules/settings/routes/configuration_quickactions/route.dart +++ b/lib/modules/settings/routes/configuration_quickactions/route.dart @@ -85,15 +85,16 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _actionTile(String title, LunaSeaDatabase action) { return LunaBlock( title: title, - trailing: ValueListenableBuilder( - valueListenable: LunaBox.lunasea.listenable([action.key]), - builder: (context, dynamic _, __) => LunaSwitch( - value: action.read(), - onChanged: (value) { - action.update(value); - if (LunaQuickActions.isSupported) - LunaQuickActions().setActionItems(); - }), + trailing: LunaBox.lunasea.watch( + selectKeys: [action.key], + builder: (context, _) => LunaSwitch( + value: action.read(), + onChanged: (value) { + action.update(value); + if (LunaQuickActions.isSupported) + LunaQuickActions().setActionItems(); + }, + ), ), ); } diff --git a/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart index 2fff67e8..2d13d308 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/connection_details.dart @@ -51,9 +51,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_radarr/pages/default_options.dart b/lib/modules/settings/routes/configuration_radarr/pages/default_options.dart index fab59214..05659de8 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/default_options.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/default_options.dart @@ -60,7 +60,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _viewMovies() { const _db = RadarrDatabase.DEFAULT_VIEW_MOVIES; - return _db.listen( + return _db.watch( builder: (context, _) { return LunaBlock( title: 'lunasea.View'.tr(), @@ -94,7 +94,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingMovies() { const _db = RadarrDatabase.DEFAULT_SORTING_MOVIES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortCategory'.tr(), body: [TextSpan(text: _db.read().readable)], @@ -125,7 +125,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingMoviesDirection() { const _db = RadarrDatabase.DEFAULT_SORTING_MOVIES_ASCENDING; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortDirection'.tr(), body: [ @@ -150,7 +150,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _filteringMovies() { const _db = RadarrDatabase.DEFAULT_FILTERING_MOVIES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.FilterCategory'.tr(), body: [TextSpan(text: _db.read().readable)], @@ -179,7 +179,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingReleases() { const _db = RadarrDatabase.DEFAULT_SORTING_RELEASES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortCategory'.tr(), body: [TextSpan(text: _db.read().readable)], @@ -207,7 +207,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingReleasesDirection() { const _db = RadarrDatabase.DEFAULT_SORTING_RELEASES_ASCENDING; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortDirection'.tr(), body: [ @@ -227,7 +227,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _filteringReleases() { const _db = RadarrDatabase.DEFAULT_FILTERING_RELEASES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.FilterCategory'.tr(), body: [TextSpan(text: _db.read().readable)], diff --git a/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart b/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart index 313c518e..5e8c893a 100644 --- a/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_radarr/pages/default_pages.dart @@ -54,7 +54,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = RadarrDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.Home'.tr(), body: [TextSpan(text: RadarrNavigationBar.titles[_db.read()])], @@ -73,7 +73,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _movieDetailsPage() { const _db = RadarrDatabase.NAVIGATION_INDEX_MOVIE_DETAILS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Movie Details', body: [ @@ -96,7 +96,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _addMoviePage() { const _db = RadarrDatabase.NAVIGATION_INDEX_ADD_MOVIE; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Add Movie', body: [TextSpan(text: RadarrAddMovieNavigationBar.titles[_db.read()])], @@ -116,7 +116,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _systemStatusPage() { const _db = RadarrDatabase.NAVIGATION_INDEX_SYSTEM_STATUS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'System Status', body: [ diff --git a/lib/modules/settings/routes/configuration_radarr/route.dart b/lib/modules/settings/routes/configuration_radarr/route.dart index 53577ba2..e9f82a24 100644 --- a/lib/modules/settings/routes/configuration_radarr/route.dart +++ b/lib/modules/settings/routes/configuration_radarr/route.dart @@ -56,9 +56,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'Enable ${LunaModule.RADARR.title}', trailing: LunaSwitch( value: LunaProfile.current.radarrEnabled ?? false, @@ -104,7 +103,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _discoverUseRadarrSuggestionsToggle() { const _db = RadarrDatabase.ADD_DISCOVER_USE_SUGGESTIONS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Discover Suggestions', body: const [TextSpan(text: 'Add Suggested Releases in Discover')], @@ -118,7 +117,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _queueSize() { const _db = RadarrDatabase.QUEUE_PAGE_SIZE; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Queue Size', body: [ diff --git a/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart b/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart index 50312350..263c4ddc 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/pages/connection_details.dart @@ -51,9 +51,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart b/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart index 6db3da31..c33ec3b3 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/pages/default_pages.dart @@ -53,7 +53,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = SABnzbdDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'lunasea.Home'.tr(), body: [TextSpan(text: SABnzbdNavigationBar.titles[_db.read()])], diff --git a/lib/modules/settings/routes/configuration_sabnzbd/route.dart b/lib/modules/settings/routes/configuration_sabnzbd/route.dart index 63cc65b8..77945447 100644 --- a/lib/modules/settings/routes/configuration_sabnzbd/route.dart +++ b/lib/modules/settings/routes/configuration_sabnzbd/route.dart @@ -55,9 +55,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'Enable ${LunaModule.SABNZBD.title}', trailing: LunaSwitch( value: LunaProfile.current.sabnzbdEnabled ?? false, diff --git a/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart b/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart index 40d1dd1c..cb739536 100644 --- a/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart +++ b/lib/modules/settings/routes/configuration_search/pages/edit_indexer.dart @@ -94,21 +94,22 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.indexers.listenable([widget.indexerId]), - builder: (context, dynamic box, __) { - if (!LunaBox.indexers.contains(widget.indexerId)) return Container(); - _indexer = LunaBox.indexers.read(widget.indexerId); - return LunaListView( - controller: scrollController, - children: [ - _displayName(), - _apiURL(), - _apiKey(), - _headers(), - ], - ); - }); + return LunaBox.indexers.watch( + selectKeys: [widget.indexerId], + builder: (context, _) { + if (!LunaBox.indexers.contains(widget.indexerId)) return Container(); + _indexer = LunaBox.indexers.read(widget.indexerId); + return LunaListView( + controller: scrollController, + children: [ + _displayName(), + _apiURL(), + _apiKey(), + _headers(), + ], + ); + }, + ); } Widget _displayName() { diff --git a/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart b/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart index 078068e9..86940557 100644 --- a/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart +++ b/lib/modules/settings/routes/configuration_search/pages/edit_indexer_headers.dart @@ -86,20 +86,21 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.indexers.listenable([widget.indexerId]), - builder: (context, dynamic box, __) { - if (!LunaBox.indexers.contains(widget.indexerId)) return Container(); - _indexer = LunaBox.indexers.read(widget.indexerId); - return LunaListView( - controller: scrollController, - children: [ - if ((_indexer!.headers ?? {}).isEmpty) - LunaMessage.inList(text: 'No Headers Added'), - ..._list(), - ], - ); - }); + return LunaBox.indexers.watch( + selectKeys: [widget.indexerId], + builder: (context, _) { + if (!LunaBox.indexers.contains(widget.indexerId)) return Container(); + _indexer = LunaBox.indexers.read(widget.indexerId); + return LunaListView( + controller: scrollController, + children: [ + if ((_indexer!.headers ?? {}).isEmpty) + LunaMessage.inList(text: 'No Headers Added'), + ..._list(), + ], + ); + }, + ); } List _list() { diff --git a/lib/modules/settings/routes/configuration_search/route.dart b/lib/modules/settings/routes/configuration_search/route.dart index 82152c6d..9ff8a631 100644 --- a/lib/modules/settings/routes/configuration_search/route.dart +++ b/lib/modules/settings/routes/configuration_search/route.dart @@ -53,9 +53,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.indexers.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.indexers.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ LunaModule.SEARCH.informationBanner(), @@ -107,7 +106,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _hideAdultCategories() { const _db = SearchDatabase.HIDE_XXX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Hide Adult Categories', body: const [TextSpan(text: 'Hide Adult Content')], @@ -121,7 +120,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _showLinks() { const _db = SearchDatabase.SHOW_LINKS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Show Links', body: const [TextSpan(text: 'Show Download and Comments Links')], diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart b/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart index 0316c1cd..958d1c02 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/connection_details.dart @@ -50,9 +50,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/default_options.dart b/lib/modules/settings/routes/configuration_sonarr/pages/default_options.dart index 3d918058..806f45e9 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/default_options.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/default_options.dart @@ -59,7 +59,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _viewSeries() { const _db = SonarrDatabase.DEFAULT_VIEW_SERIES; - return _db.listen( + return _db.watch( builder: (context, _) { LunaListViewOption _view = _db.read(); return LunaBlock( @@ -94,7 +94,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingSeries() { const _db = SonarrDatabase.DEFAULT_SORTING_SERIES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortCategory'.tr(), body: [TextSpan(text: _db.read().readable)], @@ -125,7 +125,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingSeriesDirection() { const _db = SonarrDatabase.DEFAULT_SORTING_SERIES_ASCENDING; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortDirection'.tr(), body: [ @@ -145,7 +145,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _filteringSeries() { const _db = SonarrDatabase.DEFAULT_FILTERING_SERIES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.FilterCategory'.tr(), body: [TextSpan(text: _db.read().readable)], @@ -173,7 +173,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingReleases() { const _db = SonarrDatabase.DEFAULT_SORTING_RELEASES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortCategory'.tr(), body: [TextSpan(text: _db.read().readable)], @@ -201,7 +201,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _sortingReleasesDirection() { const _db = SonarrDatabase.DEFAULT_SORTING_RELEASES_ASCENDING; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.SortDirection'.tr(), body: [ @@ -221,7 +221,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _filteringReleases() { const _db = SonarrDatabase.DEFAULT_FILTERING_RELEASES; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'settings.FilterCategory'.tr(), body: [TextSpan(text: _db.read().readable)], diff --git a/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart b/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart index 7233b6d1..36600765 100644 --- a/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_sonarr/pages/default_pages.dart @@ -52,7 +52,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = SonarrDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) { return LunaBlock( title: 'lunasea.Home'.tr(), @@ -73,7 +73,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _seriesDetailsPage() { const _db = SonarrDatabase.NAVIGATION_INDEX_SERIES_DETAILS; - return _db.listen( + return _db.watch( builder: (context, _) { return LunaBlock( title: 'sonarr.SeriesDetails'.tr(), @@ -97,7 +97,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _seasonDetailsPage() { const _db = SonarrDatabase.NAVIGATION_INDEX_SEASON_DETAILS; - return _db.listen( + return _db.watch( builder: (context, _) { return LunaBlock( title: 'sonarr.SeasonDetails'.tr(), diff --git a/lib/modules/settings/routes/configuration_sonarr/route.dart b/lib/modules/settings/routes/configuration_sonarr/route.dart index 888a0360..e1bcbb9b 100644 --- a/lib/modules/settings/routes/configuration_sonarr/route.dart +++ b/lib/modules/settings/routes/configuration_sonarr/route.dart @@ -54,9 +54,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'Enable ${LunaModule.SONARR.title}', trailing: LunaSwitch( value: LunaProfile.current.sonarrEnabled ?? false, @@ -110,7 +109,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _queueSize() { const _db = SonarrDatabase.QUEUE_PAGE_SIZE; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Queue Size', body: [ diff --git a/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart b/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart index 484ffdcc..805ca414 100644 --- a/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart +++ b/lib/modules/settings/routes/configuration_tautulli/pages/connection_details.dart @@ -50,9 +50,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ _host(), diff --git a/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart b/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart index a4a4d609..c9e3ad97 100644 --- a/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart +++ b/lib/modules/settings/routes/configuration_tautulli/pages/default_pages.dart @@ -55,7 +55,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _homePage() { const _db = TautulliDatabase.NAVIGATION_INDEX; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'lunasea.Home'.tr(), body: [TextSpan(text: TautulliNavigationBar.titles[_db.read()])], @@ -74,7 +74,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _graphsPage() { const _db = TautulliDatabase.NAVIGATION_INDEX_GRAPHS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Graphs', body: [TextSpan(text: TautulliGraphsNavigationBar.titles[_db.read()])], @@ -94,7 +94,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _libraryDetailsPage() { const _db = TautulliDatabase.NAVIGATION_INDEX_LIBRARIES_DETAILS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Library Details', body: [ @@ -117,7 +117,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _mediaDetailsPage() { const _db = TautulliDatabase.NAVIGATION_INDEX_MEDIA_DETAILS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'Media Details', body: [ @@ -139,7 +139,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _userDetailsPage() { const _db = TautulliDatabase.NAVIGATION_INDEX_USER_DETAILS; - return _db.listen( + return _db.watch( builder: (context, _) => LunaBlock( title: 'User Details', body: [ diff --git a/lib/modules/settings/routes/configuration_tautulli/route.dart b/lib/modules/settings/routes/configuration_tautulli/route.dart index cdc9cc1b..0fab6c91 100644 --- a/lib/modules/settings/routes/configuration_tautulli/route.dart +++ b/lib/modules/settings/routes/configuration_tautulli/route.dart @@ -56,9 +56,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _enabledToggle() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic _, __) => LunaBlock( + return LunaBox.profiles.watch( + builder: (context, _) => LunaBlock( title: 'Enable ${LunaModule.TAUTULLI.title}', trailing: LunaSwitch( value: LunaProfile.current.tautulliEnabled ?? false, @@ -96,7 +95,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _defaultTerminationMessage() { const _db = TautulliDatabase.TERMINATION_MESSAGE; - return _db.listen( + return _db.watch( builder: (context, _) { String message = _db.read(); return LunaBlock( @@ -115,7 +114,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _activityRefreshRate() { const _db = TautulliDatabase.REFRESH_RATE; - return _db.listen(builder: (context, _) { + return _db.watch(builder: (context, _) { String? refreshRate; if (_db.read() == 1) refreshRate = 'Every Second'; if (_db.read() != 1) refreshRate = 'Every ${_db.read()} Seconds'; @@ -133,7 +132,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { Widget _statisticsItemCount() { const _db = TautulliDatabase.STATISTICS_STATS_COUNT; - return _db.listen( + return _db.watch( builder: (context, _) { String? statisticsItems; if (_db.read() == 1) statisticsItems = '1 Item'; diff --git a/lib/modules/settings/routes/configuration_wakeonlan/route.dart b/lib/modules/settings/routes/configuration_wakeonlan/route.dart index ac0d9ee6..5d6b26cf 100644 --- a/lib/modules/settings/routes/configuration_wakeonlan/route.dart +++ b/lib/modules/settings/routes/configuration_wakeonlan/route.dart @@ -40,9 +40,8 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.profiles.listenable(), - builder: (context, dynamic box, _) => LunaListView( + return LunaBox.profiles.watch( + builder: (context, _) => LunaListView( controller: scrollController, children: [ LunaModule.WAKE_ON_LAN.informationBanner(), diff --git a/lib/modules/settings/routes/notifications/route.dart b/lib/modules/settings/routes/notifications/route.dart index 7d5c4cb3..107c71ed 100644 --- a/lib/modules/settings/routes/notifications/route.dart +++ b/lib/modules/settings/routes/notifications/route.dart @@ -74,7 +74,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { const db = LunaSeaDatabase.ENABLE_IN_APP_NOTIFICATIONS; return LunaBlock( title: 'settings.EnableInAppNotifications'.tr(), - trailing: db.listen( + trailing: db.watch( builder: (context, _) => LunaSwitch( value: db.read(), onChanged: db.update, diff --git a/lib/modules/settings/routes/profiles.dart b/lib/modules/settings/routes/profiles.dart index a023072a..582a8652 100644 --- a/lib/modules/settings/routes/profiles.dart +++ b/lib/modules/settings/routes/profiles.dart @@ -43,43 +43,105 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { children: [ SettingsBanners.PROFILES_SUPPORT.banner(), _enabledProfile(), - LunaBlock( - title: 'settings.AddProfile'.tr(), - body: [TextSpan(text: 'settings.AddProfileDescription'.tr())], - trailing: const LunaIconButton(icon: LunaIcons.ADD), - onTap: () async => LunaProfile().addProfile(), - ), - LunaBlock( - title: 'settings.RenameProfile'.tr(), - body: [TextSpan(text: 'settings.RenameProfileDescription'.tr())], - trailing: const LunaIconButton(icon: LunaIcons.RENAME), - onTap: () async => LunaProfile().renameProfile(), - ), - LunaBlock( - title: 'settings.DeleteProfile'.tr(), - body: [TextSpan(text: 'settings.DeleteProfileDescription'.tr())], - trailing: const LunaIconButton(icon: LunaIcons.DELETE), - onTap: () async => LunaProfile().deleteProfile(), - ), + _addProfile(), + _renameProfile(), + _deleteProfile(), ], ); } + Widget _addProfile() { + return LunaBlock( + title: 'settings.AddProfile'.tr(), + body: [TextSpan(text: 'settings.AddProfileDescription'.tr())], + trailing: const LunaIconButton(icon: LunaIcons.ADD), + onTap: () async { + final dialogs = SettingsDialogs(); + final context = LunaState.navigatorKey.currentContext!; + final profiles = LunaProfile.list; + + final selected = await dialogs.addProfile(context, profiles); + if (selected.item1) { + LunaProfile().create(selected.item2); + } + }, + ); + } + + Widget _renameProfile() { + return LunaBlock( + title: 'settings.RenameProfile'.tr(), + body: [TextSpan(text: 'settings.RenameProfileDescription'.tr())], + trailing: const LunaIconButton(icon: LunaIcons.RENAME), + onTap: () async { + final dialogs = SettingsDialogs(); + final context = LunaState.navigatorKey.currentContext!; + final profiles = LunaProfile.list; + + final selected = await dialogs.renameProfile(context, profiles); + if (selected.item1) { + final name = await dialogs.renameProfileSelected(context, profiles); + if (name.item1) { + LunaProfile().rename(selected.item2, name.item2); + } + } + }, + ); + } + + Widget _deleteProfile() { + return LunaBlock( + title: 'settings.DeleteProfile'.tr(), + body: [TextSpan(text: 'settings.DeleteProfileDescription'.tr())], + trailing: const LunaIconButton(icon: LunaIcons.DELETE), + onTap: () async { + final dialogs = SettingsDialogs(); + final enabledProfile = LunaSeaDatabase.ENABLED_PROFILE.read(); + final context = LunaState.navigatorKey.currentContext!; + final profiles = LunaProfile.list; + profiles.removeWhere((p) => p == enabledProfile); + + if (profiles.isEmpty) { + showLunaInfoSnackBar( + title: 'settings.NoProfilesFound'.tr(), + message: 'settings.NoAdditionalProfilesAdded'.tr(), + ); + return; + } + + final selected = await dialogs.deleteProfile(context, profiles); + if (selected.item1) { + LunaProfile().remove(selected.item2); + } + }); + } + Widget _enabledProfile() { const db = LunaSeaDatabase.ENABLED_PROFILE; - return db.listen( + return db.watch( builder: (context, _) => LunaBlock( title: 'settings.EnabledProfile'.tr(), body: [TextSpan(text: db.read())], trailing: const LunaIconButton(icon: LunaIcons.USER), onTap: () async { - Tuple2 results = - await SettingsDialogs().enabledProfile( - LunaState.navigatorKey.currentContext!, - LunaProfile().profilesList(), - ); - if (results.item1 && results.item2 != db.read()) - LunaProfile().safelyChangeProfiles(results.item2!); + final dialogs = SettingsDialogs(); + final enabledProfile = LunaSeaDatabase.ENABLED_PROFILE.read(); + final context = LunaState.navigatorKey.currentContext!; + final profiles = LunaProfile.list; + profiles.removeWhere((p) => p == enabledProfile); + + if (profiles.isEmpty) { + showLunaInfoSnackBar( + title: 'settings.NoProfilesFound'.tr(), + message: 'settings.NoAdditionalProfilesAdded'.tr(), + ); + return; + } + + final selected = await dialogs.enabledProfile(context, profiles); + if (selected.item1) { + LunaProfile().changeTo(selected.item2); + } }, ), ); diff --git a/lib/modules/settings/routes/system_logs/pages/log_details.dart b/lib/modules/settings/routes/system_logs/pages/log_details.dart index 1a363064..d542e86d 100644 --- a/lib/modules/settings/routes/system_logs/pages/log_details.dart +++ b/lib/modules/settings/routes/system_logs/pages/log_details.dart @@ -66,48 +66,51 @@ class _State extends State<_Widget> with LunaScrollControllerMixin { } Widget _body() { - return ValueListenableBuilder( - valueListenable: LunaBox.logs.listenable(), - builder: (context, dynamic box, _) { - List logs = filter(box); - if (logs.isEmpty) { - return LunaMessage.goBack( - context: context, - text: 'No Logs Found', - ); - } - return LunaListViewBuilder( - controller: scrollController, - itemCount: logs.length, - itemBuilder: (context, index) => SettingsSystemLogTile( - log: logs[index], - ), - ); - }); + return LunaBox.logs.watch(builder: (context, _) { + List logs = filter(); + if (logs.isEmpty) { + return LunaMessage.goBack( + context: context, + text: 'No Logs Found', + ); + } + return LunaListViewBuilder( + controller: scrollController, + itemCount: logs.length, + itemBuilder: (context, index) => SettingsSystemLogTile( + log: logs[index], + ), + ); + }); } - List filter(Box box) { + List filter() { List logs; + const box = LunaBox.logs; + switch (widget.type) { case LunaLogType.WARNING: - logs = - box.values.where((log) => log.type == LunaLogType.WARNING).toList(); + logs = box.data.values + .where((log) => log.type == LunaLogType.WARNING) + .toList(); break; case LunaLogType.ERROR: - logs = - box.values.where((log) => log.type == LunaLogType.ERROR).toList(); + logs = box.data.values + .where((log) => log.type == LunaLogType.ERROR) + .toList(); break; case LunaLogType.CRITICAL: - logs = box.values + logs = box.data.values .where((log) => log.type == LunaLogType.CRITICAL) .toList(); break; case LunaLogType.DEBUG: - logs = - box.values.where((log) => log.type == LunaLogType.DEBUG).toList(); + logs = box.data.values + .where((log) => log.type == LunaLogType.DEBUG) + .toList(); break; default: - logs = box.values.where((log) => log.type.enabled).toList(); + logs = box.data.values.where((log) => log.type.enabled).toList(); break; } logs.sort((a, b) => (b.timestamp).compareTo(a.timestamp)); diff --git a/lib/modules/sonarr/core/dialogs.dart b/lib/modules/sonarr/core/dialogs.dart index 7776e9f9..6251148b 100644 --- a/lib/modules/sonarr/core/dialogs.dart +++ b/lib/modules/sonarr/core/dialogs.dart @@ -556,7 +556,7 @@ class SonarrDialogs { ), ], content: [ - SonarrDatabase.REMOVE_SERIES_EXCLUSION_LIST.listen( + SonarrDatabase.REMOVE_SERIES_EXCLUSION_LIST.watch( builder: (context, _) => LunaDialog.checkbox( title: 'sonarr.AddToExclusionList'.tr(), value: SonarrDatabase.REMOVE_SERIES_EXCLUSION_LIST.read(), @@ -564,7 +564,7 @@ class SonarrDialogs { SonarrDatabase.REMOVE_SERIES_EXCLUSION_LIST.update(value!), ), ), - SonarrDatabase.REMOVE_SERIES_DELETE_FILES.listen( + SonarrDatabase.REMOVE_SERIES_DELETE_FILES.watch( builder: (context, _) => LunaDialog.checkbox( title: 'sonarr.DeleteFiles'.tr(), value: SonarrDatabase.REMOVE_SERIES_DELETE_FILES.read(), @@ -590,7 +590,7 @@ class SonarrDialogs { ], showCancelButton: false, content: [ - SonarrDatabase.ADD_SERIES_SEARCH_FOR_MISSING.listen( + SonarrDatabase.ADD_SERIES_SEARCH_FOR_MISSING.watch( builder: (context, _) => LunaDialog.checkbox( title: 'sonarr.StartSearchForMissingEpisodes'.tr(), value: SonarrDatabase.ADD_SERIES_SEARCH_FOR_MISSING.read(), @@ -598,7 +598,7 @@ class SonarrDialogs { SonarrDatabase.ADD_SERIES_SEARCH_FOR_MISSING.update(value!), ), ), - SonarrDatabase.ADD_SERIES_SEARCH_FOR_CUTOFF_UNMET.listen( + SonarrDatabase.ADD_SERIES_SEARCH_FOR_CUTOFF_UNMET.watch( builder: (context, _) => LunaDialog.checkbox( title: 'sonarr.StartSearchForCutoffUnmetEpisodes'.tr(), value: SonarrDatabase.ADD_SERIES_SEARCH_FOR_CUTOFF_UNMET.read(), @@ -689,7 +689,7 @@ class SonarrDialogs { ), ], content: [ - SonarrDatabase.QUEUE_REMOVE_DOWNLOAD_CLIENT.listen( + SonarrDatabase.QUEUE_REMOVE_DOWNLOAD_CLIENT.watch( builder: (context, _) => LunaDialog.checkbox( title: 'sonarr.RemoveFromDownloadClient'.tr(), value: SonarrDatabase.QUEUE_REMOVE_DOWNLOAD_CLIENT.read(), @@ -697,7 +697,7 @@ class SonarrDialogs { SonarrDatabase.QUEUE_REMOVE_DOWNLOAD_CLIENT.update(value!), ), ), - SonarrDatabase.QUEUE_ADD_BLOCKLIST.listen( + SonarrDatabase.QUEUE_ADD_BLOCKLIST.watch( builder: (context, _) => LunaDialog.checkbox( title: 'sonarr.AddReleaseToBlocklist'.tr(), value: SonarrDatabase.QUEUE_ADD_BLOCKLIST.read(), diff --git a/lib/modules/wake_on_lan/api/platform/wake_on_lan_io.dart b/lib/modules/wake_on_lan/api/platform/wake_on_lan_io.dart index 3967b61f..8392fcd5 100644 --- a/lib/modules/wake_on_lan/api/platform/wake_on_lan_io.dart +++ b/lib/modules/wake_on_lan/api/platform/wake_on_lan_io.dart @@ -1,5 +1,5 @@ import 'package:lunasea/core/models/configuration/profile.dart'; -import 'package:lunasea/core/system/profile.dart'; +import 'package:lunasea/system/profile.dart'; import 'package:lunasea/widgets/ui.dart'; import 'package:lunasea/system/logger.dart'; import 'package:wake_on_lan/wake_on_lan.dart'; diff --git a/lib/system/logger.dart b/lib/system/logger.dart index 933ffbdc..70cc4f94 100644 --- a/lib/system/logger.dart +++ b/lib/system/logger.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; +import 'package:lunasea/types/exception.dart'; class LunaLogger { static String get checkLogsMessage => 'lunasea.CheckLogsMessage'.tr(); @@ -81,4 +82,21 @@ class LunaLogger { LunaBox.logs.create(log); } } + + void exception(LunaException exception, [StackTrace? trace]) { + switch (exception.type) { + case LunaLogType.WARNING: + // TODO: Handle this case. + break; + case LunaLogType.ERROR: + // TODO: Handle this case. + break; + case LunaLogType.CRITICAL: + // TODO: Handle this case. + break; + case LunaLogType.DEBUG: + // TODO: Handle this case. + break; + } + } } diff --git a/lib/system/profile.dart b/lib/system/profile.dart index 1471cde2..ca7c88f1 100644 --- a/lib/system/profile.dart +++ b/lib/system/profile.dart @@ -1,3 +1,203 @@ +import 'package:lunasea/core/extensions/navigator_state.dart'; +import 'package:lunasea/core/models/configuration/profile.dart'; +import 'package:lunasea/core/state/state.dart'; +import 'package:lunasea/database/box.dart'; +import 'package:lunasea/database/tables/lunasea.dart'; +import 'package:lunasea/system/logger.dart'; +import 'package:lunasea/types/exception.dart'; +import 'package:lunasea/widgets/ui.dart'; + class LunaProfile { static const String DEFAULT_PROFILE = 'default'; + + static ProfileHiveObject get current { + final enabled = LunaSeaDatabase.ENABLED_PROFILE.read(); + return LunaBox.profiles.read(enabled) ?? ProfileHiveObject.empty(); + } + + static List get list { + final profiles = LunaBox.profiles.data.keys.cast().toList(); + profiles.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); + return profiles; + } + + bool changeTo( + String profile, { + bool showSnackbar = true, + bool popToRoot = false, + }) { + try { + if (LunaSeaDatabase.ENABLED_PROFILE.read() == profile) return true; + _changeTo(profile); + + if (showSnackbar) { + showLunaSuccessSnackBar( + title: 'settings.ChangedProfile'.tr(), + message: profile, + ); + } + + if (popToRoot) { + LunaState.navigatorKey.currentState!.lunaPopToFirst(); + } + + return true; + } on ProfileNotFoundException catch (error, trace) { + LunaLogger().exception(error, trace); + } + return false; + } + + Future create( + String profile, { + bool showSnackbar = true, + }) async { + try { + await _create(profile); + _changeTo(profile); + + if (showSnackbar) { + showLunaSuccessSnackBar( + title: 'settings.AddedProfile'.tr(), + message: profile, + ); + } + } on ProfileAlreadyExistsException catch (error, trace) { + LunaLogger().exception(error, trace); + } catch (error, trace) { + LunaLogger().error('Failed to create profile', error, trace); + } + + return false; + } + + Future remove( + String profile, { + bool showSnackbar = true, + }) async { + try { + await _remove(profile); + + if (showSnackbar) { + showLunaSuccessSnackBar( + title: 'settings.DeletedProfile'.tr(), + message: profile, + ); + } + } on ProfileNotFoundException catch (error, trace) { + LunaLogger().exception(error, trace); + } on ActiveProfileRemovalException catch (error, trace) { + LunaLogger().exception(error, trace); + } catch (error, trace) { + LunaLogger().error('Failed to delete profile', error, trace); + } + + return false; + } + + Future rename( + String oldProfile, + String newProfile, { + bool showSnackbar = true, + }) async { + try { + await _rename(oldProfile, newProfile); + + if (showSnackbar) { + showLunaSuccessSnackBar( + title: 'settings.RenamedProfile'.tr(), + message: 'settings.ProfileToProfile'.tr( + args: [oldProfile, newProfile], + ), + ); + } + + return true; + } on ProfileNotFoundException catch (error, trace) { + LunaLogger().exception(error, trace); + } on ProfileAlreadyExistsException catch (error, trace) { + LunaLogger().exception(error, trace); + } catch (error, trace) { + LunaLogger().error('Failed to rename profile', error, trace); + } + + return false; + } + + void _changeTo(String profile) { + if (!LunaBox.profiles.contains(profile)) { + throw ProfileNotFoundException(profile); + } + + LunaSeaDatabase.ENABLED_PROFILE.update(profile); + LunaState.reset(); + } + + Future _create(String profile) async { + if (LunaBox.profiles.contains(profile)) { + throw ProfileAlreadyExistsException(profile); + } + + await LunaBox.profiles.update(profile, ProfileHiveObject.empty()); + } + + Future _remove(String profile) async { + if (LunaSeaDatabase.ENABLED_PROFILE.read() == profile) { + throw ActiveProfileRemovalException(profile); + } + + if (!LunaBox.profiles.contains(profile)) { + throw ProfileNotFoundException(profile); + } + + await LunaBox.profiles.delete(profile); + } + + Future _rename(String oldProfile, String newProfile) async { + if (!LunaBox.profiles.contains(oldProfile)) { + throw ProfileNotFoundException(oldProfile); + } + + if (LunaBox.profiles.contains(newProfile)) { + throw ProfileAlreadyExistsException(newProfile); + } + + final oldDb = LunaBox.profiles.read(oldProfile)!; + final newDb = ProfileHiveObject.fromProfileHiveObject(oldDb); + + await LunaBox.profiles.update(newProfile, newDb); + _changeTo(newProfile); + + oldDb.delete(); + } +} + +class ProfileNotFoundException with ErrorExceptionMixin { + final String profile; + const ProfileNotFoundException(this.profile); + + @override + String toString() { + return 'ProfileNotFoundException: "$profile" was not found'; + } +} + +class ProfileAlreadyExistsException with ErrorExceptionMixin { + final String profile; + const ProfileAlreadyExistsException(this.profile); + + @override + String toString() { + return 'ProfileAlreadyExistsException: "$profile" already exists'; + } +} + +class ActiveProfileRemovalException with ErrorExceptionMixin { + final String profile; + const ActiveProfileRemovalException(this.profile); + + @override + String toString() { + return 'ActiveProfileRemovalException: "$profile" can\'t be removed as it is in use'; + } } diff --git a/lib/types/exception.dart b/lib/types/exception.dart new file mode 100644 index 00000000..f46ab534 --- /dev/null +++ b/lib/types/exception.dart @@ -0,0 +1,20 @@ +import 'package:lunasea/core/models/logs/log_type.dart'; + +abstract class LunaException implements Exception { + LunaLogType get type; +} + +mixin DebugExceptionMixin implements LunaException { + @override + LunaLogType get type => LunaLogType.DEBUG; +} + +mixin WarningExceptionMixin implements LunaException { + @override + LunaLogType get type => LunaLogType.WARNING; +} + +mixin ErrorExceptionMixin implements LunaException { + @override + LunaLogType get type => LunaLogType.ERROR; +} diff --git a/lib/widgets/appbar.dart b/lib/widgets/appbar.dart index 0ae6e2dd..48ecac48 100644 --- a/lib/widgets/appbar.dart +++ b/lib/widgets/appbar.dart @@ -295,7 +295,7 @@ class _State extends State { ), onSelected: (result) { HapticFeedback.selectionClick(); - LunaProfile().safelyChangeProfiles(result, popToFirst: true); + LunaProfile().changeTo(result, popToRoot: true); }, itemBuilder: (context) { return >[ diff --git a/lib/widgets/drawer/drawer.dart b/lib/widgets/drawer/drawer.dart index f5961f9f..95c5b7a3 100644 --- a/lib/widgets/drawer/drawer.dart +++ b/lib/widgets/drawer/drawer.dart @@ -34,13 +34,12 @@ class LunaDrawer extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaSeaDatabase.ENABLED_PROFILE.listen( - builder: (context, _) => ValueListenableBuilder( - valueListenable: LunaBox.indexers.listenable(), - builder: (context, dynamic indexerBox, widget) => Drawer( + return LunaSeaDatabase.ENABLED_PROFILE.watch( + builder: (context, _) => LunaBox.indexers.watch( + builder: (context, _) => Drawer( elevation: LunaUI.ELEVATION, backgroundColor: Theme.of(context).primaryColor, - child: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.listen( + child: LunaSeaDatabase.DRAWER_AUTOMATIC_MANAGE.watch( builder: (context, _) => Column( children: [ LunaDrawerHeader(page: page), diff --git a/lib/widgets/drawer/drawer_header.dart b/lib/widgets/drawer/drawer_header.dart index 08a0e6f3..38fe2e5f 100644 --- a/lib/widgets/drawer/drawer_header.dart +++ b/lib/widgets/drawer/drawer_header.dart @@ -11,7 +11,7 @@ class LunaDrawerHeader extends StatelessWidget { @override Widget build(BuildContext context) { - return LunaSeaDatabase.ENABLED_PROFILE.listen( + return LunaSeaDatabase.ENABLED_PROFILE.watch( builder: (context, _) => Container( child: LunaAppBar.dropdown( backgroundColor: Colors.transparent, diff --git a/lib/widgets/scaffold.dart b/lib/widgets/scaffold.dart index 70b6626c..fc441820 100644 --- a/lib/widgets/scaffold.dart +++ b/lib/widgets/scaffold.dart @@ -38,7 +38,7 @@ class LunaScaffold extends StatelessWidget { } Widget get scaffold { - return LunaSeaDatabase.ENABLED_PROFILE.listen( + return LunaSeaDatabase.ENABLED_PROFILE.watch( builder: (context, _) { onProfileChange?.call(context); return Scaffold( diff --git a/localization/settings/en.json b/localization/settings/en.json index da6fc189..ad201e6b 100644 --- a/localization/settings/en.json +++ b/localization/settings/en.json @@ -13,6 +13,7 @@ "settings.AddModuleSuccess": "Module Added", "settings.AddProfile": "Add Profile", "settings.AddProfileDescription": "Add a New Profile", + "settings.AddedProfile": "Added Profile", "settings.AllFieldsAreRequired": "All fields are required", "settings.AmoledTheme": "AMOLED Theme", "settings.AmoledThemeBorders": "AMOLED Theme Borders", @@ -51,6 +52,7 @@ "settings.BuildChannels": "Build Channels", "settings.BuildChannelsDescription": "Learn About Additional Build Channels", "settings.CalendarSettings": "Calendar Settings", + "settings.ChangedProfile": "Changed Profile", "settings.Channel": "Channel", "settings.ClearConfiguration": "Clear Configuration", "settings.ClearConfigurationHint1": "Are you sure you want to clear your configuration?", @@ -87,6 +89,7 @@ "settings.DecryptBackupHint1": "Please enter the encryption key for this backup.", "settings.DebugMenu": "Debug Menu", "settings.DebugMenuDescription": "Debug and Development Utilities", + "settings.DeletedProfile": "Deleted Profile", "settings.DeleteAccount": "Delete Account", "settings.DeleteAccountDescription": "Permanently Delete Your Account", "settings.DeleteAccountHint1": "Are you sure you want to delete your LunaSea account?", @@ -177,9 +180,11 @@ "settings.NewPassword": "New Password", "settings.Notifications": "Notifications", "settings.NotificationsDescription": "Set up Webhooks for Push Notifications", + "settings.NoAdditionalProfilesAdded": "No additional profiles have been added", "settings.NoBackupsFound": "No Backups Found", "settings.NoExternalModulesFound": "No External Modules Found", "settings.NoHeadersAdded": "No Headers Added", + "settings.NoProfilesFound": "No Profiles Found", "settings.OpenLinksIn": "Open Links In…", "settings.Password": "Password", "settings.PasswordHint1": "If your password includes special characters, considering adding a basic authentication header with your username and password instead for better support", @@ -196,6 +201,7 @@ "settings.ProfileAlreadyExists": "Profile Already Exists", "settings.ProfileName": "Profile Name", "settings.ProfileNameRequired": "Profile Name Required", + "settings.ProfileToProfile": "\"{}\" to \"{}\"", "settings.QuickActions": "Quick Actions", "settings.QuickActionsDescription": "Quick Actions on the Home Screen", "settings.QuickActionsBannerLine1": "Quick actions allow you to quickly jump into modules directly from the home screen or launcher on your device by long pressing LunaSea's icon.", @@ -204,6 +210,7 @@ "settings.Register": "Register", "settings.RegisteredFailure": "Failed to Register", "settings.RegisteredSuccess": "Registered", + "settings.RenamedProfile": "Renamed Profile", "settings.RenameProfile": "Rename Profile", "settings.RenameProfileDescription": "Rename an Existing Profile", "settings.ResetPassword": "Reset Password",