diff --git a/lib/data/repository/config_options_store.dart b/lib/data/repository/config_options_store.dart index 53a27e79..5cc6e763 100644 --- a/lib/data/repository/config_options_store.dart +++ b/lib/data/repository/config_options_store.dart @@ -51,8 +51,12 @@ final tunImplementationStore = PrefNotifier.provider( final mtuStore = PrefNotifier.provider("mtu", _default.mtu); final connectionTestUrlStore = PrefNotifier.provider("connection-test-url", _default.connectionTestUrl); -final urlTestIntervalStore = - PrefNotifier.provider("url-test-interval", _default.urlTestInterval); +final urlTestIntervalStore = PrefNotifier.provider( + "url-test-interval", + _default.urlTestInterval, + mapFrom: (value) => Duration(seconds: value), + mapTo: (value) => value.inSeconds, +); final enableClashApiStore = PrefNotifier.provider("enable-clash-api", _default.enableClashApi); final clashApiPortStore = diff --git a/lib/features/proxies/notifier/proxies_notifier.dart b/lib/features/proxies/notifier/proxies_notifier.dart index f7a49c66..53d3a5c0 100644 --- a/lib/features/proxies/notifier/proxies_notifier.dart +++ b/lib/features/proxies/notifier/proxies_notifier.dart @@ -26,12 +26,24 @@ enum ProxiesSort { }; } -final proxiesSortProvider = AlwaysAlivePrefNotifier.provider( - "proxies_sort_mode", - ProxiesSort.unsorted, - mapFrom: ProxiesSort.values.byName, - mapTo: (value) => value.name, -); +@Riverpod(keepAlive: true) +class ProxiesSortNotifier extends _$ProxiesSortNotifier { + late final _pref = Pref( + ref.watch(sharedPreferencesProvider), + "proxies_sort_mode", + ProxiesSort.unsorted, + mapFrom: ProxiesSort.values.byName, + mapTo: (value) => value.name, + ); + + @override + ProxiesSort build() => _pref.getValue(); + + Future update(ProxiesSort value) { + state = value; + return _pref.update(value); + } +} @riverpod class ProxiesNotifier extends _$ProxiesNotifier with AppLogger { @@ -42,7 +54,7 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger { if (!serviceRunning) { throw const CoreServiceNotRunning(); } - final sortBy = ref.watch(proxiesSortProvider); + final sortBy = ref.watch(proxiesSortNotifierProvider); yield* ref .watch(coreFacadeProvider) .watchOutbounds() diff --git a/lib/features/proxies/view/proxies_page.dart b/lib/features/proxies/view/proxies_page.dart index 1651652e..92144cb1 100644 --- a/lib/features/proxies/view/proxies_page.dart +++ b/lib/features/proxies/view/proxies_page.dart @@ -16,7 +16,7 @@ class ProxiesPage extends HookConsumerWidget with PresLogger { final asyncProxies = ref.watch(proxiesNotifierProvider); final notifier = ref.watch(proxiesNotifierProvider.notifier); - final sortBy = ref.watch(proxiesSortProvider); + final sortBy = ref.watch(proxiesSortNotifierProvider); final selectActiveProxyMutation = useMutation( initialOnFailure: (error) => @@ -55,7 +55,8 @@ class ProxiesPage extends HookConsumerWidget with PresLogger { actions: [ PopupMenuButton( initialValue: sortBy, - onSelected: ref.read(proxiesSortProvider.notifier).update, + onSelected: + ref.read(proxiesSortNotifierProvider.notifier).update, icon: const Icon(Icons.sort), tooltip: t.proxies.sortTooltip, itemBuilder: (context) { diff --git a/lib/utils/pref_notifier.dart b/lib/utils/pref_notifier.dart index d3cc6be4..90e6dfd3 100644 --- a/lib/utils/pref_notifier.dart +++ b/lib/utils/pref_notifier.dart @@ -3,7 +3,7 @@ import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class Pref with InfraLogger { +class Pref with InfraLogger { const Pref( this.prefs, this.key, @@ -15,28 +15,28 @@ class Pref with InfraLogger { final SharedPreferences prefs; final String key; final T defaultValue; - final T Function(String value)? mapFrom; - final String Function(T value)? mapTo; + final T Function(P value)? mapFrom; + final P Function(T value)? mapTo; /// Updates the value asynchronously. Future update(T value) async { loggy.debug("updating preference [$key]($T) to [$value]"); + Object? mapped = value; + if (mapTo != null) { + mapped = mapTo!(value); + } try { - if (mapTo != null && mapFrom != null) { - await prefs.setString(key, mapTo!(value)); - } else { - switch (value) { - case String _: - await prefs.setString(key, value); - case bool _: - await prefs.setBool(key, value); - case int _: - await prefs.setInt(key, value); - case double _: - await prefs.setDouble(key, value); - case List _: - await prefs.setStringList(key, value); - } + switch (mapped) { + case String _: + await prefs.setString(key, mapped); + case bool _: + await prefs.setBool(key, mapped); + case int _: + await prefs.setInt(key, mapped); + case double _: + await prefs.setDouble(key, mapped); + case List _: + await prefs.setStringList(key, mapped); } } catch (e) { loggy.warning("error updating preference[$key]: $e"); @@ -46,11 +46,12 @@ class Pref with InfraLogger { T getValue() { try { loggy.debug("getting persisted preference [$key]($T)"); - if (mapTo != null && mapFrom != null) { - final persisted = prefs.getString(key); - return persisted != null ? mapFrom!(persisted) : defaultValue; + if (mapFrom != null) { + final persisted = prefs.get(key) as P?; + if (persisted == null) return defaultValue; + return mapFrom!(persisted); } else if (T == List) { - return prefs.getStringList(key) as T ?? defaultValue; + return prefs.getStringList(key) as T? ?? defaultValue; } return prefs.get(key) as T? ?? defaultValue; } catch (e) { @@ -60,132 +61,42 @@ class Pref with InfraLogger { } } -class PrefNotifier extends AutoDisposeNotifier - with _Prefs, InfraLogger { +class PrefNotifier extends AutoDisposeNotifier with InfraLogger { PrefNotifier( - this.key, - this.defaultValue, - this.mapFrom, - this.mapTo, + this._key, + this._defaultValue, + this._mapFrom, + this._mapTo, ); - @override - final String key; - @override - final T defaultValue; - @override - final T Function(String)? mapFrom; - @override - final String Function(T)? mapTo; + final String _key; + final T _defaultValue; + final T Function(P value)? _mapFrom; + final P Function(T)? _mapTo; - static AutoDisposeNotifierProvider, T> provider( + late final Pref _pref = Pref( + ref.watch(sharedPreferencesProvider), + _key, + _defaultValue, + mapFrom: _mapFrom, + mapTo: _mapTo, + ); + + static AutoDisposeNotifierProvider, T> provider( String key, T defaultValue, { - T Function(String value)? mapFrom, - String Function(T value)? mapTo, + T Function(P value)? mapFrom, + P Function(T value)? mapTo, }) => AutoDisposeNotifierProvider( () => PrefNotifier(key, defaultValue, mapFrom, mapTo), ); - @override - SharedPreferences get prefs => ref.read(sharedPreferencesProvider); - - @override Future update(T value) async { - super.update(value); + _pref.update(value); super.state = value; } @override - T build() => getValue(); -} - -class AlwaysAlivePrefNotifier extends Notifier - with _Prefs, InfraLogger { - AlwaysAlivePrefNotifier( - this.key, - this.defaultValue, - this.mapFrom, - this.mapTo, - ); - - @override - final String key; - @override - final T defaultValue; - @override - final T Function(String)? mapFrom; - @override - final String Function(T)? mapTo; - - static NotifierProvider, T> provider( - String key, - T defaultValue, { - T Function(String value)? mapFrom, - String Function(T value)? mapTo, - }) => - NotifierProvider( - () => AlwaysAlivePrefNotifier(key, defaultValue, mapFrom, mapTo), - ); - - @override - SharedPreferences get prefs => ref.read(sharedPreferencesProvider); - - @override - Future update(T value) async { - super.update(value); - super.state = value; - } - - @override - T build() => getValue(); -} - -mixin _Prefs implements LoggerMixin { - String get key; - T get defaultValue; - T Function(String)? get mapFrom; - String Function(T)? get mapTo; - - SharedPreferences get prefs; - - /// Updates the value asynchronously. - Future update(T value) async { - loggy.debug("updating preference [$key] to [$value]"); - try { - if (mapTo != null && mapFrom != null) { - await prefs.setString(key, mapTo!(value)); - } else { - switch (value) { - case String _: - await prefs.setString(key, value); - case bool _: - await prefs.setBool(key, value); - case int _: - await prefs.setInt(key, value); - case double _: - await prefs.setDouble(key, value); - case List _: - await prefs.setStringList(key, value); - } - } - } catch (e) { - loggy.warning("error updating preference[$key]: $e"); - } - } - - T getValue() { - try { - loggy.debug("getting persisted preference [$key]"); - if (mapTo != null && mapFrom != null) { - final persisted = prefs.getString(key); - return persisted != null ? mapFrom!(persisted) : defaultValue; - } - return prefs.get(key) as T? ?? defaultValue; - } catch (e) { - loggy.warning("error getting preference[$key]: $e"); - return defaultValue; - } - } + T build() => _pref.getValue(); }