fix: prefs persistence
This commit is contained in:
@@ -51,8 +51,12 @@ final tunImplementationStore = PrefNotifier.provider(
|
|||||||
final mtuStore = PrefNotifier.provider("mtu", _default.mtu);
|
final mtuStore = PrefNotifier.provider("mtu", _default.mtu);
|
||||||
final connectionTestUrlStore =
|
final connectionTestUrlStore =
|
||||||
PrefNotifier.provider("connection-test-url", _default.connectionTestUrl);
|
PrefNotifier.provider("connection-test-url", _default.connectionTestUrl);
|
||||||
final urlTestIntervalStore =
|
final urlTestIntervalStore = PrefNotifier.provider<Duration, int>(
|
||||||
PrefNotifier.provider("url-test-interval", _default.urlTestInterval);
|
"url-test-interval",
|
||||||
|
_default.urlTestInterval,
|
||||||
|
mapFrom: (value) => Duration(seconds: value),
|
||||||
|
mapTo: (value) => value.inSeconds,
|
||||||
|
);
|
||||||
final enableClashApiStore =
|
final enableClashApiStore =
|
||||||
PrefNotifier.provider("enable-clash-api", _default.enableClashApi);
|
PrefNotifier.provider("enable-clash-api", _default.enableClashApi);
|
||||||
final clashApiPortStore =
|
final clashApiPortStore =
|
||||||
|
|||||||
@@ -26,13 +26,25 @@ enum ProxiesSort {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
final proxiesSortProvider = AlwaysAlivePrefNotifier.provider(
|
@Riverpod(keepAlive: true)
|
||||||
|
class ProxiesSortNotifier extends _$ProxiesSortNotifier {
|
||||||
|
late final _pref = Pref(
|
||||||
|
ref.watch(sharedPreferencesProvider),
|
||||||
"proxies_sort_mode",
|
"proxies_sort_mode",
|
||||||
ProxiesSort.unsorted,
|
ProxiesSort.unsorted,
|
||||||
mapFrom: ProxiesSort.values.byName,
|
mapFrom: ProxiesSort.values.byName,
|
||||||
mapTo: (value) => value.name,
|
mapTo: (value) => value.name,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ProxiesSort build() => _pref.getValue();
|
||||||
|
|
||||||
|
Future<void> update(ProxiesSort value) {
|
||||||
|
state = value;
|
||||||
|
return _pref.update(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
|
class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
|
||||||
@override
|
@override
|
||||||
@@ -42,7 +54,7 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
|
|||||||
if (!serviceRunning) {
|
if (!serviceRunning) {
|
||||||
throw const CoreServiceNotRunning();
|
throw const CoreServiceNotRunning();
|
||||||
}
|
}
|
||||||
final sortBy = ref.watch(proxiesSortProvider);
|
final sortBy = ref.watch(proxiesSortNotifierProvider);
|
||||||
yield* ref
|
yield* ref
|
||||||
.watch(coreFacadeProvider)
|
.watch(coreFacadeProvider)
|
||||||
.watchOutbounds()
|
.watchOutbounds()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ProxiesPage extends HookConsumerWidget with PresLogger {
|
|||||||
|
|
||||||
final asyncProxies = ref.watch(proxiesNotifierProvider);
|
final asyncProxies = ref.watch(proxiesNotifierProvider);
|
||||||
final notifier = ref.watch(proxiesNotifierProvider.notifier);
|
final notifier = ref.watch(proxiesNotifierProvider.notifier);
|
||||||
final sortBy = ref.watch(proxiesSortProvider);
|
final sortBy = ref.watch(proxiesSortNotifierProvider);
|
||||||
|
|
||||||
final selectActiveProxyMutation = useMutation(
|
final selectActiveProxyMutation = useMutation(
|
||||||
initialOnFailure: (error) =>
|
initialOnFailure: (error) =>
|
||||||
@@ -55,7 +55,8 @@ class ProxiesPage extends HookConsumerWidget with PresLogger {
|
|||||||
actions: [
|
actions: [
|
||||||
PopupMenuButton<ProxiesSort>(
|
PopupMenuButton<ProxiesSort>(
|
||||||
initialValue: sortBy,
|
initialValue: sortBy,
|
||||||
onSelected: ref.read(proxiesSortProvider.notifier).update,
|
onSelected:
|
||||||
|
ref.read(proxiesSortNotifierProvider.notifier).update,
|
||||||
icon: const Icon(Icons.sort),
|
icon: const Icon(Icons.sort),
|
||||||
tooltip: t.proxies.sortTooltip,
|
tooltip: t.proxies.sortTooltip,
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:hiddify/utils/custom_loggers.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class Pref<T> with InfraLogger {
|
class Pref<T, P> with InfraLogger {
|
||||||
const Pref(
|
const Pref(
|
||||||
this.prefs,
|
this.prefs,
|
||||||
this.key,
|
this.key,
|
||||||
@@ -15,28 +15,28 @@ class Pref<T> with InfraLogger {
|
|||||||
final SharedPreferences prefs;
|
final SharedPreferences prefs;
|
||||||
final String key;
|
final String key;
|
||||||
final T defaultValue;
|
final T defaultValue;
|
||||||
final T Function(String value)? mapFrom;
|
final T Function(P value)? mapFrom;
|
||||||
final String Function(T value)? mapTo;
|
final P Function(T value)? mapTo;
|
||||||
|
|
||||||
/// Updates the value asynchronously.
|
/// Updates the value asynchronously.
|
||||||
Future<void> update(T value) async {
|
Future<void> update(T value) async {
|
||||||
loggy.debug("updating preference [$key]($T) to [$value]");
|
loggy.debug("updating preference [$key]($T) to [$value]");
|
||||||
try {
|
Object? mapped = value;
|
||||||
if (mapTo != null && mapFrom != null) {
|
if (mapTo != null) {
|
||||||
await prefs.setString(key, mapTo!(value));
|
mapped = 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<String> _:
|
|
||||||
await prefs.setStringList(key, value);
|
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
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<String> _:
|
||||||
|
await prefs.setStringList(key, mapped);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loggy.warning("error updating preference[$key]: $e");
|
loggy.warning("error updating preference[$key]: $e");
|
||||||
@@ -46,11 +46,12 @@ class Pref<T> with InfraLogger {
|
|||||||
T getValue() {
|
T getValue() {
|
||||||
try {
|
try {
|
||||||
loggy.debug("getting persisted preference [$key]($T)");
|
loggy.debug("getting persisted preference [$key]($T)");
|
||||||
if (mapTo != null && mapFrom != null) {
|
if (mapFrom != null) {
|
||||||
final persisted = prefs.getString(key);
|
final persisted = prefs.get(key) as P?;
|
||||||
return persisted != null ? mapFrom!(persisted) : defaultValue;
|
if (persisted == null) return defaultValue;
|
||||||
|
return mapFrom!(persisted);
|
||||||
} else if (T == List<String>) {
|
} else if (T == List<String>) {
|
||||||
return prefs.getStringList(key) as T ?? defaultValue;
|
return prefs.getStringList(key) as T? ?? defaultValue;
|
||||||
}
|
}
|
||||||
return prefs.get(key) as T? ?? defaultValue;
|
return prefs.get(key) as T? ?? defaultValue;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -60,132 +61,42 @@ class Pref<T> with InfraLogger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrefNotifier<T> extends AutoDisposeNotifier<T>
|
class PrefNotifier<T, P> extends AutoDisposeNotifier<T> with InfraLogger {
|
||||||
with _Prefs<T>, InfraLogger {
|
|
||||||
PrefNotifier(
|
PrefNotifier(
|
||||||
this.key,
|
this._key,
|
||||||
this.defaultValue,
|
this._defaultValue,
|
||||||
this.mapFrom,
|
this._mapFrom,
|
||||||
this.mapTo,
|
this._mapTo,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
final String _key;
|
||||||
final String key;
|
final T _defaultValue;
|
||||||
@override
|
final T Function(P value)? _mapFrom;
|
||||||
final T defaultValue;
|
final P Function(T)? _mapTo;
|
||||||
@override
|
|
||||||
final T Function(String)? mapFrom;
|
|
||||||
@override
|
|
||||||
final String Function(T)? mapTo;
|
|
||||||
|
|
||||||
static AutoDisposeNotifierProvider<PrefNotifier<T>, T> provider<T>(
|
late final Pref<T, P> _pref = Pref(
|
||||||
|
ref.watch(sharedPreferencesProvider),
|
||||||
|
_key,
|
||||||
|
_defaultValue,
|
||||||
|
mapFrom: _mapFrom,
|
||||||
|
mapTo: _mapTo,
|
||||||
|
);
|
||||||
|
|
||||||
|
static AutoDisposeNotifierProvider<PrefNotifier<T, P>, T> provider<T, P>(
|
||||||
String key,
|
String key,
|
||||||
T defaultValue, {
|
T defaultValue, {
|
||||||
T Function(String value)? mapFrom,
|
T Function(P value)? mapFrom,
|
||||||
String Function(T value)? mapTo,
|
P Function(T value)? mapTo,
|
||||||
}) =>
|
}) =>
|
||||||
AutoDisposeNotifierProvider(
|
AutoDisposeNotifierProvider(
|
||||||
() => PrefNotifier(key, defaultValue, mapFrom, mapTo),
|
() => PrefNotifier(key, defaultValue, mapFrom, mapTo),
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
|
||||||
SharedPreferences get prefs => ref.read(sharedPreferencesProvider);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> update(T value) async {
|
Future<void> update(T value) async {
|
||||||
super.update(value);
|
_pref.update(value);
|
||||||
super.state = value;
|
super.state = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
T build() => getValue();
|
T build() => _pref.getValue();
|
||||||
}
|
|
||||||
|
|
||||||
class AlwaysAlivePrefNotifier<T> extends Notifier<T>
|
|
||||||
with _Prefs<T>, 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<AlwaysAlivePrefNotifier<T>, T> provider<T>(
|
|
||||||
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<void> update(T value) async {
|
|
||||||
super.update(value);
|
|
||||||
super.state = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
T build() => getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin _Prefs<T> 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<void> 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<String> _:
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user