From 204d4da745ebc720b03431093e42f3805649b04d Mon Sep 17 00:00:00 2001 From: MSOB7YY Date: Wed, 22 Nov 2023 22:23:43 +0200 Subject: [PATCH] FEAT: search in settings --- lib/controller/navigator_controller.dart | 14 +- .../settings_search_controller.dart | 270 ++++ lib/core/enums.dart | 11 + lib/core/namida_converter_ext.dart | 5 +- lib/ui/pages/main_page.dart | 12 + lib/ui/pages/settings_search_page.dart | 78 ++ lib/ui/widgets/custom_widgets.dart | 14 +- .../widgets/settings/advanced_settings.dart | 629 +++++---- .../settings/album_tile_customization.dart | 123 -- .../settings/backup_restore_settings.dart | 1089 ++++++++-------- .../settings/customization_settings.dart | 1107 ++++++++++++++-- lib/ui/widgets/settings/extra_settings.dart | 654 ++++++---- lib/ui/widgets/settings/indexer_settings.dart | 649 +++++---- .../settings/miniplayer_customization.dart | 203 --- .../widgets/settings/playback_settings.dart | 1160 +++++++++-------- lib/ui/widgets/settings/theme_settings.dart | 410 +++--- .../settings/track_tile_customization.dart | 333 ----- lib/ui/widgets/settings/youtube_settings.dart | 299 +++-- lib/ui/widgets/settings_search_bar.dart | 103 ++ 19 files changed, 4188 insertions(+), 2975 deletions(-) create mode 100644 lib/controller/settings_search_controller.dart create mode 100644 lib/ui/pages/settings_search_page.dart delete mode 100644 lib/ui/widgets/settings/album_tile_customization.dart delete mode 100644 lib/ui/widgets/settings/miniplayer_customization.dart delete mode 100644 lib/ui/widgets/settings/track_tile_customization.dart create mode 100644 lib/ui/widgets/settings_search_bar.dart diff --git a/lib/controller/navigator_controller.dart b/lib/controller/navigator_controller.dart index f13b3a8c..8681d83b 100644 --- a/lib/controller/navigator_controller.dart +++ b/lib/controller/navigator_controller.dart @@ -10,6 +10,7 @@ import 'package:namida/controller/folders_controller.dart'; import 'package:namida/controller/miniplayer_controller.dart'; import 'package:namida/controller/scroll_search_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/dimensions.dart'; import 'package:namida/core/enums.dart'; @@ -38,6 +39,8 @@ class NamidaNavigator { bool isInLanscape = false; + static const _defaultRouteAnimationDurMS = 500; + Future showMenu(Future? menuFunction) async { _currentMenusNumber++; _printMenus(); @@ -155,7 +158,7 @@ class NamidaNavigator { Widget page, { bool nested = true, Transition transition = Transition.cupertino, - int durationInMs = 500, + int durationInMs = _defaultRouteAnimationDurMS, }) async { currentWidgetStack.add(page.toNamidaRoute()); _hideEverything(); @@ -257,7 +260,7 @@ class NamidaNavigator { Widget page, { bool nested = true, Transition transition = Transition.cupertino, - int durationInMs = 500, + int durationInMs = _defaultRouteAnimationDurMS, }) async { currentWidgetStack.removeLast(); currentWidgetStack.add(page.toNamidaRoute()); @@ -298,7 +301,7 @@ class NamidaNavigator { ); } - Future popPage() async { + Future popPage({bool waitForAnimation = false}) async { if (innerDrawerKey.currentState?.isOpened ?? false) { innerDrawerKey.currentState?.close(); return; @@ -311,6 +314,10 @@ class NamidaNavigator { _hideSearchMenuAndUnfocus(); return; } + if (SettingsSearchController.inst.canShowSearch) { + SettingsSearchController.inst.closeSearch(); + return; + } if (currentRoute?.route == RouteType.PAGE_folders) { final canIgoBackPls = Folders.inst.onBackButton(); @@ -327,6 +334,7 @@ class NamidaNavigator { } else { await _doubleTapToExit(); } + if (waitForAnimation) await Future.delayed(const Duration(milliseconds: _defaultRouteAnimationDurMS)); currentRoute?.updateColorScheme(); _hideSearchMenuAndUnfocus(); } diff --git a/lib/controller/settings_search_controller.dart b/lib/controller/settings_search_controller.dart new file mode 100644 index 00000000..9343fbfe --- /dev/null +++ b/lib/controller/settings_search_controller.dart @@ -0,0 +1,270 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:get/get.dart'; + +import 'package:namida/controller/navigator_controller.dart'; +import 'package:namida/core/enums.dart'; +import 'package:namida/core/extensions.dart'; +import 'package:namida/core/icon_fonts/broken_icons.dart'; +import 'package:namida/core/translations/language.dart'; +import 'package:namida/ui/pages/settings_page.dart'; +import 'package:namida/ui/widgets/settings/advanced_settings.dart'; +import 'package:namida/ui/widgets/settings/backup_restore_settings.dart'; +import 'package:namida/ui/widgets/settings/customization_settings.dart'; +import 'package:namida/ui/widgets/settings/extra_settings.dart'; +import 'package:namida/ui/widgets/settings/indexer_settings.dart'; +import 'package:namida/ui/widgets/settings/playback_settings.dart'; +import 'package:namida/ui/widgets/settings/theme_settings.dart'; +import 'package:namida/ui/widgets/settings/youtube_settings.dart'; + +extension _SettSearcherUtils on SettingSubpageEnum { + CustomCollapsedListTile? toSettingSubPageDetails({Enum? initialItem}) { + switch (this) { + case SettingSubpageEnum.theme: + return CustomCollapsedListTile( + title: lang.THEME_SETTINGS, + subtitle: lang.THEME_SETTINGS_SUBTITLE, + icon: Broken.brush_2, + page: ThemeSetting(initialItem: initialItem), + ); + case SettingSubpageEnum.indexer: + return CustomCollapsedListTile( + title: lang.INDEXER, + subtitle: lang.INDEXER_SUBTITLE, + icon: Broken.component, + page: IndexerSettings(initialItem: initialItem), + ); + + case SettingSubpageEnum.playback: + return CustomCollapsedListTile( + title: lang.PLAYBACK_SETTING, + subtitle: lang.PLAYBACK_SETTING_SUBTITLE, + icon: Broken.play_cricle, + page: PlaybackSettings(initialItem: initialItem), + ); + case SettingSubpageEnum.customization: + return CustomCollapsedListTile( + title: lang.CUSTOMIZATIONS, + subtitle: lang.CUSTOMIZATIONS_SUBTITLE, + icon: Broken.brush_1, + page: CustomizationSettings(initialItem: initialItem), + ); + + case SettingSubpageEnum.youtube: + return CustomCollapsedListTile( + title: lang.YOUTUBE, + subtitle: lang.YOUTUBE_SETTINGS_SUBTITLE, + icon: Broken.video, + page: YoutubeSettings(initialItem: initialItem), + ); + + case SettingSubpageEnum.extra: + return CustomCollapsedListTile( + title: lang.EXTRAS, + subtitle: lang.EXTRAS_SUBTITLE, + icon: Broken.command_square, + page: ExtrasSettings(initialItem: initialItem), + ); + case SettingSubpageEnum.backupRestore: + return CustomCollapsedListTile( + title: lang.BACKUP_AND_RESTORE, + subtitle: lang.BACKUP_AND_RESTORE_SUBTITLE, + icon: Broken.refresh_circle, + page: BackupAndRestore(initialItem: initialItem), + ); + + case SettingSubpageEnum.advanced: + return CustomCollapsedListTile( + title: lang.ADVANCED_SETTINGS, + subtitle: lang.ADVANCED_SETTINGS_SUBTITLE, + icon: Broken.hierarchy_3, + page: AdvancedSettings(initialItem: initialItem), + ); + default: + return null; + } + } +} + +class SettingSearchResultItem { + final SettingSubpageEnum? page; + final Enum key; + final List titles; + + const SettingSearchResultItem({ + required this.page, + required this.key, + required this.titles, + }); +} + +class SettingsSearchController { + static final SettingsSearchController inst = SettingsSearchController._internal(); + SettingsSearchController._internal(); + + final _map = >{}; + final _allWidgets = <(SettingSubpageProvider, Map>)>[]; + final searchResults = >{}.obs; + final subpagesDetails = {}; + + bool get canShowSearch => _canShowSearch.value; + final _canShowSearch = false.obs; + + void closeSearch() { + _allWidgets.clear(); + searchResults.clear(); + _canShowSearch.value = false; + } + + GlobalKey getSettingWidgetGlobalKey(SettingSubpageEnum settingPage, Enum key) { + final keyIndex = key.index; + final c = SettingsSearchController.inst; + final r = settingPage; + c._map[r] ??= {}; + c._map[r]![keyIndex] ??= GlobalKey(); + return c._map[r]![keyIndex]!; + } + + void onSearchTap({required bool isOpen}) { + if (isOpen) { + const theme = ThemeSetting(); + const indexer = IndexerSettings(); + const playback = PlaybackSettings(); + const customization = CustomizationSettings(); + const youtube = YoutubeSettings(); + const extras = ExtrasSettings(); + const backupAndRestore = BackupAndRestore(); + const advanced = AdvancedSettings(); + _allWidgets + ..clear() + ..addAll([ + (theme, theme.lookupMap), + (indexer, indexer.lookupMap), + (playback, playback.lookupMap), + (customization, customization.lookupMap), + (youtube, youtube.lookupMap), + (extras, extras.lookupMap), + (backupAndRestore, backupAndRestore.lookupMap), + (advanced, advanced.lookupMap), + ]); + for (final p in _allWidgets) { + subpagesDetails[p.$1.settingPage] = p.$1.settingPage.toSettingSubPageDetails(); + } + _canShowSearch.value = true; + } else { + closeSearch(); + } + } + + void onSearchChanged(String val) { + final res = >{}; + _allWidgets.loop((widget, index) { + for (final e in widget.$2.entries) { + final match = e.value.any((element) => element.cleanUpForComparison.contains(val.cleanUpForComparison)); + if (match) { + final p = widget.$1.settingPage; + res.addForce( + p, + SettingSearchResultItem( + page: p, + key: e.key, + titles: e.value, + ), + ); + } + } + }); + searchResults + ..clear() + ..addAll(res); + searchResults.refresh(); + } + + Future onResultTap({ + required SettingSubpageEnum? settingPage, + required Enum key, + required BuildContext context, + }) async { + onSearchTap(isOpen: false); + + final details = subpagesDetails[settingPage]; + final page = settingPage?.toSettingSubPageDetails(initialItem: key)?.page; + if (NamidaNavigator.inst.currentRoute?.route == RouteType.SETTINGS_subpage) { + // -- navigate back if inside subpage. + // -- we can skip and just jump, but we + // -- need the blink animation & bgColor to update. + await NamidaNavigator.inst.popPage(waitForAnimation: true); + } + if (page != null) { + NamidaNavigator.inst.navigateTo( + SettingsSubPage( + title: details?.title ?? '', + child: page, + ), + ); + } + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final c = _map[settingPage]?[key.index]?.currentContext; + if (c != null) Scrollable.ensureVisible(c, alignment: 0.3); + }); + } +} + +abstract class SettingSubpageProvider extends StatelessWidget { + SettingSubpageEnum get settingPage; + Map> get lookupMap; + final Enum? initialItem; + + const SettingSubpageProvider({super.key, this.initialItem}); + + GlobalKey getSettingWidgetGlobalKey(Enum key) { + return SettingsSearchController.inst.getSettingWidgetGlobalKey(settingPage, key); + } + + Color? getBgColor(Enum key) { + return key == initialItem ? Colors.grey.withAlpha(80) : null; + } + + Widget getItemWrapper({required Enum key, required Widget child}) { + return Stack( + key: getSettingWidgetGlobalKey(key), + children: [ + child, + if (key == initialItem) + () { + bool finished = false; + return Positioned.fill( + child: IgnorePointer( + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18.0.multipliedRadius), + color: Colors.grey.withAlpha(100), + ), + ).animate( + autoPlay: true, + onComplete: (controller) async { + if (!finished) { + finished = true; + Future oneLap() async { + await controller.animateTo(controller.upperBound); + await controller.animateTo(controller.lowerBound); + } + + await oneLap(); + await oneLap(); + } + }, + effects: [ + const FadeEffect( + duration: Duration(milliseconds: 200), + delay: Duration(milliseconds: 50), + ), + ], + ), + ), + ); + }() + ], + ); + } +} diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 400c79ca..c3b126be 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -315,3 +315,14 @@ enum KillAppMode { ifNotPlaying, never, } + +enum SettingSubpageEnum { + theme, + indexer, + playback, + customization, + youtube, + extra, + backupRestore, + advanced, +} diff --git a/lib/core/namida_converter_ext.dart b/lib/core/namida_converter_ext.dart index b5c24646..bf9604e5 100644 --- a/lib/core/namida_converter_ext.dart +++ b/lib/core/namida_converter_ext.dart @@ -58,6 +58,7 @@ import 'package:namida/ui/pages/subpages/queue_tracks_subpage.dart'; import 'package:namida/ui/pages/tracks_page.dart'; import 'package:namida/ui/widgets/circular_percentages.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; +import 'package:namida/ui/widgets/settings_search_bar.dart'; import 'package:namida/ui/widgets/stats.dart'; import 'package:namida/youtube/class/youtube_id.dart'; import 'package:namida/youtube/controller/youtube_controller.dart'; @@ -810,7 +811,9 @@ extension RouteUtils on NamidaRoute { return AnimatedSwitcher( duration: const Duration(milliseconds: 400), - child: finalWidget ?? ScrollSearchController.inst.searchBarWidget, + child: (route == RouteType.SETTINGS_page || route == RouteType.SETTINGS_subpage) + ? NamidaSettingSearchBar(closedChild: finalWidget) + : finalWidget ?? ScrollSearchController.inst.searchBarWidget, ); } diff --git a/lib/ui/pages/main_page.dart b/lib/ui/pages/main_page.dart index 654bfd00..a46e1075 100644 --- a/lib/ui/pages/main_page.dart +++ b/lib/ui/pages/main_page.dart @@ -9,6 +9,7 @@ import 'package:namida/controller/player_controller.dart'; import 'package:namida/controller/scroll_search_controller.dart'; import 'package:namida/controller/search_sort_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; import 'package:namida/core/extensions.dart'; @@ -19,6 +20,7 @@ import 'package:namida/packages/searchbar_animation.dart'; import 'package:namida/ui/pages/albums_page.dart'; import 'package:namida/ui/pages/artists_page.dart'; import 'package:namida/ui/pages/search_page.dart'; +import 'package:namida/ui/pages/settings_search_page.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/youtube/class/youtube_id.dart'; @@ -112,6 +114,16 @@ class MainPage extends StatelessWidget { ), ), + // -- Settings Search Box + Positioned.fill( + child: Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + child: SettingsSearchController.inst.canShowSearch ? const SettingsSearchPage() : null, + ), + ), + ), + /// Bottom Glow/Shadow Obx( () => AnimatedSwitcher( diff --git a/lib/ui/pages/settings_search_page.dart b/lib/ui/pages/settings_search_page.dart new file mode 100644 index 00000000..15d5f6a2 --- /dev/null +++ b/lib/ui/pages/settings_search_page.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'package:namida/controller/settings_search_controller.dart'; +import 'package:namida/core/dimensions.dart'; +import 'package:namida/ui/widgets/custom_widgets.dart'; + +class SettingsSearchPage extends StatelessWidget { + const SettingsSearchPage({super.key}); + + @override + Widget build(BuildContext context) { + return BackgroundWrapper( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Obx( + () { + final keys = SettingsSearchController.inst.searchResults.keys.toList(); + return CustomScrollView( + slivers: [ + ...keys.map( + (key) { + final res = SettingsSearchController.inst.searchResults[key] ?? []; + return SliverList.builder( + itemCount: res.length + 1, + itemBuilder: (context, indexPre) { + if (indexPre == 0) { + final details = SettingsSearchController.inst.subpagesDetails[res[0].page]; + if (details == null) return const SizedBox(); + return NamidaInkWell( + borderRadius: 6.0, + margin: const EdgeInsets.symmetric(vertical: 6.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), + bgColor: context.theme.cardColor, + child: Row( + children: [ + Icon(details.icon), + const SizedBox(width: 6.0), + Text( + details.title, + style: context.textTheme.displayLarge, + ), + ], + ), + ); + } + final index = indexPre - 1; + final item = res[index]; + + final title = item.titles.firstOrNull ?? ''; + final subtitle = item.titles.length >= 2 ? item.titles[1] : null; + + return CustomListTile( + borderR: 16.0, + visualDensity: VisualDensity.compact, + title: "${index + 1}. $title", + subtitle: subtitle, + onTap: () { + SettingsSearchController.inst.onResultTap( + settingPage: item.page, + key: item.key, + context: context, + ); + }, + ); + }, + ); + }, + ), + const SliverPadding(padding: EdgeInsets.only(bottom: kBottomPadding)), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/ui/widgets/custom_widgets.dart b/lib/ui/widgets/custom_widgets.dart index d15b6170..ec780941 100644 --- a/lib/ui/widgets/custom_widgets.dart +++ b/lib/ui/widgets/custom_widgets.dart @@ -174,6 +174,7 @@ class CustomSwitchListTile extends StatelessWidget { final bool largeTitle; final int maxSubtitleLines; final VisualDensity? visualDensity; + final Color? bgColor; const CustomSwitchListTile({ Key? key, @@ -189,11 +190,13 @@ class CustomSwitchListTile extends StatelessWidget { this.largeTitle = false, this.maxSubtitleLines = 8, this.visualDensity, + this.bgColor, }) : super(key: key); @override Widget build(BuildContext context) { return CustomListTile( + bgColor: bgColor, title: title, subtitle: subtitle, enabled: enabled, @@ -238,6 +241,7 @@ class CustomListTile extends StatelessWidget { final VisualDensity? visualDensity; final TextStyle? titleStyle; final double borderR; + final Color? bgColor; const CustomListTile({ Key? key, @@ -257,6 +261,7 @@ class CustomListTile extends StatelessWidget { this.visualDensity, this.titleStyle, this.borderR = 20.0, + this.bgColor, }) : super(key: key); @override @@ -267,6 +272,7 @@ class CustomListTile extends StatelessWidget { opacity: enabled ? 1.0 : 0.5, child: ListTile( enabled: enabled, + tileColor: bgColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderR.multipliedRadius), ), @@ -849,6 +855,7 @@ class NamidaExpansionTile extends StatelessWidget { final Widget? trailing; final ValueChanged? onExpansionChanged; final bool normalRightPadding; + final Color? bgColor; const NamidaExpansionTile({ super.key, @@ -866,6 +873,7 @@ class NamidaExpansionTile extends StatelessWidget { this.trailing, this.onExpansionChanged, this.normalRightPadding = false, + this.bgColor, }); @override @@ -874,6 +882,8 @@ class NamidaExpansionTile extends StatelessWidget { // horizontalTitleGap: 12.0, dense: true, child: ExpansionTile( + collapsedBackgroundColor: bgColor, + backgroundColor: bgColor, initiallyExpanded: initiallyExpanded, onExpansionChanged: onExpansionChanged, expandedAlignment: Alignment.centerLeft, @@ -1095,12 +1105,14 @@ class CancelButton extends StatelessWidget { } class CollapsedSettingTileWidget extends StatelessWidget { - const CollapsedSettingTileWidget({super.key}); + final Color? bgColor; + const CollapsedSettingTileWidget({super.key, this.bgColor}); @override Widget build(BuildContext context) { return Obx( () => CustomSwitchListTile( + bgColor: bgColor, icon: Broken.archive, title: lang.USE_COLLAPSED_SETTING_TILES, value: settings.useSettingCollapsedTiles.value, diff --git a/lib/ui/widgets/settings/advanced_settings.dart b/lib/ui/widgets/settings/advanced_settings.dart index 08fd8b9a..041a3879 100644 --- a/lib/ui/widgets/settings/advanced_settings.dart +++ b/lib/ui/widgets/settings/advanced_settings.dart @@ -15,6 +15,7 @@ import 'package:namida/controller/history_controller.dart'; import 'package:namida/controller/indexer_controller.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/controller/video_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; @@ -31,49 +32,83 @@ import 'package:namida/ui/widgets/settings_card.dart'; import 'package:namida/youtube/controller/youtube_controller.dart'; import 'package:namida/youtube/controller/youtube_history_controller.dart'; -class AdvancedSettings extends StatelessWidget { - const AdvancedSettings({super.key}); +enum _AdvancedSettingKeys { + performanceMode, + rescanVideos, + removeSourceHistory, + updateDirPath, + fixYTDLPBigThumbnail, + compressImages, + maxImageCache, + maxVideoCache, + clearImageCache, + clearVideoCache, +} + +class AdvancedSettings extends SettingSubpageProvider { + const AdvancedSettings({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.advanced; + + @override + Map> get lookupMap => { + _AdvancedSettingKeys.performanceMode: [lang.PERFORMANCE_MODE], + _AdvancedSettingKeys.rescanVideos: [lang.RESCAN_VIDEOS], + _AdvancedSettingKeys.removeSourceHistory: [lang.REMOVE_SOURCE_FROM_HISTORY], + _AdvancedSettingKeys.updateDirPath: [lang.UPDATE_DIRECTORY_PATH], + _AdvancedSettingKeys.fixYTDLPBigThumbnail: [lang.FIX_YTDLP_BIG_THUMBNAIL_SIZE], + _AdvancedSettingKeys.compressImages: [lang.COMPRESS_IMAGES], + _AdvancedSettingKeys.maxImageCache: [lang.MAX_IMAGE_CACHE_SIZE], + _AdvancedSettingKeys.maxVideoCache: [lang.MAX_VIDEO_CACHE_SIZE], + _AdvancedSettingKeys.clearImageCache: [lang.CLEAR_IMAGE_CACHE], + _AdvancedSettingKeys.clearVideoCache: [lang.CLEAR_VIDEO_CACHE], + }; Widget getPerformanceTile(BuildContext context) { - return CustomListTile( - icon: Broken.cpu_setting, - title: lang.PERFORMANCE_MODE, - trailingRaw: NamidaPopupWrapper( - children: [ - ...PerformanceMode.values.map( - (e) => Obx( - () => NamidaInkWell( - margin: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0), - padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 6.0), - borderRadius: 6.0, - bgColor: settings.performanceMode.value == e ? context.theme.cardColor : null, - child: Row( - children: [ - Icon( - e.toIcon(), - size: 18.0, - ), - const SizedBox(width: 6.0), - Text( - e.toText(), - style: context.textTheme.displayMedium?.copyWith(fontSize: 14.0.multipliedFontScale), - ), - ], + return getItemWrapper( + key: _AdvancedSettingKeys.performanceMode, + child: CustomListTile( + bgColor: getBgColor(_AdvancedSettingKeys.performanceMode), + icon: Broken.cpu_setting, + title: lang.PERFORMANCE_MODE, + trailingRaw: NamidaPopupWrapper( + children: [ + ...PerformanceMode.values.map( + (e) => Obx( + () => NamidaInkWell( + margin: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 6.0), + borderRadius: 6.0, + bgColor: settings.performanceMode.value == e ? context.theme.cardColor : null, + child: Row( + children: [ + Icon( + e.toIcon(), + size: 18.0, + ), + const SizedBox(width: 6.0), + Text( + e.toText(), + style: context.textTheme.displayMedium?.copyWith(fontSize: 14.0.multipliedFontScale), + ), + ], + ), + onTap: () { + e.execute(); + settings.save(performanceMode: e); + NamidaNavigator.inst.popMenu(handleClosing: false); + Navigator.of(context).pop(); + }, ), - onTap: () { - e.execute(); - settings.save(performanceMode: e); - NamidaNavigator.inst.popMenu(handleClosing: false); - Navigator.of(context).pop(); - }, ), ), - ), - ], - child: Obx( - () => Text( - settings.performanceMode.value.toText(), - style: context.textTheme.displaySmall?.copyWith(color: context.theme.colorScheme.onBackground.withAlpha(200)), + ], + child: Obx( + () => Text( + settings.performanceMode.value.toText(), + style: context.textTheme.displaySmall?.copyWith(color: context.theme.colorScheme.onBackground.withAlpha(200)), + ), ), ), ), @@ -90,195 +125,222 @@ class AdvancedSettings extends StatelessWidget { child: Column( children: [ getPerformanceTile(context), - CustomListTile( - leading: const StackedIcon( - baseIcon: Broken.video, - secondaryIcon: Broken.refresh, - ), - trailingRaw: Obx( - () { - final current = VideoController.inst.localVideoExtractCurrent.value; - final total = VideoController.inst.localVideoExtractTotal.value; - final isCounterVisible = total != 0; - final isLoadingVisible = current != null; - - if (!isCounterVisible && !isLoadingVisible) return Text("${VideoController.inst.localVideosTotalCount}"); - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (isCounterVisible) Text("$current/$total"), - if (isLoadingVisible) const LoadingIndicator(), - ], - ); + getItemWrapper( + key: _AdvancedSettingKeys.rescanVideos, + child: CustomListTile( + bgColor: getBgColor(_AdvancedSettingKeys.rescanVideos), + leading: const StackedIcon( + baseIcon: Broken.video, + secondaryIcon: Broken.refresh, + ), + trailingRaw: Obx( + () { + final current = VideoController.inst.localVideoExtractCurrent.value; + final total = VideoController.inst.localVideoExtractTotal.value; + final isCounterVisible = total != 0; + final isLoadingVisible = current != null; + + if (!isCounterVisible && !isLoadingVisible) return Text("${VideoController.inst.localVideosTotalCount}"); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (isCounterVisible) Text("$current/$total"), + if (isLoadingVisible) const LoadingIndicator(), + ], + ); + }, + ), + title: lang.RESCAN_VIDEOS, + onTap: () async { + await VideoController.inst.scanLocalVideos(forceReScan: true, fillPathsOnly: true); + snackyy(title: lang.DONE, message: lang.FINISHED_UPDATING_LIBRARY); }, ), - title: lang.RESCAN_VIDEOS, - onTap: () async { - await VideoController.inst.scanLocalVideos(forceReScan: true, fillPathsOnly: true); - snackyy(title: lang.DONE, message: lang.FINISHED_UPDATING_LIBRARY); - }, ), - CustomListTile( - leading: const StackedIcon( - baseIcon: Broken.trash, - secondaryIcon: Broken.refresh, - ), - title: lang.REMOVE_SOURCE_FROM_HISTORY, - onTap: () async { - final RxList sourcesToDelete = [].obs; - bool isActive(TrackSource e) => sourcesToDelete.contains(e); - - final RxMap sourcesMap = {}.obs; - void resetSourcesMap() { - TrackSource.values.loop((e, index) { - sourcesMap[e] = 0; - }); - } - - final RxInt totalTracksToBeRemoved = 0.obs; - - final RxInt totalTracksBetweenDates = 0.obs; - - void calculateTotalTracks(DateTime? oldest, DateTime? newest) { - final sussyDays = HistoryController.inst.historyDays.toList(); - final isBetweenDays = oldest != null && newest != null; - if (isBetweenDays) { - final oldestDay = oldest.toDaysSince1970(); - final newestDay = newest.toDaysSince1970(); - - sussyDays.retainWhere((element) => element >= oldestDay && element <= newestDay); - printy(sussyDays); - } - resetSourcesMap(); - sussyDays.loop((d, index) { - final tracks = HistoryController.inst.historyMap.value[d] ?? []; - tracks.loop((twd, index) { - sourcesMap.update(twd.source, (value) => value + 1, ifAbsent: () => 1); + getItemWrapper( + key: _AdvancedSettingKeys.removeSourceHistory, + child: CustomListTile( + bgColor: getBgColor(_AdvancedSettingKeys.removeSourceHistory), + leading: const StackedIcon( + baseIcon: Broken.trash, + secondaryIcon: Broken.refresh, + ), + title: lang.REMOVE_SOURCE_FROM_HISTORY, + onTap: () async { + final RxList sourcesToDelete = [].obs; + bool isActive(TrackSource e) => sourcesToDelete.contains(e); + + final RxMap sourcesMap = {}.obs; + void resetSourcesMap() { + TrackSource.values.loop((e, index) { + sourcesMap[e] = 0; }); - }); - if (isBetweenDays) { - totalTracksBetweenDates.value = sourcesMap.values.reduce((value, element) => value + element); } - if (sourcesToDelete.isNotEmpty) { - totalTracksToBeRemoved.value = 0; - sourcesToDelete.loop((e, index) { - totalTracksToBeRemoved.value += sourcesMap[e] ?? 0; + + final RxInt totalTracksToBeRemoved = 0.obs; + + final RxInt totalTracksBetweenDates = 0.obs; + + void calculateTotalTracks(DateTime? oldest, DateTime? newest) { + final sussyDays = HistoryController.inst.historyDays.toList(); + final isBetweenDays = oldest != null && newest != null; + if (isBetweenDays) { + final oldestDay = oldest.toDaysSince1970(); + final newestDay = newest.toDaysSince1970(); + + sussyDays.retainWhere((element) => element >= oldestDay && element <= newestDay); + printy(sussyDays); + } + resetSourcesMap(); + sussyDays.loop((d, index) { + final tracks = HistoryController.inst.historyMap.value[d] ?? []; + tracks.loop((twd, index) { + sourcesMap.update(twd.source, (value) => value + 1, ifAbsent: () => 1); + }); }); + if (isBetweenDays) { + totalTracksBetweenDates.value = sourcesMap.values.reduce((value, element) => value + element); + } + if (sourcesToDelete.isNotEmpty) { + totalTracksToBeRemoved.value = 0; + sourcesToDelete.loop((e, index) { + totalTracksToBeRemoved.value += sourcesMap[e] ?? 0; + }); + } } - } - // -- filling each source with its tracks number. - calculateTotalTracks(null, null); + // -- filling each source with its tracks number. + calculateTotalTracks(null, null); - DateTime? oldestDate; - DateTime? newestDate; + DateTime? oldestDate; + DateTime? newestDate; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.CHOOSE, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.REMOVE, - onPressed: () async { - final removedNum = await HistoryController.inst.removeSourcesTracksFromHistory( - sourcesToDelete, - oldestDate: oldestDate, - newestDate: newestDate, - ); - NamidaNavigator.inst.closeDialog(); - snackyy(title: lang.NOTE, message: "${lang.REMOVED} ${removedNum.displayTrackKeyword}"); - }, - ) - ], - child: Obx( - () => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 12.0), - Row( - children: [ - const SizedBox(width: 8.0), - const Icon(Broken.danger), - const SizedBox(width: 8.0), - Obx(() => Text( - '${lang.TOTAL_TRACKS}: ${totalTracksToBeRemoved.value}', - style: context.textTheme.displayMedium, - )), - ], - ), - const SizedBox(height: 12.0), - ...sourcesMap.entries.map( - (e) { - final source = e.key; - final count = e.value; - return Padding( - padding: const EdgeInsets.only(top: 10.0), - child: Obx( - () => ListTileWithCheckMark( - active: isActive(source), - title: '${source.convertToString} (${count.formatDecimal()})', - onTap: () { - if (isActive(source)) { - sourcesToDelete.remove(source); - totalTracksToBeRemoved.value -= count; - } else { - sourcesToDelete.add(source); - totalTracksToBeRemoved.value += count; - } - }, + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.CHOOSE, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.REMOVE, + onPressed: () async { + final removedNum = await HistoryController.inst.removeSourcesTracksFromHistory( + sourcesToDelete, + oldestDate: oldestDate, + newestDate: newestDate, + ); + NamidaNavigator.inst.closeDialog(); + snackyy(title: lang.NOTE, message: "${lang.REMOVED} ${removedNum.displayTrackKeyword}"); + }, + ) + ], + child: Obx( + () => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 12.0), + Row( + children: [ + const SizedBox(width: 8.0), + const Icon(Broken.danger), + const SizedBox(width: 8.0), + Obx(() => Text( + '${lang.TOTAL_TRACKS}: ${totalTracksToBeRemoved.value}', + style: context.textTheme.displayMedium, + )), + ], + ), + const SizedBox(height: 12.0), + ...sourcesMap.entries.map( + (e) { + final source = e.key; + final count = e.value; + return Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Obx( + () => ListTileWithCheckMark( + active: isActive(source), + title: '${source.convertToString} (${count.formatDecimal()})', + onTap: () { + if (isActive(source)) { + sourcesToDelete.remove(source); + totalTracksToBeRemoved.value -= count; + } else { + sourcesToDelete.add(source); + totalTracksToBeRemoved.value += count; + } + }, + ), ), - ), - ); - }, - ), - const SizedBox(height: 12.0), - BetweenDatesTextButton( - useHistoryDates: true, - onConfirm: (dates) { - oldestDate = dates.firstOrNull; - newestDate = dates.lastOrNull; - calculateTotalTracks(oldestDate, newestDate); - NamidaNavigator.inst.closeDialog(); - }, - tracksLength: totalTracksBetweenDates.value, - ), - ], + ); + }, + ), + const SizedBox(height: 12.0), + BetweenDatesTextButton( + useHistoryDates: true, + onConfirm: (dates) { + oldestDate = dates.firstOrNull; + newestDate = dates.lastOrNull; + calculateTotalTracks(oldestDate, newestDate); + NamidaNavigator.inst.closeDialog(); + }, + tracksLength: totalTracksBetweenDates.value, + ), + ], + ), ), ), - ), - ); - }, + ); + }, + ), + ), + getItemWrapper( + key: _AdvancedSettingKeys.updateDirPath, + child: UpdateDirectoryPathListTile( + bgColor: getBgColor(_AdvancedSettingKeys.updateDirPath), + ), ), - const UpdateDirectoryPathListTile(), // -- this will loop all choosen files, get yt thumbnail (download or cache), edit tags, without affecting file modified time. - const _FixYTDLPThumbnailSizeListTile(), - const _CompressImagesListTile(), + getItemWrapper( + key: _AdvancedSettingKeys.fixYTDLPBigThumbnail, + child: _FixYTDLPThumbnailSizeListTile( + bgColor: getBgColor(_AdvancedSettingKeys.fixYTDLPBigThumbnail), + ), + ), + getItemWrapper( + key: _AdvancedSettingKeys.compressImages, + child: _CompressImagesListTile( + bgColor: getBgColor(_AdvancedSettingKeys.compressImages), + ), + ), () { const stepper = 8 * 4; const minimumValue = stepper; int getValue(int mb) => (mb - minimumValue) ~/ stepper; - return Obx( - () { - return CustomListTile( - leading: const StackedIcon( - baseIcon: Broken.gallery, - secondaryIcon: Broken.cpu, - ), - title: lang.MAX_IMAGE_CACHE_SIZE, - trailing: NamidaWheelSlider( - totalCount: getValue(4 * 1024), // 4 GB - initValue: getValue(settings.imagesMaxCacheInMB.value), - itemSize: 5, - text: (settings.imagesMaxCacheInMB.value * 1024 * 1024).fileSizeFormatted, - onValueChanged: (val) { - settings.save(imagesMaxCacheInMB: minimumValue + (val * stepper)); - }, - ), - ); - }, + return getItemWrapper( + key: _AdvancedSettingKeys.maxImageCache, + child: Obx( + () { + return CustomListTile( + bgColor: getBgColor(_AdvancedSettingKeys.maxImageCache), + leading: const StackedIcon( + baseIcon: Broken.gallery, + secondaryIcon: Broken.cpu, + ), + title: lang.MAX_IMAGE_CACHE_SIZE, + trailing: NamidaWheelSlider( + totalCount: getValue(4 * 1024), // 4 GB + initValue: getValue(settings.imagesMaxCacheInMB.value), + itemSize: 5, + text: (settings.imagesMaxCacheInMB.value * 1024 * 1024).fileSizeFormatted, + onValueChanged: (val) { + settings.save(imagesMaxCacheInMB: minimumValue + (val * stepper)); + }, + ), + ); + }, + ), ); }(), @@ -286,68 +348,81 @@ class AdvancedSettings extends StatelessWidget { const stepper = 8 * 32; const minimumValue = stepper; int getValue(int mb) => (mb - minimumValue) ~/ stepper; - return Obx( - () { - return CustomListTile( - leading: const StackedIcon( - baseIcon: Broken.video, - secondaryIcon: Broken.cpu, - ), - title: lang.MAX_VIDEO_CACHE_SIZE, - trailing: NamidaWheelSlider( - totalCount: getValue(10 * 1024), // 10 GB - initValue: getValue(settings.videosMaxCacheInMB.value), - itemSize: 5, - text: (settings.videosMaxCacheInMB.value * 1024 * 1024).fileSizeFormatted, - onValueChanged: (val) { - settings.save(videosMaxCacheInMB: minimumValue + (val * stepper)); - }, - ), - ); - }, + return getItemWrapper( + key: _AdvancedSettingKeys.maxVideoCache, + child: Obx( + () { + return CustomListTile( + bgColor: getBgColor(_AdvancedSettingKeys.maxVideoCache), + leading: const StackedIcon( + baseIcon: Broken.video, + secondaryIcon: Broken.cpu, + ), + title: lang.MAX_VIDEO_CACHE_SIZE, + trailing: NamidaWheelSlider( + totalCount: getValue(10 * 1024), // 10 GB + initValue: getValue(settings.videosMaxCacheInMB.value), + itemSize: 5, + text: (settings.videosMaxCacheInMB.value * 1024 * 1024).fileSizeFormatted, + onValueChanged: (val) { + settings.save(videosMaxCacheInMB: minimumValue + (val * stepper)); + }, + ), + ); + }, + ), ); }(), - const _ClearImageCacheListTile(), + getItemWrapper( + key: _AdvancedSettingKeys.clearImageCache, + child: _ClearImageCacheListTile( + bgColor: getBgColor(_AdvancedSettingKeys.clearImageCache), + ), + ), - Obx( - () => CustomListTile( - leading: const StackedIcon( - baseIcon: Broken.video, - secondaryIcon: Broken.close_circle, - ), - title: lang.CLEAR_VIDEO_CACHE, - trailingText: Indexer.inst.videosSizeInStorage.value.fileSizeFormatted, - onTap: () async { - final allvideo = VideoController.inst.getCurrentVideosInCache(); + getItemWrapper( + key: _AdvancedSettingKeys.clearVideoCache, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_AdvancedSettingKeys.clearVideoCache), + leading: const StackedIcon( + baseIcon: Broken.video, + secondaryIcon: Broken.close_circle, + ), + title: lang.CLEAR_VIDEO_CACHE, + trailingText: Indexer.inst.videosSizeInStorage.value.fileSizeFormatted, + onTap: () async { + final allvideo = VideoController.inst.getCurrentVideosInCache(); - /// First Dialog - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - isWarning: true, - normalTitleStyle: true, - bodyText: "${_getVideoSubtitleText(allvideo)}\n${lang.CLEAR_VIDEO_CACHE_NOTE}", - actions: [ - /// Pressing Choose - NamidaButton( - text: lang.CHOOSE, - onPressed: () { - NamidaNavigator.inst.closeDialog(); - _showChooseVideosToDeleteDialog(allvideo); - }, - ), - const CancelButton(), - NamidaButton( - text: lang.DELETE.toUpperCase(), - onPressed: () async { - NamidaNavigator.inst.closeDialog(); - await Indexer.inst.clearVideoCache(); - }, - ), - ], - ), - ); - }, + /// First Dialog + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + isWarning: true, + normalTitleStyle: true, + bodyText: "${_getVideoSubtitleText(allvideo)}\n${lang.CLEAR_VIDEO_CACHE_NOTE}", + actions: [ + /// Pressing Choose + NamidaButton( + text: lang.CHOOSE, + onPressed: () { + NamidaNavigator.inst.closeDialog(); + _showChooseVideosToDeleteDialog(allvideo); + }, + ), + const CancelButton(), + NamidaButton( + text: lang.DELETE.toUpperCase(), + onPressed: () async { + NamidaNavigator.inst.closeDialog(); + await Indexer.inst.clearVideoCache(); + }, + ), + ], + ), + ); + }, + ), ), ), ], @@ -580,7 +655,9 @@ class AdvancedSettings extends StatelessWidget { } class _ClearImageCacheListTile extends StatefulWidget { - const _ClearImageCacheListTile(); + final Color? bgColor; + + const _ClearImageCacheListTile({this.bgColor}); @override State<_ClearImageCacheListTile> createState() => __ClearImageCacheListTileState(); @@ -629,6 +706,7 @@ class __ClearImageCacheListTileState extends State<_ClearImageCacheListTile> { Widget build(BuildContext context) { return Obx( () => CustomListTile( + bgColor: widget.bgColor, leading: const StackedIcon( baseIcon: Broken.image, secondaryIcon: Broken.close_circle, @@ -692,11 +770,20 @@ class UpdateDirectoryPathListTile extends StatelessWidget { final Color? colorScheme; final String? oldPath; final Iterable? tracksPaths; - const UpdateDirectoryPathListTile({super.key, this.colorScheme, this.oldPath, this.tracksPaths}); + final Color? bgColor; + + const UpdateDirectoryPathListTile({ + super.key, + this.colorScheme, + this.oldPath, + this.tracksPaths, + this.bgColor, + }); @override Widget build(BuildContext context) { return CustomListTile( + bgColor: bgColor, leading: StackedIcon( baseIcon: Broken.folder, secondaryIcon: Broken.music, @@ -825,7 +912,9 @@ class UpdateDirectoryPathListTile extends StatelessWidget { } class _FixYTDLPThumbnailSizeListTile extends StatelessWidget { - const _FixYTDLPThumbnailSizeListTile(); + final Color? bgColor; + + const _FixYTDLPThumbnailSizeListTile({this.bgColor}); Future _onFixYTDLPPress() async { if (!await requestManageStoragePermission()) return; @@ -846,6 +935,7 @@ class _FixYTDLPThumbnailSizeListTile extends StatelessWidget { final totalFailed = p?.totalFailed ?? 0; final failedSubtitle = totalFailed > 0 ? "${lang.FAILED}: $totalFailed" : null; return CustomListTile( + bgColor: bgColor, leading: const StackedIcon( baseIcon: Broken.document_code_2, secondaryIcon: Broken.video_square, @@ -861,7 +951,9 @@ class _FixYTDLPThumbnailSizeListTile extends StatelessWidget { } class _CompressImagesListTile extends StatelessWidget { - const _CompressImagesListTile(); + final Color? bgColor; + + const _CompressImagesListTile({this.bgColor}); Future _onCompressImagePress() async { if (NamidaFFMPEG.inst.currentOperations[OperationType.imageCompress]?.value.currentFilePath != null) return; // return if currently compressing. @@ -968,6 +1060,7 @@ class _CompressImagesListTile extends StatelessWidget { final totalImagesToCompress = p?.totalFiles ?? 0; final totalFailed = p?.totalFailed ?? 0; return CustomListTile( + bgColor: bgColor, leading: const StackedIcon( baseIcon: Broken.gallery, secondaryIcon: Broken.magicpen, diff --git a/lib/ui/widgets/settings/album_tile_customization.dart b/lib/ui/widgets/settings/album_tile_customization.dart deleted file mode 100644 index cc87480d..00000000 --- a/lib/ui/widgets/settings/album_tile_customization.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:get/get.dart'; - -import 'package:namida/controller/navigator_controller.dart'; -import 'package:namida/controller/settings_controller.dart'; -import 'package:namida/core/icon_fonts/broken_icons.dart'; -import 'package:namida/core/translations/language.dart'; -import 'package:namida/ui/widgets/custom_widgets.dart'; -import 'package:namida/ui/dialogs/setting_dialog_with_text_field.dart'; - -class AlbumTileCustomization extends StatelessWidget { - final Color? currentTrackColor; - const AlbumTileCustomization({super.key, this.currentTrackColor}); - - @override - Widget build(BuildContext context) { - return NamidaExpansionTile( - initiallyExpanded: settings.useSettingCollapsedTiles.value, - leading: const StackedIcon( - baseIcon: Broken.brush, - secondaryIcon: Broken.music_dashboard, - ), - titleText: lang.ALBUM_TILE_CUSTOMIZATION, - children: [ - /// Track Number in a small Box - Obx( - () => CustomSwitchListTile( - icon: Broken.card_remove, - title: lang.DISPLAY_TRACK_NUMBER_IN_ALBUM_PAGE, - subtitle: lang.DISPLAY_TRACK_NUMBER_IN_ALBUM_PAGE_SUBTITLE, - value: settings.displayTrackNumberinAlbumPage.value, - onChanged: (p0) => settings.save(displayTrackNumberinAlbumPage: !p0), - ), - ), - - /// Album Card Top Right Date - Obx( - () => CustomSwitchListTile( - icon: Broken.notification_status, - title: lang.DISPLAY_ALBUM_CARD_TOP_RIGHT_DATE, - subtitle: lang.DISPLAY_ALBUM_CARD_TOP_RIGHT_DATE_SUBTITLE, - onChanged: (p0) => settings.save(albumCardTopRightDate: !p0), - value: settings.albumCardTopRightDate.value, - ), - ), - - /// Force Squared Album Thumbnail - Obx( - () => CustomSwitchListTile( - icon: Broken.crop, - title: lang.FORCE_SQUARED_ALBUM_THUMBNAIL, - value: settings.forceSquaredAlbumThumbnail.value, - onChanged: (p0) { - settings.save(forceSquaredAlbumThumbnail: !p0); - if (!p0 && settings.albumThumbnailSizeinList.toInt() != settings.albumListTileHeight.toInt()) { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - isWarning: true, - bodyText: lang.FORCE_SQUARED_THUMBNAIL_NOTE, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.CONFIRM, - onPressed: () { - settings.save(albumThumbnailSizeinList: settings.albumListTileHeight.value); - NamidaNavigator.inst.closeDialog(); - }, - ), - ], - ), - ); - } - }, - ), - ), - - /// Staggered Album Gridview - Obx( - () => CustomSwitchListTile( - icon: Broken.element_4, - title: lang.STAGGERED_ALBUM_GRID_VIEW, - value: settings.useAlbumStaggeredGridView.value, - onChanged: (p0) => settings.save(useAlbumStaggeredGridView: !p0), - ), - ), - - /// Album Thumbnail Size in List - Obx( - () => CustomListTile( - icon: Broken.maximize_3, - title: lang.ALBUM_THUMBNAIL_SIZE_IN_LIST, - trailingText: "${settings.albumThumbnailSizeinList.toInt()}", - onTap: () { - showSettingDialogWithTextField( - title: lang.ALBUM_THUMBNAIL_SIZE_IN_LIST, - albumThumbnailSizeinList: true, - icon: Broken.maximize_3, - ); - }, - ), - ), - - /// Album Tile Height - Obx( - () => CustomListTile( - icon: Broken.pharagraphspacing, - title: lang.HEIGHT_OF_ALBUM_TILE, - trailingText: "${settings.albumListTileHeight.toInt()}", - onTap: () { - showSettingDialogWithTextField( - title: lang.HEIGHT_OF_ALBUM_TILE, - albumListTileHeight: true, - icon: Broken.pharagraphspacing, - ); - }, - ), - ), - ], - ); - } -} diff --git a/lib/ui/widgets/settings/backup_restore_settings.dart b/lib/ui/widgets/settings/backup_restore_settings.dart index e842f601..b8f473bc 100644 --- a/lib/ui/widgets/settings/backup_restore_settings.dart +++ b/lib/ui/widgets/settings/backup_restore_settings.dart @@ -10,6 +10,7 @@ import 'package:namida/controller/history_controller.dart'; import 'package:namida/controller/json_to_history_parser.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; import 'package:namida/core/extensions.dart'; @@ -20,8 +21,28 @@ import 'package:namida/ui/widgets/circular_percentages.dart'; import 'package:namida/ui/widgets/settings/extra_settings.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class BackupAndRestore extends StatelessWidget { - const BackupAndRestore({super.key}); +enum _BackupAndRestoreKeys { + create, + restore, + defaultLocation, + importYT, + importLastfm, +} + +class BackupAndRestore extends SettingSubpageProvider { + const BackupAndRestore({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.backupRestore; + + @override + Map> get lookupMap => { + _BackupAndRestoreKeys.create: [lang.CREATE_BACKUP], + _BackupAndRestoreKeys.restore: [lang.RESTORE_BACKUP], + _BackupAndRestoreKeys.defaultLocation: [lang.DEFAULT_BACKUP_LOCATION], + _BackupAndRestoreKeys.importYT: [lang.IMPORT_YOUTUBE_HISTORY], + _BackupAndRestoreKeys.importLastfm: [lang.IMPORT_LAST_FM_HISTORY], + }; bool _canDoImport() { if (JsonToHistoryParser.inst.isParsing.value || HistoryController.inst.isLoadingHistory) { @@ -53,596 +74,616 @@ class BackupAndRestore extends StatelessWidget { // TODO(feat): option inside namida to move track in android. // -- Create Backup - CustomListTile( - title: lang.CREATE_BACKUP, - icon: Broken.box_add, - trailingRaw: ObxShow( - showIf: BackupController.inst.isCreatingBackup, - child: const LoadingIndicator(), - ), - onTap: () { - bool isActive(List items) => items.every((element) => settings.backupItemslist.contains(element)); + getItemWrapper( + key: _BackupAndRestoreKeys.create, + child: CustomListTile( + bgColor: getBgColor(_BackupAndRestoreKeys.create), + title: lang.CREATE_BACKUP, + icon: Broken.box_add, + trailingRaw: ObxShow( + showIf: BackupController.inst.isCreatingBackup, + child: const LoadingIndicator(), + ), + onTap: () { + bool isActive(List items) => items.every((element) => settings.backupItemslist.contains(element)); - void onItemTap(List items) { - if (isActive(items)) { - settings.removeFromList(backupItemslistAll: items); - } else { - settings.save(backupItemslist: items); + void onItemTap(List items) { + if (isActive(items)) { + settings.removeFromList(backupItemslistAll: items); + } else { + settings.save(backupItemslist: items); + } } - } - int doIt(List forWhat) { - int totalSize = 0; - forWhat.loop((e, _) { - if (FileSystemEntity.typeSync(e) == FileSystemEntityType.file) { + int doIt(List forWhat) { + int totalSize = 0; + forWhat.loop((e, _) { + if (FileSystemEntity.typeSync(e) == FileSystemEntityType.file) { totalSize += File(e).fileSizeSync() ?? 0; - } else if (FileSystemEntity.typeSync(e) == FileSystemEntityType.directory) { - int size = 0; - Directory(e).listSync().loop((e, index) { + } else if (FileSystemEntity.typeSync(e) == FileSystemEntityType.directory) { + int size = 0; + Directory(e).listSync().loop((e, index) { size += (e is File ? File(e.path).fileSizeSync() ?? 0 : 0); - }); - totalSize += size; - } - }); - return totalSize; - } + }); + totalSize += size; + } + }); + return totalSize; + } - Widget getItemWidget({ - required String title, - required IconData icon, - required List items, - required bool youtubeAvailable, - bool youtubeForceFollowItems = false, - required List youtubeItems, - }) { - final totalSizeLocal = doIt(items); - final totalSizeYoutube = doIt(youtubeItems); + Widget getItemWidget({ + required String title, + required IconData icon, + required List items, + required bool youtubeAvailable, + bool youtubeForceFollowItems = false, + required List youtubeItems, + }) { + final totalSizeLocal = doIt(items); + final totalSizeYoutube = doIt(youtubeItems); - return Obx( - () { - final isLocalIconChecked = isActive(items); - final isYoutubeIconChecked = youtubeForceFollowItems - ? isActive(items) - : !youtubeAvailable - ? false - : youtubeItems.isEmpty - ? false - : isActive(youtubeItems); - return Row( - children: [ - Expanded( - child: ListTileWithCheckMark( - active: isLocalIconChecked, - titleWidget: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: context.textTheme.displayMedium, - ), - Row( - children: [ - AnimatedOpacity( - duration: const Duration(milliseconds: 200), - opacity: isLocalIconChecked ? 1.0 : 0.5, - child: Text( - "(${totalSizeLocal.fileSizeFormatted})", - style: context.textTheme.displaySmall, - ), - ), - if (totalSizeYoutube > 0) + return Obx( + () { + final isLocalIconChecked = isActive(items); + final isYoutubeIconChecked = youtubeForceFollowItems + ? isActive(items) + : !youtubeAvailable + ? false + : youtubeItems.isEmpty + ? false + : isActive(youtubeItems); + return Row( + children: [ + Expanded( + child: ListTileWithCheckMark( + active: isLocalIconChecked, + titleWidget: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: context.textTheme.displayMedium, + ), + Row( + children: [ AnimatedOpacity( duration: const Duration(milliseconds: 200), - opacity: isYoutubeIconChecked ? 1.0 : 0.5, + opacity: isLocalIconChecked ? 1.0 : 0.5, child: Text( - " + (${totalSizeYoutube.fileSizeFormatted})", + "(${totalSizeLocal.fileSizeFormatted})", style: context.textTheme.displaySmall, ), ), - ], - ), - ], + if (totalSizeYoutube > 0) + AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: isYoutubeIconChecked ? 1.0 : 0.5, + child: Text( + " + (${totalSizeYoutube.fileSizeFormatted})", + style: context.textTheme.displaySmall, + ), + ), + ], + ), + ], + ), + icon: icon, + onTap: () => onItemTap(items), ), - icon: icon, - onTap: () => onItemTap(items), ), - ), - const SizedBox(width: 8.0), - AnimatedOpacity( - duration: const Duration(milliseconds: 300), - opacity: youtubeAvailable ? 1.0 : 0.6, - child: NamidaIconButton( - tooltip: lang.YOUTUBE, - horizontalPadding: 0.0, - icon: null, - onPressed: () { - if (youtubeAvailable) { - onItemTap(youtubeItems); - } - }, - child: StackedIcon( - iconSize: 28.0, - baseIcon: Broken.video_square, - smallChild: NamidaCheckMark( - size: 12.0, - active: isYoutubeIconChecked, + const SizedBox(width: 8.0), + AnimatedOpacity( + duration: const Duration(milliseconds: 300), + opacity: youtubeAvailable ? 1.0 : 0.6, + child: NamidaIconButton( + tooltip: lang.YOUTUBE, + horizontalPadding: 0.0, + icon: null, + onPressed: () { + if (youtubeAvailable) { + onItemTap(youtubeItems); + } + }, + child: StackedIcon( + iconSize: 28.0, + baseIcon: Broken.video_square, + smallChild: NamidaCheckMark( + size: 12.0, + active: isYoutubeIconChecked, + ), ), ), ), + const SizedBox(width: 8.0), + ], + ); + }, + ); + } + + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.CREATE_BACKUP, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.CREATE_BACKUP, + onPressed: () { + if (settings.backupItemslist.isNotEmpty) { + NamidaNavigator.inst.closeDialog(); + BackupController.inst.createBackupFile(settings.backupItemslist); + } + }, + ), + ], + child: SizedBox( + height: Get.height / 2, + child: SingleChildScrollView( + child: Column( + children: [ + getItemWidget( + title: lang.DATABASE, + icon: Broken.box_1, + items: [ + AppPaths.TRACKS, + AppPaths.TRACKS_STATS, + AppPaths.TOTAL_LISTEN_TIME, + AppPaths.VIDEOS_CACHE, + AppPaths.VIDEOS_LOCAL, + AppDirs.YT_DOWNLOAD_TASKS, + ], + youtubeAvailable: true, + youtubeItems: [ + AppDirs.YT_STATS, + ], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.PLAYLISTS, + icon: Broken.music_library_2, + items: [ + AppDirs.PLAYLISTS, + AppPaths.FAVOURITES_PLAYLIST, + ], + youtubeAvailable: true, + youtubeItems: [ + AppDirs.YT_PLAYLISTS, + AppPaths.YT_LIKES_PLAYLIST, + ], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.HISTORY, + icon: Broken.refresh, + items: [ + AppDirs.HISTORY_PLAYLIST, + ], + youtubeAvailable: true, + youtubeItems: [ + AppDirs.YT_HISTORY_PLAYLIST, + ], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.SETTINGS, + icon: Broken.setting, + items: [ + AppPaths.SETTINGS, + ], + youtubeAvailable: false, + youtubeItems: [], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.LYRICS, + icon: Broken.document, + items: [ + AppDirs.LYRICS, + ], + youtubeAvailable: false, + youtubeItems: [], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.QUEUES, + icon: Broken.driver, + items: [ + AppDirs.QUEUES, + AppPaths.LATEST_QUEUE, + ], + youtubeAvailable: false, + youtubeItems: [], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.COLOR_PALETTES, + icon: Broken.colorfilter, + items: [ + AppDirs.PALETTES, + ], + youtubeAvailable: true, + youtubeForceFollowItems: false, + youtubeItems: [ + AppDirs.YT_PALETTES, + ], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.VIDEO_CACHE, + icon: Broken.video, + items: [ + AppDirs.VIDEOS_CACHE, + ], + youtubeAvailable: false, + youtubeForceFollowItems: true, + youtubeItems: [], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.AUDIO_CACHE, + icon: Broken.audio_square, + items: [ + AppDirs.AUDIOS_CACHE, + ], + youtubeAvailable: false, + youtubeForceFollowItems: true, + youtubeItems: [], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.ARTWORKS, + icon: Broken.image, + items: [ + AppDirs.ARTWORKS, + ], + youtubeAvailable: false, + youtubeItems: [], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.THUMBNAILS, + icon: Broken.image, + items: [ + AppDirs.THUMBNAILS, + ], + youtubeAvailable: true, + youtubeItems: [ + AppDirs.YT_THUMBNAILS, + AppDirs.YT_THUMBNAILS_CHANNELS, + ], + ), + const SizedBox(height: 12.0), + getItemWidget( + title: lang.METADATA_CACHE, + icon: Broken.message_text, + items: [ + AppDirs.YT_METADATA, + AppDirs.YT_METADATA_CHANNELS, + AppDirs.YT_METADATA_COMMENTS, + ], + youtubeAvailable: true, + youtubeForceFollowItems: true, + youtubeItems: [], + ), + ], ), - const SizedBox(width: 8.0), - ], - ); - }, + ), + ), + ), ); - } + }, + ), + ), - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.CREATE_BACKUP, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.CREATE_BACKUP, - onPressed: () { - if (settings.backupItemslist.isNotEmpty) { - NamidaNavigator.inst.closeDialog(); - BackupController.inst.createBackupFile(settings.backupItemslist); - } - }, - ), - ], - child: SizedBox( - height: Get.height / 2, + // -- Restore Backup + getItemWrapper( + key: _BackupAndRestoreKeys.restore, + child: CustomListTile( + bgColor: getBgColor(_BackupAndRestoreKeys.restore), + title: lang.RESTORE_BACKUP, + icon: Broken.back_square, + trailingRaw: ObxShow( + showIf: BackupController.inst.isRestoringBackup, + child: const LoadingIndicator(), + ), + onTap: () async { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + title: lang.RESTORE_BACKUP, child: SingleChildScrollView( child: Column( children: [ - getItemWidget( - title: lang.DATABASE, - icon: Broken.box_1, - items: [ - AppPaths.TRACKS, - AppPaths.TRACKS_STATS, - AppPaths.TOTAL_LISTEN_TIME, - AppPaths.VIDEOS_CACHE, - AppPaths.VIDEOS_LOCAL, - AppDirs.YT_DOWNLOAD_TASKS, - ], - youtubeAvailable: true, - youtubeItems: [ - AppDirs.YT_STATS, - ], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.PLAYLISTS, - icon: Broken.music_library_2, - items: [ - AppDirs.PLAYLISTS, - AppPaths.FAVOURITES_PLAYLIST, - ], - youtubeAvailable: true, - youtubeItems: [ - AppDirs.YT_PLAYLISTS, - AppPaths.YT_LIKES_PLAYLIST, - ], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.HISTORY, - icon: Broken.refresh, - items: [ - AppDirs.HISTORY_PLAYLIST, - ], - youtubeAvailable: true, - youtubeItems: [ - AppDirs.YT_HISTORY_PLAYLIST, - ], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.SETTINGS, - icon: Broken.setting, - items: [ - AppPaths.SETTINGS, - ], - youtubeAvailable: false, - youtubeItems: [], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.LYRICS, - icon: Broken.document, - items: [ - AppDirs.LYRICS, - ], - youtubeAvailable: false, - youtubeItems: [], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.QUEUES, - icon: Broken.driver, - items: [ - AppDirs.QUEUES, - AppPaths.LATEST_QUEUE, - ], - youtubeAvailable: false, - youtubeItems: [], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.COLOR_PALETTES, - icon: Broken.colorfilter, - items: [ - AppDirs.PALETTES, - ], - youtubeAvailable: true, - youtubeForceFollowItems: false, - youtubeItems: [ - AppDirs.YT_PALETTES, - ], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.VIDEO_CACHE, - icon: Broken.video, - items: [ - AppDirs.VIDEOS_CACHE, - ], - youtubeAvailable: false, - youtubeForceFollowItems: true, - youtubeItems: [], + CustomListTile( + title: lang.AUTOMATIC_BACKUP, + subtitle: lang.AUTOMATIC_BACKUP_SUBTITLE, + icon: Broken.autobrightness, + maxSubtitleLines: 22, + onTap: () => BackupController.inst.restoreBackupOnTap(true), ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.AUDIO_CACHE, - icon: Broken.audio_square, - items: [ - AppDirs.AUDIOS_CACHE, - ], - youtubeAvailable: false, - youtubeForceFollowItems: true, - youtubeItems: [], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.ARTWORKS, - icon: Broken.image, - items: [ - AppDirs.ARTWORKS, - ], - youtubeAvailable: false, - youtubeItems: [], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.THUMBNAILS, - icon: Broken.image, - items: [ - AppDirs.THUMBNAILS, - ], - youtubeAvailable: true, - youtubeItems: [ - AppDirs.YT_THUMBNAILS, - AppDirs.YT_THUMBNAILS_CHANNELS, - ], - ), - const SizedBox(height: 12.0), - getItemWidget( - title: lang.METADATA_CACHE, - icon: Broken.message_text, - items: [ - AppDirs.YT_METADATA, - AppDirs.YT_METADATA_CHANNELS, - AppDirs.YT_METADATA_COMMENTS, - ], - youtubeAvailable: true, - youtubeForceFollowItems: true, - youtubeItems: [], + CustomListTile( + title: lang.MANUAL_BACKUP, + subtitle: lang.MANUAL_BACKUP_SUBTITLE, + maxSubtitleLines: 22, + icon: Broken.hashtag, + onTap: () => BackupController.inst.restoreBackupOnTap(false), ), ], ), ), ), - ), - ); - }, - ), - - // -- Restore Backup - CustomListTile( - title: lang.RESTORE_BACKUP, - icon: Broken.back_square, - trailingRaw: ObxShow( - showIf: BackupController.inst.isRestoringBackup, - child: const LoadingIndicator(), + ); + }, ), - onTap: () async { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - title: lang.RESTORE_BACKUP, - child: SingleChildScrollView( - child: Column( - children: [ - CustomListTile( - title: lang.AUTOMATIC_BACKUP, - subtitle: lang.AUTOMATIC_BACKUP_SUBTITLE, - icon: Broken.autobrightness, - maxSubtitleLines: 22, - onTap: () => BackupController.inst.restoreBackupOnTap(true), - ), - CustomListTile( - title: lang.MANUAL_BACKUP, - subtitle: lang.MANUAL_BACKUP_SUBTITLE, - maxSubtitleLines: 22, - icon: Broken.hashtag, - onTap: () => BackupController.inst.restoreBackupOnTap(false), - ), - ], - ), - ), - ), - ); - }, ), // -- Default Backup Location - Obx( - () => CustomListTile( - title: lang.DEFAULT_BACKUP_LOCATION, - icon: Broken.direct_inbox, - subtitle: settings.defaultBackupLocation.value, - onTap: () async { - final path = await FilePicker.platform.getDirectoryPath(); + getItemWrapper( + key: _BackupAndRestoreKeys.defaultLocation, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_BackupAndRestoreKeys.defaultLocation), + title: lang.DEFAULT_BACKUP_LOCATION, + icon: Broken.direct_inbox, + subtitle: settings.defaultBackupLocation.value, + onTap: () async { + final path = await FilePicker.platform.getDirectoryPath(); - if (path != null) { - settings.save(defaultBackupLocation: path); - } - }, + if (path != null) { + settings.save(defaultBackupLocation: path); + } + }, + ), ), ), // -- Import Youtube History - CustomListTile( - title: lang.IMPORT_YOUTUBE_HISTORY, - leading: StackedIcon( - baseIcon: Broken.import_2, - smallChild: ClipRRect( - borderRadius: BorderRadius.circular(12.0.multipliedRadius), - child: Image.asset( - 'assets/icons/youtube.png', - width: 12, - height: 12, + getItemWrapper( + key: _BackupAndRestoreKeys.importYT, + child: CustomListTile( + bgColor: getBgColor(_BackupAndRestoreKeys.importYT), + title: lang.IMPORT_YOUTUBE_HISTORY, + leading: StackedIcon( + baseIcon: Broken.import_2, + smallChild: ClipRRect( + borderRadius: BorderRadius.circular(12.0.multipliedRadius), + child: Image.asset( + 'assets/icons/youtube.png', + width: 12, + height: 12, + ), ), ), - ), - trailing: const SizedBox( - height: 32.0, - width: 32.0, - child: ParsingJsonPercentage( - size: 32.0, - source: TrackSource.youtube, - forceDisplay: false, + trailing: const SizedBox( + height: 32.0, + width: 32.0, + child: ParsingJsonPercentage( + size: 32.0, + source: TrackSource.youtube, + forceDisplay: false, + ), ), - ), - onTap: () { - if (!_canDoImport()) return; + onTap: () { + if (!_canDoImport()) return; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.GUIDE, - actions: [ - NamidaButton( - text: lang.CONFIRM, - onPressed: () async { - NamidaNavigator.inst.closeDialog(); + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.GUIDE, + actions: [ + NamidaButton( + text: lang.CONFIRM, + onPressed: () async { + NamidaNavigator.inst.closeDialog(); - Widget getTitleText(String text) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0).add(const EdgeInsets.only(bottom: 10.0)), - child: Text("- $text", style: Get.textTheme.displayLarge), - ); + Widget getTitleText(String text) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0).add(const EdgeInsets.only(bottom: 10.0)), + child: Text("- $text", style: Get.textTheme.displayLarge), + ); - final jsonfile = await FilePicker.platform.pickFiles(allowedExtensions: ['json'], type: FileType.custom); - if (jsonfile != null) { - final RxBool isMatchingTypeLink = true.obs; - final RxBool isMatchingTypeTitleAndArtist = false.obs; - final RxBool matchYT = true.obs; - final RxBool matchYTMusic = true.obs; - final RxBool matchAll = false.obs; - final oldestDate = Rxn(); - DateTime? newestDate; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.CONFIGURE, - actions: [ - Obx( - () => NamidaButton( - enabled: isMatchingTypeLink.value || isMatchingTypeTitleAndArtist.value, - textWidget: Obx(() => Text(oldestDate.value != null ? lang.IMPORT_TIME_RANGE : lang.IMPORT_ALL)), - onPressed: () async { - NamidaNavigator.inst.closeDialog(); - await JsonToHistoryParser.inst.addFileSourceToNamidaHistory( - file: File(jsonfile.files.first.path!), - source: TrackSource.youtube, - ytIsMatchingTypeLink: isMatchingTypeLink.value, - isMatchingTypeTitleAndArtist: isMatchingTypeTitleAndArtist.value, - ytMatchYT: matchYT.value, - ytMatchYTMusic: matchYTMusic.value, - oldestDate: oldestDate.value, - newestDate: newestDate, - matchAll: matchAll.value, - ); - }, - ), - ) - ], - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - getTitleText(lang.SOURCE), + final jsonfile = await FilePicker.platform.pickFiles(allowedExtensions: ['json'], type: FileType.custom); + if (jsonfile != null) { + final RxBool isMatchingTypeLink = true.obs; + final RxBool isMatchingTypeTitleAndArtist = false.obs; + final RxBool matchYT = true.obs; + final RxBool matchYTMusic = true.obs; + final RxBool matchAll = false.obs; + final oldestDate = Rxn(); + DateTime? newestDate; + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.CONFIGURE, + actions: [ Obx( - () => ListTileWithCheckMark( - active: matchYT.value, - title: lang.YOUTUBE, - onTap: () => matchYT.value = !matchYT.value, + () => NamidaButton( + enabled: isMatchingTypeLink.value || isMatchingTypeTitleAndArtist.value, + textWidget: Obx(() => Text(oldestDate.value != null ? lang.IMPORT_TIME_RANGE : lang.IMPORT_ALL)), + onPressed: () async { + NamidaNavigator.inst.closeDialog(); + await JsonToHistoryParser.inst.addFileSourceToNamidaHistory( + file: File(jsonfile.files.first.path!), + source: TrackSource.youtube, + ytIsMatchingTypeLink: isMatchingTypeLink.value, + isMatchingTypeTitleAndArtist: isMatchingTypeTitleAndArtist.value, + ytMatchYT: matchYT.value, + ytMatchYTMusic: matchYTMusic.value, + oldestDate: oldestDate.value, + newestDate: newestDate, + matchAll: matchAll.value, + ); + }, ), - ), - const SizedBox(height: 8.0), - Obx( - () => ListTileWithCheckMark( - active: matchYTMusic.value, - title: lang.YOUTUBE_MUSIC, - onTap: () => matchYTMusic.value = !matchYTMusic.value, + ) + ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + getTitleText(lang.SOURCE), + Obx( + () => ListTileWithCheckMark( + active: matchYT.value, + title: lang.YOUTUBE, + onTap: () => matchYT.value = !matchYT.value, + ), ), - ), - getDivider(), - getTitleText(lang.MATCHING_TYPE), - Obx( - () => ListTileWithCheckMark( - active: isMatchingTypeLink.value, - title: lang.LINK, - onTap: () => isMatchingTypeLink.value = !isMatchingTypeLink.value, + const SizedBox(height: 8.0), + Obx( + () => ListTileWithCheckMark( + active: matchYTMusic.value, + title: lang.YOUTUBE_MUSIC, + onTap: () => matchYTMusic.value = !matchYTMusic.value, + ), ), - ), - const SizedBox(height: 8.0), - Obx( - () => ListTileWithCheckMark( - active: isMatchingTypeTitleAndArtist.value, - title: [lang.TITLE, lang.ARTIST].join(' & '), - onTap: () => isMatchingTypeTitleAndArtist.value = !isMatchingTypeTitleAndArtist.value, + getDivider(), + getTitleText(lang.MATCHING_TYPE), + Obx( + () => ListTileWithCheckMark( + active: isMatchingTypeLink.value, + title: lang.LINK, + onTap: () => isMatchingTypeLink.value = !isMatchingTypeLink.value, + ), ), - ), - getDivider(), - Obx( - () => matchAllTracksListTile( - active: matchAll.value, - onTap: () => matchAll.value = !matchAll.value, - displayPerfWarning: isMatchingTypeTitleAndArtist.value, // link matching wont result in perf issue + const SizedBox(height: 8.0), + Obx( + () => ListTileWithCheckMark( + active: isMatchingTypeTitleAndArtist.value, + title: [lang.TITLE, lang.ARTIST].join(' & '), + onTap: () => isMatchingTypeTitleAndArtist.value = !isMatchingTypeTitleAndArtist.value, + ), ), - ), - getDivider(), - BetweenDatesTextButton( - useHistoryDates: false, - maxToday: true, - onConfirm: (dates) { - oldestDate.value = dates.firstOrNull; - newestDate = dates.lastOrNull; - NamidaNavigator.inst.closeDialog(); - }, - ), - ], + getDivider(), + Obx( + () => matchAllTracksListTile( + active: matchAll.value, + onTap: () => matchAll.value = !matchAll.value, + displayPerfWarning: isMatchingTypeTitleAndArtist.value, // link matching wont result in perf issue + ), + ), + getDivider(), + BetweenDatesTextButton( + useHistoryDates: false, + maxToday: true, + onConfirm: (dates) { + oldestDate.value = dates.firstOrNull; + newestDate = dates.lastOrNull; + NamidaNavigator.inst.closeDialog(); + }, + ), + ], + ), ), - ), - ); - } - }, + ); + } + }, + ), + ], + child: NamidaSelectableAutoLinkText( + text: lang.IMPORT_YOUTUBE_HISTORY_GUIDE.replaceFirst('_TAKEOUT_LINK_', 'https://takeout.google.com/takeout/custom/youtube'), ), - ], - child: NamidaSelectableAutoLinkText( - text: lang.IMPORT_YOUTUBE_HISTORY_GUIDE.replaceFirst('_TAKEOUT_LINK_', 'https://takeout.google.com/takeout/custom/youtube'), ), - ), - ); - }, + ); + }, + ), ), // -- Import last.fm History - CustomListTile( - title: lang.IMPORT_LAST_FM_HISTORY, - leading: StackedIcon( - baseIcon: Broken.import_2, - smallChild: ClipRRect( - borderRadius: BorderRadius.circular(12.0.multipliedRadius), - child: Image.asset( - 'assets/icons/lastfm.png', - width: 12, - height: 12, + getItemWrapper( + key: _BackupAndRestoreKeys.importLastfm, + child: CustomListTile( + bgColor: getBgColor(_BackupAndRestoreKeys.importLastfm), + title: lang.IMPORT_LAST_FM_HISTORY, + leading: StackedIcon( + baseIcon: Broken.import_2, + smallChild: ClipRRect( + borderRadius: BorderRadius.circular(12.0.multipliedRadius), + child: Image.asset( + 'assets/icons/lastfm.png', + width: 12, + height: 12, + ), ), ), - ), - trailing: const SizedBox( - height: 32.0, - width: 32.0, - child: ParsingJsonPercentage( - size: 32.0, - source: TrackSource.lastfm, - forceDisplay: false, + trailing: const SizedBox( + height: 32.0, + width: 32.0, + child: ParsingJsonPercentage( + size: 32.0, + source: TrackSource.lastfm, + forceDisplay: false, + ), ), - ), - onTap: () { - if (!_canDoImport()) return; + onTap: () { + if (!_canDoImport()) return; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.GUIDE, - actions: [ - NamidaButton( - text: lang.CONFIRM, - onPressed: () async { - NamidaNavigator.inst.closeDialog(); + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.GUIDE, + actions: [ + NamidaButton( + text: lang.CONFIRM, + onPressed: () async { + NamidaNavigator.inst.closeDialog(); - final csvFiles = await FilePicker.platform.pickFiles(allowedExtensions: ['csv'], type: FileType.custom); - final csvFilePath = csvFiles?.files.first.path; - if (csvFiles != null && csvFilePath != null) { - final oldestDate = Rxn(); - DateTime? newestDate; - final matchAll = false.obs; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - insetPadding: const EdgeInsets.all(38.0), - title: lang.CONFIGURE, - actions: [ - const CancelButton(), - NamidaButton( - textWidget: Obx(() => Text(oldestDate.value != null ? lang.IMPORT_TIME_RANGE : lang.IMPORT_ALL)), - onPressed: () async { - NamidaNavigator.inst.closeDialog(); - await JsonToHistoryParser.inst.addFileSourceToNamidaHistory( - file: File(csvFilePath), - source: TrackSource.lastfm, - oldestDate: oldestDate.value, - newestDate: newestDate, - matchAll: matchAll.value, - ); - }, - ) - ], - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Obx( - () => matchAllTracksListTile( - active: matchAll.value, - onTap: () => matchAll.value = !matchAll.value, - displayPerfWarning: true, - ), - ), - getDivider(), - BetweenDatesTextButton( - useHistoryDates: false, - maxToday: true, - onConfirm: (dates) { + final csvFiles = await FilePicker.platform.pickFiles(allowedExtensions: ['csv'], type: FileType.custom); + final csvFilePath = csvFiles?.files.first.path; + if (csvFiles != null && csvFilePath != null) { + final oldestDate = Rxn(); + DateTime? newestDate; + final matchAll = false.obs; + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + insetPadding: const EdgeInsets.all(38.0), + title: lang.CONFIGURE, + actions: [ + const CancelButton(), + NamidaButton( + textWidget: Obx(() => Text(oldestDate.value != null ? lang.IMPORT_TIME_RANGE : lang.IMPORT_ALL)), + onPressed: () async { NamidaNavigator.inst.closeDialog(); - oldestDate.value = dates.firstOrNull; - newestDate = dates.lastOrNull; + await JsonToHistoryParser.inst.addFileSourceToNamidaHistory( + file: File(csvFilePath), + source: TrackSource.lastfm, + oldestDate: oldestDate.value, + newestDate: newestDate, + matchAll: matchAll.value, + ); }, - ), + ) ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Obx( + () => matchAllTracksListTile( + active: matchAll.value, + onTap: () => matchAll.value = !matchAll.value, + displayPerfWarning: true, + ), + ), + getDivider(), + BetweenDatesTextButton( + useHistoryDates: false, + maxToday: true, + onConfirm: (dates) { + NamidaNavigator.inst.closeDialog(); + oldestDate.value = dates.firstOrNull; + newestDate = dates.lastOrNull; + }, + ), + ], + ), ), - ), - ); - } - }, + ); + } + }, + ), + ], + child: NamidaSelectableAutoLinkText( + text: lang.IMPORT_LAST_FM_HISTORY_GUIDE.replaceFirst('_LASTFM_CSV_LINK_', 'https://benjaminbenben.com/lastfm-to-csv/'), ), - ], - child: NamidaSelectableAutoLinkText( - text: lang.IMPORT_LAST_FM_HISTORY_GUIDE.replaceFirst('_LASTFM_CSV_LINK_', 'https://benjaminbenben.com/lastfm-to-csv/'), ), - ), - ); - }, + ); + }, + ), ), ], ), diff --git a/lib/ui/widgets/settings/customization_settings.dart b/lib/ui/widgets/settings/customization_settings.dart index 4156092e..14a97846 100644 --- a/lib/ui/widgets/settings/customization_settings.dart +++ b/lib/ui/widgets/settings/customization_settings.dart @@ -1,22 +1,108 @@ -import 'package:flutter/material.dart'; +// ignore_for_file: constant_identifier_names +import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:namida/class/track.dart'; import 'package:namida/controller/navigator_controller.dart'; +import 'package:namida/controller/player_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; +import 'package:namida/core/extensions.dart'; import 'package:namida/core/icon_fonts/broken_icons.dart'; +import 'package:namida/core/namida_converter_ext.dart'; import 'package:namida/core/translations/language.dart'; -import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/ui/dialogs/setting_dialog_with_text_field.dart'; -import 'package:namida/ui/widgets/settings/album_tile_customization.dart'; -import 'package:namida/ui/widgets/settings/miniplayer_customization.dart'; -import 'package:namida/ui/widgets/settings/track_tile_customization.dart'; +import 'package:namida/ui/widgets/artwork.dart'; +import 'package:namida/ui/widgets/custom_widgets.dart'; +import 'package:namida/ui/widgets/library/track_tile.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class CustomizationSettings extends StatelessWidget { - const CustomizationSettings({super.key}); +enum _CustomizationSettingsKeys { + enableBlur, + enableGlow, + enableParallax, + displayRemainingDur, + brMultiplier, + fontScale, + hourFormat12, + dateTimeFormat, + // ----------- + ALBUMTILECUSTOMIZATION, + trackNumberInAlbumPage, + albumCardTopRightDate, + forceSquaredAlbumThumb, + staggeredAlbumGridview, + sizeOfAlbumThumb, + heightOfAlbumTile, + // ----------- + TRACKTILECUSTOMIZATION, + forceSquaredTrackThumb, + sizeOfTrackThumb, + heightOfTrackTile, + displayThirdRow, + displayThirdItemInRow, + displayFavButtonInTrackTile, + itemsSeparator, + // ----------- + MINIPLAYERCUSTOMIZATION, + partyMode, + edgeColorsSwitching, + movingParticles, + thumbAnimationIntensity, + thumbInverseAnimation, + waveformBarsCount, + displayAudioInfo, + displayArtistBeforeTitle, +} + +class CustomizationSettings extends SettingSubpageProvider { + const CustomizationSettings({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.customization; + + @override + Map> get lookupMap => { + _CustomizationSettingsKeys.enableBlur: [lang.ENABLE_BLUR_EFFECT], + _CustomizationSettingsKeys.enableGlow: [lang.ENABLE_GLOW_EFFECT], + _CustomizationSettingsKeys.enableParallax: [lang.ENABLE_PARALLAX_EFFECT], + _CustomizationSettingsKeys.displayRemainingDur: [lang.DISPLAY_REMAINING_DURATION_INSTEAD_OF_TOTAL], + _CustomizationSettingsKeys.brMultiplier: [lang.BORDER_RADIUS_MULTIPLIER], + _CustomizationSettingsKeys.fontScale: [lang.FONT_SCALE], + _CustomizationSettingsKeys.hourFormat12: [lang.HOUR_FORMAT_12], + _CustomizationSettingsKeys.dateTimeFormat: [lang.DATE_TIME_FORMAT], + // ----------- + _CustomizationSettingsKeys.ALBUMTILECUSTOMIZATION: [lang.ALBUM_TILE_CUSTOMIZATION], + _CustomizationSettingsKeys.trackNumberInAlbumPage: [lang.DISPLAY_TRACK_NUMBER_IN_ALBUM_PAGE, lang.DISPLAY_TRACK_NUMBER_IN_ALBUM_PAGE_SUBTITLE], + _CustomizationSettingsKeys.albumCardTopRightDate: [lang.DISPLAY_ALBUM_CARD_TOP_RIGHT_DATE, lang.DISPLAY_ALBUM_CARD_TOP_RIGHT_DATE_SUBTITLE], + _CustomizationSettingsKeys.forceSquaredAlbumThumb: [lang.FORCE_SQUARED_ALBUM_THUMBNAIL], + _CustomizationSettingsKeys.staggeredAlbumGridview: [lang.STAGGERED_ALBUM_GRID_VIEW], + _CustomizationSettingsKeys.sizeOfAlbumThumb: [lang.ALBUM_THUMBNAIL_SIZE_IN_LIST], + _CustomizationSettingsKeys.heightOfAlbumTile: [lang.HEIGHT_OF_ALBUM_TILE], + // ----------- + _CustomizationSettingsKeys.TRACKTILECUSTOMIZATION: [lang.TRACK_TILE_CUSTOMIZATION], + _CustomizationSettingsKeys.forceSquaredTrackThumb: [lang.FORCE_SQUARED_TRACK_THUMBNAIL], + _CustomizationSettingsKeys.sizeOfTrackThumb: [lang.TRACK_THUMBNAIL_SIZE_IN_LIST], + _CustomizationSettingsKeys.heightOfTrackTile: [lang.HEIGHT_OF_TRACK_TILE], + _CustomizationSettingsKeys.displayThirdRow: [lang.DISPLAY_THIRD_ROW_IN_TRACK_TILE], + _CustomizationSettingsKeys.displayThirdItemInRow: [lang.DISPLAY_THIRD_ITEM_IN_ROW_IN_TRACK_TILE], + _CustomizationSettingsKeys.displayFavButtonInTrackTile: [lang.DISPLAY_FAVOURITE_ICON_IN_TRACK_TILE], + _CustomizationSettingsKeys.itemsSeparator: [lang.TRACK_TILE_ITEMS_SEPARATOR], + // ----------- + _CustomizationSettingsKeys.MINIPLAYERCUSTOMIZATION: [lang.MINIPLAYER_CUSTOMIZATION], + _CustomizationSettingsKeys.partyMode: [lang.ENABLE_PARTY_MODE, lang.ENABLE_PARTY_MODE_SUBTITLE], + _CustomizationSettingsKeys.edgeColorsSwitching: [lang.EDGE_COLORS_SWITCHING], + _CustomizationSettingsKeys.movingParticles: [lang.ENABLE_MINIPLAYER_PARTICLES], + _CustomizationSettingsKeys.thumbAnimationIntensity: [lang.ANIMATING_THUMBNAIL_INTENSITY], + _CustomizationSettingsKeys.thumbInverseAnimation: [lang.ANIMATING_THUMBNAIL_INVERSED, lang.ANIMATING_THUMBNAIL_INVERSED_SUBTITLE], + _CustomizationSettingsKeys.waveformBarsCount: [lang.WAVEFORM_BARS_COUNT], + _CustomizationSettingsKeys.displayAudioInfo: [lang.DISPLAY_AUDIO_INFO_IN_MINIPLAYER], + _CustomizationSettingsKeys.displayArtistBeforeTitle: [lang.DISPLAY_ARTIST_BEFORE_TITLE], + }; @override Widget build(BuildContext context) { @@ -24,146 +110,891 @@ class CustomizationSettings extends StatelessWidget { title: lang.CUSTOMIZATIONS, subtitle: lang.CUSTOMIZATIONS_SUBTITLE, icon: Broken.brush_1, - child: Obx( - () => Column( - children: [ - CustomSwitchListTile( - icon: Broken.drop, - title: lang.ENABLE_BLUR_EFFECT, - subtitle: lang.PERFORMANCE_NOTE, - onChanged: (p0) { - settings.save( - enableBlurEffect: !p0, - performanceMode: PerformanceMode.custom, - ); - }, - value: settings.enableBlurEffect.value, - ), - CustomSwitchListTile( - icon: Broken.sun_1, - title: lang.ENABLE_GLOW_EFFECT, - subtitle: lang.PERFORMANCE_NOTE, - onChanged: (p0) { - settings.save( - enableGlowEffect: !p0, + child: Column( + children: [ + getItemWrapper( + key: _CustomizationSettingsKeys.enableBlur, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.enableBlur), + icon: Broken.drop, + title: lang.ENABLE_BLUR_EFFECT, + subtitle: lang.PERFORMANCE_NOTE, + onChanged: (p0) { + settings.save( + enableBlurEffect: !p0, + performanceMode: PerformanceMode.custom, + ); + }, + value: settings.enableBlurEffect.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.enableGlow, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.enableGlow), + icon: Broken.sun_1, + title: lang.ENABLE_GLOW_EFFECT, + subtitle: lang.PERFORMANCE_NOTE, + onChanged: (p0) { + settings.save( + enableGlowEffect: !p0, + performanceMode: PerformanceMode.custom, + ); + }, + value: settings.enableGlowEffect.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.enableParallax, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.enableParallax), + icon: Broken.maximize, + title: lang.ENABLE_PARALLAX_EFFECT, + subtitle: lang.PERFORMANCE_NOTE, + onChanged: (isTrue) => settings.save( + enableMiniplayerParallaxEffect: !isTrue, performanceMode: PerformanceMode.custom, - ); - }, - value: settings.enableGlowEffect.value, - ), - CustomSwitchListTile( - icon: Broken.maximize, - title: lang.ENABLE_PARALLAX_EFFECT, - subtitle: lang.PERFORMANCE_NOTE, - onChanged: (isTrue) => settings.save( - enableMiniplayerParallaxEffect: !isTrue, - performanceMode: PerformanceMode.custom, - ), - value: settings.enableMiniplayerParallaxEffect.value, - ), - CustomSwitchListTile( - icon: Broken.timer, - title: lang.DISPLAY_REMAINING_DURATION_INSTEAD_OF_TOTAL, - onChanged: (isTrue) => settings.save(displayRemainingDurInsteadOfTotal: !isTrue), - value: settings.displayRemainingDurInsteadOfTotal.value, - ), - CustomListTile( - icon: Broken.rotate_left_1, - title: lang.BORDER_RADIUS_MULTIPLIER, - trailingText: "${settings.borderRadiusMultiplier.value}", - onTap: () { - showSettingDialogWithTextField( - title: lang.BORDER_RADIUS_MULTIPLIER, - borderRadiusMultiplier: true, - icon: Broken.rotate_left_1, - ); - }, - ), - CustomListTile( - icon: Broken.text, - title: lang.FONT_SCALE, - trailingText: "${(settings.fontScaleFactor.value * 100).toInt()}%", - onTap: () { - showSettingDialogWithTextField( - title: lang.FONT_SCALE, - fontScaleFactor: true, - icon: Broken.text, - ); - }, - ), - CustomSwitchListTile( - icon: Broken.clock, - title: lang.HOUR_FORMAT_12, - onChanged: (p0) { - settings.save(hourFormat12: !p0); - }, - value: settings.hourFormat12.value, - ), - CustomListTile( - icon: Broken.calendar_edit, - title: lang.DATE_TIME_FORMAT, - trailingText: "${settings.dateTimeFormat}", - onTap: () { - final ScrollController scrollController = ScrollController(); - - showSettingDialogWithTextField( - title: lang.DATE_TIME_FORMAT, - icon: Broken.calendar_edit, - dateTimeFormat: true, - topWidget: SizedBox( - height: Get.height * 0.4, - child: Padding( - padding: const EdgeInsets.only(bottom: 58.0), - child: Stack( - children: [ - SingleChildScrollView( - controller: scrollController, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ...kDefaultDateTimeStrings.entries.map( - (e) => SmallListTile( - title: e.value, - active: settings.dateTimeFormat.value == e.key, - onTap: () { - settings.save(dateTimeFormat: e.key); - NamidaNavigator.inst.closeDialog(); - }, + ), + value: settings.enableMiniplayerParallaxEffect.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.displayRemainingDur, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.displayRemainingDur), + icon: Broken.timer, + title: lang.DISPLAY_REMAINING_DURATION_INSTEAD_OF_TOTAL, + onChanged: (isTrue) => settings.save(displayRemainingDurInsteadOfTotal: !isTrue), + value: settings.displayRemainingDurInsteadOfTotal.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.brMultiplier, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.brMultiplier), + icon: Broken.rotate_left_1, + title: lang.BORDER_RADIUS_MULTIPLIER, + trailingText: "${settings.borderRadiusMultiplier.value}", + onTap: () { + showSettingDialogWithTextField( + title: lang.BORDER_RADIUS_MULTIPLIER, + borderRadiusMultiplier: true, + icon: Broken.rotate_left_1, + ); + }, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.fontScale, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.fontScale), + icon: Broken.text, + title: lang.FONT_SCALE, + trailingText: "${(settings.fontScaleFactor.value * 100).toInt()}%", + onTap: () { + showSettingDialogWithTextField( + title: lang.FONT_SCALE, + fontScaleFactor: true, + icon: Broken.text, + ); + }, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.hourFormat12, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.hourFormat12), + icon: Broken.clock, + title: lang.HOUR_FORMAT_12, + onChanged: (p0) { + settings.save(hourFormat12: !p0); + }, + value: settings.hourFormat12.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.dateTimeFormat, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.dateTimeFormat), + icon: Broken.calendar_edit, + title: lang.DATE_TIME_FORMAT, + trailingText: "${settings.dateTimeFormat}", + onTap: () { + final ScrollController scrollController = ScrollController(); + + showSettingDialogWithTextField( + title: lang.DATE_TIME_FORMAT, + icon: Broken.calendar_edit, + dateTimeFormat: true, + topWidget: SizedBox( + height: Get.height * 0.4, + child: Padding( + padding: const EdgeInsets.only(bottom: 58.0), + child: Stack( + children: [ + SingleChildScrollView( + controller: scrollController, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...kDefaultDateTimeStrings.entries.map( + (e) => SmallListTile( + title: e.value, + active: settings.dateTimeFormat.value == e.key, + onTap: () { + settings.save(dateTimeFormat: e.key); + NamidaNavigator.inst.closeDialog(); + }, + ), ), - ), - ], - ), - ), - Positioned( - bottom: 20, - right: 0, - child: Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration(color: Get.theme.cardTheme.color, shape: BoxShape.circle), - child: NamidaIconButton( - icon: Broken.arrow_circle_down, - onPressed: () { - scrollController.animateTo( - scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, + ], ), ), - ) - ], + Positioned( + bottom: 20, + right: 0, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration(color: Get.theme.cardTheme.color, shape: BoxShape.circle), + child: NamidaIconButton( + icon: Broken.arrow_circle_down, + onPressed: () { + scrollController.animateTo( + scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + }, + ), + ), + ) + ], + ), ), + )); + }, + ), + ), + ), + _getAlbumCustomizationsTile(), + _getTrackTileCustomizationsTile(context), + _getMiniplayerCustomizationsTile(), + ], + ), + ); + } + + Widget _getAlbumCustomizationsTile() { + return getItemWrapper( + key: _CustomizationSettingsKeys.ALBUMTILECUSTOMIZATION, + child: NamidaExpansionTile( + bgColor: getBgColor(_CustomizationSettingsKeys.ALBUMTILECUSTOMIZATION), + initiallyExpanded: settings.useSettingCollapsedTiles.value, + leading: const StackedIcon( + baseIcon: Broken.brush, + secondaryIcon: Broken.music_dashboard, + ), + titleText: lang.ALBUM_TILE_CUSTOMIZATION, + children: [ + /// Track Number in a small Box + getItemWrapper( + key: _CustomizationSettingsKeys.trackNumberInAlbumPage, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.trackNumberInAlbumPage), + icon: Broken.card_remove, + title: lang.DISPLAY_TRACK_NUMBER_IN_ALBUM_PAGE, + subtitle: lang.DISPLAY_TRACK_NUMBER_IN_ALBUM_PAGE_SUBTITLE, + value: settings.displayTrackNumberinAlbumPage.value, + onChanged: (p0) => settings.save(displayTrackNumberinAlbumPage: !p0), + ), + ), + ), + + /// Album Card Top Right Date + getItemWrapper( + key: _CustomizationSettingsKeys.albumCardTopRightDate, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.albumCardTopRightDate), + icon: Broken.notification_status, + title: lang.DISPLAY_ALBUM_CARD_TOP_RIGHT_DATE, + subtitle: lang.DISPLAY_ALBUM_CARD_TOP_RIGHT_DATE_SUBTITLE, + onChanged: (p0) => settings.save(albumCardTopRightDate: !p0), + value: settings.albumCardTopRightDate.value, + ), + ), + ), + + /// Force Squared Album Thumbnail + getItemWrapper( + key: _CustomizationSettingsKeys.forceSquaredAlbumThumb, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.forceSquaredAlbumThumb), + icon: Broken.crop, + title: lang.FORCE_SQUARED_ALBUM_THUMBNAIL, + value: settings.forceSquaredAlbumThumbnail.value, + onChanged: (p0) { + settings.save(forceSquaredAlbumThumbnail: !p0); + if (!p0 && settings.albumThumbnailSizeinList.toInt() != settings.albumListTileHeight.toInt()) { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + isWarning: true, + bodyText: lang.FORCE_SQUARED_THUMBNAIL_NOTE, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.CONFIRM, + onPressed: () { + settings.save(albumThumbnailSizeinList: settings.albumListTileHeight.value); + NamidaNavigator.inst.closeDialog(); + }, + ), + ], + ), + ); + } + }, + ), + ), + ), + + /// Staggered Album Gridview + getItemWrapper( + key: _CustomizationSettingsKeys.staggeredAlbumGridview, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.staggeredAlbumGridview), + icon: Broken.element_4, + title: lang.STAGGERED_ALBUM_GRID_VIEW, + value: settings.useAlbumStaggeredGridView.value, + onChanged: (p0) => settings.save(useAlbumStaggeredGridView: !p0), + ), + ), + ), + + /// Album Thumbnail Size in List + getItemWrapper( + key: _CustomizationSettingsKeys.sizeOfAlbumThumb, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.sizeOfAlbumThumb), + icon: Broken.maximize_3, + title: lang.ALBUM_THUMBNAIL_SIZE_IN_LIST, + trailingText: "${settings.albumThumbnailSizeinList.toInt()}", + onTap: () { + showSettingDialogWithTextField( + title: lang.ALBUM_THUMBNAIL_SIZE_IN_LIST, + albumThumbnailSizeinList: true, + icon: Broken.maximize_3, + ); + }, + ), + ), + ), + + /// Album Tile Height + getItemWrapper( + key: _CustomizationSettingsKeys.heightOfAlbumTile, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.heightOfAlbumTile), + icon: Broken.pharagraphspacing, + title: lang.HEIGHT_OF_ALBUM_TILE, + trailingText: "${settings.albumListTileHeight.toInt()}", + onTap: () { + showSettingDialogWithTextField( + title: lang.HEIGHT_OF_ALBUM_TILE, + albumListTileHeight: true, + icon: Broken.pharagraphspacing, + ); + }, + ), + ), + ), + ], + ), + ); + } + + void _onSettingsChanged() => TrackTileManager.onTrackItemPropChange(); + + void _showTrackItemsDialog(TrackTilePosition p) { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.CHOOSE, + normalTitleStyle: true, + insetPadding: const EdgeInsets.all(64.0), + child: SizedBox( + height: Get.height * 0.5, + width: Get.width, + child: NamidaListView( + padding: EdgeInsets.zero, + itemBuilder: (context, i) { + final trItem = TrackTileItem.values[i]; + return SmallListTile( + key: ValueKey(i), + title: trItem.toText(), + onTap: () { + settings.updateTrackItemList(p, trItem); + _onSettingsChanged(); + NamidaNavigator.inst.closeDialog(); + }, + active: settings.trackItem[p] == trItem, + ); + }, + itemCount: TrackTileItem.values.length, + itemExtents: null, + ), + ), + ), + ); + } + + Widget _getTrackTileCustomizationsTile(BuildContext context) { + return getItemWrapper( + key: _CustomizationSettingsKeys.TRACKTILECUSTOMIZATION, + child: NamidaExpansionTile( + bgColor: getBgColor(_CustomizationSettingsKeys.TRACKTILECUSTOMIZATION), + initiallyExpanded: settings.useSettingCollapsedTiles.value, + leading: const StackedIcon( + baseIcon: Broken.brush, + secondaryIcon: Broken.music_circle, + ), + titleText: lang.TRACK_TILE_CUSTOMIZATION, + children: [ + getItemWrapper( + key: _CustomizationSettingsKeys.forceSquaredTrackThumb, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.forceSquaredTrackThumb), + icon: Broken.crop, + title: lang.FORCE_SQUARED_TRACK_THUMBNAIL, + value: settings.forceSquaredTrackThumbnail.value, + onChanged: (value) { + settings.save(forceSquaredTrackThumbnail: !value); + Player.inst.refreshRxVariables(); + _onSettingsChanged(); + if (!value && settings.trackThumbnailSizeinList.toInt() != settings.trackListTileHeight.toInt()) { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + isWarning: true, + bodyText: lang.FORCE_SQUARED_THUMBNAIL_NOTE, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.CONFIRM, + onPressed: () { + settings.save(trackThumbnailSizeinList: settings.trackListTileHeight.value); + NamidaNavigator.inst.closeDialog(); + }, + ), + ], ), - )); - }, + ); + } + }, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.sizeOfTrackThumb, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.sizeOfTrackThumb), + icon: Broken.maximize_3, + title: lang.TRACK_THUMBNAIL_SIZE_IN_LIST, + trailingText: "${settings.trackThumbnailSizeinList.toInt()}", + onTap: () { + showSettingDialogWithTextField( + title: lang.TRACK_THUMBNAIL_SIZE_IN_LIST, + trackThumbnailSizeinList: true, + icon: Broken.maximize_3, + ); + }, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.heightOfTrackTile, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.heightOfTrackTile), + icon: Broken.pharagraphspacing, + title: lang.HEIGHT_OF_TRACK_TILE, + trailingText: "${settings.trackListTileHeight.toInt()}", + onTap: () { + showSettingDialogWithTextField( + title: lang.HEIGHT_OF_TRACK_TILE, + trackListTileHeight: true, + icon: Broken.pharagraphspacing, + ); + }, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.displayThirdRow, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.displayThirdRow), + icon: Broken.chart_1, + rotateIcon: 1, + title: lang.DISPLAY_THIRD_ROW_IN_TRACK_TILE, + onChanged: (isTrue) { + settings.save(displayThirdRow: !isTrue); + _onSettingsChanged(); + }, + value: settings.displayThirdRow.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.displayThirdItemInRow, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.displayThirdItemInRow), + icon: Broken.coin, + rotateIcon: 3, + title: lang.DISPLAY_THIRD_ITEM_IN_ROW_IN_TRACK_TILE, + onChanged: (isTrue) { + settings.save(displayThirdItemInEachRow: !isTrue); + _onSettingsChanged(); + }, + value: settings.displayThirdItemInEachRow.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.displayFavButtonInTrackTile, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.displayFavButtonInTrackTile), + icon: Broken.heart, + title: lang.DISPLAY_FAVOURITE_ICON_IN_TRACK_TILE, + onChanged: (isTrue) { + settings.save(displayFavouriteIconInListTile: !isTrue); + _onSettingsChanged(); + }, + value: settings.displayFavouriteIconInListTile.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.itemsSeparator, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.itemsSeparator), + icon: Broken.minus_square, + title: lang.TRACK_TILE_ITEMS_SEPARATOR, + trailingText: settings.trackTileSeparator.value, + onTap: () => showSettingDialogWithTextField( + title: lang.TRACK_TILE_ITEMS_SEPARATOR, + trackTileSeparator: true, + icon: Broken.minus_square, + ), + ), + ), + ), + Obx( + () => Container( + color: context.theme.cardTheme.color, + width: context.width, + height: settings.trackListTileHeight * 1.5, + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(vertical: 7.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + const SizedBox( + width: 12.0, + ), + Container( + margin: const EdgeInsets.symmetric( + horizontal: 0.0, + ), + width: settings.trackThumbnailSizeinList.value, + height: settings.trackThumbnailSizeinList.value, + child: ArtworkWidget( + thumbnailSize: settings.trackThumbnailSizeinList.value, + path: allTracksInLibrary.firstOrNull?.pathToImage, + forceSquared: settings.forceSquaredTrackThumbnail.value, + ), + ), + const SizedBox( + width: 12.0, + ), + + /// Main Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FittedBox( + child: Row( + children: [ + TrackTilePosition.row1Item1, + TrackTilePosition.row1Item2, + if (settings.displayThirdItemInEachRow.value) TrackTilePosition.row1Item3, + ] + .map( + (e) => TrackItemSmallBox( + text: settings.trackItem[e]?.label, + onTap: () => _showTrackItemsDialog(e), + ), + ) + .addSeparators(separator: const SizedBox(width: 6.0)) + .toList(), + ), + ), + const SizedBox( + height: 4.0, + ), + FittedBox( + child: Row( + children: [ + TrackTilePosition.row2Item1, + TrackTilePosition.row2Item2, + if (settings.displayThirdItemInEachRow.value) TrackTilePosition.row2Item3, + ] + .map( + (e) => TrackItemSmallBox( + text: settings.trackItem[e]?.label, + onTap: () => _showTrackItemsDialog(e), + ), + ) + .addSeparators(separator: const SizedBox(width: 6.0)) + .toList(), + ), + ), + const SizedBox( + height: 4.0, + ), + if (settings.displayThirdRow.value) + FittedBox( + child: Row( + children: [ + TrackTilePosition.row3Item1, + TrackTilePosition.row3Item2, + if (settings.displayThirdItemInEachRow.value) TrackTilePosition.row3Item3, + ] + .map( + (e) => TrackItemSmallBox( + text: settings.trackItem[e]?.label, + onTap: () => _showTrackItemsDialog(e), + ), + ) + .addSeparators(separator: const SizedBox(width: 6.0)) + .toList(), + ), + ), + ], + ), + ), + const SizedBox(width: 6.0), + + /// Right Items + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...[ + TrackTilePosition.rightItem1, + TrackTilePosition.rightItem2, + ] + .map( + (e) => TrackItemSmallBox( + text: settings.trackItem[e]?.label, + onTap: () => _showTrackItemsDialog(e), + ), + ) + .addSeparators(separator: const SizedBox(height: 3.0)) + .toList(), + if (settings.displayFavouriteIconInListTile.value) ...[ + const SizedBox(height: 3.0), + const NamidaLikeButton( + track: null, + size: 20, + ), + ] + ], + ), + const SizedBox(width: 6.0), + const MoreIcon( + iconSize: 20, + ), + const SizedBox(width: 6.0), + ], + ), ), - const AlbumTileCustomization(), - const TrackTileCustomization(), - const MiniplayerCustomization(), - ], + ) + ], + ), + ); + } + + Widget _getMiniplayerCustomizationsTile() { + return getItemWrapper( + key: _CustomizationSettingsKeys.MINIPLAYERCUSTOMIZATION, + child: NamidaExpansionTile( + bgColor: getBgColor(_CustomizationSettingsKeys.MINIPLAYERCUSTOMIZATION), + initiallyExpanded: settings.useSettingCollapsedTiles.value, + leading: const StackedIcon( + baseIcon: Broken.brush, + secondaryIcon: Broken.external_drive, ), + titleText: lang.MINIPLAYER_CUSTOMIZATION, + children: [ + getItemWrapper( + key: _CustomizationSettingsKeys.partyMode, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.partyMode), + icon: Broken.slider_horizontal_1, + title: lang.ENABLE_PARTY_MODE, + subtitle: lang.ENABLE_PARTY_MODE_SUBTITLE, + onChanged: (value) { + // disable + if (value) { + settings.save(enablePartyModeInMiniplayer: false); + } + // pls lemme enable + if (!value) { + if (settings.didSupportNamida) { + settings.save(enablePartyModeInMiniplayer: true); + } else { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + title: 'uwu', + actions: const [NamidaSupportButton()], + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onDoubleTap: () { + settings.save(didSupportNamida: true); + }, + child: const Text('a- ano...'), + ), + const Text( + 'this one is actually supposed to be for supporters, if you don\'t mind u can support namida and get the power to unleash this cool feature', + ), + GestureDetector( + onTap: () { + NamidaNavigator.inst.closeDialog(); + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + title: '!!', + bodyText: "EH? YOU DON'T WANT TO SUPPORT?", + actions: [ + NamidaSupportButton(title: lang.YES), + NamidaButton( + text: lang.NO, + onPressed: () { + NamidaNavigator.inst.closeDialog(); + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: 'kechi', + bodyText: 'hidoii ಥ_ಥ here use it as much as u can, dw im not upset or anything ^^, or am i?', + actions: [ + NamidaButton( + text: lang.UNLOCK.toUpperCase(), + onPressed: () { + NamidaNavigator.inst.closeDialog(); + settings.save(enablePartyModeInMiniplayer: true); + }, + ), + NamidaButton( + text: lang.SUPPORT.toUpperCase(), + onPressed: () { + NamidaNavigator.inst.closeDialog(); + launchUrlString(AppSocial.DONATE_BUY_ME_A_COFFEE); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ); + }, + child: const Text('or you just wanna use it like that? mattaku'), + ) + ], + ), + ), + ); + } + } + }, + value: settings.enablePartyModeInMiniplayer.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.edgeColorsSwitching, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.edgeColorsSwitching), + enabled: settings.enablePartyModeInMiniplayer.value, + icon: Broken.colors_square, + title: lang.EDGE_COLORS_SWITCHING, + onChanged: (value) { + settings.save(enablePartyModeColorSwap: !value); + }, + value: settings.enablePartyModeColorSwap.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.movingParticles, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.movingParticles), + icon: Broken.buy_crypto, + title: lang.ENABLE_MINIPLAYER_PARTICLES, + onChanged: (value) => settings.save(enableMiniplayerParticles: !value), + value: settings.enableMiniplayerParticles.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.thumbAnimationIntensity, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.thumbAnimationIntensity), + icon: Broken.flash, + title: lang.ANIMATING_THUMBNAIL_INTENSITY, + trailing: NamidaWheelSlider( + totalCount: 25, + initValue: settings.animatingThumbnailIntensity.value, + itemSize: 6, + onValueChanged: (val) { + settings.save(animatingThumbnailIntensity: val as int); + }, + text: "${(settings.animatingThumbnailIntensity.value * 4).toStringAsFixed(0)}%", + ), + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.thumbInverseAnimation, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.thumbInverseAnimation), + icon: Broken.arrange_circle_2, + title: lang.ANIMATING_THUMBNAIL_INVERSED, + subtitle: lang.ANIMATING_THUMBNAIL_INVERSED_SUBTITLE, + onChanged: (value) { + settings.save(animatingThumbnailInversed: !value); + }, + value: settings.animatingThumbnailInversed.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.waveformBarsCount, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.waveformBarsCount), + icon: Broken.sound, + title: lang.WAVEFORM_BARS_COUNT, + trailing: SizedBox( + width: 80, + child: Column( + children: [ + NamidaWheelSlider( + totalCount: 360, + initValue: settings.waveformTotalBars.value - 40, + itemSize: 6, + onValueChanged: (val) { + final v = (val + 40); + settings.save(waveformTotalBars: v); + }, + text: settings.waveformTotalBars.value.toString(), + ), + ], + ), + ), + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.displayAudioInfo, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.displayAudioInfo), + icon: Broken.text_block, + title: lang.DISPLAY_AUDIO_INFO_IN_MINIPLAYER, + onChanged: (value) => settings.save(displayAudioInfoMiniplayer: !value), + value: settings.displayAudioInfoMiniplayer.value, + ), + ), + ), + getItemWrapper( + key: _CustomizationSettingsKeys.displayArtistBeforeTitle, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_CustomizationSettingsKeys.displayArtistBeforeTitle), + icon: Broken.align_left, + title: lang.DISPLAY_ARTIST_BEFORE_TITLE, + onChanged: (value) { + settings.save(displayArtistBeforeTitle: !value); + Player.inst.refreshRxVariables(); + }, + value: settings.displayArtistBeforeTitle.value, + ), + ), + ), + ], ), ); } } + +class TrackItemSmallBox extends StatelessWidget { + final void Function()? onTap; + final Widget? child; + final String? text; + const TrackItemSmallBox({super.key, this.onTap, this.child, this.text}); + + @override + Widget build(BuildContext context) { + return NamidaInkWell( + bgColor: context.theme.colorScheme.background.withAlpha(160), + onTap: onTap, + borderRadius: 8.0, + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + child: text != null + ? Text( + text!, + style: context.theme.textTheme.displaySmall, + ) + : child, + ); + } +} diff --git a/lib/ui/widgets/settings/extra_settings.dart b/lib/ui/widgets/settings/extra_settings.dart index fe0dc00d..e6ad516a 100644 --- a/lib/ui/widgets/settings/extra_settings.dart +++ b/lib/ui/widgets/settings/extra_settings.dart @@ -8,6 +8,7 @@ import 'package:namida/controller/indexer_controller.dart'; import 'package:namida/controller/miniplayer_controller.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; import 'package:namida/core/extensions.dart'; @@ -17,8 +18,42 @@ import 'package:namida/core/translations/language.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class ExtrasSettings extends StatelessWidget { - const ExtrasSettings({super.key}); +enum _ExtraSettingsKeys { + collapsedTiles, + bottomNavBar, + pip, + foldersHierarchy, + defaultLibraryTab, + libraryTabs, + filterTracksBy, + searchCleanup, + prioritizeEmbeddedLyrics, + immersiveMode, + swipeToOpenDrawer, + extractAllPalettes, +} + +class ExtrasSettings extends SettingSubpageProvider { + const ExtrasSettings({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.extra; + + @override + Map> get lookupMap => { + _ExtraSettingsKeys.collapsedTiles: [lang.USE_COLLAPSED_SETTING_TILES], + _ExtraSettingsKeys.bottomNavBar: [lang.ENABLE_BOTTOM_NAV_BAR, lang.ENABLE_BOTTOM_NAV_BAR_SUBTITLE], + _ExtraSettingsKeys.pip: ["${lang.ENABLE_PICTURE_IN_PICTURE} (${lang.BETA})"], + _ExtraSettingsKeys.foldersHierarchy: [lang.ENABLE_FOLDERS_HIERARCHY], + _ExtraSettingsKeys.defaultLibraryTab: [lang.DEFAULT_LIBRARY_TAB], + _ExtraSettingsKeys.libraryTabs: [lang.LIBRARY_TABS], + _ExtraSettingsKeys.filterTracksBy: [lang.FILTER_TRACKS_BY], + _ExtraSettingsKeys.searchCleanup: [lang.ENABLE_SEARCH_CLEANUP, lang.ENABLE_SEARCH_CLEANUP_SUBTITLE], + _ExtraSettingsKeys.prioritizeEmbeddedLyrics: [lang.PRIORITIZE_EMBEDDED_LYRICS], + _ExtraSettingsKeys.immersiveMode: [lang.IMMERSIVE_MODE, lang.IMMERSIVE_MODE_SUBTITLE], + _ExtraSettingsKeys.swipeToOpenDrawer: [lang.SWIPE_TO_OPEN_DRAWER], + _ExtraSettingsKeys.extractAllPalettes: [lang.EXTRACT_ALL_COLOR_PALETTES], + }; @override Widget build(BuildContext context) { @@ -27,90 +62,111 @@ class ExtrasSettings extends StatelessWidget { subtitle: lang.EXTRAS_SUBTITLE, icon: Broken.command_square, child: Column( - children: [ - const CollapsedSettingTileWidget(), - Obx( - () => CustomSwitchListTile( - icon: Broken.direct, - title: lang.ENABLE_BOTTOM_NAV_BAR, - subtitle: lang.ENABLE_BOTTOM_NAV_BAR_SUBTITLE, - value: settings.enableBottomNavBar.value, - onChanged: (p0) { - settings.save(enableBottomNavBar: !p0); - MiniPlayerController.inst.updateBottomNavBarRelatedDimensions(!p0); - }, + children: [ + getItemWrapper( + key: _ExtraSettingsKeys.collapsedTiles, + child: CollapsedSettingTileWidget( + bgColor: getBgColor(_ExtraSettingsKeys.collapsedTiles), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.screenmirroring, - title: "${lang.ENABLE_PICTURE_IN_PICTURE} (${lang.BETA})", - value: settings.enablePip.value, - onChanged: (isTrue) => settings.save(enablePip: !isTrue), + getItemWrapper( + key: _ExtraSettingsKeys.bottomNavBar, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.bottomNavBar), + icon: Broken.direct, + title: lang.ENABLE_BOTTOM_NAV_BAR, + subtitle: lang.ENABLE_BOTTOM_NAV_BAR_SUBTITLE, + value: settings.enableBottomNavBar.value, + onChanged: (p0) { + settings.save(enableBottomNavBar: !p0); + MiniPlayerController.inst.updateBottomNavBarRelatedDimensions(!p0); + }, + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.folder_open, - title: lang.ENABLE_FOLDERS_HIERARCHY, - value: settings.enableFoldersHierarchy.value, - onChanged: (p0) { - settings.save(enableFoldersHierarchy: !p0); - Folders.inst.isHome.value = true; - Folders.inst.isInside.value = false; - }, + getItemWrapper( + key: _ExtraSettingsKeys.pip, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.pip), + icon: Broken.screenmirroring, + title: "${lang.ENABLE_PICTURE_IN_PICTURE} (${lang.BETA})", + value: settings.enablePip.value, + onChanged: (isTrue) => settings.save(enablePip: !isTrue), + ), ), ), - Obx( - () => CustomListTile( - icon: Broken.receipt_1, - title: lang.DEFAULT_LIBRARY_TAB, - trailingText: settings.autoLibraryTab.value ? lang.AUTO : settings.selectedLibraryTab.value.toText(), - onTap: () => NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.DEFAULT_LIBRARY_TAB, - actions: [ - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ), - ], - child: SizedBox( - width: Get.width, - child: Column( - children: [ - Container( - margin: const EdgeInsets.all(4.0), - child: Obx( - () => ListTileWithCheckMark( - title: lang.AUTO, - icon: Broken.recovery_convert, - onTap: () => settings.save(autoLibraryTab: true), - active: settings.autoLibraryTab.value, + getItemWrapper( + key: _ExtraSettingsKeys.foldersHierarchy, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.foldersHierarchy), + icon: Broken.folder_open, + title: lang.ENABLE_FOLDERS_HIERARCHY, + value: settings.enableFoldersHierarchy.value, + onChanged: (p0) { + settings.save(enableFoldersHierarchy: !p0); + Folders.inst.isHome.value = true; + Folders.inst.isInside.value = false; + }, + ), + ), + ), + getItemWrapper( + key: _ExtraSettingsKeys.defaultLibraryTab, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_ExtraSettingsKeys.defaultLibraryTab), + icon: Broken.receipt_1, + title: lang.DEFAULT_LIBRARY_TAB, + trailingText: settings.autoLibraryTab.value ? lang.AUTO : settings.selectedLibraryTab.value.toText(), + onTap: () => NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.DEFAULT_LIBRARY_TAB, + actions: [ + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, + ), + ], + child: SizedBox( + width: Get.width, + child: Column( + children: [ + Container( + margin: const EdgeInsets.all(4.0), + child: Obx( + () => ListTileWithCheckMark( + title: lang.AUTO, + icon: Broken.recovery_convert, + onTap: () => settings.save(autoLibraryTab: true), + active: settings.autoLibraryTab.value, + ), ), ), - ), - const SizedBox(height: 12.0), - ...settings.libraryTabs.asMap().entries.map( - (e) => Obx( - () => Container( - margin: const EdgeInsets.all(4.0), - child: ListTileWithCheckMark( - title: "${e.key + 1}. ${e.value.toText()}", - icon: e.value.toIcon(), - onTap: () { - settings.save( - selectedLibraryTab: e.value, - staticLibraryTab: e.value, - autoLibraryTab: false, - ); - }, - active: settings.selectedLibraryTab.value == e.value, + const SizedBox(height: 12.0), + ...settings.libraryTabs.asMap().entries.map( + (e) => Obx( + () => Container( + margin: const EdgeInsets.all(4.0), + child: ListTileWithCheckMark( + title: "${e.key + 1}. ${e.value.toText()}", + icon: e.value.toIcon(), + onTap: () { + settings.save( + selectedLibraryTab: e.value, + staticLibraryTab: e.value, + autoLibraryTab: false, + ); + }, + active: settings.selectedLibraryTab.value == e.value, + ), ), ), ), - ), - ], + ], + ), ), ), ), @@ -118,140 +174,164 @@ class ExtrasSettings extends StatelessWidget { ), ), getLibraryTabsTile(context), - Obx( - () => CustomListTile( - icon: Broken.filter_search, - title: lang.FILTER_TRACKS_BY, - trailingText: "${settings.trackSearchFilter.length}", - onTap: () => NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.FILTER_TRACKS_BY, - actions: [ - IconButton( - icon: const Icon(Broken.refresh), - tooltip: lang.RESTORE_DEFAULTS, - onPressed: () { - settings.removeFromList(trackSearchFilterAll: TrackSearchFilter.values); + getItemWrapper( + key: _ExtraSettingsKeys.filterTracksBy, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_ExtraSettingsKeys.filterTracksBy), + icon: Broken.filter_search, + title: lang.FILTER_TRACKS_BY, + trailingText: "${settings.trackSearchFilter.length}", + onTap: () => NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.FILTER_TRACKS_BY, + actions: [ + IconButton( + icon: const Icon(Broken.refresh), + tooltip: lang.RESTORE_DEFAULTS, + onPressed: () { + settings.removeFromList(trackSearchFilterAll: TrackSearchFilter.values); - settings.save(trackSearchFilter: [ - TrackSearchFilter.filename, - TrackSearchFilter.title, - TrackSearchFilter.artist, - TrackSearchFilter.album, - ]); - }, - ), - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ), - ], - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ...TrackSearchFilter.values.map( - (e) => Padding( - padding: const EdgeInsets.all(4.0), - child: Obx( - () => ListTileWithCheckMark( - title: e.toText(), - onTap: () => _trackFilterOnTap(e), - active: settings.trackSearchFilter.contains(e), + settings.save(trackSearchFilter: [ + TrackSearchFilter.filename, + TrackSearchFilter.title, + TrackSearchFilter.artist, + TrackSearchFilter.album, + ]); + }, + ), + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, + ), + ], + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...TrackSearchFilter.values.map( + (e) => Padding( + padding: const EdgeInsets.all(4.0), + child: Obx( + () => ListTileWithCheckMark( + title: e.toText(), + onTap: () => _trackFilterOnTap(e), + active: settings.trackSearchFilter.contains(e), + ), ), ), ), - ), - ], + ], + ), ), ), ), ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.document_filter, - title: lang.ENABLE_SEARCH_CLEANUP, - subtitle: lang.ENABLE_SEARCH_CLEANUP_SUBTITLE, - value: settings.enableSearchCleanup.value, - onChanged: (p0) => settings.save(enableSearchCleanup: !p0), + getItemWrapper( + key: _ExtraSettingsKeys.searchCleanup, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.searchCleanup), + icon: Broken.document_filter, + title: lang.ENABLE_SEARCH_CLEANUP, + subtitle: lang.ENABLE_SEARCH_CLEANUP_SUBTITLE, + value: settings.enableSearchCleanup.value, + onChanged: (p0) => settings.save(enableSearchCleanup: !p0), + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.mobile_programming, - title: lang.PRIORITIZE_EMBEDDED_LYRICS, - value: settings.prioritizeEmbeddedLyrics.value, - onChanged: (p0) => settings.save(prioritizeEmbeddedLyrics: !p0), + getItemWrapper( + key: _ExtraSettingsKeys.prioritizeEmbeddedLyrics, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.prioritizeEmbeddedLyrics), + icon: Broken.mobile_programming, + title: lang.PRIORITIZE_EMBEDDED_LYRICS, + value: settings.prioritizeEmbeddedLyrics.value, + onChanged: (p0) => settings.save(prioritizeEmbeddedLyrics: !p0), + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.external_drive, - title: lang.IMMERSIVE_MODE, - subtitle: lang.IMMERSIVE_MODE_SUBTITLE, - value: settings.hideStatusBarInExpandedMiniplayer.value, - onChanged: (p0) => settings.save(hideStatusBarInExpandedMiniplayer: !p0), + getItemWrapper( + key: _ExtraSettingsKeys.immersiveMode, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.immersiveMode), + icon: Broken.external_drive, + title: lang.IMMERSIVE_MODE, + subtitle: lang.IMMERSIVE_MODE_SUBTITLE, + value: settings.hideStatusBarInExpandedMiniplayer.value, + onChanged: (p0) => settings.save(hideStatusBarInExpandedMiniplayer: !p0), + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.sidebar_right, - title: lang.SWIPE_TO_OPEN_DRAWER, - value: settings.swipeableDrawer.value, - onChanged: (isTrue) => settings.save(swipeableDrawer: !isTrue), + getItemWrapper( + key: _ExtraSettingsKeys.swipeToOpenDrawer, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ExtraSettingsKeys.swipeToOpenDrawer), + icon: Broken.sidebar_right, + title: lang.SWIPE_TO_OPEN_DRAWER, + value: settings.swipeableDrawer.value, + onChanged: (isTrue) => settings.save(swipeableDrawer: !isTrue), + ), ), ), - CustomListTile( - icon: Broken.colorfilter, - title: lang.EXTRACT_ALL_COLOR_PALETTES, - trailing: Obx( - () => Column( - children: [ - Text("${Indexer.inst.colorPalettesInStorage.value}/${Indexer.inst.artworksInStorage.value}"), - if (CurrentColor.inst.isGeneratingAllColorPalettes.value) const LoadingIndicator(), - ], + getItemWrapper( + key: _ExtraSettingsKeys.extractAllPalettes, + child: CustomListTile( + bgColor: getBgColor(_ExtraSettingsKeys.extractAllPalettes), + icon: Broken.colorfilter, + title: lang.EXTRACT_ALL_COLOR_PALETTES, + trailing: Obx( + () => Column( + children: [ + Text("${Indexer.inst.colorPalettesInStorage.value}/${Indexer.inst.artworksInStorage.value}"), + if (CurrentColor.inst.isGeneratingAllColorPalettes.value) const LoadingIndicator(), + ], + ), ), + onTap: () async { + if (CurrentColor.inst.isGeneratingAllColorPalettes.value) { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.NOTE, + bodyText: lang.FORCE_STOP_COLOR_PALETTE_GENERATION, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.STOP, + onPressed: () { + CurrentColor.inst.stopGeneratingColorPalettes(); + NamidaNavigator.inst.closeDialog(); + }, + ), + ], + ), + ); + } else { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.NOTE, + bodyText: lang.EXTRACT_ALL_COLOR_PALETTES_SUBTITLE + .replaceFirst('_REMAINING_COLOR_PALETTES_', '${allTracksInLibrary.length - Indexer.inst.colorPalettesInStorage.value}'), + actions: [ + const CancelButton(), + NamidaButton( + text: lang.EXTRACT, + onPressed: () { + CurrentColor.inst.generateAllColorPalettes(); + NamidaNavigator.inst.closeDialog(); + }, + ), + ], + ), + ); + } + }, ), - onTap: () async { - if (CurrentColor.inst.isGeneratingAllColorPalettes.value) { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.NOTE, - bodyText: lang.FORCE_STOP_COLOR_PALETTE_GENERATION, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.STOP, - onPressed: () { - CurrentColor.inst.stopGeneratingColorPalettes(); - NamidaNavigator.inst.closeDialog(); - }, - ), - ], - ), - ); - } else { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.NOTE, - bodyText: lang.EXTRACT_ALL_COLOR_PALETTES_SUBTITLE - .replaceFirst('_REMAINING_COLOR_PALETTES_', '${allTracksInLibrary.length - Indexer.inst.colorPalettesInStorage.value}'), - actions: [ - const CancelButton(), - NamidaButton( - text: lang.EXTRACT, - onPressed: () { - CurrentColor.inst.generateAllColorPalettes(); - NamidaNavigator.inst.closeDialog(); - }, - ), - ], - ), - ); - } - }, ), ], ), @@ -259,115 +339,119 @@ class ExtrasSettings extends StatelessWidget { } Widget getLibraryTabsTile(BuildContext context) { - return Obx( - () => CustomListTile( - icon: Broken.color_swatch, - title: lang.LIBRARY_TABS, - trailingText: "${settings.libraryTabs.length}", - onTap: () { - final subList = [].obs; + return getItemWrapper( + key: _ExtraSettingsKeys.libraryTabs, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_ExtraSettingsKeys.libraryTabs), + icon: Broken.color_swatch, + title: lang.LIBRARY_TABS, + trailingText: "${settings.libraryTabs.length}", + onTap: () { + final subList = [].obs; - LibraryTab.values.loop((e, index) { - if (!settings.libraryTabs.contains(e)) { - subList.add(e); - } - }); + LibraryTab.values.loop((e, index) { + if (!settings.libraryTabs.contains(e)) { + subList.add(e); + } + }); - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.LIBRARY_TABS, - actions: [ - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ), - ], - child: SizedBox( - width: Get.width, - height: Get.height * 0.5, - child: Obx( - () => Column( - children: [ - Text( - lang.LIBRARY_TABS_REORDER, - style: context.textTheme.displayMedium, - ), - const SizedBox(height: 12.0), - Expanded( - flex: 6, - child: ReorderableListView.builder( - shrinkWrap: true, - proxyDecorator: (child, index, animation) => child, - padding: EdgeInsets.zero, - itemCount: settings.libraryTabs.length, - itemBuilder: (context, i) { - final tab = settings.libraryTabs[i]; - return Container( - key: ValueKey(i), - margin: const EdgeInsets.all(4.0), - child: ListTileWithCheckMark( - title: "${i + 1}. ${tab.toText()}", - icon: tab.toIcon(), - onTap: () { - if (settings.libraryTabs.length > 3) { - settings.removeFromList(libraryTab1: tab); - settings.save(selectedLibraryTab: settings.libraryTabs[0]); - subList.add(tab); - } else { - showMinimumItemsSnack(3); - } - }, - active: settings.libraryTabs.contains(tab), - ), - ); - }, - onReorder: (oldIndex, newIndex) { - if (newIndex > oldIndex) { - newIndex -= 1; - } - final item = settings.libraryTabs.elementAt(oldIndex); - settings.removeFromList( - libraryTab1: item, - ); - settings.insertInList(newIndex, libraryTab1: item); - }, + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.LIBRARY_TABS, + actions: [ + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, + ), + ], + child: SizedBox( + width: Get.width, + height: Get.height * 0.5, + child: Obx( + () => Column( + children: [ + Text( + lang.LIBRARY_TABS_REORDER, + style: context.textTheme.displayMedium, ), - ), - const NamidaContainerDivider(height: 4.0, margin: EdgeInsets.symmetric(vertical: 4.0)), - const SizedBox(height: 8.0), - if (subList.isNotEmpty) + const SizedBox(height: 12.0), Expanded( - flex: subList.length, - child: ListView.builder( + flex: 6, + child: ReorderableListView.builder( + shrinkWrap: true, + proxyDecorator: (child, index, animation) => child, padding: EdgeInsets.zero, - itemCount: subList.length, - itemBuilder: (context, index) { - final item = subList[index]; - return Material( - type: MaterialType.transparency, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), - child: ListTileWithCheckMark( - title: "${index + 1}. ${item.toText()}", - icon: item.toIcon(), - onTap: () { - settings.save(libraryTabs: [item]); - subList.remove(item); - }, - active: settings.libraryTabs.contains(item), - ), + itemCount: settings.libraryTabs.length, + itemBuilder: (context, i) { + final tab = settings.libraryTabs[i]; + return Container( + key: ValueKey(i), + margin: const EdgeInsets.all(4.0), + child: ListTileWithCheckMark( + title: "${i + 1}. ${tab.toText()}", + icon: tab.toIcon(), + onTap: () { + if (settings.libraryTabs.length > 3) { + settings.removeFromList(libraryTab1: tab); + settings.save(selectedLibraryTab: settings.libraryTabs[0]); + subList.add(tab); + } else { + showMinimumItemsSnack(3); + } + }, + active: settings.libraryTabs.contains(tab), ), ); }, + onReorder: (oldIndex, newIndex) { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final item = settings.libraryTabs.elementAt(oldIndex); + settings.removeFromList( + libraryTab1: item, + ); + settings.insertInList(newIndex, libraryTab1: item); + }, ), ), - ], + const NamidaContainerDivider(height: 4.0, margin: EdgeInsets.symmetric(vertical: 4.0)), + const SizedBox(height: 8.0), + if (subList.isNotEmpty) + Expanded( + flex: subList.length, + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: subList.length, + itemBuilder: (context, index) { + final item = subList[index]; + return Material( + type: MaterialType.transparency, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTileWithCheckMark( + title: "${index + 1}. ${item.toText()}", + icon: item.toIcon(), + onTap: () { + settings.save(libraryTabs: [item]); + subList.remove(item); + }, + active: settings.libraryTabs.contains(item), + ), + ), + ); + }, + ), + ), + ], + ), ), ), ), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/ui/widgets/settings/indexer_settings.dart b/lib/ui/widgets/settings/indexer_settings.dart index 996ec3af..22cdfa68 100644 --- a/lib/ui/widgets/settings/indexer_settings.dart +++ b/lib/ui/widgets/settings/indexer_settings.dart @@ -6,6 +6,7 @@ import 'package:get/get.dart'; import 'package:namida/controller/indexer_controller.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/controller/video_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; @@ -18,8 +19,46 @@ import 'package:namida/ui/widgets/circular_percentages.dart'; import 'package:namida/ui/widgets/settings/extra_settings.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class IndexerSettings extends StatelessWidget { - const IndexerSettings({super.key}); +enum _IndexerSettingsKeys { + preventDuplicatedTracks, + respectNoMedia, + extractFtArtist, + groupArtworksByAlbum, + albumIdentifiers, + artistSeparators, + genreSeparators, + minimumFileSize, + minimumTrackDur, + useMediaStore, + reindex, + refreshLibrary, + foldersToScan, + foldersToExclude, +} + +class IndexerSettings extends SettingSubpageProvider { + const IndexerSettings({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.indexer; + + @override + Map> get lookupMap => { + _IndexerSettingsKeys.preventDuplicatedTracks: [lang.PREVENT_DUPLICATED_TRACKS, lang.PREVENT_DUPLICATED_TRACKS_SUBTITLE], + _IndexerSettingsKeys.respectNoMedia: [lang.RESPECT_NO_MEDIA, lang.RESPECT_NO_MEDIA_SUBTITLE], + _IndexerSettingsKeys.extractFtArtist: [lang.EXTRACT_FEAT_ARTIST, lang.EXTRACT_FEAT_ARTIST_SUBTITLE], + _IndexerSettingsKeys.groupArtworksByAlbum: [lang.GROUP_ARTWORKS_BY_ALBUM], + _IndexerSettingsKeys.albumIdentifiers: [lang.ALBUM_IDENTIFIERS], + _IndexerSettingsKeys.artistSeparators: [lang.TRACK_ARTISTS_SEPARATOR], + _IndexerSettingsKeys.genreSeparators: [lang.TRACK_GENRES_SEPARATOR], + _IndexerSettingsKeys.minimumFileSize: [lang.MIN_FILE_SIZE], + _IndexerSettingsKeys.minimumTrackDur: [lang.MIN_FILE_DURATION], + _IndexerSettingsKeys.useMediaStore: [lang.USE_MEDIA_STORE, lang.USE_MEDIA_STORE_SUBTITLE], + _IndexerSettingsKeys.reindex: [lang.RE_INDEX, lang.RE_INDEX_SUBTITLE], + _IndexerSettingsKeys.refreshLibrary: [lang.REFRESH_LIBRARY, lang.REFRESH_LIBRARY_SUBTITLE], + _IndexerSettingsKeys.foldersToScan: [lang.LIST_OF_FOLDERS], + _IndexerSettingsKeys.foldersToExclude: [lang.EXCLUDED_FODLERS], + }; Future _showRefreshPromptDialog(bool didModifyFolder) async { _RefreshLibraryIcon.controller.repeat(); @@ -79,31 +118,39 @@ class IndexerSettings extends StatelessWidget { } Widget getMediaStoreWidget() { - return Obx( - () => CustomSwitchListTile( - icon: Broken.airdrop, - title: lang.USE_MEDIA_STORE, - subtitle: lang.USE_MEDIA_STORE_SUBTITLE, - value: settings.useMediaStore.value, - onChanged: (isTrue) { - settings.save(useMediaStore: !isTrue); - _showRefreshPromptDialog(false); - }, + return getItemWrapper( + key: _IndexerSettingsKeys.useMediaStore, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_IndexerSettingsKeys.useMediaStore), + icon: Broken.airdrop, + title: lang.USE_MEDIA_STORE, + subtitle: lang.USE_MEDIA_STORE_SUBTITLE, + value: settings.useMediaStore.value, + onChanged: (isTrue) { + settings.save(useMediaStore: !isTrue); + _showRefreshPromptDialog(false); + }, + ), ), ); } Widget getGroupArtworksByAlbumWidget() { - return Obx( - () => CustomSwitchListTile( - icon: Broken.backward_item, - title: lang.GROUP_ARTWORKS_BY_ALBUM, - subtitle: lang.REQUIRES_CLEARING_IMAGE_CACHE_AND_RE_INDEXING, - value: settings.groupArtworksByAlbum.value, - onChanged: (isTrue) { - settings.save(groupArtworksByAlbum: !isTrue); - _showReindexingPrompt(title: lang.GROUP_ARTWORKS_BY_ALBUM, body: lang.REQUIRES_CLEARING_IMAGE_CACHE_AND_RE_INDEXING); - }, + return getItemWrapper( + key: _IndexerSettingsKeys.groupArtworksByAlbum, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_IndexerSettingsKeys.groupArtworksByAlbum), + icon: Broken.backward_item, + title: lang.GROUP_ARTWORKS_BY_ALBUM, + subtitle: lang.REQUIRES_CLEARING_IMAGE_CACHE_AND_RE_INDEXING, + value: settings.groupArtworksByAlbum.value, + onChanged: (isTrue) { + settings.save(groupArtworksByAlbum: !isTrue); + _showReindexingPrompt(title: lang.GROUP_ARTWORKS_BY_ALBUM, body: lang.REQUIRES_CLEARING_IMAGE_CACHE_AND_RE_INDEXING); + }, + ), ), ); } @@ -112,63 +159,67 @@ class IndexerSettings extends StatelessWidget { required BuildContext context, bool initiallyExpanded = false, }) { - return Obx( - () => NamidaExpansionTile( - initiallyExpanded: initiallyExpanded, - icon: Broken.folder, - titleText: lang.LIST_OF_FOLDERS, - textColor: context.textTheme.displayLarge!.color, - trailing: Row( - mainAxisSize: MainAxisSize.min, + return getItemWrapper( + key: _IndexerSettingsKeys.foldersToScan, + child: Obx( + () => NamidaExpansionTile( + bgColor: getBgColor(_IndexerSettingsKeys.foldersToScan), + initiallyExpanded: initiallyExpanded, + icon: Broken.folder, + titleText: lang.LIST_OF_FOLDERS, + textColor: context.textTheme.displayLarge!.color, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + addFolderButton((dirPath) { + settings.save(directoriesToScan: [dirPath]); + }), + const SizedBox(width: 8.0), + const Icon(Broken.arrow_down_2), + ], + ), children: [ - addFolderButton((dirPath) { - settings.save(directoriesToScan: [dirPath]); - }), - const SizedBox(width: 8.0), - const Icon(Broken.arrow_down_2), - ], - ), - children: [ - ...settings.directoriesToScan.map( - (e) => ListTile( - title: Text( - e, - style: context.textTheme.displayMedium, - ), - trailing: TextButton( - onPressed: () { - if (settings.directoriesToScan.length == 1) { - snackyy( - title: lang.MINIMUM_ONE_ITEM, - message: lang.MINIMUM_ONE_FOLDER_SUBTITLE, - displaySeconds: 4, - ); - } else { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - isWarning: true, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.REMOVE, - onPressed: () { - settings.removeFromList(directoriesToScan1: e); - NamidaNavigator.inst.closeDialog(); - _showRefreshPromptDialog(true); - }, - ), - ], - bodyText: "${lang.REMOVE} \"$e\"?", - ), - ); - } - }, - child: Text(lang.REMOVE.toUpperCase()), + ...settings.directoriesToScan.map( + (e) => ListTile( + title: Text( + e, + style: context.textTheme.displayMedium, + ), + trailing: TextButton( + onPressed: () { + if (settings.directoriesToScan.length == 1) { + snackyy( + title: lang.MINIMUM_ONE_ITEM, + message: lang.MINIMUM_ONE_FOLDER_SUBTITLE, + displaySeconds: 4, + ); + } else { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + isWarning: true, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.REMOVE, + onPressed: () { + settings.removeFromList(directoriesToScan1: e); + NamidaNavigator.inst.closeDialog(); + _showRefreshPromptDialog(true); + }, + ), + ], + bodyText: "${lang.REMOVE} \"$e\"?", + ), + ); + } + }, + child: Text(lang.REMOVE.toUpperCase()), + ), ), ), - ), - ], + ], + ), ), ); } @@ -177,48 +228,52 @@ class IndexerSettings extends StatelessWidget { required BuildContext context, bool initiallyExpanded = false, }) { - return Obx( - () => NamidaExpansionTile( - initiallyExpanded: initiallyExpanded, - icon: Broken.folder_minus, - titleText: lang.EXCLUDED_FODLERS, - textColor: context.textTheme.displayLarge!.color, - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - addFolderButton((dirPath) { - settings.save(directoriesToExclude: [dirPath]); - }), - const SizedBox(width: 8.0), - const Icon(Broken.arrow_down_2), - ], - ), - children: settings.directoriesToExclude.isEmpty - ? [ - ListTile( - title: Text( - lang.NO_EXCLUDED_FOLDERS, - style: context.textTheme.displayMedium, - ), - ), - ] - : [ - ...settings.directoriesToExclude.map( - (e) => ListTile( + return getItemWrapper( + key: _IndexerSettingsKeys.foldersToExclude, + child: Obx( + () => NamidaExpansionTile( + bgColor: getBgColor(_IndexerSettingsKeys.foldersToExclude), + initiallyExpanded: initiallyExpanded, + icon: Broken.folder_minus, + titleText: lang.EXCLUDED_FODLERS, + textColor: context.textTheme.displayLarge!.color, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + addFolderButton((dirPath) { + settings.save(directoriesToExclude: [dirPath]); + }), + const SizedBox(width: 8.0), + const Icon(Broken.arrow_down_2), + ], + ), + children: settings.directoriesToExclude.isEmpty + ? [ + ListTile( title: Text( - e, + lang.NO_EXCLUDED_FOLDERS, style: context.textTheme.displayMedium, ), - trailing: TextButton( - onPressed: () { - settings.removeFromList(directoriesToExclude1: e); - _showRefreshPromptDialog(true); - }, - child: Text(lang.REMOVE.toUpperCase()), + ), + ] + : [ + ...settings.directoriesToExclude.map( + (e) => ListTile( + title: Text( + e, + style: context.textTheme.displayMedium, + ), + trailing: TextButton( + onPressed: () { + settings.removeFromList(directoriesToExclude1: e); + _showRefreshPromptDialog(true); + }, + child: Text(lang.REMOVE.toUpperCase()), + ), ), ), - ), - ], + ], + ), ), ); } @@ -316,198 +371,238 @@ class IndexerSettings extends StatelessWidget { ); }, ), - Obx( - () => CustomSwitchListTile( - icon: Broken.copy, - title: lang.PREVENT_DUPLICATED_TRACKS, - subtitle: "${lang.PREVENT_DUPLICATED_TRACKS_SUBTITLE}. ${lang.INDEX_REFRESH_REQUIRED}", - onChanged: (isTrue) => settings.save(preventDuplicatedTracks: !isTrue), - value: settings.preventDuplicatedTracks.value, + getItemWrapper( + key: _IndexerSettingsKeys.preventDuplicatedTracks, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_IndexerSettingsKeys.preventDuplicatedTracks), + icon: Broken.copy, + title: lang.PREVENT_DUPLICATED_TRACKS, + subtitle: "${lang.PREVENT_DUPLICATED_TRACKS_SUBTITLE}. ${lang.INDEX_REFRESH_REQUIRED}", + onChanged: (isTrue) => settings.save(preventDuplicatedTracks: !isTrue), + value: settings.preventDuplicatedTracks.value, + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.cd, - title: lang.RESPECT_NO_MEDIA, - subtitle: "${lang.RESPECT_NO_MEDIA_SUBTITLE}. ${lang.INDEX_REFRESH_REQUIRED}", - onChanged: (isTrue) => settings.save(respectNoMedia: !isTrue), - value: settings.respectNoMedia.value, + getItemWrapper( + key: _IndexerSettingsKeys.respectNoMedia, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_IndexerSettingsKeys.respectNoMedia), + icon: Broken.cd, + title: lang.RESPECT_NO_MEDIA, + subtitle: "${lang.RESPECT_NO_MEDIA_SUBTITLE}. ${lang.INDEX_REFRESH_REQUIRED}", + onChanged: (isTrue) => settings.save(respectNoMedia: !isTrue), + value: settings.respectNoMedia.value, + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.microphone, - title: lang.EXTRACT_FEAT_ARTIST, - subtitle: "${lang.EXTRACT_FEAT_ARTIST_SUBTITLE} ${lang.INSTANTLY_APPLIES}.", - onChanged: (isTrue) async { - settings.save(extractFeatArtistFromTitle: !isTrue); - await Indexer.inst.prepareTracksFile(); - }, - value: settings.extractFeatArtistFromTitle.value, + getItemWrapper( + key: _IndexerSettingsKeys.extractFtArtist, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_IndexerSettingsKeys.extractFtArtist), + icon: Broken.microphone, + title: lang.EXTRACT_FEAT_ARTIST, + subtitle: "${lang.EXTRACT_FEAT_ARTIST_SUBTITLE} ${lang.INSTANTLY_APPLIES}.", + onChanged: (isTrue) async { + settings.save(extractFeatArtistFromTitle: !isTrue); + await Indexer.inst.prepareTracksFile(); + }, + value: settings.extractFeatArtistFromTitle.value, + ), ), ), getGroupArtworksByAlbumWidget(), - Obx( - () => CustomListTile( - icon: Broken.arrow_square, - title: lang.ALBUM_IDENTIFIERS, - trailingText: settings.albumIdentifiers.length.toString(), - onTap: () { - final tempList = List.from(settings.albumIdentifiers).obs; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.ALBUM_IDENTIFIERS, - actions: [ - const CancelButton(), - const SizedBox(width: 8.0), - Obx( - () { - return NamidaButton( - enabled: settings.albumIdentifiers.any((element) => !tempList.contains(element)) || - tempList.any((element) => !settings.albumIdentifiers.contains(element)), // isEqualTo wont work cuz order shouldnt matter - text: lang.SAVE, - onPressed: () async { - NamidaNavigator.inst.closeDialog(); - settings.removeFromList(albumIdentifiersAll: AlbumIdentifier.values); - settings.save(albumIdentifiers: tempList); + getItemWrapper( + key: _IndexerSettingsKeys.albumIdentifiers, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.albumIdentifiers), + icon: Broken.arrow_square, + title: lang.ALBUM_IDENTIFIERS, + trailingText: settings.albumIdentifiers.length.toString(), + onTap: () { + final tempList = List.from(settings.albumIdentifiers).obs; + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.ALBUM_IDENTIFIERS, + actions: [ + const CancelButton(), + const SizedBox(width: 8.0), + Obx( + () { + return NamidaButton( + enabled: settings.albumIdentifiers.any((element) => !tempList.contains(element)) || + tempList.any((element) => !settings.albumIdentifiers.contains(element)), // isEqualTo wont work cuz order shouldnt matter + text: lang.SAVE, + onPressed: () async { + NamidaNavigator.inst.closeDialog(); + settings.removeFromList(albumIdentifiersAll: AlbumIdentifier.values); + settings.save(albumIdentifiers: tempList); - Indexer.inst.prepareTracksFile(); + Indexer.inst.prepareTracksFile(); - _showReindexingPrompt(title: lang.ALBUM_IDENTIFIERS, body: lang.REQUIRES_CLEARING_IMAGE_CACHE_AND_RE_INDEXING); - }, - ); - }, - ), - ], - child: Column( - children: [ - ...AlbumIdentifier.values.map( - (e) { - final isForcelyEnabled = e == AlbumIdentifier.albumName; - return NamidaOpacity( - opacity: isForcelyEnabled ? 0.7 : 1.0, - child: Padding( - padding: const EdgeInsets.all(6.0), - child: Obx( - () => ListTileWithCheckMark( - title: e.toText(), - active: tempList.contains(e), - onTap: () { - if (isForcelyEnabled) return; - tempList.addOrRemove(e); - }, - ), - ), - ), + _showReindexingPrompt(title: lang.ALBUM_IDENTIFIERS, body: lang.REQUIRES_CLEARING_IMAGE_CACHE_AND_RE_INDEXING); + }, ); }, ), ], + child: Column( + children: [ + ...AlbumIdentifier.values.map( + (e) { + final isForcelyEnabled = e == AlbumIdentifier.albumName; + return NamidaOpacity( + opacity: isForcelyEnabled ? 0.7 : 1.0, + child: Padding( + padding: const EdgeInsets.all(6.0), + child: Obx( + () => ListTileWithCheckMark( + title: e.toText(), + active: tempList.contains(e), + onTap: () { + if (isForcelyEnabled) return; + tempList.addOrRemove(e); + }, + ), + ), + ), + ); + }, + ), + ], + ), ), - ), - ); - }, + ); + }, + ), ), ), - Obx( - () => CustomListTile( - icon: Broken.profile_2user, - title: lang.TRACK_ARTISTS_SEPARATOR, - subtitle: lang.INSTANTLY_APPLIES, - trailingText: "${settings.trackArtistsSeparators.length}", - onTap: () async { - await _showSeparatorSymbolsDialog( - lang.TRACK_ARTISTS_SEPARATOR, - settings.trackArtistsSeparators, - trackArtistsSeparators: true, - ); - }, + getItemWrapper( + key: _IndexerSettingsKeys.artistSeparators, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.artistSeparators), + icon: Broken.profile_2user, + title: lang.TRACK_ARTISTS_SEPARATOR, + subtitle: lang.INSTANTLY_APPLIES, + trailingText: "${settings.trackArtistsSeparators.length}", + onTap: () async { + await _showSeparatorSymbolsDialog( + lang.TRACK_ARTISTS_SEPARATOR, + settings.trackArtistsSeparators, + trackArtistsSeparators: true, + ); + }, + ), ), ), - Obx( - () => CustomListTile( - icon: Broken.smileys, - title: lang.TRACK_GENRES_SEPARATOR, - subtitle: lang.INSTANTLY_APPLIES, - trailingText: "${settings.trackGenresSeparators.length}", - onTap: () async { - await _showSeparatorSymbolsDialog( - lang.TRACK_GENRES_SEPARATOR, - settings.trackGenresSeparators, - trackGenresSeparators: true, - ); - }, + getItemWrapper( + key: _IndexerSettingsKeys.genreSeparators, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.genreSeparators), + icon: Broken.smileys, + title: lang.TRACK_GENRES_SEPARATOR, + subtitle: lang.INSTANTLY_APPLIES, + trailingText: "${settings.trackGenresSeparators.length}", + onTap: () async { + await _showSeparatorSymbolsDialog( + lang.TRACK_GENRES_SEPARATOR, + settings.trackGenresSeparators, + trackGenresSeparators: true, + ); + }, + ), ), ), - Obx( - () => CustomListTile( - icon: Broken.unlimited, - title: lang.MIN_FILE_SIZE, - subtitle: lang.INDEX_REFRESH_REQUIRED, - trailing: NamidaWheelSlider( - width: 100.0, - totalCount: 1024, - squeeze: 0.2, - initValue: settings.indexMinFileSizeInB.value.toInt() / 1024 ~/ 10, - itemSize: 1, - onValueChanged: (val) { - final d = (val as int); - settings.save(indexMinFileSizeInB: d * 1024 * 10); - }, - text: settings.indexMinFileSizeInB.value.fileSizeFormatted, + getItemWrapper( + key: _IndexerSettingsKeys.minimumFileSize, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.minimumFileSize), + icon: Broken.unlimited, + title: lang.MIN_FILE_SIZE, + subtitle: lang.INDEX_REFRESH_REQUIRED, + trailing: NamidaWheelSlider( + width: 100.0, + totalCount: 1024, + squeeze: 0.2, + initValue: settings.indexMinFileSizeInB.value.toInt() / 1024 ~/ 10, + itemSize: 1, + onValueChanged: (val) { + final d = (val as int); + settings.save(indexMinFileSizeInB: d * 1024 * 10); + }, + text: settings.indexMinFileSizeInB.value.fileSizeFormatted, + ), ), ), ), - Obx( - () => CustomListTile( - icon: Broken.timer_1, - title: lang.MIN_FILE_DURATION, - subtitle: lang.INDEX_REFRESH_REQUIRED, - trailing: NamidaWheelSlider( - width: 100.0, - totalCount: 180, - initValue: settings.indexMinDurationInSec.value, - itemSize: 5, - onValueChanged: (val) { - final d = (val as int); - settings.save(indexMinDurationInSec: d); - }, - text: "${settings.indexMinDurationInSec.value} s", + getItemWrapper( + key: _IndexerSettingsKeys.minimumTrackDur, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.minimumTrackDur), + icon: Broken.timer_1, + title: lang.MIN_FILE_DURATION, + subtitle: lang.INDEX_REFRESH_REQUIRED, + trailing: NamidaWheelSlider( + width: 100.0, + totalCount: 180, + initValue: settings.indexMinDurationInSec.value, + itemSize: 5, + onValueChanged: (val) { + final d = (val as int); + settings.save(indexMinDurationInSec: d); + }, + text: "${settings.indexMinDurationInSec.value} s", + ), ), ), ), getMediaStoreWidget(), - CustomListTile( - icon: Broken.refresh, - title: lang.RE_INDEX, - subtitle: lang.RE_INDEX_SUBTITLE, - onTap: () async { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - isWarning: true, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.RE_INDEX, - onPressed: () async { - NamidaNavigator.inst.closeDialog(); - Future.delayed(const Duration(milliseconds: 500), () { - Indexer.inst.refreshLibraryAndCheckForDiff(forceReIndex: true); - }); - }, - ), - ], - bodyText: lang.RE_INDEX_WARNING, - ), - ); - }, + getItemWrapper( + key: _IndexerSettingsKeys.reindex, + child: CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.reindex), + icon: Broken.refresh, + title: lang.RE_INDEX, + subtitle: lang.RE_INDEX_SUBTITLE, + onTap: () async { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + normalTitleStyle: true, + isWarning: true, + actions: [ + const CancelButton(), + NamidaButton( + text: lang.RE_INDEX, + onPressed: () async { + NamidaNavigator.inst.closeDialog(); + Future.delayed(const Duration(milliseconds: 500), () { + Indexer.inst.refreshLibraryAndCheckForDiff(forceReIndex: true); + }); + }, + ), + ], + bodyText: lang.RE_INDEX_WARNING, + ), + ); + }, + ), ), - CustomListTile( - leading: const _RefreshLibraryIcon(), - title: lang.REFRESH_LIBRARY, - subtitle: lang.REFRESH_LIBRARY_SUBTITLE, - onTap: () => _showRefreshPromptDialog(false), + getItemWrapper( + key: _IndexerSettingsKeys.refreshLibrary, + child: CustomListTile( + bgColor: getBgColor(_IndexerSettingsKeys.refreshLibrary), + leading: const _RefreshLibraryIcon(), + title: lang.REFRESH_LIBRARY, + subtitle: lang.REFRESH_LIBRARY_SUBTITLE, + onTap: () => _showRefreshPromptDialog(false), + ), ), getFoldersToScanWidget(context: context), getFoldersToExcludeWidget(context: context), diff --git a/lib/ui/widgets/settings/miniplayer_customization.dart b/lib/ui/widgets/settings/miniplayer_customization.dart deleted file mode 100644 index 51a00d48..00000000 --- a/lib/ui/widgets/settings/miniplayer_customization.dart +++ /dev/null @@ -1,203 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:get/get.dart'; -import 'package:url_launcher/url_launcher_string.dart'; - -import 'package:namida/controller/navigator_controller.dart'; -import 'package:namida/controller/player_controller.dart'; -import 'package:namida/controller/settings_controller.dart'; -import 'package:namida/core/constants.dart'; -import 'package:namida/core/icon_fonts/broken_icons.dart'; -import 'package:namida/core/translations/language.dart'; -import 'package:namida/ui/widgets/custom_widgets.dart'; - -class MiniplayerCustomization extends StatelessWidget { - const MiniplayerCustomization({super.key}); - - @override - Widget build(BuildContext context) { - return NamidaExpansionTile( - initiallyExpanded: settings.useSettingCollapsedTiles.value, - leading: const StackedIcon( - baseIcon: Broken.brush, - secondaryIcon: Broken.external_drive, - ), - titleText: lang.MINIPLAYER_CUSTOMIZATION, - children: [ - Obx( - () => CustomSwitchListTile( - icon: Broken.slider_horizontal_1, - title: lang.ENABLE_PARTY_MODE, - subtitle: lang.ENABLE_PARTY_MODE_SUBTITLE, - onChanged: (value) { - // disable - if (value) { - settings.save(enablePartyModeInMiniplayer: false); - } - // pls lemme enable - if (!value) { - if (settings.didSupportNamida) { - settings.save(enablePartyModeInMiniplayer: true); - } else { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - title: 'uwu', - actions: const [NamidaSupportButton()], - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onDoubleTap: () { - settings.save(didSupportNamida: true); - }, - child: const Text('a- ano...'), - ), - const Text( - 'this one is actually supposed to be for supporters, if you don\'t mind u can support namida and get the power to unleash this cool feature', - ), - GestureDetector( - onTap: () { - NamidaNavigator.inst.closeDialog(); - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - title: '!!', - bodyText: "EH? YOU DON'T WANT TO SUPPORT?", - actions: [ - NamidaSupportButton(title: lang.YES), - NamidaButton( - text: lang.NO, - onPressed: () { - NamidaNavigator.inst.closeDialog(); - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: 'kechi', - bodyText: 'hidoii ಥ_ಥ here use it as much as u can, dw im not upset or anything ^^, or am i?', - actions: [ - NamidaButton( - text: lang.UNLOCK.toUpperCase(), - onPressed: () { - NamidaNavigator.inst.closeDialog(); - settings.save(enablePartyModeInMiniplayer: true); - }, - ), - NamidaButton( - text: lang.SUPPORT.toUpperCase(), - onPressed: () { - NamidaNavigator.inst.closeDialog(); - launchUrlString(AppSocial.DONATE_BUY_ME_A_COFFEE); - }, - ), - ], - ), - ); - }, - ), - ], - ), - ); - }, - child: const Text('or you just wanna use it like that? mattaku'), - ) - ], - ), - ), - ); - } - } - }, - value: settings.enablePartyModeInMiniplayer.value, - ), - ), - Obx( - () => CustomSwitchListTile( - enabled: settings.enablePartyModeInMiniplayer.value, - icon: Broken.colors_square, - title: lang.EDGE_COLORS_SWITCHING, - onChanged: (value) { - settings.save(enablePartyModeColorSwap: !value); - }, - value: settings.enablePartyModeColorSwap.value, - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.buy_crypto, - title: lang.ENABLE_MINIPLAYER_PARTICLES, - onChanged: (value) => settings.save(enableMiniplayerParticles: !value), - value: settings.enableMiniplayerParticles.value, - ), - ), - Obx( - () => CustomListTile( - icon: Broken.flash, - title: lang.ANIMATING_THUMBNAIL_INTENSITY, - trailing: NamidaWheelSlider( - totalCount: 25, - initValue: settings.animatingThumbnailIntensity.value, - itemSize: 6, - onValueChanged: (val) { - settings.save(animatingThumbnailIntensity: val as int); - }, - text: "${(settings.animatingThumbnailIntensity.value * 4).toStringAsFixed(0)}%", - ), - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.arrange_circle_2, - title: lang.ANIMATING_THUMBNAIL_INVERSED, - subtitle: lang.ANIMATING_THUMBNAIL_INVERSED_SUBTITLE, - onChanged: (value) { - settings.save(animatingThumbnailInversed: !value); - }, - value: settings.animatingThumbnailInversed.value, - ), - ), - Obx( - () => CustomListTile( - icon: Broken.sound, - title: lang.WAVEFORM_BARS_COUNT, - trailing: SizedBox( - width: 80, - child: Column( - children: [ - NamidaWheelSlider( - totalCount: 360, - initValue: settings.waveformTotalBars.value - 40, - itemSize: 6, - onValueChanged: (val) { - final v = (val + 40); - settings.save(waveformTotalBars: v); - }, - text: settings.waveformTotalBars.value.toString(), - ), - ], - ), - ), - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.text_block, - title: lang.DISPLAY_AUDIO_INFO_IN_MINIPLAYER, - onChanged: (value) => settings.save(displayAudioInfoMiniplayer: !value), - value: settings.displayAudioInfoMiniplayer.value, - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.align_left, - title: lang.DISPLAY_ARTIST_BEFORE_TITLE, - onChanged: (value) { - settings.save(displayArtistBeforeTitle: !value); - Player.inst.refreshRxVariables(); - }, - value: settings.displayArtistBeforeTitle.value, - ), - ), - ], - ); - } -} diff --git a/lib/ui/widgets/settings/playback_settings.dart b/lib/ui/widgets/settings/playback_settings.dart index b7bb4a48..d794fb04 100644 --- a/lib/ui/widgets/settings/playback_settings.dart +++ b/lib/ui/widgets/settings/playback_settings.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/player_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/controller/video_controller.dart'; import 'package:namida/core/constants.dart'; import 'package:namida/core/enums.dart'; @@ -15,599 +16,730 @@ import 'package:namida/core/translations/language.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class PlaybackSettings extends StatelessWidget { +enum _PlaybackSettingsKeys { + enableVideoPlayback, + videoSource, + videoQuality, + localVideoMatching, + keepScreenAwake, + displayFavButtonInNotif, + killPlayerAfterDismissing, + onNotificationTap, + dismissibleMiniplayer, + skipSilence, + crossfade, + fadeEffectOnPlayPause, + autoPlayOnNextPrev, + infinityQueue, + onVolume0, + onInterruption, + jumpToFirstTrackAfterFinishing, + seekDuration, + minimumTrackDurToRestoreLastPosition, + countListenAfter, +} + +class PlaybackSettings extends SettingSubpageProvider { final bool isInDialog; - const PlaybackSettings({super.key, this.isInDialog = false}); + const PlaybackSettings({super.key, super.initialItem, this.isInDialog = false}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.playback; + + @override + Map> get lookupMap => { + _PlaybackSettingsKeys.enableVideoPlayback: [lang.ENABLE_VIDEO_PLAYBACK], + _PlaybackSettingsKeys.videoSource: [lang.VIDEO_PLAYBACK_SOURCE], + _PlaybackSettingsKeys.videoQuality: [lang.VIDEO_QUALITY], + _PlaybackSettingsKeys.localVideoMatching: [lang.LOCAL_VIDEO_MATCHING], + _PlaybackSettingsKeys.keepScreenAwake: [lang.KEEP_SCREEN_AWAKE_WHEN], + _PlaybackSettingsKeys.displayFavButtonInNotif: [lang.DISPLAY_FAV_BUTTON_IN_NOTIFICATION], + _PlaybackSettingsKeys.killPlayerAfterDismissing: [lang.KILL_PLAYER_AFTER_DISMISSING_APP], + _PlaybackSettingsKeys.onNotificationTap: [lang.ON_NOTIFICATION_TAP], + _PlaybackSettingsKeys.dismissibleMiniplayer: [lang.DISMISSIBLE_MINIPLAYER], + _PlaybackSettingsKeys.skipSilence: [lang.SKIP_SILENCE], + _PlaybackSettingsKeys.crossfade: [lang.ENABLE_CROSSFADE_EFFECT, lang.CROSSFADE_DURATION, lang.CROSSFADE_TRIGGER_SECONDS], + _PlaybackSettingsKeys.fadeEffectOnPlayPause: [lang.ENABLE_FADE_EFFECT_ON_PLAY_PAUSE, lang.PLAY_FADE_DURATION, lang.PAUSE_FADE_DURATION], + _PlaybackSettingsKeys.autoPlayOnNextPrev: [lang.PLAY_AFTER_NEXT_PREV], + _PlaybackSettingsKeys.infinityQueue: [lang.INFINITY_QUEUE_ON_NEXT_PREV, lang.INFINITY_QUEUE_ON_NEXT_PREV_SUBTITLE], + _PlaybackSettingsKeys.onVolume0: [lang.ON_VOLUME_ZERO], + _PlaybackSettingsKeys.onInterruption: [lang.ON_INTERRUPTION], + _PlaybackSettingsKeys.jumpToFirstTrackAfterFinishing: [lang.JUMP_TO_FIRST_TRACK_AFTER_QUEUE_FINISH], + _PlaybackSettingsKeys.seekDuration: [lang.SEEK_DURATION, lang.SEEK_DURATION_INFO], + _PlaybackSettingsKeys.minimumTrackDurToRestoreLastPosition: [lang.MIN_TRACK_DURATION_TO_RESTORE_LAST_POSITION], + _PlaybackSettingsKeys.countListenAfter: [lang.MIN_VALUE_TO_COUNT_TRACK_LISTEN], + }; @override Widget build(BuildContext context) { final children = [ - Obx( - () => CustomSwitchListTile( - title: lang.ENABLE_VIDEO_PLAYBACK, - icon: Broken.video, - value: settings.enableVideoPlayback.value, - onChanged: (p0) async => await VideoController.inst.toggleVideoPlayback(), + getItemWrapper( + key: _PlaybackSettingsKeys.enableVideoPlayback, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.enableVideoPlayback), + title: lang.ENABLE_VIDEO_PLAYBACK, + icon: Broken.video, + value: settings.enableVideoPlayback.value, + onChanged: (p0) async => await VideoController.inst.toggleVideoPlayback(), + ), ), ), - Obx( - () => CustomListTile( - enabled: settings.enableVideoPlayback.value, - title: lang.VIDEO_PLAYBACK_SOURCE, - icon: Broken.scroll, - trailingText: settings.videoPlaybackSource.value.toText(), - onTap: () { - bool isEnabled(VideoPlaybackSource val) { - return settings.videoPlaybackSource.value == val; - } + getItemWrapper( + key: _PlaybackSettingsKeys.videoSource, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.videoSource), + enabled: settings.enableVideoPlayback.value, + title: lang.VIDEO_PLAYBACK_SOURCE, + icon: Broken.scroll, + trailingText: settings.videoPlaybackSource.value.toText(), + onTap: () { + bool isEnabled(VideoPlaybackSource val) { + return settings.videoPlaybackSource.value == val; + } - void tileOnTap(VideoPlaybackSource val) { - settings.save(videoPlaybackSource: val); - } + void tileOnTap(VideoPlaybackSource val) { + settings.save(videoPlaybackSource: val); + } - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.VIDEO_PLAYBACK_SOURCE, - actions: [ - IconButton( - onPressed: () => tileOnTap(VideoPlaybackSource.auto), - icon: const Icon(Broken.refresh), - ), - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ), - ], - child: Obx( - () => ListView( - padding: EdgeInsets.zero, - shrinkWrap: true, - children: [ - ...VideoPlaybackSource.values.map( - (e) => Padding( - padding: const EdgeInsets.only(bottom: 12.0), - child: ListTileWithCheckMark( - active: isEnabled(e), - title: e.toText(), - subtitle: e.toSubtitle() ?? '', - onTap: () => tileOnTap(e), + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.VIDEO_PLAYBACK_SOURCE, + actions: [ + IconButton( + onPressed: () => tileOnTap(VideoPlaybackSource.auto), + icon: const Icon(Broken.refresh), + ), + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, + ), + ], + child: Obx( + () => ListView( + padding: EdgeInsets.zero, + shrinkWrap: true, + children: [ + ...VideoPlaybackSource.values.map( + (e) => Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: ListTileWithCheckMark( + active: isEnabled(e), + title: e.toText(), + subtitle: e.toSubtitle() ?? '', + onTap: () => tileOnTap(e), + ), ), ), - ), - ], + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ), - Obx( - () => CustomListTile( - enabled: settings.enableVideoPlayback.value, - title: lang.VIDEO_QUALITY, - icon: Broken.story, - trailingText: settings.youtubeVideoQualities.first, - onTap: () { - bool isEnabled(String val) => settings.youtubeVideoQualities.contains(val); + getItemWrapper( + key: _PlaybackSettingsKeys.videoQuality, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.videoQuality), + enabled: settings.enableVideoPlayback.value, + title: lang.VIDEO_QUALITY, + icon: Broken.story, + trailingText: settings.youtubeVideoQualities.first, + onTap: () { + bool isEnabled(String val) => settings.youtubeVideoQualities.contains(val); - void tileOnTap(String val, int index) { - if (isEnabled(val)) { - if (settings.youtubeVideoQualities.length == 1) { - showMinimumItemsSnack(1); + void tileOnTap(String val, int index) { + if (isEnabled(val)) { + if (settings.youtubeVideoQualities.length == 1) { + showMinimumItemsSnack(1); + } else { + settings.removeFromList(youtubeVideoQualities1: val); + } } else { - settings.removeFromList(youtubeVideoQualities1: val); + settings.save(youtubeVideoQualities: [val]); } - } else { - settings.save(youtubeVideoQualities: [val]); + // sorts and saves dec + settings.youtubeVideoQualities.sortByReverse((e) => kStockVideoQualities.indexOf(e)); + settings.save(youtubeVideoQualities: settings.youtubeVideoQualities); } - // sorts and saves dec - settings.youtubeVideoQualities.sortByReverse((e) => kStockVideoQualities.indexOf(e)); - settings.save(youtubeVideoQualities: settings.youtubeVideoQualities); - } - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.VIDEO_QUALITY, - actions: [ - // IconButton( - // onPressed: () => tileOnTap(0), - // icon: const Icon(Broken.refresh), - // ), - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ), - ], - child: DefaultTextStyle( - style: context.textTheme.displaySmall!, - child: Obx( - () => Column( - children: [ - Text(lang.VIDEO_QUALITY_SUBTITLE), - const SizedBox( - height: 12.0, - ), - Text("${lang.NOTE}: ${lang.VIDEO_QUALITY_SUBTITLE_NOTE}"), - const SizedBox(height: 18.0), - SizedBox( - width: Get.width, - height: Get.height * 0.4, - child: ListView( - padding: EdgeInsets.zero, - children: [ - ...kStockVideoQualities.asMap().entries.map( - (e) => Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), - child: ListTileWithCheckMark( - active: isEnabled(e.value), - title: e.value, - onTap: () => tileOnTap(e.value, e.key), + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.VIDEO_QUALITY, + actions: [ + // IconButton( + // onPressed: () => tileOnTap(0), + // icon: const Icon(Broken.refresh), + // ), + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, + ), + ], + child: DefaultTextStyle( + style: context.textTheme.displaySmall!, + child: Obx( + () => Column( + children: [ + Text(lang.VIDEO_QUALITY_SUBTITLE), + const SizedBox( + height: 12.0, + ), + Text("${lang.NOTE}: ${lang.VIDEO_QUALITY_SUBTITLE_NOTE}"), + const SizedBox(height: 18.0), + SizedBox( + width: Get.width, + height: Get.height * 0.4, + child: ListView( + padding: EdgeInsets.zero, + children: [ + ...kStockVideoQualities.asMap().entries.map( + (e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTileWithCheckMark( + icon: Broken.story, + active: isEnabled(e.value), + title: e.value, + onTap: () => tileOnTap(e.value, e.key), + ), ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ), ), - ), - ); - }, + ); + }, + ), ), ), - Obx( - () => CustomListTile( - enabled: settings.enableVideoPlayback.value, - icon: Broken.video_tick, - title: lang.LOCAL_VIDEO_MATCHING, - trailingText: settings.localVideoMatchingType.value.toText(), - onTap: () { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.LOCAL_VIDEO_MATCHING, - actions: [ - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ), - ], - child: Column( - children: [ - Obx( - () => CustomListTile( - icon: Broken.video_tick, - title: lang.MATCHING_TYPE, - trailingText: settings.localVideoMatchingType.value.toText(), - onTap: () { - final e = settings.localVideoMatchingType.value.nextElement(LocalVideoMatchingType.values); - settings.save(localVideoMatchingType: e); - }, - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.folder, - title: lang.SAME_DIRECTORY_ONLY, - value: settings.localVideoMatchingCheckSameDir.value, - onChanged: (isTrue) => settings.save(localVideoMatchingCheckSameDir: !isTrue), - ), + getItemWrapper( + key: _PlaybackSettingsKeys.localVideoMatching, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.localVideoMatching), + enabled: settings.enableVideoPlayback.value, + icon: Broken.video_tick, + title: lang.LOCAL_VIDEO_MATCHING, + trailingText: settings.localVideoMatchingType.value.toText(), + onTap: () { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.LOCAL_VIDEO_MATCHING, + actions: [ + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, ), ], + child: Column( + children: [ + Obx( + () => CustomListTile( + icon: Broken.video_tick, + title: lang.MATCHING_TYPE, + trailingText: settings.localVideoMatchingType.value.toText(), + onTap: () { + final e = settings.localVideoMatchingType.value.nextElement(LocalVideoMatchingType.values); + settings.save(localVideoMatchingType: e); + }, + ), + ), + Obx( + () => CustomSwitchListTile( + icon: Broken.folder, + title: lang.SAME_DIRECTORY_ONLY, + value: settings.localVideoMatchingCheckSameDir.value, + onChanged: (isTrue) => settings.save(localVideoMatchingCheckSameDir: !isTrue), + ), + ), + ], + ), ), - ), - ); - }, + ); + }, + ), ), ), - Obx( - () => CustomListTile( - title: '${lang.KEEP_SCREEN_AWAKE_WHEN}:', - subtitle: settings.wakelockMode.value.toText(), - icon: Broken.external_drive, - onTap: () { - final e = settings.wakelockMode.value.nextElement(WakelockMode.values); - settings.save(wakelockMode: e); - }, + getItemWrapper( + key: _PlaybackSettingsKeys.keepScreenAwake, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.keepScreenAwake), + title: '${lang.KEEP_SCREEN_AWAKE_WHEN}:', + subtitle: settings.wakelockMode.value.toText(), + icon: Broken.external_drive, + onTap: () { + final e = settings.wakelockMode.value.nextElement(WakelockMode.values); + settings.save(wakelockMode: e); + }, + ), ), ), - Obx( - () => CustomSwitchListTile( - title: lang.DISPLAY_FAV_BUTTON_IN_NOTIFICATION, - icon: Broken.heart_tick, - value: settings.displayFavouriteButtonInNotification.value, - onChanged: (val) { - settings.save(displayFavouriteButtonInNotification: !val); - Player.inst.refreshNotification(); - if (!val && kSdkVersion < 31) { - snackyy(title: lang.NOTE, message: lang.DISPLAY_FAV_BUTTON_IN_NOTIFICATION_SUBTITLE); - } - }, + getItemWrapper( + key: _PlaybackSettingsKeys.displayFavButtonInNotif, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.displayFavButtonInNotif), + title: lang.DISPLAY_FAV_BUTTON_IN_NOTIFICATION, + icon: Broken.heart_tick, + value: settings.displayFavouriteButtonInNotification.value, + onChanged: (val) { + settings.save(displayFavouriteButtonInNotification: !val); + Player.inst.refreshNotification(); + if (!val && kSdkVersion < 31) { + snackyy(title: lang.NOTE, message: lang.DISPLAY_FAV_BUTTON_IN_NOTIFICATION_SUBTITLE); + } + }, + ), ), ), - Obx( - () => CustomListTile( - title: lang.KILL_PLAYER_AFTER_DISMISSING_APP, - icon: Broken.forbidden_2, - onTap: () { - final element = settings.killPlayerAfterDismissingAppMode.value.nextElement(KillAppMode.values); - settings.save(killPlayerAfterDismissingAppMode: element); - }, - trailingText: settings.killPlayerAfterDismissingAppMode.value.toText(), + getItemWrapper( + key: _PlaybackSettingsKeys.killPlayerAfterDismissing, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.killPlayerAfterDismissing), + title: lang.KILL_PLAYER_AFTER_DISMISSING_APP, + icon: Broken.forbidden_2, + onTap: () { + final element = settings.killPlayerAfterDismissingAppMode.value.nextElement(KillAppMode.values); + settings.save(killPlayerAfterDismissingAppMode: element); + }, + trailingText: settings.killPlayerAfterDismissingAppMode.value.toText(), + ), ), ), - Obx( - () => CustomListTile( - title: lang.ON_NOTIFICATION_TAP, - trailingText: settings.onNotificationTapAction.value.toText(), - icon: Broken.card, - onTap: () { - final element = settings.onNotificationTapAction.value.nextElement(NotificationTapAction.values); - settings.save(onNotificationTapAction: element); - }, + getItemWrapper( + key: _PlaybackSettingsKeys.onNotificationTap, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.onNotificationTap), + title: lang.ON_NOTIFICATION_TAP, + trailingText: settings.onNotificationTapAction.value.toText(), + icon: Broken.card, + onTap: () { + final element = settings.onNotificationTapAction.value.nextElement(NotificationTapAction.values); + settings.save(onNotificationTapAction: element); + }, + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.sidebar_bottom, - title: lang.DISMISSIBLE_MINIPLAYER, - onChanged: (value) => settings.save(dismissibleMiniplayer: !value), - value: settings.dismissibleMiniplayer.value, + getItemWrapper( + key: _PlaybackSettingsKeys.dismissibleMiniplayer, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.dismissibleMiniplayer), + icon: Broken.sidebar_bottom, + title: lang.DISMISSIBLE_MINIPLAYER, + onChanged: (value) => settings.save(dismissibleMiniplayer: !value), + value: settings.dismissibleMiniplayer.value, + ), ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.forward, - title: lang.SKIP_SILENCE, - onChanged: (value) async { - final willBeTrue = !value; - settings.save(playerSkipSilenceEnabled: willBeTrue); - await Player.inst.setSkipSilenceEnabled(willBeTrue); - }, - value: settings.playerSkipSilenceEnabled.value, + getItemWrapper( + key: _PlaybackSettingsKeys.skipSilence, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.skipSilence), + icon: Broken.forward, + title: lang.SKIP_SILENCE, + onChanged: (value) async { + final willBeTrue = !value; + settings.save(playerSkipSilenceEnabled: willBeTrue); + await Player.inst.setSkipSilenceEnabled(willBeTrue); + }, + value: settings.playerSkipSilenceEnabled.value, + ), ), ), // -- Crossfade - NamidaExpansionTile( - normalRightPadding: true, - initiallyExpanded: settings.enableCrossFade.value, - leading: const StackedIcon( - baseIcon: Broken.play, - secondaryIcon: Broken.recovery_convert, + getItemWrapper( + key: _PlaybackSettingsKeys.crossfade, + child: NamidaExpansionTile( + bgColor: getBgColor(_PlaybackSettingsKeys.crossfade), + normalRightPadding: true, + initiallyExpanded: settings.enableCrossFade.value, + leading: const StackedIcon( + baseIcon: Broken.play, + secondaryIcon: Broken.recovery_convert, + ), + childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), + iconColor: context.defaultIconColor(), + titleText: lang.ENABLE_CROSSFADE_EFFECT, + onExpansionChanged: (value) { + settings.save(enableCrossFade: value); + }, + trailing: Obx(() => CustomSwitch(active: settings.enableCrossFade.value)), + children: [ + Obx( + () { + const stepper = 100; + const minVal = 100; + return CustomListTile( + enabled: settings.enableCrossFade.value, + icon: Broken.blend_2, + title: lang.CROSSFADE_DURATION, + trailing: NamidaWheelSlider( + totalCount: (10000 - minVal) ~/ stepper, + initValue: settings.crossFadeDurationMS.value ~/ stepper, + itemSize: 5, + squeeze: 1, + onValueChanged: (val) { + final v = (val * stepper + minVal); + settings.save(crossFadeDurationMS: v); + }, + text: settings.crossFadeDurationMS.value >= 1000 ? "${settings.crossFadeDurationMS.value / 1000}s" : "${settings.crossFadeDurationMS.value}ms", + ), + ); + }, + ), + Obx( + () { + final val = settings.crossFadeAutoTriggerSeconds.value; + return CustomListTile( + enabled: settings.enableCrossFade.value, + icon: Broken.blend, + title: val == 0 ? lang.CROSSFADE_TRIGGER_SECONDS_DISABLED : lang.CROSSFADE_TRIGGER_SECONDS.replaceFirst('_SECONDS_', "$val"), + trailing: NamidaWheelSlider( + totalCount: 30, + initValue: val, + itemSize: 7, + squeeze: 1.4, + onValueChanged: (val) { + settings.save(crossFadeAutoTriggerSeconds: val); + }, + text: "${val}s", + ), + ); + }, + ), + ], ), - childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), - iconColor: context.defaultIconColor(), - titleText: lang.ENABLE_CROSSFADE_EFFECT, - onExpansionChanged: (value) { - settings.save(enableCrossFade: value); - }, - trailing: Obx(() => CustomSwitch(active: settings.enableCrossFade.value)), - children: [ - Obx( - () { - const stepper = 100; - const minVal = 100; - return CustomListTile( - enabled: settings.enableCrossFade.value, - icon: Broken.blend_2, - title: lang.CROSSFADE_DURATION, + ), + // -- Play/Pause Fade + getItemWrapper( + key: _PlaybackSettingsKeys.fadeEffectOnPlayPause, + child: NamidaExpansionTile( + bgColor: getBgColor(_PlaybackSettingsKeys.fadeEffectOnPlayPause), + normalRightPadding: true, + initiallyExpanded: settings.enableVolumeFadeOnPlayPause.value, + leading: const StackedIcon( + baseIcon: Broken.play, + secondaryIcon: Broken.pause, + ), + childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), + iconColor: context.defaultIconColor(), + titleText: lang.ENABLE_FADE_EFFECT_ON_PLAY_PAUSE, + onExpansionChanged: (value) { + settings.save(enableVolumeFadeOnPlayPause: value); + Player.inst.setVolume(settings.playerVolume.value); + }, + trailing: Obx(() => CustomSwitch(active: settings.enableVolumeFadeOnPlayPause.value)), + children: [ + Obx( + () => CustomListTile( + enabled: settings.enableVolumeFadeOnPlayPause.value, + icon: Broken.play, + title: lang.PLAY_FADE_DURATION, trailing: NamidaWheelSlider( - totalCount: (10000 - minVal) ~/ stepper, - initValue: settings.crossFadeDurationMS.value ~/ stepper, - itemSize: 5, - squeeze: 1, + totalCount: 1900 ~/ 50, + initValue: settings.playerPlayFadeDurInMilli.value ~/ 50, + itemSize: 2, + squeeze: 0.4, onValueChanged: (val) { - final v = (val * stepper + minVal); - settings.save(crossFadeDurationMS: v); + final v = (val * 50 + 100); + settings.save(playerPlayFadeDurInMilli: v); }, - text: settings.crossFadeDurationMS.value >= 1000 ? "${settings.crossFadeDurationMS.value / 1000}s" : "${settings.crossFadeDurationMS.value}ms", + text: "${settings.playerPlayFadeDurInMilli.value}ms", ), - ); - }, - ), - Obx( - () { - final val = settings.crossFadeAutoTriggerSeconds.value; - return CustomListTile( - enabled: settings.enableCrossFade.value, - icon: Broken.blend, - title: val == 0 ? lang.CROSSFADE_TRIGGER_SECONDS_DISABLED : lang.CROSSFADE_TRIGGER_SECONDS.replaceFirst('_SECONDS_', "$val"), + ), + ), + Obx( + () => CustomListTile( + enabled: settings.enableVolumeFadeOnPlayPause.value, + icon: Broken.pause, + title: lang.PAUSE_FADE_DURATION, trailing: NamidaWheelSlider( - totalCount: 30, - initValue: val, - itemSize: 7, - squeeze: 1.4, + totalCount: 1900 ~/ 50, + initValue: settings.playerPauseFadeDurInMilli.value ~/ 50, + itemSize: 2, + squeeze: 0.4, onValueChanged: (val) { - settings.save(crossFadeAutoTriggerSeconds: val); + final v = (val * 50 + 100); + settings.save(playerPauseFadeDurInMilli: v); }, - text: "${val}s", + text: "${settings.playerPauseFadeDurInMilli.value}ms", ), - ); - }, - ), - ], - ), - // -- Play/Pause Fade - NamidaExpansionTile( - normalRightPadding: true, - initiallyExpanded: settings.enableVolumeFadeOnPlayPause.value, - leading: const StackedIcon( - baseIcon: Broken.play, - secondaryIcon: Broken.pause, - ), - childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), - iconColor: context.defaultIconColor(), - titleText: lang.ENABLE_FADE_EFFECT_ON_PLAY_PAUSE, - onExpansionChanged: (value) { - settings.save(enableVolumeFadeOnPlayPause: value); - Player.inst.setVolume(settings.playerVolume.value); - }, - trailing: Obx(() => CustomSwitch(active: settings.enableVolumeFadeOnPlayPause.value)), - children: [ - Obx( - () => CustomListTile( - enabled: settings.enableVolumeFadeOnPlayPause.value, - icon: Broken.play, - title: lang.PLAY_FADE_DURATION, - trailing: NamidaWheelSlider( - totalCount: 1900 ~/ 50, - initValue: settings.playerPlayFadeDurInMilli.value ~/ 50, - itemSize: 2, - squeeze: 0.4, - onValueChanged: (val) { - final v = (val * 50 + 100); - settings.save(playerPlayFadeDurInMilli: v); - }, - text: "${settings.playerPlayFadeDurInMilli.value}ms", ), ), - ), - Obx( - () => CustomListTile( - enabled: settings.enableVolumeFadeOnPlayPause.value, - icon: Broken.pause, - title: lang.PAUSE_FADE_DURATION, - trailing: NamidaWheelSlider( - totalCount: 1900 ~/ 50, - initValue: settings.playerPauseFadeDurInMilli.value ~/ 50, - itemSize: 2, - squeeze: 0.4, - onValueChanged: (val) { - final v = (val * 50 + 100); - settings.save(playerPauseFadeDurInMilli: v); - }, - text: "${settings.playerPauseFadeDurInMilli.value}ms", - ), - ), - ), - ], + ], + ), ), - Obx( - () => CustomSwitchListTile( - leading: const StackedIcon( - baseIcon: Broken.play, - secondaryIcon: Broken.record, + getItemWrapper( + key: _PlaybackSettingsKeys.autoPlayOnNextPrev, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.autoPlayOnNextPrev), + leading: const StackedIcon( + baseIcon: Broken.play, + secondaryIcon: Broken.record, + ), + title: lang.PLAY_AFTER_NEXT_PREV, + onChanged: (value) => settings.save(playerPlayOnNextPrev: !value), + value: settings.playerPlayOnNextPrev.value, ), - title: lang.PLAY_AFTER_NEXT_PREV, - onChanged: (value) => settings.save(playerPlayOnNextPrev: !value), - value: settings.playerPlayOnNextPrev.value, ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.repeat, - title: lang.INFINITY_QUEUE_ON_NEXT_PREV, - subtitle: lang.INFINITY_QUEUE_ON_NEXT_PREV_SUBTITLE, - onChanged: (value) => settings.save(playerInfiniyQueueOnNextPrevious: !value), - value: settings.playerInfiniyQueueOnNextPrevious.value, + getItemWrapper( + key: _PlaybackSettingsKeys.infinityQueue, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.infinityQueue), + icon: Broken.repeat, + title: lang.INFINITY_QUEUE_ON_NEXT_PREV, + subtitle: lang.INFINITY_QUEUE_ON_NEXT_PREV_SUBTITLE, + onChanged: (value) => settings.save(playerInfiniyQueueOnNextPrevious: !value), + value: settings.playerInfiniyQueueOnNextPrevious.value, + ), ), ), - NamidaExpansionTile( - childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), - iconColor: context.defaultIconColor(), - icon: Broken.volume_slash, - titleText: lang.ON_VOLUME_ZERO, - children: [ - Obx( - () => CustomSwitchListTile( - icon: Broken.pause_circle, - title: lang.PAUSE_PLAYBACK, - onChanged: (value) => settings.save(playerPauseOnVolume0: !value), - value: settings.playerPauseOnVolume0.value, + getItemWrapper( + key: _PlaybackSettingsKeys.onVolume0, + child: NamidaExpansionTile( + bgColor: getBgColor(_PlaybackSettingsKeys.onVolume0), + childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), + iconColor: context.defaultIconColor(), + icon: Broken.volume_slash, + titleText: lang.ON_VOLUME_ZERO, + children: [ + Obx( + () => CustomSwitchListTile( + icon: Broken.pause_circle, + title: lang.PAUSE_PLAYBACK, + onChanged: (value) => settings.save(playerPauseOnVolume0: !value), + value: settings.playerPauseOnVolume0.value, + ), ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.play_circle, - title: lang.RESUME_IF_WAS_PAUSED_BY_VOLUME, - onChanged: (value) => settings.save(playerResumeAfterOnVolume0Pause: !value), - value: settings.playerResumeAfterOnVolume0Pause.value, + Obx( + () => CustomSwitchListTile( + icon: Broken.play_circle, + title: lang.RESUME_IF_WAS_PAUSED_BY_VOLUME, + onChanged: (value) => settings.save(playerResumeAfterOnVolume0Pause: !value), + value: settings.playerResumeAfterOnVolume0Pause.value, + ), ), - ), - ], + ], + ), ), - NamidaExpansionTile( - childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), - iconColor: context.defaultIconColor(), - icon: Broken.notification_bing, - titleText: lang.ON_INTERRUPTION, - children: [ - ...InterruptionType.values.map( - (type) { - return CustomListTile( - icon: type.toIcon(), - title: type.toText(), - subtitle: type.toSubtitle(), - trailingRaw: PopupMenuButton( - child: Obx(() { - final actionInSetting = settings.playerOnInterrupted[type] ?? InterruptionAction.pause; - return Text(actionInSetting.toText()); - }), - itemBuilder: (context) => >[ - ...InterruptionAction.values.map( - (action) => PopupMenuItem( - value: action, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Icon(action.toIcon(), size: 22.0), - const SizedBox(width: 6.0), - Text(action.toText()), - const Spacer(), - Obx( - () { - final actionInSetting = settings.playerOnInterrupted[type] ?? InterruptionAction.pause; - return NamidaCheckMark( - size: 16.0, - active: actionInSetting == action, - ); - }, - ), - const SizedBox(width: 6.0), - ], + getItemWrapper( + key: _PlaybackSettingsKeys.onInterruption, + child: NamidaExpansionTile( + bgColor: getBgColor(_PlaybackSettingsKeys.onInterruption), + childrenPadding: const EdgeInsets.symmetric(horizontal: 12.0), + iconColor: context.defaultIconColor(), + icon: Broken.notification_bing, + titleText: lang.ON_INTERRUPTION, + children: [ + ...InterruptionType.values.map( + (type) { + return CustomListTile( + icon: type.toIcon(), + title: type.toText(), + subtitle: type.toSubtitle(), + trailingRaw: PopupMenuButton( + child: Obx(() { + final actionInSetting = settings.playerOnInterrupted[type] ?? InterruptionAction.pause; + return Text(actionInSetting.toText()); + }), + itemBuilder: (context) => >[ + ...InterruptionAction.values.map( + (action) => PopupMenuItem( + value: action, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(action.toIcon(), size: 22.0), + const SizedBox(width: 6.0), + Text(action.toText()), + const Spacer(), + Obx( + () { + final actionInSetting = settings.playerOnInterrupted[type] ?? InterruptionAction.pause; + return NamidaCheckMark( + size: 16.0, + active: actionInSetting == action, + ); + }, + ), + const SizedBox(width: 6.0), + ], + ), ), ), - ), - ], - onSelected: (action) => settings.updatePlayerInterruption(type, action), - ), - ); - }, - ), - const NamidaContainerDivider(margin: EdgeInsets.symmetric(horizontal: 16.0)), - Obx( - () => CustomSwitchListTile( - icon: Broken.play_circle, - value: settings.playerResumeAfterWasInterrupted.value, - onChanged: (isTrue) => settings.save(playerResumeAfterWasInterrupted: !isTrue), - title: lang.RESUME_IF_WAS_INTERRUPTED, + ], + onSelected: (action) => settings.updatePlayerInterruption(type, action), + ), + ); + }, ), - ), - const SizedBox(height: 6.0), - ], + const NamidaContainerDivider(margin: EdgeInsets.symmetric(horizontal: 16.0)), + Obx( + () => CustomSwitchListTile( + icon: Broken.play_circle, + value: settings.playerResumeAfterWasInterrupted.value, + onChanged: (isTrue) => settings.save(playerResumeAfterWasInterrupted: !isTrue), + title: lang.RESUME_IF_WAS_INTERRUPTED, + ), + ), + const SizedBox(height: 6.0), + ], + ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.rotate_left, - title: lang.JUMP_TO_FIRST_TRACK_AFTER_QUEUE_FINISH, - onChanged: (value) => settings.save(jumpToFirstTrackAfterFinishingQueue: !value), - value: settings.jumpToFirstTrackAfterFinishingQueue.value, + getItemWrapper( + key: _PlaybackSettingsKeys.jumpToFirstTrackAfterFinishing, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.jumpToFirstTrackAfterFinishing), + icon: Broken.rotate_left, + title: lang.JUMP_TO_FIRST_TRACK_AFTER_QUEUE_FINISH, + onChanged: (value) => settings.save(jumpToFirstTrackAfterFinishingQueue: !value), + value: settings.jumpToFirstTrackAfterFinishingQueue.value, + ), ), ), - Obx( - () => CustomListTile( - icon: Broken.forward_5_seconds, - title: "${lang.SEEK_DURATION} (${settings.isSeekDurationPercentage.value ? lang.PERCENTAGE : lang.SECONDS})", - subtitle: lang.SEEK_DURATION_INFO, - onTap: () => settings.save(isSeekDurationPercentage: !settings.isSeekDurationPercentage.value), - trailing: settings.isSeekDurationPercentage.value - ? NamidaWheelSlider( - totalCount: 50, - initValue: settings.seekDurationInPercentage.value, - itemSize: 2, - squeeze: 0.4, - onValueChanged: (val) { - final v = (val) as int; - settings.save(seekDurationInPercentage: v); - }, - text: "${settings.seekDurationInPercentage.value}%", - ) - : NamidaWheelSlider( - totalCount: 120, - initValue: settings.seekDurationInSeconds.value, - itemSize: 2, - squeeze: 0.4, - onValueChanged: (val) { - final v = (val) as int; - settings.save(seekDurationInSeconds: v); - }, - text: "${settings.seekDurationInSeconds.value}s", - ), + getItemWrapper( + key: _PlaybackSettingsKeys.seekDuration, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.seekDuration), + icon: Broken.forward_5_seconds, + title: "${lang.SEEK_DURATION} (${settings.isSeekDurationPercentage.value ? lang.PERCENTAGE : lang.SECONDS})", + subtitle: lang.SEEK_DURATION_INFO, + onTap: () => settings.save(isSeekDurationPercentage: !settings.isSeekDurationPercentage.value), + trailing: settings.isSeekDurationPercentage.value + ? NamidaWheelSlider( + totalCount: 50, + initValue: settings.seekDurationInPercentage.value, + itemSize: 2, + squeeze: 0.4, + onValueChanged: (val) { + final v = (val) as int; + settings.save(seekDurationInPercentage: v); + }, + text: "${settings.seekDurationInPercentage.value}%", + ) + : NamidaWheelSlider( + totalCount: 120, + initValue: settings.seekDurationInSeconds.value, + itemSize: 2, + squeeze: 0.4, + onValueChanged: (val) { + final v = (val) as int; + settings.save(seekDurationInSeconds: v); + }, + text: "${settings.seekDurationInSeconds.value}s", + ), + ), ), ), - Obx( - () { - final valInSet = settings.minTrackDurationToRestoreLastPosInMinutes.value; - const max = 121; - return CustomListTile( - icon: Broken.refresh_left_square, - title: lang.MIN_TRACK_DURATION_TO_RESTORE_LAST_POSITION, - trailing: NamidaWheelSlider( - totalCount: max, - initValue: valInSet, - itemSize: 2, - squeeze: 0.4, - onValueChanged: (val) { - final v = (val) as int; - settings.save(minTrackDurationToRestoreLastPosInMinutes: v >= max ? -1 : v); - }, - text: valInSet == 0 - ? lang.ALWAYS_RESTORE - : valInSet <= -1 - ? lang.DONT_RESTORE_POSITION - : "${valInSet}m", - ), - ); - }, + getItemWrapper( + key: _PlaybackSettingsKeys.minimumTrackDurToRestoreLastPosition, + child: Obx( + () { + final valInSet = settings.minTrackDurationToRestoreLastPosInMinutes.value; + const max = 121; + return CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.minimumTrackDurToRestoreLastPosition), + icon: Broken.refresh_left_square, + title: lang.MIN_TRACK_DURATION_TO_RESTORE_LAST_POSITION, + trailing: NamidaWheelSlider( + totalCount: max, + initValue: valInSet, + itemSize: 2, + squeeze: 0.4, + onValueChanged: (val) { + final v = (val) as int; + settings.save(minTrackDurationToRestoreLastPosInMinutes: v >= max ? -1 : v); + }, + text: valInSet == 0 + ? lang.ALWAYS_RESTORE + : valInSet <= -1 + ? lang.DONT_RESTORE_POSITION + : "${valInSet}m", + ), + ); + }, + ), ), - Obx( - () => CustomListTile( - icon: Broken.timer, - title: lang.MIN_VALUE_TO_COUNT_TRACK_LISTEN, - onTap: () => NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.CHOOSE, - child: Column( - children: [ - Text( - lang.MIN_VALUE_TO_COUNT_TRACK_LISTEN, - style: context.textTheme.displayLarge, - ), - const SizedBox( - height: 32.0, - ), - Obx( - () => Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - NamidaWheelSlider( - totalCount: 160, - initValue: settings.isTrackPlayedSecondsCount.value - 20, - itemSize: 6, - onValueChanged: (val) { - final v = (val + 20); - settings.save(isTrackPlayedSecondsCount: v); - }, - text: "${settings.isTrackPlayedSecondsCount.value}s", - topText: lang.SECONDS.capitalizeFirst, - textPadding: 8.0, - ), - Text( - lang.OR, - style: context.textTheme.displayMedium, - ), - NamidaWheelSlider( - totalCount: 80, - initValue: settings.isTrackPlayedPercentageCount.value - 20, - itemSize: 6, - onValueChanged: (val) { - final v = (val + 20); - settings.save(isTrackPlayedPercentageCount: v); - }, - text: "${settings.isTrackPlayedPercentageCount.value}%", - topText: lang.PERCENTAGE, - textPadding: 8.0, - ), - ], + getItemWrapper( + key: _PlaybackSettingsKeys.countListenAfter, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_PlaybackSettingsKeys.countListenAfter), + icon: Broken.timer, + title: lang.MIN_VALUE_TO_COUNT_TRACK_LISTEN, + onTap: () => NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.CHOOSE, + child: Column( + children: [ + Text( + lang.MIN_VALUE_TO_COUNT_TRACK_LISTEN, + style: context.textTheme.displayLarge, ), - ), - ], + const SizedBox( + height: 32.0, + ), + Obx( + () => Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + NamidaWheelSlider( + totalCount: 160, + initValue: settings.isTrackPlayedSecondsCount.value - 20, + itemSize: 6, + onValueChanged: (val) { + final v = (val + 20); + settings.save(isTrackPlayedSecondsCount: v); + }, + text: "${settings.isTrackPlayedSecondsCount.value}s", + topText: lang.SECONDS.capitalizeFirst, + textPadding: 8.0, + ), + Text( + lang.OR, + style: context.textTheme.displayMedium, + ), + NamidaWheelSlider( + totalCount: 80, + initValue: settings.isTrackPlayedPercentageCount.value - 20, + itemSize: 6, + onValueChanged: (val) { + final v = (val + 20); + settings.save(isTrackPlayedPercentageCount: v); + }, + text: "${settings.isTrackPlayedPercentageCount.value}%", + topText: lang.PERCENTAGE, + textPadding: 8.0, + ), + ], + ), + ), + ], + ), ), ), + trailingText: "${settings.isTrackPlayedSecondsCount.value}s | ${settings.isTrackPlayedPercentageCount.value}%", ), - trailingText: "${settings.isTrackPlayedSecondsCount.value}s | ${settings.isTrackPlayedPercentageCount.value}%", ), ), ]; diff --git a/lib/ui/widgets/settings/theme_settings.dart b/lib/ui/widgets/settings/theme_settings.dart index 25859c30..3062ea27 100644 --- a/lib/ui/widgets/settings/theme_settings.dart +++ b/lib/ui/widgets/settings/theme_settings.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:get/get.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -13,7 +12,9 @@ import 'package:namida/controller/current_color.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/player_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/constants.dart'; +import 'package:namida/core/enums.dart'; import 'package:namida/core/extensions.dart'; import 'package:namida/core/icon_fonts/broken_icons.dart'; import 'package:namida/core/namida_converter_ext.dart'; @@ -22,8 +23,34 @@ import 'package:namida/core/translations/language.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class ThemeSetting extends StatelessWidget { - const ThemeSetting({super.key}); +enum _ThemeSettingsKeys { + themeMode, + autoColoring, + wallpaperColors, + forceMiniplayerColors, + pitchBlack, + defaultColor, + defaultColorDark, + language, +} + +class ThemeSetting extends SettingSubpageProvider { + const ThemeSetting({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.theme; + + @override + Map> get lookupMap => { + _ThemeSettingsKeys.themeMode: [lang.THEME_MODE], + _ThemeSettingsKeys.autoColoring: [lang.AUTO_COLORING, lang.AUTO_COLORING_SUBTITLE], + _ThemeSettingsKeys.wallpaperColors: [lang.PICK_COLORS_FROM_DEVICE_WALLPAPER], + _ThemeSettingsKeys.forceMiniplayerColors: [lang.FORCE_MINIPLAYER_FOLLOW_TRACK_COLORS], + _ThemeSettingsKeys.pitchBlack: [lang.USE_PITCH_BLACK, lang.USE_PITCH_BLACK_SUBTITLE], + _ThemeSettingsKeys.defaultColor: [lang.DEFAULT_COLOR, lang.DEFAULT_COLOR_SUBTITLE], + _ThemeSettingsKeys.defaultColorDark: ["${lang.DEFAULT_COLOR} (${lang.THEME_MODE_DARK})", lang.DEFAULT_COLOR_SUBTITLE], + _ThemeSettingsKeys.language: [lang.LANGUAGE], + }; Future _refreshColorCurrentTrack() async { if (Player.inst.currentQueueYoutube.isNotEmpty && Player.inst.latestExtractedColor != null) { @@ -34,109 +61,117 @@ class ThemeSetting extends StatelessWidget { } Widget getThemeTile() { - return CustomListTile( - icon: Broken.brush_4, - title: lang.THEME_MODE, - trailing: const ToggleThemeModeContainer(), + return getItemWrapper( + key: _ThemeSettingsKeys.themeMode, + child: CustomListTile( + bgColor: getBgColor(_ThemeSettingsKeys.themeMode), + icon: Broken.brush_4, + title: lang.THEME_MODE, + trailing: const ToggleThemeModeContainer(), + ), ); } Widget getLanguageTile(BuildContext context) { - return Obx( - () => CustomListTile( - icon: Broken.language_square, - title: lang.LANGUAGE, - subtitle: lang.currentLanguage.name, - onTap: () { - final Rx selectedLang = lang.currentLanguage.obs; - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.LANGUAGE, - normalTitleStyle: true, - actions: [ - NamidaButton( - onPressed: () async { - final files = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['json', 'JSON']); - final path = files?.files.firstOrNull?.path; - if (path != null) { - try { - final st = await File(path).readAsString(); - final map = await jsonDecode(st); - final didUpdate = await Language.inst.loadLanguage(path.getFilenameWOExt, map); - if (didUpdate) { - NamidaNavigator.inst.closeDialog(); - } else { - snackyy(title: lang.ERROR, message: 'Unknown Error', isError: true); + return getItemWrapper( + key: _ThemeSettingsKeys.language, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_ThemeSettingsKeys.language), + icon: Broken.language_square, + title: lang.LANGUAGE, + subtitle: lang.currentLanguage.name, + onTap: () { + final Rx selectedLang = lang.currentLanguage.obs; + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: lang.LANGUAGE, + normalTitleStyle: true, + actions: [ + NamidaButton( + onPressed: () async { + final files = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['json', 'JSON']); + final path = files?.files.firstOrNull?.path; + if (path != null) { + try { + final st = await File(path).readAsString(); + final map = await jsonDecode(st); + final didUpdate = await Language.inst.loadLanguage(path.getFilenameWOExt, map); + if (didUpdate) { + NamidaNavigator.inst.closeDialog(); + } else { + snackyy(title: lang.ERROR, message: 'Unknown Error', isError: true); + } + } catch (e) { + snackyy(title: lang.ERROR, message: e.toString(), isError: true); } - } catch (e) { - snackyy(title: lang.ERROR, message: e.toString(), isError: true); } - } - }, - text: lang.LOCAL, - ), - const CancelButton(), - NamidaButton( - onPressed: () async => (await lang.update(lang: selectedLang.value)).closeDialog(), - text: lang.CONFIRM, - ) - ], - child: SingleChildScrollView( - child: Column( - children: [ - ...Language.availableLanguages.map( - (e) => Padding( - key: Key(e.code), - padding: const EdgeInsets.symmetric(vertical: 4.0), - child: Obx( - () => ListTileWithCheckMark( - leading: Container( - padding: const EdgeInsets.all(4.0), - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - width: 1.5, - color: context.theme.colorScheme.onBackground.withAlpha(100), + }, + text: lang.LOCAL, + ), + const CancelButton(), + NamidaButton( + onPressed: () async => (await lang.update(lang: selectedLang.value)).closeDialog(), + text: lang.CONFIRM, + ) + ], + child: SingleChildScrollView( + child: Column( + children: [ + ...Language.availableLanguages.map( + (e) => Padding( + key: Key(e.code), + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Obx( + () => ListTileWithCheckMark( + leading: Container( + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + width: 1.5, + color: context.theme.colorScheme.onBackground.withAlpha(100), + ), + ), + child: Text( + e.name[0], + style: const TextStyle(fontSize: 13.0), ), ), - child: Text( - e.name[0], - style: const TextStyle(fontSize: 13.0), - ), - ), - titleWidget: RichText( - text: TextSpan( - text: e.name, - style: context.textTheme.displayMedium, - children: [ - TextSpan( - text: " (${e.country})", - style: context.textTheme.displaySmall, - ), - ], + titleWidget: RichText( + text: TextSpan( + text: e.name, + style: context.textTheme.displayMedium, + children: [ + TextSpan( + text: " (${e.country})", + style: context.textTheme.displaySmall, + ), + ], + ), ), + active: e == selectedLang.value, + onTap: () => selectedLang.value = e, ), - active: e == selectedLang.value, - onTap: () => selectedLang.value = e, ), ), ), - ), - CustomListTile( - visualDensity: VisualDensity.compact, - icon: Broken.add_circle, - title: lang.ADD_LANGUAGE, - subtitle: lang.ADD_LANGUAGE_SUBTITLE, - onTap: () { - launchUrl(Uri.parse(AppSocial.TRANSLATION_REPO)); - }, - ), - ], + CustomListTile( + visualDensity: VisualDensity.compact, + icon: Broken.add_circle, + title: lang.ADD_LANGUAGE, + subtitle: lang.ADD_LANGUAGE_SUBTITLE, + onTap: () { + launchUrl(Uri.parse(AppSocial.TRANSLATION_REPO)); + }, + ), + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ); } @@ -152,108 +187,131 @@ class ThemeSetting extends StatelessWidget { child: Column( children: [ getThemeTile(), - Obx( - () => CustomSwitchListTile( - icon: Broken.colorfilter, - title: lang.AUTO_COLORING, - subtitle: lang.AUTO_COLORING_SUBTITLE, - value: settings.autoColor.value, - onChanged: (isTrue) async { - settings.save(autoColor: !isTrue); - if (isTrue) { - CurrentColor.inst.updatePlayerColorFromColor(playerStaticColor); - } else { - await _refreshColorCurrentTrack(); - } - }, + getItemWrapper( + key: _ThemeSettingsKeys.autoColoring, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ThemeSettingsKeys.autoColoring), + icon: Broken.colorfilter, + title: lang.AUTO_COLORING, + subtitle: lang.AUTO_COLORING_SUBTITLE, + value: settings.autoColor.value, + onChanged: (isTrue) async { + settings.save(autoColor: !isTrue); + if (isTrue) { + CurrentColor.inst.updatePlayerColorFromColor(playerStaticColor); + } else { + await _refreshColorCurrentTrack(); + } + }, + ), ), ), // Android S/12+ if (kSdkVersion >= 31) - Obx( + getItemWrapper( + key: _ThemeSettingsKeys.wallpaperColors, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ThemeSettingsKeys.wallpaperColors), + enabled: settings.autoColor.value, + icon: Broken.gallery_import, + title: lang.PICK_COLORS_FROM_DEVICE_WALLPAPER, + value: settings.pickColorsFromDeviceWallpaper.value, + onChanged: (isTrue) async { + settings.save(pickColorsFromDeviceWallpaper: !isTrue); + + await _refreshColorCurrentTrack(); + }, + ), + ), + ), + getItemWrapper( + key: _ThemeSettingsKeys.forceMiniplayerColors, + child: Obx( () => CustomSwitchListTile( - enabled: settings.autoColor.value, - icon: Broken.gallery_import, - title: lang.PICK_COLORS_FROM_DEVICE_WALLPAPER, - value: settings.pickColorsFromDeviceWallpaper.value, + bgColor: getBgColor(_ThemeSettingsKeys.forceMiniplayerColors), + icon: Broken.slider_horizontal, + title: lang.FORCE_MINIPLAYER_FOLLOW_TRACK_COLORS, + subtitle: '${lang.IGNORES}: ${lang.AUTO_COLORING}, ${lang.PICK_COLORS_FROM_DEVICE_WALLPAPER} & ${lang.DEFAULT_COLOR}', + value: settings.forceMiniplayerTrackColor.value, onChanged: (isTrue) async { - settings.save(pickColorsFromDeviceWallpaper: !isTrue); - + settings.save(forceMiniplayerTrackColor: !isTrue); await _refreshColorCurrentTrack(); }, ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.slider_horizontal, - title: lang.FORCE_MINIPLAYER_FOLLOW_TRACK_COLORS, - subtitle: '${lang.IGNORES}: ${lang.AUTO_COLORING}, ${lang.PICK_COLORS_FROM_DEVICE_WALLPAPER} & ${lang.DEFAULT_COLOR}', - value: settings.forceMiniplayerTrackColor.value, - onChanged: (isTrue) async { - settings.save(forceMiniplayerTrackColor: !isTrue); - await _refreshColorCurrentTrack(); - }, - ), ), - Obx( - () => CustomSwitchListTile( - icon: Broken.mirror, - title: lang.USE_PITCH_BLACK, - subtitle: lang.USE_PITCH_BLACK_SUBTITLE, - value: settings.pitchBlack.value, - onChanged: (isTrue) async { - settings.save(pitchBlack: !isTrue); - await _refreshColorCurrentTrack(); - }, + getItemWrapper( + key: _ThemeSettingsKeys.pitchBlack, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_ThemeSettingsKeys.pitchBlack), + icon: Broken.mirror, + title: lang.USE_PITCH_BLACK, + subtitle: lang.USE_PITCH_BLACK_SUBTITLE, + value: settings.pitchBlack.value, + onChanged: (isTrue) async { + settings.save(pitchBlack: !isTrue); + await _refreshColorCurrentTrack(); + }, + ), ), ), ...[false, true].map( - (isDark) => Obx( - () { - final darkText = isDark ? " (${lang.THEME_MODE_DARK})" : ''; - final color = isDark ? playerStaticColorDark : playerStaticColorLight; - return CustomListTile( - enabled: !settings.autoColor.value, - icon: !isDark ? Broken.bucket : null, - leading: isDark - ? const StackedIcon( - baseIcon: Broken.bucket, - secondaryIcon: Broken.moon, - ) - : null, - title: "${lang.DEFAULT_COLOR}$darkText", - subtitle: lang.DEFAULT_COLOR_SUBTITLE, - trailing: CircleAvatar( - minRadius: 12, - backgroundColor: color, - ), - onTap: () { - NamidaNavigator.inst.navigateDialog( - dialog: Obx( - () => Theme( - data: AppThemes.inst.getAppTheme(color), - child: NamidaColorPickerDialog( - initialColor: color, - doneText: lang.DONE, - onColorChanged: (value) => _updateColor(value, isDark), - onDonePressed: NamidaNavigator.inst.closeDialog, - onRefreshButtonPressed: () { - if (isDark) { - _updateColor(kMainColorDark, true); - } else { - _updateColor(kMainColorLight, false); - } - NamidaNavigator.inst.closeDialog(); - }, - cancelButton: false, - ), - ), + (isDark) { + final darkText = isDark ? " (${lang.THEME_MODE_DARK})" : ''; + final key = isDark ? _ThemeSettingsKeys.defaultColorDark : _ThemeSettingsKeys.defaultColor; + return getItemWrapper( + key: key, + child: Obx( + () { + final color = isDark ? playerStaticColorDark : playerStaticColorLight; + return CustomListTile( + bgColor: getBgColor(key), + enabled: !settings.autoColor.value, + icon: !isDark ? Broken.bucket : null, + leading: isDark + ? const StackedIcon( + baseIcon: Broken.bucket, + secondaryIcon: Broken.moon, + ) + : null, + title: "${lang.DEFAULT_COLOR}$darkText", + subtitle: lang.DEFAULT_COLOR_SUBTITLE, + trailing: CircleAvatar( + minRadius: 12, + backgroundColor: color, ), + onTap: () { + NamidaNavigator.inst.navigateDialog( + dialog: Obx( + () => Theme( + data: AppThemes.inst.getAppTheme(color), + child: NamidaColorPickerDialog( + initialColor: color, + doneText: lang.DONE, + onColorChanged: (value) => _updateColor(value, isDark), + onDonePressed: NamidaNavigator.inst.closeDialog, + onRefreshButtonPressed: () { + if (isDark) { + _updateColor(kMainColorDark, true); + } else { + _updateColor(kMainColorLight, false); + } + NamidaNavigator.inst.closeDialog(); + }, + cancelButton: false, + ), + ), + ), + ); + }, ); }, - ); - }, - ), + ), + ); + }, ), getLanguageTile(context), ], diff --git a/lib/ui/widgets/settings/track_tile_customization.dart b/lib/ui/widgets/settings/track_tile_customization.dart deleted file mode 100644 index 8c3eee7d..00000000 --- a/lib/ui/widgets/settings/track_tile_customization.dart +++ /dev/null @@ -1,333 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:get/get.dart'; - -import 'package:namida/class/track.dart'; -import 'package:namida/controller/navigator_controller.dart'; -import 'package:namida/controller/player_controller.dart'; -import 'package:namida/controller/settings_controller.dart'; -import 'package:namida/core/constants.dart'; -import 'package:namida/core/enums.dart'; -import 'package:namida/core/extensions.dart'; -import 'package:namida/core/icon_fonts/broken_icons.dart'; -import 'package:namida/core/namida_converter_ext.dart'; -import 'package:namida/core/translations/language.dart'; -import 'package:namida/ui/widgets/artwork.dart'; -import 'package:namida/ui/widgets/custom_widgets.dart'; -import 'package:namida/ui/dialogs/setting_dialog_with_text_field.dart'; -import 'package:namida/ui/widgets/library/track_tile.dart'; - -class TrackTileCustomization extends StatelessWidget { - const TrackTileCustomization({super.key}); - - void _onSettingsChanged() => TrackTileManager.onTrackItemPropChange(); - - @override - Widget build(BuildContext context) { - return NamidaExpansionTile( - initiallyExpanded: settings.useSettingCollapsedTiles.value, - leading: const StackedIcon( - baseIcon: Broken.brush, - secondaryIcon: Broken.music_circle, - ), - titleText: lang.TRACK_TILE_CUSTOMIZATION, - children: [ - Obx( - () => CustomSwitchListTile( - icon: Broken.crop, - title: lang.FORCE_SQUARED_TRACK_THUMBNAIL, - value: settings.forceSquaredTrackThumbnail.value, - onChanged: (value) { - settings.save(forceSquaredTrackThumbnail: !value); - Player.inst.refreshRxVariables(); - _onSettingsChanged(); - if (!value && settings.trackThumbnailSizeinList.toInt() != settings.trackListTileHeight.toInt()) { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - normalTitleStyle: true, - isWarning: true, - bodyText: lang.FORCE_SQUARED_THUMBNAIL_NOTE, - actions: [ - const CancelButton(), - NamidaButton( - text: lang.CONFIRM, - onPressed: () { - settings.save(trackThumbnailSizeinList: settings.trackListTileHeight.value); - NamidaNavigator.inst.closeDialog(); - }, - ), - ], - ), - ); - } - }, - ), - ), - Obx( - () => CustomListTile( - icon: Broken.maximize_3, - title: lang.TRACK_THUMBNAIL_SIZE_IN_LIST, - trailingText: "${settings.trackThumbnailSizeinList.toInt()}", - onTap: () { - showSettingDialogWithTextField( - title: lang.TRACK_THUMBNAIL_SIZE_IN_LIST, - trackThumbnailSizeinList: true, - icon: Broken.maximize_3, - ); - }, - ), - ), - Obx( - () => CustomListTile( - icon: Broken.pharagraphspacing, - title: lang.HEIGHT_OF_TRACK_TILE, - trailingText: "${settings.trackListTileHeight.toInt()}", - onTap: () { - showSettingDialogWithTextField( - title: lang.HEIGHT_OF_TRACK_TILE, - trackListTileHeight: true, - icon: Broken.pharagraphspacing, - ); - }, - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.chart_1, - rotateIcon: 1, - title: lang.DISPLAY_THIRD_ROW_IN_TRACK_TILE, - onChanged: (isTrue) { - settings.save(displayThirdRow: !isTrue); - _onSettingsChanged(); - }, - value: settings.displayThirdRow.value, - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.coin, - rotateIcon: 3, - title: lang.DISPLAY_THIRD_ITEM_IN_ROW_IN_TRACK_TILE, - onChanged: (isTrue) { - settings.save(displayThirdItemInEachRow: !isTrue); - _onSettingsChanged(); - }, - value: settings.displayThirdItemInEachRow.value, - ), - ), - Obx( - () => CustomSwitchListTile( - icon: Broken.heart, - title: lang.DISPLAY_FAVOURITE_ICON_IN_TRACK_TILE, - onChanged: (isTrue) { - settings.save(displayFavouriteIconInListTile: !isTrue); - _onSettingsChanged(); - }, - value: settings.displayFavouriteIconInListTile.value, - ), - ), - Obx( - () => CustomListTile( - icon: Broken.minus_square, - title: lang.TRACK_TILE_ITEMS_SEPARATOR, - trailingText: settings.trackTileSeparator.value, - onTap: () => showSettingDialogWithTextField( - title: lang.TRACK_TILE_ITEMS_SEPARATOR, - trackTileSeparator: true, - icon: Broken.minus_square, - ), - ), - ), - Obx( - () => Container( - color: context.theme.cardTheme.color, - width: context.width, - height: settings.trackListTileHeight * 1.5, - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(vertical: 7.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - const SizedBox( - width: 12.0, - ), - Container( - margin: const EdgeInsets.symmetric( - horizontal: 0.0, - ), - width: settings.trackThumbnailSizeinList.value, - height: settings.trackThumbnailSizeinList.value, - child: ArtworkWidget( - thumbnailSize: settings.trackThumbnailSizeinList.value, - path: allTracksInLibrary.firstOrNull?.pathToImage, - forceSquared: settings.forceSquaredTrackThumbnail.value, - ), - ), - const SizedBox( - width: 12.0, - ), - - /// Main Info - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FittedBox( - child: Row( - children: [ - TrackTilePosition.row1Item1, - TrackTilePosition.row1Item2, - if (settings.displayThirdItemInEachRow.value) TrackTilePosition.row1Item3, - ] - .map( - (e) => TrackItemSmallBox( - text: settings.trackItem[e]?.label, - onTap: () => _showTrackItemsDialog(e), - ), - ) - .addSeparators(separator: const SizedBox(width: 6.0)) - .toList(), - ), - ), - const SizedBox( - height: 4.0, - ), - FittedBox( - child: Row( - children: [ - TrackTilePosition.row2Item1, - TrackTilePosition.row2Item2, - if (settings.displayThirdItemInEachRow.value) TrackTilePosition.row2Item3, - ] - .map( - (e) => TrackItemSmallBox( - text: settings.trackItem[e]?.label, - onTap: () => _showTrackItemsDialog(e), - ), - ) - .addSeparators(separator: const SizedBox(width: 6.0)) - .toList(), - ), - ), - const SizedBox( - height: 4.0, - ), - if (settings.displayThirdRow.value) - FittedBox( - child: Row( - children: [ - TrackTilePosition.row3Item1, - TrackTilePosition.row3Item2, - if (settings.displayThirdItemInEachRow.value) TrackTilePosition.row3Item3, - ] - .map( - (e) => TrackItemSmallBox( - text: settings.trackItem[e]?.label, - onTap: () => _showTrackItemsDialog(e), - ), - ) - .addSeparators(separator: const SizedBox(width: 6.0)) - .toList(), - ), - ), - ], - ), - ), - const SizedBox(width: 6.0), - - /// Right Items - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ...[ - TrackTilePosition.rightItem1, - TrackTilePosition.rightItem2, - ] - .map( - (e) => TrackItemSmallBox( - text: settings.trackItem[e]?.label, - onTap: () => _showTrackItemsDialog(e), - ), - ) - .addSeparators(separator: const SizedBox(height: 3.0)) - .toList(), - if (settings.displayFavouriteIconInListTile.value) ...[ - const SizedBox(height: 3.0), - const NamidaLikeButton( - track: null, - size: 20, - ), - ] - ], - ), - const SizedBox(width: 6.0), - const MoreIcon( - iconSize: 20, - ), - const SizedBox(width: 6.0), - ], - ), - ), - ) - ], - ); - } - - void _showTrackItemsDialog(TrackTilePosition p) { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: lang.CHOOSE, - normalTitleStyle: true, - insetPadding: const EdgeInsets.all(64.0), - child: SizedBox( - height: Get.height * 0.5, - width: Get.width, - child: NamidaListView( - padding: EdgeInsets.zero, - itemBuilder: (context, i) { - final trItem = TrackTileItem.values[i]; - return SmallListTile( - key: ValueKey(i), - title: trItem.toText(), - onTap: () { - settings.updateTrackItemList(p, trItem); - _onSettingsChanged(); - NamidaNavigator.inst.closeDialog(); - }, - active: settings.trackItem[p] == trItem, - ); - }, - itemCount: TrackTileItem.values.length, - itemExtents: null, - ), - ), - ), - ); - } -} - -class TrackItemSmallBox extends StatelessWidget { - final void Function()? onTap; - final Widget? child; - final String? text; - const TrackItemSmallBox({super.key, this.onTap, this.child, this.text}); - - @override - Widget build(BuildContext context) { - return NamidaInkWell( - bgColor: context.theme.colorScheme.background.withAlpha(160), - onTap: onTap, - borderRadius: 8.0, - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: text != null - ? Text( - text!, - style: context.theme.textTheme.displaySmall, - ) - : child, - ); - } -} diff --git a/lib/ui/widgets/settings/youtube_settings.dart b/lib/ui/widgets/settings/youtube_settings.dart index 274f5ef3..2b7aab72 100644 --- a/lib/ui/widgets/settings/youtube_settings.dart +++ b/lib/ui/widgets/settings/youtube_settings.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:namida/controller/navigator_controller.dart'; import 'package:namida/controller/settings_controller.dart'; +import 'package:namida/controller/settings_search_controller.dart'; import 'package:namida/core/enums.dart'; import 'package:namida/core/icon_fonts/broken_icons.dart'; import 'package:namida/core/namida_converter_ext.dart'; @@ -10,8 +11,28 @@ import 'package:namida/core/translations/language.dart'; import 'package:namida/ui/widgets/custom_widgets.dart'; import 'package:namida/ui/widgets/settings_card.dart'; -class YoutubeSettings extends StatelessWidget { - const YoutubeSettings({super.key}); +enum _YoutubeSettingKeys { + preferNewComments, + dimMiniplayerAfter, + dimIntensity, + downloadsMetadataTags, + onOpeningYTLink, +} + +class YoutubeSettings extends SettingSubpageProvider { + const YoutubeSettings({super.key, super.initialItem}); + + @override + SettingSubpageEnum get settingPage => SettingSubpageEnum.youtube; + + @override + Map> get lookupMap => { + _YoutubeSettingKeys.preferNewComments: [lang.YT_PREFER_NEW_COMMENTS, lang.YT_PREFER_NEW_COMMENTS_SUBTITLE], + _YoutubeSettingKeys.dimMiniplayerAfter: [lang.DIM_MINIPLAYER_AFTER_SECONDS], + _YoutubeSettingKeys.dimIntensity: [lang.DIM_INTENSITY], + _YoutubeSettingKeys.downloadsMetadataTags: [lang.DOWNLOADS_METADATA_TAGS, lang.DOWNLOADS_METADATA_TAGS_SUBTITLE], + _YoutubeSettingKeys.onOpeningYTLink: [lang.ON_OPENING_YOUTUBE_LINK], + }; @override Widget build(BuildContext context) { @@ -21,151 +42,171 @@ class YoutubeSettings extends StatelessWidget { icon: Broken.video, child: Column( children: [ - Obx( - () => CustomSwitchListTile( - leading: const StackedIcon( - baseIcon: Broken.document, - secondaryIcon: Broken.global_refresh, - secondaryIconSize: 12.0, + getItemWrapper( + key: _YoutubeSettingKeys.preferNewComments, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_YoutubeSettingKeys.preferNewComments), + leading: const StackedIcon( + baseIcon: Broken.document, + secondaryIcon: Broken.global_refresh, + secondaryIconSize: 12.0, + ), + title: lang.YT_PREFER_NEW_COMMENTS, + subtitle: lang.YT_PREFER_NEW_COMMENTS_SUBTITLE, + value: settings.ytPreferNewComments.value, + onChanged: (isTrue) => settings.save(ytPreferNewComments: !isTrue), ), - title: lang.YT_PREFER_NEW_COMMENTS, - subtitle: lang.YT_PREFER_NEW_COMMENTS_SUBTITLE, - value: settings.ytPreferNewComments.value, - onChanged: (isTrue) => settings.save(ytPreferNewComments: !isTrue), ), ), - Obx( - () => CustomListTile( - leading: const StackedIcon( - baseIcon: Broken.moon, - secondaryIcon: Broken.clock, - secondaryIconSize: 12.0, - ), - title: lang.DIM_MINIPLAYER_AFTER_SECONDS.replaceFirst( - '_SECONDS_', - "${settings.ytMiniplayerDimAfterSeconds.value}", - ), - trailing: NamidaWheelSlider( - totalCount: 120, - initValue: settings.ytMiniplayerDimAfterSeconds.value, - itemSize: 4, - text: "${settings.ytMiniplayerDimAfterSeconds.value}s", - onValueChanged: (val) { - settings.save(ytMiniplayerDimAfterSeconds: val); - }, + getItemWrapper( + key: _YoutubeSettingKeys.dimMiniplayerAfter, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_YoutubeSettingKeys.dimMiniplayerAfter), + leading: const StackedIcon( + baseIcon: Broken.moon, + secondaryIcon: Broken.clock, + secondaryIconSize: 12.0, + ), + title: lang.DIM_MINIPLAYER_AFTER_SECONDS.replaceFirst( + '_SECONDS_', + "${settings.ytMiniplayerDimAfterSeconds.value}", + ), + trailing: NamidaWheelSlider( + totalCount: 120, + initValue: settings.ytMiniplayerDimAfterSeconds.value, + itemSize: 4, + text: "${settings.ytMiniplayerDimAfterSeconds.value}s", + onValueChanged: (val) { + settings.save(ytMiniplayerDimAfterSeconds: val); + }, + ), ), ), ), - Obx( - () => CustomListTile( - enabled: settings.ytMiniplayerDimAfterSeconds.value > 0, - leading: Stack( - alignment: Alignment.center, - children: [ - Icon( - Broken.devices, - size: 24.0, - color: context.defaultIconColor(), - ), - // -- hide middle part - Container( - width: 7.0, - height: 7.0, - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: context.theme.scaffoldBackgroundColor, - blurRadius: 1.0, - offset: const Offset(0, 2.0), - ), - ], + getItemWrapper( + key: _YoutubeSettingKeys.dimIntensity, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_YoutubeSettingKeys.dimIntensity), + enabled: settings.ytMiniplayerDimAfterSeconds.value > 0, + leading: Stack( + alignment: Alignment.center, + children: [ + Icon( + Broken.devices, + size: 24.0, + color: context.defaultIconColor(), ), - ), - // -- needle - Obx( - () { - const multiplier = 4.5; - const minus = multiplier / 2; - const height = 7.0; - const origin = height / 2; - return Transform.rotate( - origin: const Offset(0, origin), - angle: (settings.ytMiniplayerDimOpacity.value * multiplier) - minus, - child: Container( - width: 2.0, - height: height, - decoration: BoxDecoration( - color: context.defaultIconColor(), - borderRadius: BorderRadius.circular(8.0), + // -- hide middle part + Container( + width: 7.0, + height: 7.0, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: context.theme.scaffoldBackgroundColor, + blurRadius: 1.0, + offset: const Offset(0, 2.0), ), - ), - ); - }, - ) - ], - ), - title: lang.DIM_INTENSITY, - trailing: NamidaWheelSlider( - totalCount: 100, - initValue: (settings.ytMiniplayerDimOpacity.value * 100).round(), - itemSize: 4, - text: "${(settings.ytMiniplayerDimOpacity.value * 100).round()}%", - onValueChanged: (val) { - settings.save(ytMiniplayerDimOpacity: val / 100); - }, + ], + ), + ), + // -- needle + Obx( + () { + const multiplier = 4.5; + const minus = multiplier / 2; + const height = 7.0; + const origin = height / 2; + return Transform.rotate( + origin: const Offset(0, origin), + angle: (settings.ytMiniplayerDimOpacity.value * multiplier) - minus, + child: Container( + width: 2.0, + height: height, + decoration: BoxDecoration( + color: context.defaultIconColor(), + borderRadius: BorderRadius.circular(8.0), + ), + ), + ); + }, + ) + ], + ), + title: lang.DIM_INTENSITY, + trailing: NamidaWheelSlider( + totalCount: 100, + initValue: (settings.ytMiniplayerDimOpacity.value * 100).round(), + itemSize: 4, + text: "${(settings.ytMiniplayerDimOpacity.value * 100).round()}%", + onValueChanged: (val) { + settings.save(ytMiniplayerDimOpacity: val / 100); + }, + ), ), ), ), - Obx( - () => CustomSwitchListTile( - leading: const StackedIcon( - baseIcon: Broken.import, - secondaryIcon: Broken.tick_circle, - secondaryIconSize: 12.0, + getItemWrapper( + key: _YoutubeSettingKeys.downloadsMetadataTags, + child: Obx( + () => CustomSwitchListTile( + bgColor: getBgColor(_YoutubeSettingKeys.downloadsMetadataTags), + leading: const StackedIcon( + baseIcon: Broken.import, + secondaryIcon: Broken.tick_circle, + secondaryIconSize: 12.0, + ), + title: lang.DOWNLOADS_METADATA_TAGS, + subtitle: lang.DOWNLOADS_METADATA_TAGS_SUBTITLE, + value: settings.ytAutoExtractVideoTagsFromInfo.value, + onChanged: (isTrue) => settings.save(ytAutoExtractVideoTagsFromInfo: !isTrue), ), - title: lang.DOWNLOADS_METADATA_TAGS, - subtitle: lang.DOWNLOADS_METADATA_TAGS_SUBTITLE, - value: settings.ytAutoExtractVideoTagsFromInfo.value, - onChanged: (isTrue) => settings.save(ytAutoExtractVideoTagsFromInfo: !isTrue), ), ), - Obx( - () => CustomListTile( - icon: Broken.import_1, - title: lang.ON_OPENING_YOUTUBE_LINK, - trailingText: settings.onYoutubeLinkOpen.value.toText(), - onTap: () { - NamidaNavigator.inst.navigateDialog( - dialog: CustomBlurryDialog( - title: '', - actions: [ - NamidaButton( - text: lang.DONE, - onPressed: NamidaNavigator.inst.closeDialog, - ) - ], - child: Column( - children: [ - ...OnYoutubeLinkOpenAction.values.map( - (e) => Padding( - padding: const EdgeInsets.all(6.0), - child: Obx( - () => ListTileWithCheckMark( - icon: e.toIcon(), - title: e.toText(), - active: settings.onYoutubeLinkOpen.value == e, - onTap: () { - settings.save(onYoutubeLinkOpen: e); - }, + getItemWrapper( + key: _YoutubeSettingKeys.onOpeningYTLink, + child: Obx( + () => CustomListTile( + bgColor: getBgColor(_YoutubeSettingKeys.onOpeningYTLink), + icon: Broken.import_1, + title: lang.ON_OPENING_YOUTUBE_LINK, + trailingText: settings.onYoutubeLinkOpen.value.toText(), + onTap: () { + NamidaNavigator.inst.navigateDialog( + dialog: CustomBlurryDialog( + title: '', + actions: [ + NamidaButton( + text: lang.DONE, + onPressed: NamidaNavigator.inst.closeDialog, + ) + ], + child: Column( + children: [ + ...OnYoutubeLinkOpenAction.values.map( + (e) => Padding( + padding: const EdgeInsets.all(6.0), + child: Obx( + () => ListTileWithCheckMark( + icon: e.toIcon(), + title: e.toText(), + active: settings.onYoutubeLinkOpen.value == e, + onTap: () { + settings.save(onYoutubeLinkOpen: e); + }, + ), ), ), ), - ), - ], + ], + ), ), - ), - ); - }, + ); + }, + ), ), ), ], diff --git a/lib/ui/widgets/settings_search_bar.dart b/lib/ui/widgets/settings_search_bar.dart new file mode 100644 index 00000000..730b6fdf --- /dev/null +++ b/lib/ui/widgets/settings_search_bar.dart @@ -0,0 +1,103 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'package:namida/controller/settings_search_controller.dart'; +import 'package:namida/core/extensions.dart'; +import 'package:namida/core/icon_fonts/broken_icons.dart'; +import 'package:namida/packages/searchbar_animation.dart'; +import 'package:namida/ui/widgets/custom_widgets.dart'; + +class NamidaSettingSearchBar extends StatefulWidget { + final Widget? closedChild; + const NamidaSettingSearchBar({super.key, this.closedChild}); + + @override + State createState() => _NamidaSettingSearchBarState(); +} + +class _NamidaSettingSearchBarState extends State { + late final TextEditingController controller; + bool canShowClosedChild = true; + + @override + void initState() { + super.initState(); + controller = TextEditingController(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + void _onSearch({required bool isOpen}) { + SettingsSearchController.inst.onSearchTap(isOpen: isOpen); + if (controller.text != '') { + SettingsSearchController.inst.onSearchChanged(controller.text); + } + } + + @override + Widget build(BuildContext context) { + const animationMs = 300; + return Stack( + alignment: Alignment.centerLeft, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: animationMs), + child: canShowClosedChild && widget.closedChild != null ? widget.closedChild! : null, + ), + SearchBarAnimation( + isSearchBoxOnRightSide: true, + textAlignToRight: false, + durationInMilliSeconds: animationMs, + enableKeyboardFocus: true, + isOriginalAnimation: false, + textEditingController: controller, + hintText: 'Search Settings', + searchBoxWidth: context.width / 1.2, + buttonColour: Colors.transparent, + enableBoxShadow: false, + buttonShadowColour: Colors.transparent, + hintTextStyle: (height) => context.textTheme.displaySmall?.copyWith( + fontSize: 17.0.multipliedFontScale, + height: height * 1.1, + ), + searchBoxColour: context.theme.cardColor.withAlpha(200), + enteredTextStyle: context.theme.textTheme.displayMedium, + cursorColour: context.theme.colorScheme.onBackground, + buttonBorderColour: Colors.black45, + cursorRadius: const Radius.circular(12.0), + buttonWidget: const IgnorePointer( + child: NamidaIconButton( + icon: Broken.search_normal, + ), + ), + secondaryButtonWidget: const IgnorePointer( + child: NamidaIconButton( + icon: Broken.search_status_1, + ), + ), + trailingWidget: NamidaIconButton( + icon: Broken.close_circle, + padding: EdgeInsets.zero, + iconSize: 22, + onPressed: () { + controller.clear(); + SettingsSearchController.inst.searchResults.clear(); + }, + ), + onTap: () { + _onSearch(isOpen: true); + }, + onPressButton: (isOpen) { + _onSearch(isOpen: isOpen); + setState(() => canShowClosedChild = !isOpen); + }, + onChanged: SettingsSearchController.inst.onSearchChanged, + ), + ], + ); + } +}