fix: changed settings are not persisting after force stop #821

This commit is contained in:
Kingkor Roy Tirtho 2023-11-13 20:59:54 +06:00
parent 694ddf07a3
commit e29a38dfa4
9 changed files with 287 additions and 305 deletions

View File

@ -49,6 +49,7 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
final scheme = preferences.accentColorScheme;
final active = useState<String>(colorsMap.firstWhere(
(element) {
@ -57,7 +58,7 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
).name);
onOk() {
preferences.setAccentColorScheme(
preferencesNotifier.setAccentColorScheme(
colorsMap.firstWhere(
(element) {
return element.name == active.value;

View File

@ -16,6 +16,7 @@ class SettingsAboutSection extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
return SectionCardWithHeading(
heading: context.l10n.about,
@ -68,7 +69,7 @@ class SettingsAboutSection extends HookConsumerWidget {
secondary: const Icon(SpotubeIcons.update),
title: Text(context.l10n.check_for_updates),
value: preferences.checkUpdate,
onChanged: (checked) => preferences.setCheckUpdate(checked),
onChanged: (checked) => preferencesNotifier.setCheckUpdate(checked),
),
ListTile(
leading: const Icon(SpotubeIcons.info),

View File

@ -15,6 +15,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
final pickColorScheme = useCallback(() {
return () => showDialog(
context: context,
@ -33,7 +34,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
value: preferences.layoutMode,
onChanged: (value) {
if (value != null) {
preferences.setLayoutMode(value);
preferencesNotifier.setLayoutMode(value);
}
},
options: [
@ -71,7 +72,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
],
onChanged: (value) {
if (value != null) {
preferences.setThemeMode(value);
preferencesNotifier.setThemeMode(value);
}
},
),
@ -80,7 +81,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
title: Text(context.l10n.use_amoled_mode),
subtitle: Text(context.l10n.pitch_dark_theme),
value: preferences.amoledDarkTheme,
onChanged: preferences.setAmoledDarkTheme,
onChanged: preferencesNotifier.setAmoledDarkTheme,
),
ListTile(
leading: const Icon(SpotubeIcons.palette),
@ -101,7 +102,7 @@ class SettingsAppearanceSection extends HookConsumerWidget {
title: Text(context.l10n.sync_album_color),
subtitle: Text(context.l10n.sync_album_color_description),
value: preferences.albumColorSync,
onChanged: preferences.setAlbumColorSync,
onChanged: preferencesNotifier.setAlbumColorSync,
),
],
);

View File

@ -12,6 +12,7 @@ class SettingsDesktopSection extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
return SectionCardWithHeading(
heading: context.l10n.desktop,
@ -32,7 +33,7 @@ class SettingsDesktopSection extends HookConsumerWidget {
],
onChanged: (value) {
if (value != null) {
preferences.setCloseBehavior(value);
preferencesNotifier.setCloseBehavior(value);
}
},
),
@ -40,13 +41,13 @@ class SettingsDesktopSection extends HookConsumerWidget {
secondary: const Icon(SpotubeIcons.tray),
title: Text(context.l10n.show_tray_icon),
value: preferences.showSystemTrayIcon,
onChanged: preferences.setShowSystemTrayIcon,
onChanged: preferencesNotifier.setShowSystemTrayIcon,
),
SwitchListTile(
secondary: const Icon(SpotubeIcons.window),
title: Text(context.l10n.use_system_title_bar),
value: preferences.systemTitleBar,
onChanged: preferences.setSystemTitleBar,
onChanged: preferencesNotifier.setSystemTitleBar,
),
],
);

View File

@ -14,6 +14,7 @@ class SettingsDownloadsSection extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
final preferences = ref.watch(userPreferencesProvider);
final pickDownloadLocation = useCallback(() async {
@ -22,13 +23,13 @@ class SettingsDownloadsSection extends HookConsumerWidget {
initialDirectory: preferences.downloadLocation,
);
if (dirStr == null) return;
preferences.setDownloadLocation(dirStr);
preferencesNotifier.setDownloadLocation(dirStr);
} else {
String? dirStr = await getDirectoryPath(
initialDirectory: preferences.downloadLocation,
);
if (dirStr == null) return;
preferences.setDownloadLocation(dirStr);
preferencesNotifier.setDownloadLocation(dirStr);
}
}, [preferences.downloadLocation]);

View File

@ -17,6 +17,7 @@ class SettingsLanguageRegionSection extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
final mediaQuery = MediaQuery.of(context);
return SectionCardWithHeading(
@ -26,7 +27,7 @@ class SettingsLanguageRegionSection extends HookConsumerWidget {
value: preferences.locale,
onChanged: (locale) {
if (locale == null) return;
preferences.setLocale(locale);
preferencesNotifier.setLocale(locale);
},
title: Text(context.l10n.language),
secondary: const Icon(SpotubeIcons.language),
@ -57,7 +58,7 @@ class SettingsLanguageRegionSection extends HookConsumerWidget {
value: preferences.recommendationMarket,
onChanged: (value) {
if (value == null) return;
preferences.setRecommendationMarket(value);
preferencesNotifier.setRecommendationMarket(value);
},
options: spotifyMarkets
.map(

View File

@ -18,6 +18,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
final theme = Theme.of(context);
return SectionCardWithHeading(
@ -39,7 +40,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
],
onChanged: (value) {
if (value != null) {
preferences.setAudioQuality(value);
preferencesNotifier.setAudioQuality(value);
}
},
),
@ -55,7 +56,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
.toList(),
onChanged: (value) {
if (value == null) return;
preferences.setYoutubeApiType(value);
preferencesNotifier.setYoutubeApiType(value);
},
),
AnimatedSwitcher(
@ -113,7 +114,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
.toList(),
onChanged: (value) {
if (value != null) {
preferences.setPipedInstance(value);
preferencesNotifier.setPipedInstance(value);
}
},
);
@ -141,7 +142,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
.toList(),
onChanged: (value) {
if (value == null) return;
preferences.setSearchMode(value);
preferencesNotifier.setSearchMode(value);
},
),
),
@ -155,7 +156,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
title: Text(context.l10n.skip_non_music),
value: preferences.skipNonMusic,
onChanged: (state) {
preferences.setSkipNonMusic(state);
preferencesNotifier.setSkipNonMusic(state);
},
),
),
@ -172,7 +173,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
secondary: const Icon(SpotubeIcons.normalize),
title: Text(context.l10n.normalize_audio),
value: preferences.normalizeAudio,
onChanged: preferences.setNormalizeAudio,
onChanged: preferencesNotifier.setNormalizeAudio,
),
AdaptiveSelectTile<MusicCodec>(
secondary: const Icon(SpotubeIcons.stream),
@ -190,7 +191,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
.toList(),
onChanged: (value) {
if (value == null) return;
preferences.setStreamMusicCodec(value);
preferencesNotifier.setStreamMusicCodec(value);
},
),
AdaptiveSelectTile<MusicCodec>(
@ -209,7 +210,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
.toList(),
onChanged: (value) {
if (value == null) return;
preferences.setDownloadMusicCodec(value);
preferencesNotifier.setDownloadMusicCodec(value);
},
),
],

View File

@ -20,7 +20,7 @@ class SettingsPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final preferences = ref.watch(userPreferencesProvider);
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
return SafeArea(
bottom: false,
@ -49,7 +49,7 @@ class SettingsPage extends HookConsumerWidget {
const SettingsAboutSection(),
Center(
child: FilledButton(
onPressed: preferences.reset,
onPressed: preferencesNotifier.reset,
child: Text(context.l10n.restore_defaults),
),
),

View File

@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -13,7 +12,7 @@ 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_change_notifier.dart';
import 'package:spotube/utils/persisted_state_notifier.dart';
import 'package:spotube/utils/platform.dart';
import 'package:path/path.dart' as path;
@ -48,220 +47,103 @@ enum MusicCodec {
const MusicCodec._(this.label);
}
class UserPreferences extends PersistedChangeNotifier {
AudioQuality audioQuality;
bool albumColorSync;
bool amoledDarkTheme;
bool checkUpdate;
bool normalizeAudio;
bool showSystemTrayIcon;
bool skipNonMusic;
bool systemTitleBar;
CloseBehavior closeBehavior;
late SpotubeColor accentColorScheme;
LayoutMode layoutMode;
Locale locale;
Market recommendationMarket;
SearchMode searchMode;
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;
String pipedInstance;
ThemeMode themeMode;
YoutubeApiType youtubeApiType;
MusicCodec streamMusicCodec;
MusicCodec downloadMusicCodec;
final String pipedInstance;
final ThemeMode themeMode;
final YoutubeApiType youtubeApiType;
final MusicCodec streamMusicCodec;
final MusicCodec downloadMusicCodec;
final Ref ref;
UserPreferences(
this.ref, {
this.recommendationMarket = Market.US,
this.themeMode = ThemeMode.system,
this.layoutMode = LayoutMode.adaptive,
this.albumColorSync = true,
this.checkUpdate = true,
this.audioQuality = AudioQuality.high,
this.downloadLocation = "",
this.closeBehavior = CloseBehavior.close,
this.showSystemTrayIcon = true,
this.locale = const Locale("system", "system"),
this.pipedInstance = "https://pipedapi.kavin.rocks",
this.searchMode = SearchMode.youtube,
this.skipNonMusic = true,
this.youtubeApiType = YoutubeApiType.youtube,
this.systemTitleBar = false,
this.amoledDarkTheme = false,
this.normalizeAudio = true,
this.streamMusicCodec = MusicCodec.weba,
this.downloadMusicCodec = MusicCodec.m4a,
SpotubeColor? accentColorScheme,
}) : super() {
this.accentColorScheme =
accentColorScheme ?? SpotubeColor(Colors.blue.value, name: "Blue");
if (downloadLocation.isEmpty && !kIsWeb) {
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) {
downloadLocation = value;
},
(value) => this.downloadLocation = value,
);
}
}
void reset() {
setRecommendationMarket(Market.US);
setThemeMode(ThemeMode.system);
setLayoutMode(LayoutMode.adaptive);
setAlbumColorSync(true);
setCheckUpdate(true);
setAudioQuality(AudioQuality.high);
setDownloadLocation("");
setCloseBehavior(CloseBehavior.close);
setShowSystemTrayIcon(true);
setLocale(const Locale("system", "system"));
setPipedInstance("https://pipedapi.kavin.rocks");
setSearchMode(SearchMode.youtube);
setSkipNonMusic(true);
setYoutubeApiType(YoutubeApiType.youtube);
setSystemTitleBar(false);
setAmoledDarkTheme(false);
setNormalizeAudio(true);
setAccentColorScheme(SpotubeColor(Colors.blue.value, name: "Blue"));
setStreamMusicCodec(MusicCodec.weba);
setDownloadMusicCodec(MusicCodec.m4a);
}
void setStreamMusicCodec(MusicCodec codec) {
streamMusicCodec = codec;
notifyListeners();
updatePersistence();
}
void setDownloadMusicCodec(MusicCodec codec) {
downloadMusicCodec = codec;
notifyListeners();
updatePersistence();
}
void setThemeMode(ThemeMode mode) {
themeMode = mode;
notifyListeners();
updatePersistence();
}
void setRecommendationMarket(Market country) {
recommendationMarket = country;
notifyListeners();
updatePersistence();
}
void setAccentColorScheme(SpotubeColor color) {
accentColorScheme = color;
notifyListeners();
updatePersistence();
}
void setAlbumColorSync(bool sync) {
albumColorSync = sync;
if (!sync) {
ref.read(paletteProvider.notifier).state = null;
} else {
ref.read(ProxyPlaylistNotifier.notifier).updatePalette();
}
notifyListeners();
updatePersistence();
}
void setCheckUpdate(bool check) {
checkUpdate = check;
notifyListeners();
updatePersistence();
}
void setAudioQuality(AudioQuality quality) {
audioQuality = quality;
notifyListeners();
updatePersistence();
}
void setDownloadLocation(String downloadDir) {
if (downloadDir.isEmpty) return;
downloadLocation = downloadDir;
notifyListeners();
updatePersistence();
}
void setLayoutMode(LayoutMode mode) {
layoutMode = mode;
notifyListeners();
updatePersistence();
}
void setCloseBehavior(CloseBehavior behavior) {
closeBehavior = behavior;
notifyListeners();
updatePersistence();
}
void setShowSystemTrayIcon(bool show) {
showSystemTrayIcon = show;
notifyListeners();
updatePersistence();
}
void setLocale(Locale locale) {
this.locale = locale;
notifyListeners();
updatePersistence();
}
void setPipedInstance(String instance) {
pipedInstance = instance;
notifyListeners();
updatePersistence();
}
void setSearchMode(SearchMode mode) {
searchMode = mode;
notifyListeners();
updatePersistence();
}
void setSkipNonMusic(bool skip) {
skipNonMusic = skip;
notifyListeners();
updatePersistence();
}
void setYoutubeApiType(YoutubeApiType type) {
youtubeApiType = type;
notifyListeners();
updatePersistence();
}
void setSystemTitleBar(bool isSystemTitleBar) {
systemTitleBar = isSystemTitleBar;
if (DesktopTools.platform.isDesktop) {
DesktopTools.window.setTitleBarStyle(
systemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden,
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,
);
}
notifyListeners();
updatePersistence();
}
void setAmoledDarkTheme(bool isAmoled) {
amoledDarkTheme = isAmoled;
notifyListeners();
updatePersistence();
}
void setNormalizeAudio(bool normalize) {
normalizeAudio = normalize;
audioPlayer.setAudioNormalization(normalize);
notifyListeners();
updatePersistence();
}
Future<String> _getDefaultDownloadDirectory() async {
static Future<String> _getDefaultDownloadDirectory() async {
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
if (kIsMacOS) {
@ -273,102 +155,71 @@ class UserPreferences extends PersistedChangeNotifier {
});
}
@override
FutureOr<void> loadFromLocal(Map<String, dynamic> map) async {
recommendationMarket = Market.values.firstWhere(
(market) =>
market.name == (map["recommendationMarket"] ?? recommendationMarket),
orElse: () => Market.US,
static Future<UserPreferences> fromJson(Map<String, dynamic> 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,
);
checkUpdate = map["checkUpdate"] ?? checkUpdate;
themeMode = ThemeMode.values[map["themeMode"] ?? 0];
accentColorScheme = map["accentColorScheme"] != null
? SpotubeColor.fromString(map["accentColorScheme"])
: accentColorScheme;
albumColorSync = map["albumColorSync"] ?? albumColorSync;
audioQuality = map["audioQuality"] != null
? AudioQuality.values[map["audioQuality"]]
: audioQuality;
if (!kIsWeb) {
downloadLocation =
map["downloadLocation"] ?? await _getDefaultDownloadDirectory();
}
layoutMode = LayoutMode.values.firstWhere(
(mode) => mode.name == map["layoutMode"],
orElse: () => kIsDesktop ? LayoutMode.extended : LayoutMode.compact,
);
closeBehavior = map["closeBehavior"] != null
? CloseBehavior.values[map["closeBehavior"]]
: closeBehavior;
showSystemTrayIcon = map["showSystemTrayIcon"] ?? showSystemTrayIcon;
final localeMap = map["locale"] != null ? jsonDecode(map["locale"]) : null;
locale =
localeMap != null ? Locale(localeMap?["lc"], localeMap?["cc"]) : locale;
pipedInstance = map["pipedInstance"] ?? pipedInstance;
searchMode = SearchMode.values.firstWhere(
(mode) => mode.name == map["searchMode"],
orElse: () => SearchMode.youtube,
);
skipNonMusic = map["skipNonMusic"] ?? skipNonMusic;
youtubeApiType = YoutubeApiType.values.firstWhere(
(type) => type.name == map["youtubeApiType"],
orElse: () => YoutubeApiType.youtube,
);
systemTitleBar = map["systemTitleBar"] ?? systemTitleBar;
// updates the title bar
setSystemTitleBar(systemTitleBar);
amoledDarkTheme = map["amoledDarkTheme"] ?? amoledDarkTheme;
normalizeAudio = map["normalizeAudio"] ?? normalizeAudio;
final normalizeAudio = json["normalizeAudio"] ?? true;
audioPlayer.setAudioNormalization(normalizeAudio);
streamMusicCodec = MusicCodec.values.firstWhere(
(codec) => codec.name == map["streamMusicCodec"],
orElse: () => MusicCodec.weba,
);
downloadMusicCodec = MusicCodec.values.firstWhere(
(codec) => codec.name == map["downloadMusicCodec"],
orElse: () => MusicCodec.m4a,
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"]],
);
}
@override
FutureOr<Map<String, dynamic>> toMap() {
Map<String, dynamic> toJson() {
return {
"recommendationMarket": recommendationMarket.name,
"recommendationMarket": recommendationMarket.index,
"themeMode": themeMode.index,
"accentColorScheme": accentColorScheme.toString(),
"albumColorSync": albumColorSync,
"checkUpdate": checkUpdate,
"audioQuality": audioQuality.index,
"downloadLocation": downloadLocation,
"layoutMode": layoutMode.name,
"layoutMode": layoutMode.index,
"closeBehavior": closeBehavior.index,
"showSystemTrayIcon": showSystemTrayIcon,
"locale":
jsonEncode({"lc": locale.languageCode, "cc": locale.countryCode}),
"pipedInstance": pipedInstance,
"searchMode": searchMode.name,
"searchMode": searchMode.index,
"skipNonMusic": skipNonMusic,
"youtubeApiType": youtubeApiType.name,
"youtubeApiType": youtubeApiType.index,
'systemTitleBar': systemTitleBar,
"amoledDarkTheme": amoledDarkTheme,
"normalizeAudio": normalizeAudio,
"streamMusicCodec": streamMusicCodec.name,
"downloadMusicCodec": downloadMusicCodec.name,
"streamMusicCodec": streamMusicCodec.index,
"downloadMusicCodec": downloadMusicCodec.index,
};
}
@ -389,9 +240,13 @@ class UserPreferences extends PersistedChangeNotifier {
YoutubeApiType? youtubeApiType,
Market? recommendationMarket,
bool? saveTrackLyrics,
bool? amoledDarkTheme,
bool? normalizeAudio,
MusicCodec? downloadMusicCodec,
MusicCodec? streamMusicCodec,
bool? systemTitleBar,
}) {
return UserPreferences(
ref,
themeMode: themeMode ?? this.themeMode,
accentColorScheme: accentColorScheme ?? this.accentColorScheme,
albumColorSync: albumColorSync ?? this.albumColorSync,
@ -407,10 +262,130 @@ class UserPreferences extends PersistedChangeNotifier {
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,
);
}
}
final userPreferencesProvider = ChangeNotifierProvider(
(ref) => UserPreferences(ref),
class UserPreferencesNotifier extends PersistedStateNotifier<UserPreferences> {
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<UserPreferences> fromJson(Map<String, dynamic> json) {
return UserPreferences.fromJson(json);
}
@override
Map<String, dynamic> toJson() {
return state.toJson();
}
}
final userPreferencesProvider =
StateNotifierProvider<UserPreferencesNotifier, UserPreferences>(
(ref) => UserPreferencesNotifier(ref),
);