import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'settings_provider.g.dart'; enum ImportMode { copy('Copy to internal storage'), inplace('In-place indexing'), mixed('Mixed (both copy and in-place)'); const ImportMode(this.displayName); final String displayName; } enum DefaultPlayerScreen { cover('Cover'), lyrics('Lyrics'), queue('Queue'); const DefaultPlayerScreen(this.displayName); final String displayName; } enum LyricsMode { curved('Curved'), flat('Flat'), auto('Auto'); const LyricsMode(this.displayName); final String displayName; } class SettingsState { final ImportMode importMode; final bool autoScan; final bool watchForChanges; final DefaultPlayerScreen defaultPlayerScreen; final LyricsMode lyricsMode; final Set supportedFormats; const SettingsState({ this.importMode = ImportMode.mixed, this.autoScan = true, this.watchForChanges = true, this.defaultPlayerScreen = DefaultPlayerScreen.cover, this.lyricsMode = LyricsMode.auto, this.supportedFormats = const { '.mp3', '.flac', '.wav', '.m4a', '.aac', '.ogg', '.wma', '.opus', }, }); SettingsState copyWith({ ImportMode? importMode, bool? autoScan, bool? watchForChanges, DefaultPlayerScreen? defaultPlayerScreen, LyricsMode? lyricsMode, Set? supportedFormats, }) { return SettingsState( importMode: importMode ?? this.importMode, autoScan: autoScan ?? this.autoScan, watchForChanges: watchForChanges ?? this.watchForChanges, defaultPlayerScreen: defaultPlayerScreen ?? this.defaultPlayerScreen, lyricsMode: lyricsMode ?? this.lyricsMode, supportedFormats: supportedFormats ?? this.supportedFormats, ); } } @riverpod class SettingsNotifier extends _$SettingsNotifier { static const String _importModeKey = 'import_mode'; static const String _autoScanKey = 'auto_scan'; static const String _watchForChangesKey = 'watch_for_changes'; static const String _defaultPlayerScreenKey = 'default_player_screen'; static const String _lyricsModeKey = 'lyrics_mode'; @override Future build() async { final prefs = await SharedPreferences.getInstance(); final importModeIndex = prefs.getInt(_importModeKey) ?? 0; final importMode = ImportMode.values[importModeIndex]; final autoScan = prefs.getBool(_autoScanKey) ?? true; final watchForChanges = prefs.getBool(_watchForChangesKey) ?? true; final defaultPlayerScreenIndex = prefs.getInt(_defaultPlayerScreenKey) ?? 0; final defaultPlayerScreen = DefaultPlayerScreen.values[defaultPlayerScreenIndex]; final lyricsModeIndex = prefs.getInt(_lyricsModeKey) ?? 2; // Auto is default final lyricsMode = LyricsMode.values[lyricsModeIndex]; return SettingsState( importMode: importMode, autoScan: autoScan, watchForChanges: watchForChanges, defaultPlayerScreen: defaultPlayerScreen, lyricsMode: lyricsMode, ); } Future setImportMode(ImportMode mode) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_importModeKey, mode.index); if (state.hasValue) { state = AsyncValue.data(state.value!.copyWith(importMode: mode)); } } Future setAutoScan(bool enabled) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_autoScanKey, enabled); if (state.hasValue) { state = AsyncValue.data(state.value!.copyWith(autoScan: enabled)); } } Future setWatchForChanges(bool enabled) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_watchForChangesKey, enabled); if (state.hasValue) { state = AsyncValue.data(state.value!.copyWith(watchForChanges: enabled)); } } Future setDefaultPlayerScreen(DefaultPlayerScreen screen) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_defaultPlayerScreenKey, screen.index); if (state.hasValue) { state = AsyncValue.data( state.value!.copyWith(defaultPlayerScreen: screen), ); } } Future setLyricsMode(LyricsMode mode) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_lyricsModeKey, mode.index); if (state.hasValue) { state = AsyncValue.data(state.value!.copyWith(lyricsMode: mode)); } } } // Convenience providers for specific settings @riverpod class ImportModeNotifier extends _$ImportModeNotifier { @override ImportMode build() { return ref .watch(settingsProvider) .when( data: (settings) => settings.importMode, loading: () => ImportMode.mixed, error: (_, _) => ImportMode.mixed, ); } Future update(ImportMode mode) async { await ref.read(settingsProvider.notifier).setImportMode(mode); } } @riverpod class AutoScanNotifier extends _$AutoScanNotifier { @override bool build() { return ref .watch(settingsProvider) .when( data: (settings) => settings.autoScan, loading: () => true, error: (_, _) => true, ); } Future update(bool enabled) async { await ref.read(settingsProvider.notifier).setAutoScan(enabled); } } @riverpod class WatchForChangesNotifier extends _$WatchForChangesNotifier { @override bool build() { return ref .watch(settingsProvider) .when( data: (settings) => settings.watchForChanges, loading: () => true, error: (_, _) => true, ); } Future update(bool enabled) async { await ref.read(settingsProvider.notifier).setWatchForChanges(enabled); } } @riverpod class DefaultPlayerScreenNotifier extends _$DefaultPlayerScreenNotifier { @override DefaultPlayerScreen build() { return ref .watch(settingsProvider) .when( data: (settings) => settings.defaultPlayerScreen, loading: () => DefaultPlayerScreen.cover, error: (_, _) => DefaultPlayerScreen.cover, ); } Future update(DefaultPlayerScreen screen) async { await ref.read(settingsProvider.notifier).setDefaultPlayerScreen(screen); } } @riverpod class LyricsModeNotifier extends _$LyricsModeNotifier { @override LyricsMode build() { return ref .watch(settingsProvider) .when( data: (settings) => settings.lyricsMode, loading: () => LyricsMode.auto, error: (_, _) => LyricsMode.auto, ); } Future update(LyricsMode mode) async { await ref.read(settingsProvider.notifier).setLyricsMode(mode); } }