fix: prefs persistence

This commit is contained in:
problematicconsumer
2023-09-17 14:24:25 +03:30
parent 493be47c8f
commit e6eab038ce
4 changed files with 73 additions and 145 deletions

View File

@@ -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<T> with InfraLogger {
class Pref<T, P> with InfraLogger {
const Pref(
this.prefs,
this.key,
@@ -15,28 +15,28 @@ class Pref<T> 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<void> 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<String> _:
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<String> _:
await prefs.setStringList(key, mapped);
}
} catch (e) {
loggy.warning("error updating preference[$key]: $e");
@@ -46,11 +46,12 @@ class Pref<T> 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<String>) {
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<T> with InfraLogger {
}
}
class PrefNotifier<T> extends AutoDisposeNotifier<T>
with _Prefs<T>, InfraLogger {
class PrefNotifier<T, P> extends AutoDisposeNotifier<T> 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<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,
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<void> update(T value) async {
super.update(value);
_pref.update(value);
super.state = value;
}
@override
T build() => 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;
}
}
T build() => _pref.getValue();
}