diff --git a/lib/components/library/user_local_tracks.dart b/lib/components/library/user_local_tracks.dart index 0546c2a7..354d9fe6 100644 --- a/lib/components/library/user_local_tracks.dart +++ b/lib/components/library/user_local_tracks.dart @@ -22,7 +22,7 @@ import 'package:spotube/components/shared/track_table/track_tile.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/local_track.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FfiException; diff --git a/lib/components/player/sibling_tracks_sheet.dart b/lib/components/player/sibling_tracks_sheet.dart index f6702e0c..ee8d9719 100644 --- a/lib/components/player/sibling_tracks_sheet.dart +++ b/lib/components/player/sibling_tracks_sheet.dart @@ -15,7 +15,8 @@ import 'package:spotube/hooks/utils/use_debounce.dart'; import 'package:spotube/models/matched_track.dart'; import 'package:spotube/models/spotube_track.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/provider/youtube_provider.dart'; import 'package:spotube/services/youtube/youtube.dart'; import 'package:spotube/utils/service_utils.dart'; diff --git a/lib/components/root/bottom_player.dart b/lib/components/root/bottom_player.dart index 4bc08fc0..617e760b 100644 --- a/lib/components/root/bottom_player.dart +++ b/lib/components/root/bottom_player.dart @@ -19,7 +19,8 @@ import 'package:spotube/models/logger.dart'; import 'package:flutter/material.dart'; import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; diff --git a/lib/components/root/sidebar.dart b/lib/components/root/sidebar.dart index f7cdcac3..ac5233ed 100644 --- a/lib/components/root/sidebar.dart +++ b/lib/components/root/sidebar.dart @@ -16,7 +16,8 @@ import 'package:spotube/hooks/controllers/use_sidebarx_controller.dart'; import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/authentication_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; diff --git a/lib/components/root/spotube_navigation_bar.dart b/lib/components/root/spotube_navigation_bar.dart index d602f390..0853c60c 100644 --- a/lib/components/root/spotube_navigation_bar.dart +++ b/lib/components/root/spotube_navigation_bar.dart @@ -11,7 +11,8 @@ import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/utils/use_brightness_value.dart'; import 'package:spotube/provider/download_manager_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; final navigationPanelHeight = StateProvider((ref) => 50); diff --git a/lib/components/settings/color_scheme_picker_dialog.dart b/lib/components/settings/color_scheme_picker_dialog.dart index f06a9d84..e0c3d618 100644 --- a/lib/components/settings/color_scheme_picker_dialog.dart +++ b/lib/components/settings/color_scheme_picker_dialog.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:system_theme/system_theme.dart'; class SpotubeColor extends Color { diff --git a/lib/components/shared/dialogs/piped_down_dialog.dart b/lib/components/shared/dialogs/piped_down_dialog.dart index 03362ed4..6220adeb 100644 --- a/lib/components/shared/dialogs/piped_down_dialog.dart +++ b/lib/components/shared/dialogs/piped_down_dialog.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class PipedDownDialog extends HookConsumerWidget { const PipedDownDialog({Key? key}) : super(key: key); diff --git a/lib/components/shared/page_window_title_bar.dart b/lib/components/shared/page_window_title_bar.dart index 50d468aa..43435f7d 100644 --- a/lib/components/shared/page_window_title_bar.dart +++ b/lib/components/shared/page_window_title_bar.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/utils/platform.dart'; import 'package:titlebar_buttons/titlebar_buttons.dart'; import 'dart:math'; diff --git a/lib/components/shared/track_table/tracks_table_view.dart b/lib/components/shared/track_table/tracks_table_view.dart index d03e92d7..14a4f1a9 100644 --- a/lib/components/shared/track_table/tracks_table_view.dart +++ b/lib/components/shared/track_table/tracks_table_view.dart @@ -22,7 +22,8 @@ import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/utils/service_utils.dart'; final trackCollectionSortState = diff --git a/lib/hooks/configurators/use_init_sys_tray.dart b/lib/hooks/configurators/use_init_sys_tray.dart index f342c24a..db4964ce 100644 --- a/lib/hooks/configurators/use_init_sys_tray.dart +++ b/lib/hooks/configurators/use_init_sys_tray.dart @@ -7,7 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/collections/intents.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; void useInitSysTray(WidgetRef ref) { final context = useContext(); diff --git a/lib/hooks/configurators/use_update_checker.dart b/lib/hooks/configurators/use_update_checker.dart index 515d6701..1a6a5be5 100644 --- a/lib/hooks/configurators/use_update_checker.dart +++ b/lib/hooks/configurators/use_update_checker.dart @@ -9,7 +9,7 @@ import 'package:spotube/collections/env.dart'; import 'package:spotube/components/shared/links/anchor_button.dart'; import 'package:spotube/hooks/controllers/use_package_info.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:version/version.dart'; diff --git a/lib/main.dart b/lib/main.dart index e1f0bd53..f46f02c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ import 'package:spotube/models/logger.dart'; import 'package:spotube/models/matched_track.dart'; import 'package:spotube/models/skip_segment.dart'; import 'package:spotube/provider/palette_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/cli/cli.dart'; import 'package:spotube/services/connectivity_adapter.dart'; diff --git a/lib/models/spotube_track.dart b/lib/models/spotube_track.dart index 68641010..67b09ad8 100644 --- a/lib/models/spotube_track.dart +++ b/lib/models/spotube_track.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:spotify/spotify.dart'; import 'package:spotube/extensions/track.dart'; import 'package:spotube/models/matched_track.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/services/youtube/youtube.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:collection/collection.dart'; diff --git a/lib/pages/home/genres.dart b/lib/pages/home/genres.dart index 84082811..b3904e2e 100644 --- a/lib/pages/home/genres.dart +++ b/lib/pages/home/genres.dart @@ -10,7 +10,7 @@ import 'package:spotube/components/shared/expandable_search/expandable_search.da import 'package:spotube/components/shared/shimmers/shimmer_categories.dart'; import 'package:spotube/components/shared/waypoint.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/queries/queries.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart'; diff --git a/lib/pages/library/playlist_generate/playlist_generate.dart b/lib/pages/library/playlist_generate/playlist_generate.dart index 33e244b0..4b8dddaf 100644 --- a/lib/pages/library/playlist_generate/playlist_generate.dart +++ b/lib/pages/library/playlist_generate/playlist_generate.dart @@ -17,7 +17,7 @@ import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/pages/library/playlist_generate/playlist_generate_result.dart'; import 'package:spotube/provider/spotify_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; diff --git a/lib/pages/settings/sections/about.dart b/lib/pages/settings/sections/about.dart index 85181355..9fe59662 100644 --- a/lib/pages/settings/sections/about.dart +++ b/lib/pages/settings/sections/about.dart @@ -7,7 +7,7 @@ import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/settings/section_card_with_heading.dart'; import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; class SettingsAboutSection extends HookConsumerWidget { diff --git a/lib/pages/settings/sections/appearance.dart b/lib/pages/settings/sections/appearance.dart index 5e1ffa50..5de36c63 100644 --- a/lib/pages/settings/sections/appearance.dart +++ b/lib/pages/settings/sections/appearance.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -7,7 +6,8 @@ import 'package:spotube/components/settings/color_scheme_picker_dialog.dart'; import 'package:spotube/components/settings/section_card_with_heading.dart'; import 'package:spotube/components/shared/adaptive/adaptive_select_tile.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; class SettingsAppearanceSection extends HookConsumerWidget { const SettingsAppearanceSection({Key? key}) : super(key: key); diff --git a/lib/pages/settings/sections/desktop.dart b/lib/pages/settings/sections/desktop.dart index 1cc2c5c8..41d6d61e 100644 --- a/lib/pages/settings/sections/desktop.dart +++ b/lib/pages/settings/sections/desktop.dart @@ -4,7 +4,8 @@ import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/settings/section_card_with_heading.dart'; import 'package:spotube/components/shared/adaptive/adaptive_select_tile.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; class SettingsDesktopSection extends HookConsumerWidget { const SettingsDesktopSection({Key? key}) : super(key: key); diff --git a/lib/pages/settings/sections/downloads.dart b/lib/pages/settings/sections/downloads.dart index ff64cdea..12026909 100644 --- a/lib/pages/settings/sections/downloads.dart +++ b/lib/pages/settings/sections/downloads.dart @@ -7,7 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/settings/section_card_with_heading.dart'; import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class SettingsDownloadsSection extends HookConsumerWidget { const SettingsDownloadsSection({Key? key}) : super(key: key); diff --git a/lib/pages/settings/sections/language_region.dart b/lib/pages/settings/sections/language_region.dart index ece28455..9465feb3 100644 --- a/lib/pages/settings/sections/language_region.dart +++ b/lib/pages/settings/sections/language_region.dart @@ -9,7 +9,7 @@ import 'package:spotube/components/shared/adaptive/adaptive_select_tile.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/l10n/l10n.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class SettingsLanguageRegionSection extends HookConsumerWidget { const SettingsLanguageRegionSection({Key? key}) : super(key: key); diff --git a/lib/pages/settings/sections/playback.dart b/lib/pages/settings/sections/playback.dart index 39d9b7c2..5e000231 100644 --- a/lib/pages/settings/sections/playback.dart +++ b/lib/pages/settings/sections/playback.dart @@ -10,7 +10,8 @@ import 'package:spotube/components/shared/adaptive/adaptive_select_tile.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/matched_track.dart'; import 'package:spotube/provider/piped_instances_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; class SettingsPlaybackSection extends HookConsumerWidget { const SettingsPlaybackSection({Key? key}) : super(key: key); diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index f14fb453..842d5240 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -13,7 +13,7 @@ import 'package:spotube/pages/settings/sections/developers.dart'; import 'package:spotube/pages/settings/sections/downloads.dart'; import 'package:spotube/pages/settings/sections/language_region.dart'; import 'package:spotube/pages/settings/sections/playback.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class SettingsPage extends HookConsumerWidget { const SettingsPage({Key? key}) : super(key: key); diff --git a/lib/provider/download_manager_provider.dart b/lib/provider/download_manager_provider.dart index 46c7ee7e..889641f4 100644 --- a/lib/provider/download_manager_provider.dart +++ b/lib/provider/download_manager_provider.dart @@ -10,7 +10,8 @@ import 'package:metadata_god/metadata_god.dart'; import 'package:path/path.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/models/spotube_track.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/provider/youtube_provider.dart'; import 'package:spotube/services/download_manager/download_manager.dart'; import 'package:spotube/services/youtube/youtube.dart'; diff --git a/lib/provider/proxy_playlist/next_fetcher_mixin.dart b/lib/provider/proxy_playlist/next_fetcher_mixin.dart index f6776234..b447f1ef 100644 --- a/lib/provider/proxy_playlist/next_fetcher_mixin.dart +++ b/lib/provider/proxy_playlist/next_fetcher_mixin.dart @@ -6,7 +6,7 @@ import 'package:spotube/models/logger.dart'; import 'package:spotube/models/matched_track.dart'; import 'package:spotube/models/spotube_track.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/services/supabase.dart'; import 'package:spotube/services/youtube/youtube.dart'; diff --git a/lib/provider/proxy_playlist/proxy_playlist_provider.dart b/lib/provider/proxy_playlist/proxy_playlist_provider.dart index bf7293ce..50024661 100644 --- a/lib/provider/proxy_playlist/proxy_playlist_provider.dart +++ b/lib/provider/proxy_playlist/proxy_playlist_provider.dart @@ -20,7 +20,8 @@ import 'package:spotube/provider/palette_provider.dart'; import 'package:spotube/provider/proxy_playlist/next_fetcher_mixin.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart'; import 'package:spotube/provider/scrobbler_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/provider/youtube_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_services/audio_services.dart'; diff --git a/lib/provider/user_preferences/user_preferences_provider.dart b/lib/provider/user_preferences/user_preferences_provider.dart new file mode 100644 index 00000000..db4b73dc --- /dev/null +++ b/lib/provider/user_preferences/user_preferences_provider.dart @@ -0,0 +1,165 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/components/settings/color_scheme_picker_dialog.dart'; +import 'package:spotube/models/matched_track.dart'; +import 'package:spotube/provider/palette_provider.dart'; +import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; +import 'package:spotube/services/audio_player/audio_player.dart'; + +import 'package:spotube/utils/persisted_state_notifier.dart'; +import 'package:spotube/utils/platform.dart'; +import 'package:path/path.dart' as path; + +class UserPreferencesNotifier extends PersistedStateNotifier { + final Ref ref; + + UserPreferencesNotifier(this.ref) + : super(UserPreferences.withDefaults(), "preferences"); + + void reset() { + state = UserPreferences.withDefaults(); + } + + void setStreamMusicCodec(MusicCodec codec) { + state = state.copyWith(streamMusicCodec: codec); + } + + void setDownloadMusicCodec(MusicCodec codec) { + state = state.copyWith(downloadMusicCodec: codec); + } + + void setThemeMode(ThemeMode mode) { + state = state.copyWith(themeMode: mode); + } + + void setRecommendationMarket(Market country) { + state = state.copyWith(recommendationMarket: country); + } + + void setAccentColorScheme(SpotubeColor color) { + state = state.copyWith(accentColorScheme: color); + } + + void setAlbumColorSync(bool sync) { + state = state.copyWith(albumColorSync: sync); + + if (!sync) { + ref.read(paletteProvider.notifier).state = null; + } else { + ref.read(ProxyPlaylistNotifier.notifier).updatePalette(); + } + } + + void setCheckUpdate(bool check) { + state = state.copyWith(checkUpdate: check); + } + + void setAudioQuality(AudioQuality quality) { + state = state.copyWith(audioQuality: quality); + } + + void setDownloadLocation(String downloadDir) { + if (downloadDir.isEmpty) return; + state = state.copyWith(downloadLocation: downloadDir); + } + + void setLayoutMode(LayoutMode mode) { + state = state.copyWith(layoutMode: mode); + } + + void setCloseBehavior(CloseBehavior behavior) { + state = state.copyWith(closeBehavior: behavior); + } + + void setShowSystemTrayIcon(bool show) { + state = state.copyWith(showSystemTrayIcon: show); + } + + void setLocale(Locale locale) { + state = state.copyWith(locale: locale); + } + + void setPipedInstance(String instance) { + state = state.copyWith(pipedInstance: instance); + } + + void setSearchMode(SearchMode mode) { + state = state.copyWith(searchMode: mode); + } + + void setSkipNonMusic(bool skip) { + state = state.copyWith(skipNonMusic: skip); + } + + void setYoutubeApiType(YoutubeApiType type) { + state = state.copyWith(youtubeApiType: type); + } + + void setSystemTitleBar(bool isSystemTitleBar) { + state = state.copyWith(systemTitleBar: isSystemTitleBar); + if (DesktopTools.platform.isDesktop) { + DesktopTools.window.setTitleBarStyle( + isSystemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden, + ); + } + } + + void setAmoledDarkTheme(bool isAmoled) { + state = state.copyWith(amoledDarkTheme: isAmoled); + } + + void setNormalizeAudio(bool normalize) { + state = state.copyWith(normalizeAudio: normalize); + audioPlayer.setAudioNormalization(normalize); + } + + Future _getDefaultDownloadDirectory() async { + if (kIsAndroid) return "/storage/emulated/0/Download/Spotube"; + + if (kIsMacOS) { + return path.join((await getLibraryDirectory()).path, "Caches"); + } + + return getDownloadsDirectory().then((dir) { + return path.join(dir!.path, "Spotube"); + }); + } + + @override + FutureOr onInit() async { + if (state.downloadLocation.isEmpty) { + state = state.copyWith( + downloadLocation: await _getDefaultDownloadDirectory(), + ); + } + + if (DesktopTools.platform.isDesktop) { + await DesktopTools.window.setTitleBarStyle( + state.systemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden, + ); + } + + await audioPlayer.setAudioNormalization(state.normalizeAudio); + } + + @override + FutureOr fromJson(Map json) { + return UserPreferences.fromJson(json); + } + + @override + Map toJson() { + return state.toJson(); + } +} + +final userPreferencesProvider = + StateNotifierProvider( + (ref) => UserPreferencesNotifier(ref), +); diff --git a/lib/provider/user_preferences/user_preferences_state.dart b/lib/provider/user_preferences/user_preferences_state.dart new file mode 100644 index 00000000..ff98fa8e --- /dev/null +++ b/lib/provider/user_preferences/user_preferences_state.dart @@ -0,0 +1,273 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/components/settings/color_scheme_picker_dialog.dart'; +import 'package:spotube/models/matched_track.dart'; + +part 'user_preferences_state.g.dart'; + +@JsonEnum() +enum LayoutMode { + compact, + extended, + adaptive, +} + +@JsonEnum() +enum AudioQuality { + high, + low, +} + +@JsonEnum() +enum CloseBehavior { + minimizeToTray, + close, +} + +@JsonEnum() +enum YoutubeApiType { + youtube, + piped; + + String get label => name[0].toUpperCase() + name.substring(1); +} + +@JsonEnum() +enum MusicCodec { + m4a._("M4a (Best for downloaded music)"), + weba._("WebA (Best for streamed music)\nDoesn't support audio metadata"); + + final String label; + const MusicCodec._(this.label); +} + +@JsonSerializable() +final class UserPreferences { + @JsonKey( + defaultValue: AudioQuality.high, + unknownEnumValue: AudioQuality.high, + ) + final AudioQuality audioQuality; + + @JsonKey(defaultValue: true) + final bool albumColorSync; + + @JsonKey(defaultValue: false) + final bool amoledDarkTheme; + + @JsonKey(defaultValue: true) + final bool checkUpdate; + + @JsonKey(defaultValue: false) + final bool normalizeAudio; + + @JsonKey(defaultValue: true) + final bool showSystemTrayIcon; + + @JsonKey(defaultValue: true) + final bool skipNonMusic; + + @JsonKey(defaultValue: false) + final bool systemTitleBar; + + @JsonKey( + defaultValue: CloseBehavior.minimizeToTray, + unknownEnumValue: CloseBehavior.minimizeToTray, + ) + final CloseBehavior closeBehavior; + + static SpotubeColor _accentColorSchemeFromJson(Map json) { + return SpotubeColor.fromString(json["color"]); + } + + static Map? _accentColorSchemeReadValue( + Map json, String key) { + if (json[key] is String) { + return {"color": json[key]}; + } + + return json[key] as Map?; + } + + static Map _accentColorSchemeToJson(SpotubeColor color) { + return {"color": color.toString()}; + } + + static SpotubeColor _defaultAccentColorScheme() => + const SpotubeColor(0xFF2196F3, name: "Blue"); + + @JsonKey( + defaultValue: UserPreferences._defaultAccentColorScheme, + fromJson: UserPreferences._accentColorSchemeFromJson, + toJson: UserPreferences._accentColorSchemeToJson, + readValue: UserPreferences._accentColorSchemeReadValue, + ) + final SpotubeColor accentColorScheme; + + @JsonKey( + defaultValue: LayoutMode.adaptive, + unknownEnumValue: LayoutMode.adaptive, + ) + final LayoutMode layoutMode; + + static Locale _localeFromJson(Map json) { + return Locale(json["languageCode"], json["countryCode"]); + } + + static Map _localeToJson(Locale locale) { + return { + "languageCode": locale.languageCode, + "countryCode": locale.countryCode, + }; + } + + static Map? _localeReadValue( + Map json, String key) { + if (json[key] is String) { + final map = jsonDecode(json[key]); + return { + "languageCode": map["lc"], + "countryCode": map["cc"], + }; + } + + return json[key] as Map?; + } + + static Locale _defaultLocaleValue() => const Locale("system", "system"); + + @JsonKey( + defaultValue: UserPreferences._defaultLocaleValue, + toJson: UserPreferences._localeToJson, + fromJson: UserPreferences._localeFromJson, + readValue: UserPreferences._localeReadValue, + ) + final Locale locale; + + @JsonKey( + defaultValue: Market.US, + unknownEnumValue: Market.US, + ) + final Market recommendationMarket; + + @JsonKey( + defaultValue: SearchMode.youtube, + unknownEnumValue: SearchMode.youtube, + ) + final SearchMode searchMode; + + @JsonKey(defaultValue: "") + final String downloadLocation; + + @JsonKey(defaultValue: "https://pipedapi.kavin.rocks") + final String pipedInstance; + + @JsonKey( + defaultValue: ThemeMode.system, + unknownEnumValue: ThemeMode.system, + ) + final ThemeMode themeMode; + + @JsonKey( + defaultValue: YoutubeApiType.youtube, + unknownEnumValue: YoutubeApiType.youtube, + ) + final YoutubeApiType youtubeApiType; + + @JsonKey( + defaultValue: MusicCodec.weba, + unknownEnumValue: MusicCodec.weba, + ) + final MusicCodec streamMusicCodec; + + @JsonKey( + defaultValue: MusicCodec.m4a, + unknownEnumValue: MusicCodec.m4a, + ) + final MusicCodec downloadMusicCodec; + + UserPreferences({ + required this.audioQuality, + required this.albumColorSync, + required this.amoledDarkTheme, + required this.checkUpdate, + required this.normalizeAudio, + required this.showSystemTrayIcon, + required this.skipNonMusic, + required this.systemTitleBar, + required this.closeBehavior, + required this.accentColorScheme, + required this.layoutMode, + required this.locale, + required this.recommendationMarket, + required this.searchMode, + required this.downloadLocation, + required this.pipedInstance, + required this.themeMode, + required this.youtubeApiType, + required this.streamMusicCodec, + required this.downloadMusicCodec, + }); + + factory UserPreferences.withDefaults() { + return UserPreferences.fromJson({}); + } + + factory UserPreferences.fromJson(Map json) { + return _$UserPreferencesFromJson(json); + } + + Map toJson() { + return _$UserPreferencesToJson(this); + } + + UserPreferences copyWith({ + ThemeMode? themeMode, + SpotubeColor? accentColorScheme, + bool? albumColorSync, + bool? checkUpdate, + AudioQuality? audioQuality, + String? downloadLocation, + LayoutMode? layoutMode, + CloseBehavior? closeBehavior, + bool? showSystemTrayIcon, + Locale? locale, + String? pipedInstance, + SearchMode? searchMode, + bool? skipNonMusic, + YoutubeApiType? youtubeApiType, + Market? recommendationMarket, + bool? saveTrackLyrics, + bool? amoledDarkTheme, + bool? normalizeAudio, + MusicCodec? downloadMusicCodec, + MusicCodec? streamMusicCodec, + bool? systemTitleBar, + }) { + return UserPreferences( + themeMode: themeMode ?? this.themeMode, + accentColorScheme: accentColorScheme ?? this.accentColorScheme, + albumColorSync: albumColorSync ?? this.albumColorSync, + checkUpdate: checkUpdate ?? this.checkUpdate, + audioQuality: audioQuality ?? this.audioQuality, + downloadLocation: downloadLocation ?? this.downloadLocation, + layoutMode: layoutMode ?? this.layoutMode, + closeBehavior: closeBehavior ?? this.closeBehavior, + showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon, + locale: locale ?? this.locale, + pipedInstance: pipedInstance ?? this.pipedInstance, + searchMode: searchMode ?? this.searchMode, + skipNonMusic: skipNonMusic ?? this.skipNonMusic, + youtubeApiType: youtubeApiType ?? this.youtubeApiType, + recommendationMarket: recommendationMarket ?? this.recommendationMarket, + amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme, + downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec, + normalizeAudio: normalizeAudio ?? this.normalizeAudio, + streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec, + systemTitleBar: systemTitleBar ?? this.systemTitleBar, + ); + } +} diff --git a/lib/provider/user_preferences/user_preferences_state.g.dart b/lib/provider/user_preferences/user_preferences_state.g.dart new file mode 100644 index 00000000..9e3eeee9 --- /dev/null +++ b/lib/provider/user_preferences/user_preferences_state.g.dart @@ -0,0 +1,381 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_preferences_state.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserPreferences _$UserPreferencesFromJson(Map json) => + UserPreferences( + audioQuality: $enumDecodeNullable( + _$AudioQualityEnumMap, json['audioQuality'], + unknownValue: AudioQuality.high) ?? + AudioQuality.high, + albumColorSync: json['albumColorSync'] as bool? ?? true, + amoledDarkTheme: json['amoledDarkTheme'] as bool? ?? false, + checkUpdate: json['checkUpdate'] as bool? ?? true, + normalizeAudio: json['normalizeAudio'] as bool? ?? false, + showSystemTrayIcon: json['showSystemTrayIcon'] as bool? ?? true, + skipNonMusic: json['skipNonMusic'] as bool? ?? true, + systemTitleBar: json['systemTitleBar'] as bool? ?? false, + closeBehavior: $enumDecodeNullable( + _$CloseBehaviorEnumMap, json['closeBehavior'], + unknownValue: CloseBehavior.minimizeToTray) ?? + CloseBehavior.minimizeToTray, + accentColorScheme: UserPreferences._accentColorSchemeReadValue( + json, 'accentColorScheme') == + null + ? UserPreferences._defaultAccentColorScheme() + : UserPreferences._accentColorSchemeFromJson( + UserPreferences._accentColorSchemeReadValue( + json, 'accentColorScheme') as Map), + layoutMode: $enumDecodeNullable(_$LayoutModeEnumMap, json['layoutMode'], + unknownValue: LayoutMode.adaptive) ?? + LayoutMode.adaptive, + locale: UserPreferences._localeReadValue(json, 'locale') == null + ? UserPreferences._defaultLocaleValue() + : UserPreferences._localeFromJson( + UserPreferences._localeReadValue(json, 'locale') + as Map), + recommendationMarket: $enumDecodeNullable( + _$MarketEnumMap, json['recommendationMarket'], + unknownValue: Market.US) ?? + Market.US, + searchMode: $enumDecodeNullable(_$SearchModeEnumMap, json['searchMode'], + unknownValue: SearchMode.youtube) ?? + SearchMode.youtube, + downloadLocation: json['downloadLocation'] as String? ?? '', + pipedInstance: + json['pipedInstance'] as String? ?? 'https://pipedapi.kavin.rocks', + themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode'], + unknownValue: ThemeMode.system) ?? + ThemeMode.system, + youtubeApiType: $enumDecodeNullable( + _$YoutubeApiTypeEnumMap, json['youtubeApiType'], + unknownValue: YoutubeApiType.youtube) ?? + YoutubeApiType.youtube, + streamMusicCodec: $enumDecodeNullable( + _$MusicCodecEnumMap, json['streamMusicCodec'], + unknownValue: MusicCodec.weba) ?? + MusicCodec.weba, + downloadMusicCodec: $enumDecodeNullable( + _$MusicCodecEnumMap, json['downloadMusicCodec'], + unknownValue: MusicCodec.m4a) ?? + MusicCodec.m4a, + ); + +Map _$UserPreferencesToJson(UserPreferences instance) => + { + 'audioQuality': _$AudioQualityEnumMap[instance.audioQuality]!, + 'albumColorSync': instance.albumColorSync, + 'amoledDarkTheme': instance.amoledDarkTheme, + 'checkUpdate': instance.checkUpdate, + 'normalizeAudio': instance.normalizeAudio, + 'showSystemTrayIcon': instance.showSystemTrayIcon, + 'skipNonMusic': instance.skipNonMusic, + 'systemTitleBar': instance.systemTitleBar, + 'closeBehavior': _$CloseBehaviorEnumMap[instance.closeBehavior]!, + 'accentColorScheme': + UserPreferences._accentColorSchemeToJson(instance.accentColorScheme), + 'layoutMode': _$LayoutModeEnumMap[instance.layoutMode]!, + 'locale': UserPreferences._localeToJson(instance.locale), + 'recommendationMarket': _$MarketEnumMap[instance.recommendationMarket]!, + 'searchMode': _$SearchModeEnumMap[instance.searchMode]!, + 'downloadLocation': instance.downloadLocation, + 'pipedInstance': instance.pipedInstance, + 'themeMode': _$ThemeModeEnumMap[instance.themeMode]!, + 'youtubeApiType': _$YoutubeApiTypeEnumMap[instance.youtubeApiType]!, + 'streamMusicCodec': _$MusicCodecEnumMap[instance.streamMusicCodec]!, + 'downloadMusicCodec': _$MusicCodecEnumMap[instance.downloadMusicCodec]!, + }; + +const _$AudioQualityEnumMap = { + AudioQuality.high: 'high', + AudioQuality.low: 'low', +}; + +const _$CloseBehaviorEnumMap = { + CloseBehavior.minimizeToTray: 'minimizeToTray', + CloseBehavior.close: 'close', +}; + +const _$LayoutModeEnumMap = { + LayoutMode.compact: 'compact', + LayoutMode.extended: 'extended', + LayoutMode.adaptive: 'adaptive', +}; + +const _$MarketEnumMap = { + Market.AD: 'AD', + Market.AE: 'AE', + Market.AF: 'AF', + Market.AG: 'AG', + Market.AI: 'AI', + Market.AL: 'AL', + Market.AM: 'AM', + Market.AO: 'AO', + Market.AQ: 'AQ', + Market.AR: 'AR', + Market.AS: 'AS', + Market.AT: 'AT', + Market.AU: 'AU', + Market.AW: 'AW', + Market.AX: 'AX', + Market.AZ: 'AZ', + Market.BA: 'BA', + Market.BB: 'BB', + Market.BD: 'BD', + Market.BE: 'BE', + Market.BF: 'BF', + Market.BG: 'BG', + Market.BH: 'BH', + Market.BI: 'BI', + Market.BJ: 'BJ', + Market.BL: 'BL', + Market.BM: 'BM', + Market.BN: 'BN', + Market.BO: 'BO', + Market.BQ: 'BQ', + Market.BR: 'BR', + Market.BS: 'BS', + Market.BT: 'BT', + Market.BV: 'BV', + Market.BW: 'BW', + Market.BY: 'BY', + Market.BZ: 'BZ', + Market.CA: 'CA', + Market.CC: 'CC', + Market.CD: 'CD', + Market.CF: 'CF', + Market.CG: 'CG', + Market.CH: 'CH', + Market.CI: 'CI', + Market.CK: 'CK', + Market.CL: 'CL', + Market.CM: 'CM', + Market.CN: 'CN', + Market.CO: 'CO', + Market.CR: 'CR', + Market.CU: 'CU', + Market.CV: 'CV', + Market.CW: 'CW', + Market.CX: 'CX', + Market.CY: 'CY', + Market.CZ: 'CZ', + Market.DE: 'DE', + Market.DJ: 'DJ', + Market.DK: 'DK', + Market.DM: 'DM', + Market.DO: 'DO', + Market.DZ: 'DZ', + Market.EC: 'EC', + Market.EE: 'EE', + Market.EG: 'EG', + Market.EH: 'EH', + Market.ER: 'ER', + Market.ES: 'ES', + Market.ET: 'ET', + Market.FI: 'FI', + Market.FJ: 'FJ', + Market.FK: 'FK', + Market.FM: 'FM', + Market.FO: 'FO', + Market.FR: 'FR', + Market.GA: 'GA', + Market.GB: 'GB', + Market.GD: 'GD', + Market.GE: 'GE', + Market.GF: 'GF', + Market.GG: 'GG', + Market.GH: 'GH', + Market.GI: 'GI', + Market.GL: 'GL', + Market.GM: 'GM', + Market.GN: 'GN', + Market.GP: 'GP', + Market.GQ: 'GQ', + Market.GR: 'GR', + Market.GS: 'GS', + Market.GT: 'GT', + Market.GU: 'GU', + Market.GW: 'GW', + Market.GY: 'GY', + Market.HK: 'HK', + Market.HM: 'HM', + Market.HN: 'HN', + Market.HR: 'HR', + Market.HT: 'HT', + Market.HU: 'HU', + Market.ID: 'ID', + Market.IE: 'IE', + Market.IL: 'IL', + Market.IM: 'IM', + Market.IN: 'IN', + Market.IO: 'IO', + Market.IQ: 'IQ', + Market.IR: 'IR', + Market.IS: 'IS', + Market.IT: 'IT', + Market.JE: 'JE', + Market.JM: 'JM', + Market.JO: 'JO', + Market.JP: 'JP', + Market.KE: 'KE', + Market.KG: 'KG', + Market.KH: 'KH', + Market.KI: 'KI', + Market.KM: 'KM', + Market.KN: 'KN', + Market.KP: 'KP', + Market.KR: 'KR', + Market.KW: 'KW', + Market.KY: 'KY', + Market.KZ: 'KZ', + Market.LA: 'LA', + Market.LB: 'LB', + Market.LC: 'LC', + Market.LI: 'LI', + Market.LK: 'LK', + Market.LR: 'LR', + Market.LS: 'LS', + Market.LT: 'LT', + Market.LU: 'LU', + Market.LV: 'LV', + Market.LY: 'LY', + Market.MA: 'MA', + Market.MC: 'MC', + Market.MD: 'MD', + Market.ME: 'ME', + Market.MF: 'MF', + Market.MG: 'MG', + Market.MH: 'MH', + Market.MK: 'MK', + Market.ML: 'ML', + Market.MM: 'MM', + Market.MN: 'MN', + Market.MO: 'MO', + Market.MP: 'MP', + Market.MQ: 'MQ', + Market.MR: 'MR', + Market.MS: 'MS', + Market.MT: 'MT', + Market.MU: 'MU', + Market.MV: 'MV', + Market.MW: 'MW', + Market.MX: 'MX', + Market.MY: 'MY', + Market.MZ: 'MZ', + Market.NA: 'NA', + Market.NC: 'NC', + Market.NE: 'NE', + Market.NF: 'NF', + Market.NG: 'NG', + Market.NI: 'NI', + Market.NL: 'NL', + Market.NO: 'NO', + Market.NP: 'NP', + Market.NR: 'NR', + Market.NU: 'NU', + Market.NZ: 'NZ', + Market.OM: 'OM', + Market.PA: 'PA', + Market.PE: 'PE', + Market.PF: 'PF', + Market.PG: 'PG', + Market.PH: 'PH', + Market.PK: 'PK', + Market.PL: 'PL', + Market.PM: 'PM', + Market.PN: 'PN', + Market.PR: 'PR', + Market.PS: 'PS', + Market.PT: 'PT', + Market.PW: 'PW', + Market.PY: 'PY', + Market.QA: 'QA', + Market.RE: 'RE', + Market.RO: 'RO', + Market.RS: 'RS', + Market.RU: 'RU', + Market.RW: 'RW', + Market.SA: 'SA', + Market.SB: 'SB', + Market.SC: 'SC', + Market.SD: 'SD', + Market.SE: 'SE', + Market.SG: 'SG', + Market.SH: 'SH', + Market.SI: 'SI', + Market.SJ: 'SJ', + Market.SK: 'SK', + Market.SL: 'SL', + Market.SM: 'SM', + Market.SN: 'SN', + Market.SO: 'SO', + Market.SR: 'SR', + Market.SS: 'SS', + Market.ST: 'ST', + Market.SV: 'SV', + Market.SX: 'SX', + Market.SY: 'SY', + Market.SZ: 'SZ', + Market.TC: 'TC', + Market.TD: 'TD', + Market.TF: 'TF', + Market.TG: 'TG', + Market.TH: 'TH', + Market.TJ: 'TJ', + Market.TK: 'TK', + Market.TL: 'TL', + Market.TM: 'TM', + Market.TN: 'TN', + Market.TO: 'TO', + Market.TR: 'TR', + Market.TT: 'TT', + Market.TV: 'TV', + Market.TW: 'TW', + Market.TZ: 'TZ', + Market.UA: 'UA', + Market.UG: 'UG', + Market.UM: 'UM', + Market.US: 'US', + Market.UY: 'UY', + Market.UZ: 'UZ', + Market.VA: 'VA', + Market.VC: 'VC', + Market.VE: 'VE', + Market.VG: 'VG', + Market.VI: 'VI', + Market.VN: 'VN', + Market.VU: 'VU', + Market.WF: 'WF', + Market.WS: 'WS', + Market.XK: 'XK', + Market.YE: 'YE', + Market.YT: 'YT', + Market.ZA: 'ZA', + Market.ZM: 'ZM', + Market.ZW: 'ZW', +}; + +const _$SearchModeEnumMap = { + SearchMode.youtube: 'youtube', + SearchMode.youtubeMusic: 'youtubeMusic', +}; + +const _$ThemeModeEnumMap = { + ThemeMode.system: 'system', + ThemeMode.light: 'light', + ThemeMode.dark: 'dark', +}; + +const _$YoutubeApiTypeEnumMap = { + YoutubeApiType.youtube: 'youtube', + YoutubeApiType.piped: 'piped', +}; + +const _$MusicCodecEnumMap = { + MusicCodec.m4a: 'm4a', + MusicCodec.weba: 'weba', +}; diff --git a/lib/provider/user_preferences_provider.dart b/lib/provider/user_preferences_provider.dart deleted file mode 100644 index 80c71de9..00000000 --- a/lib/provider/user_preferences_provider.dart +++ /dev/null @@ -1,391 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:spotify/spotify.dart'; -import 'package:spotube/components/settings/color_scheme_picker_dialog.dart'; -import 'package:spotube/models/matched_track.dart'; -import 'package:spotube/provider/palette_provider.dart'; -import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; -import 'package:spotube/services/audio_player/audio_player.dart'; - -import 'package:spotube/utils/persisted_state_notifier.dart'; -import 'package:spotube/utils/platform.dart'; -import 'package:path/path.dart' as path; - -enum LayoutMode { - compact, - extended, - adaptive, -} - -enum AudioQuality { - high, - low, -} - -enum CloseBehavior { - minimizeToTray, - close, -} - -enum YoutubeApiType { - youtube, - piped; - - String get label => name[0].toUpperCase() + name.substring(1); -} - -enum MusicCodec { - m4a._("M4a (Best for downloaded music)"), - weba._("WebA (Best for streamed music)\nDoesn't support audio metadata"); - - final String label; - const MusicCodec._(this.label); -} - -class UserPreferences { - final AudioQuality audioQuality; - final bool albumColorSync; - final bool amoledDarkTheme; - final bool checkUpdate; - final bool normalizeAudio; - final bool showSystemTrayIcon; - final bool skipNonMusic; - final bool systemTitleBar; - final CloseBehavior closeBehavior; - final SpotubeColor accentColorScheme; - final LayoutMode layoutMode; - final Locale locale; - final Market recommendationMarket; - final SearchMode searchMode; - String downloadLocation; - final String pipedInstance; - final ThemeMode themeMode; - final YoutubeApiType youtubeApiType; - final MusicCodec streamMusicCodec; - final MusicCodec downloadMusicCodec; - - UserPreferences({ - required AudioQuality? audioQuality, - required bool? albumColorSync, - required bool? amoledDarkTheme, - required bool? checkUpdate, - required bool? normalizeAudio, - required bool? showSystemTrayIcon, - required bool? skipNonMusic, - required bool? systemTitleBar, - required CloseBehavior? closeBehavior, - required SpotubeColor? accentColorScheme, - required LayoutMode? layoutMode, - required Locale? locale, - required Market? recommendationMarket, - required SearchMode? searchMode, - required String? downloadLocation, - required String? pipedInstance, - required ThemeMode? themeMode, - required YoutubeApiType? youtubeApiType, - required MusicCodec? streamMusicCodec, - required MusicCodec? downloadMusicCodec, - }) : accentColorScheme = - accentColorScheme ?? const SpotubeColor(0xFF2196F3, name: "Blue"), - albumColorSync = albumColorSync ?? true, - amoledDarkTheme = amoledDarkTheme ?? false, - audioQuality = audioQuality ?? AudioQuality.high, - checkUpdate = checkUpdate ?? true, - closeBehavior = closeBehavior ?? CloseBehavior.close, - downloadLocation = downloadLocation ?? "", - downloadMusicCodec = downloadMusicCodec ?? MusicCodec.m4a, - layoutMode = layoutMode ?? LayoutMode.adaptive, - locale = locale ?? const Locale("system", "system"), - normalizeAudio = normalizeAudio ?? true, - pipedInstance = pipedInstance ?? "https://pipedapi.kavin.rocks", - recommendationMarket = recommendationMarket ?? Market.US, - searchMode = searchMode ?? SearchMode.youtube, - showSystemTrayIcon = showSystemTrayIcon ?? true, - skipNonMusic = skipNonMusic ?? true, - streamMusicCodec = streamMusicCodec ?? MusicCodec.weba, - systemTitleBar = systemTitleBar ?? false, - themeMode = themeMode ?? ThemeMode.system, - youtubeApiType = youtubeApiType ?? YoutubeApiType.youtube { - if (downloadLocation == null) { - _getDefaultDownloadDirectory().then( - (value) => this.downloadLocation = value, - ); - } - } - - factory UserPreferences.withDefaults() { - return UserPreferences( - audioQuality: null, - albumColorSync: null, - amoledDarkTheme: null, - checkUpdate: null, - normalizeAudio: null, - showSystemTrayIcon: null, - skipNonMusic: null, - systemTitleBar: null, - closeBehavior: null, - accentColorScheme: null, - layoutMode: null, - locale: null, - recommendationMarket: null, - searchMode: null, - downloadLocation: null, - pipedInstance: null, - themeMode: null, - youtubeApiType: null, - streamMusicCodec: null, - downloadMusicCodec: null, - ); - } - - static Future _getDefaultDownloadDirectory() async { - if (kIsAndroid) return "/storage/emulated/0/Download/Spotube"; - - if (kIsMacOS) { - return path.join((await getLibraryDirectory()).path, "Caches"); - } - - return getDownloadsDirectory().then((dir) { - return path.join(dir!.path, "Spotube"); - }); - } - - static Future fromJson(Map json) async { - final localeMap = - json["locale"] != null ? jsonDecode(json["locale"]) : null; - - final systemTitleBar = json["systemTitleBar"] ?? false; - if (DesktopTools.platform.isDesktop) { - await DesktopTools.window.setTitleBarStyle( - systemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden, - ); - } - - final normalizeAudio = json["normalizeAudio"] ?? true; - audioPlayer.setAudioNormalization(normalizeAudio); - - return UserPreferences( - accentColorScheme: json["accentColorScheme"] == null - ? null - : SpotubeColor.fromString(json["accentColorScheme"]), - albumColorSync: json["albumColorSync"], - amoledDarkTheme: json["amoledDarkTheme"], - audioQuality: AudioQuality.values[json["audioQuality"]], - checkUpdate: json["checkUpdate"], - closeBehavior: CloseBehavior.values[json["closeBehavior"]], - downloadLocation: - json["downloadLocation"] ?? await _getDefaultDownloadDirectory(), - downloadMusicCodec: MusicCodec.values[json["downloadMusicCodec"]], - layoutMode: LayoutMode.values[json["layoutMode"]], - locale: - localeMap == null ? null : Locale(localeMap?["lc"], localeMap?["cc"]), - normalizeAudio: json["normalizeAudio"], - pipedInstance: json["pipedInstance"], - recommendationMarket: Market.values[json["recommendationMarket"]], - searchMode: SearchMode.values[json["searchMode"]], - showSystemTrayIcon: json["showSystemTrayIcon"], - skipNonMusic: json["skipNonMusic"], - streamMusicCodec: MusicCodec.values[json["streamMusicCodec"]], - systemTitleBar: json["systemTitleBar"], - themeMode: ThemeMode.values[json["themeMode"]], - youtubeApiType: YoutubeApiType.values[json["youtubeApiType"]], - ); - } - - Map toJson() { - return { - "recommendationMarket": recommendationMarket.index, - "themeMode": themeMode.index, - "accentColorScheme": accentColorScheme.toString(), - "albumColorSync": albumColorSync, - "checkUpdate": checkUpdate, - "audioQuality": audioQuality.index, - "downloadLocation": downloadLocation, - "layoutMode": layoutMode.index, - "closeBehavior": closeBehavior.index, - "showSystemTrayIcon": showSystemTrayIcon, - "locale": - jsonEncode({"lc": locale.languageCode, "cc": locale.countryCode}), - "pipedInstance": pipedInstance, - "searchMode": searchMode.index, - "skipNonMusic": skipNonMusic, - "youtubeApiType": youtubeApiType.index, - 'systemTitleBar': systemTitleBar, - "amoledDarkTheme": amoledDarkTheme, - "normalizeAudio": normalizeAudio, - "streamMusicCodec": streamMusicCodec.index, - "downloadMusicCodec": downloadMusicCodec.index, - }; - } - - UserPreferences copyWith({ - ThemeMode? themeMode, - SpotubeColor? accentColorScheme, - bool? albumColorSync, - bool? checkUpdate, - AudioQuality? audioQuality, - String? downloadLocation, - LayoutMode? layoutMode, - CloseBehavior? closeBehavior, - bool? showSystemTrayIcon, - Locale? locale, - String? pipedInstance, - SearchMode? searchMode, - bool? skipNonMusic, - YoutubeApiType? youtubeApiType, - Market? recommendationMarket, - bool? saveTrackLyrics, - bool? amoledDarkTheme, - bool? normalizeAudio, - MusicCodec? downloadMusicCodec, - MusicCodec? streamMusicCodec, - bool? systemTitleBar, - }) { - return UserPreferences( - themeMode: themeMode ?? this.themeMode, - accentColorScheme: accentColorScheme ?? this.accentColorScheme, - albumColorSync: albumColorSync ?? this.albumColorSync, - checkUpdate: checkUpdate ?? this.checkUpdate, - audioQuality: audioQuality ?? this.audioQuality, - downloadLocation: downloadLocation ?? this.downloadLocation, - layoutMode: layoutMode ?? this.layoutMode, - closeBehavior: closeBehavior ?? this.closeBehavior, - showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon, - locale: locale ?? this.locale, - pipedInstance: pipedInstance ?? this.pipedInstance, - searchMode: searchMode ?? this.searchMode, - skipNonMusic: skipNonMusic ?? this.skipNonMusic, - youtubeApiType: youtubeApiType ?? this.youtubeApiType, - recommendationMarket: recommendationMarket ?? this.recommendationMarket, - amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme, - downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec, - normalizeAudio: normalizeAudio ?? this.normalizeAudio, - streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec, - systemTitleBar: systemTitleBar ?? this.systemTitleBar, - ); - } -} - -class UserPreferencesNotifier extends PersistedStateNotifier { - final Ref ref; - - UserPreferencesNotifier(this.ref) - : super(UserPreferences.withDefaults(), "preferences"); - - void reset() { - state = UserPreferences.withDefaults(); - } - - void setStreamMusicCodec(MusicCodec codec) { - state = state.copyWith(streamMusicCodec: codec); - } - - void setDownloadMusicCodec(MusicCodec codec) { - state = state.copyWith(downloadMusicCodec: codec); - } - - void setThemeMode(ThemeMode mode) { - state = state.copyWith(themeMode: mode); - } - - void setRecommendationMarket(Market country) { - state = state.copyWith(recommendationMarket: country); - } - - void setAccentColorScheme(SpotubeColor color) { - state = state.copyWith(accentColorScheme: color); - } - - void setAlbumColorSync(bool sync) { - state = state.copyWith(albumColorSync: sync); - - if (!sync) { - ref.read(paletteProvider.notifier).state = null; - } else { - ref.read(ProxyPlaylistNotifier.notifier).updatePalette(); - } - } - - void setCheckUpdate(bool check) { - state = state.copyWith(checkUpdate: check); - } - - void setAudioQuality(AudioQuality quality) { - state = state.copyWith(audioQuality: quality); - } - - void setDownloadLocation(String downloadDir) { - if (downloadDir.isEmpty) return; - state = state.copyWith(downloadLocation: downloadDir); - } - - void setLayoutMode(LayoutMode mode) { - state = state.copyWith(layoutMode: mode); - } - - void setCloseBehavior(CloseBehavior behavior) { - state = state.copyWith(closeBehavior: behavior); - } - - void setShowSystemTrayIcon(bool show) { - state = state.copyWith(showSystemTrayIcon: show); - } - - void setLocale(Locale locale) { - state = state.copyWith(locale: locale); - } - - void setPipedInstance(String instance) { - state = state.copyWith(pipedInstance: instance); - } - - void setSearchMode(SearchMode mode) { - state = state.copyWith(searchMode: mode); - } - - void setSkipNonMusic(bool skip) { - state = state.copyWith(skipNonMusic: skip); - } - - void setYoutubeApiType(YoutubeApiType type) { - state = state.copyWith(youtubeApiType: type); - } - - void setSystemTitleBar(bool isSystemTitleBar) { - state = state.copyWith(systemTitleBar: isSystemTitleBar); - if (DesktopTools.platform.isDesktop) { - DesktopTools.window.setTitleBarStyle( - isSystemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden, - ); - } - } - - void setAmoledDarkTheme(bool isAmoled) { - state = state.copyWith(amoledDarkTheme: isAmoled); - } - - void setNormalizeAudio(bool normalize) { - state = state.copyWith(normalizeAudio: normalize); - audioPlayer.setAudioNormalization(normalize); - } - - @override - FutureOr fromJson(Map json) { - return UserPreferences.fromJson(json); - } - - @override - Map toJson() { - return state.toJson(); - } -} - -final userPreferencesProvider = - StateNotifierProvider( - (ref) => UserPreferencesNotifier(ref), -); diff --git a/lib/provider/youtube_provider.dart b/lib/provider/youtube_provider.dart index 0e7b7d0e..33e0496f 100644 --- a/lib/provider/youtube_provider.dart +++ b/lib/provider/youtube_provider.dart @@ -1,5 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/youtube/youtube.dart'; final youtubeProvider = Provider((ref) { diff --git a/lib/services/queries/album.dart b/lib/services/queries/album.dart index 2e2e8f14..546b3d15 100644 --- a/lib/services/queries/album.dart +++ b/lib/services/queries/album.dart @@ -4,7 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/hooks/spotify/use_spotify_infinite_query.dart'; import 'package:spotube/hooks/spotify/use_spotify_query.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class AlbumQueries { const AlbumQueries(); diff --git a/lib/services/queries/category.dart b/lib/services/queries/category.dart index dbdd2a11..960b5702 100644 --- a/lib/services/queries/category.dart +++ b/lib/services/queries/category.dart @@ -6,7 +6,7 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/spotify/use_spotify_infinite_query.dart'; import 'package:spotube/provider/custom_spotify_endpoint_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class CategoryQueries { const CategoryQueries(); diff --git a/lib/services/queries/playlist.dart b/lib/services/queries/playlist.dart index c4532aa8..2c6c38be 100644 --- a/lib/services/queries/playlist.dart +++ b/lib/services/queries/playlist.dart @@ -11,7 +11,7 @@ import 'package:spotube/hooks/spotify/use_spotify_infinite_query.dart'; import 'package:spotube/hooks/spotify/use_spotify_query.dart'; import 'package:spotube/pages/library/playlist_generate/playlist_generate.dart'; import 'package:spotube/provider/custom_spotify_endpoint_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; typedef RecommendationParameters = ({ RecommendationAttribute acousticness, diff --git a/lib/services/queries/views.dart b/lib/services/queries/views.dart index b56f07d9..4864ffe1 100644 --- a/lib/services/queries/views.dart +++ b/lib/services/queries/views.dart @@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/custom_spotify_endpoint_provider.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; class ViewsQueries { const ViewsQueries(); diff --git a/lib/services/youtube/youtube.dart b/lib/services/youtube/youtube.dart index c8c277e3..2b52864b 100644 --- a/lib/services/youtube/youtube.dart +++ b/lib/services/youtube/youtube.dart @@ -4,7 +4,7 @@ import 'package:piped_client/piped_client.dart'; import 'package:spotube/collections/routes.dart'; import 'package:spotube/components/shared/dialogs/piped_down_dialog.dart'; import 'package:spotube/models/matched_track.dart'; -import 'package:spotube/provider/user_preferences_provider.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_state.dart'; import 'package:spotube/utils/primitive_utils.dart'; import 'package:youtube_explode_dart/youtube_explode_dart.dart';