2026-01-17 13:09:20 +03:00
|
|
|
import 'package:umbrix/core/preferences/preferences_provider.dart';
|
|
|
|
|
import 'package:umbrix/utils/custom_loggers.dart';
|
2024-03-02 22:53:14 +03:30
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
|
|
|
|
|
|
class PreferencesEntry<T, P> with InfraLogger {
|
|
|
|
|
PreferencesEntry({
|
|
|
|
|
required this.preferences,
|
|
|
|
|
required this.key,
|
|
|
|
|
required this.defaultValue,
|
|
|
|
|
this.mapFrom,
|
|
|
|
|
this.mapTo,
|
|
|
|
|
this.validator,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final SharedPreferences preferences;
|
|
|
|
|
final String key;
|
|
|
|
|
final T defaultValue;
|
|
|
|
|
final T Function(P value)? mapFrom;
|
|
|
|
|
final P Function(T value)? mapTo;
|
|
|
|
|
final bool Function(T value)? validator;
|
|
|
|
|
|
|
|
|
|
T read() {
|
|
|
|
|
try {
|
|
|
|
|
loggy.debug("getting persisted preference [$key]($T)");
|
|
|
|
|
final T value;
|
|
|
|
|
if (mapFrom != null) {
|
|
|
|
|
final persisted = preferences.get(key) as P?;
|
|
|
|
|
if (persisted == null) {
|
|
|
|
|
value = defaultValue;
|
|
|
|
|
} else {
|
|
|
|
|
value = mapFrom!(persisted);
|
|
|
|
|
}
|
|
|
|
|
} else if (T == List<String>) {
|
|
|
|
|
value = preferences.getStringList(key) as T? ?? defaultValue;
|
|
|
|
|
} else {
|
|
|
|
|
value = preferences.get(key) as T? ?? defaultValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (validator?.call(value) ?? true) return value;
|
|
|
|
|
return defaultValue;
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
loggy.warning("error getting preference[$key]: $e", e, stackTrace);
|
|
|
|
|
return defaultValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<bool> write(T value) async {
|
|
|
|
|
Object? mapped = value;
|
|
|
|
|
if (mapTo != null) {
|
|
|
|
|
mapped = mapTo!(value);
|
|
|
|
|
}
|
|
|
|
|
loggy.debug("updating preference [$key]($T) to [$mapped]");
|
|
|
|
|
try {
|
|
|
|
|
if (!(validator?.call(value) ?? true)) {
|
|
|
|
|
loggy.warning("invalid value [$value] for preference [$key]($T)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return switch (mapped) {
|
|
|
|
|
final String value => await preferences.setString(key, value),
|
|
|
|
|
final bool value => await preferences.setBool(key, value),
|
|
|
|
|
final int value => await preferences.setInt(key, value),
|
|
|
|
|
final double value => await preferences.setDouble(key, value),
|
|
|
|
|
final List<String> value => await preferences.setStringList(key, value),
|
|
|
|
|
_ => throw const FormatException("Invalid Type"),
|
|
|
|
|
};
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
loggy.warning("error updating preference[$key]: $e", e, stackTrace);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-04 15:58:56 +03:30
|
|
|
Future<T?> writeRaw(P input) async {
|
|
|
|
|
final T value;
|
|
|
|
|
if (mapFrom != null) {
|
|
|
|
|
value = mapFrom!(input);
|
|
|
|
|
} else {
|
|
|
|
|
value = input as T;
|
|
|
|
|
}
|
|
|
|
|
if (await write(value)) return value;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 22:53:14 +03:30
|
|
|
Future<void> remove() async {
|
|
|
|
|
try {
|
|
|
|
|
await preferences.remove(key);
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
loggy.warning("error removing preference[$key]: $e", e, stackTrace);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class PreferencesNotifier<T, P> extends StateNotifier<T> {
|
|
|
|
|
PreferencesNotifier._({
|
|
|
|
|
required Ref ref,
|
|
|
|
|
required this.entry,
|
|
|
|
|
this.overrideValue,
|
2024-08-20 01:53:26 -04:00
|
|
|
this.possibleValues,
|
2024-03-02 22:53:14 +03:30
|
|
|
}) : _ref = ref,
|
|
|
|
|
super(overrideValue ?? entry.read());
|
|
|
|
|
|
|
|
|
|
final Ref _ref;
|
|
|
|
|
final PreferencesEntry<T, P> entry;
|
|
|
|
|
final T? overrideValue;
|
2024-08-20 01:53:26 -04:00
|
|
|
final List<T>? possibleValues;
|
2024-03-02 22:53:14 +03:30
|
|
|
|
2024-08-20 01:53:26 -04:00
|
|
|
static StateNotifierProvider<PreferencesNotifier<T, P>, T> create<T, P>(String key, T defaultValue,
|
2026-01-15 12:28:40 +03:00
|
|
|
{T Function(Ref ref)? defaultValueFunction, T Function(P value)? mapFrom, P Function(T value)? mapTo, bool Function(T value)? validator, T? overrideValue, List<T>? possibleValues,}) =>
|
2024-03-02 22:53:14 +03:30
|
|
|
StateNotifierProvider(
|
|
|
|
|
(ref) => PreferencesNotifier._(
|
2024-08-20 01:53:26 -04:00
|
|
|
ref: ref,
|
|
|
|
|
entry: PreferencesEntry<T, P>(
|
|
|
|
|
preferences: ref.read(sharedPreferencesProvider).requireValue,
|
|
|
|
|
key: key,
|
|
|
|
|
defaultValue: defaultValueFunction?.call(ref) ?? defaultValue,
|
|
|
|
|
mapFrom: mapFrom,
|
|
|
|
|
mapTo: mapTo,
|
|
|
|
|
validator: validator,
|
|
|
|
|
),
|
|
|
|
|
overrideValue: overrideValue,
|
2026-01-15 12:28:40 +03:00
|
|
|
possibleValues: possibleValues,),
|
2024-03-02 22:53:14 +03:30
|
|
|
);
|
|
|
|
|
|
2024-07-27 23:51:54 +02:00
|
|
|
static AutoDisposeStateNotifierProvider<PreferencesNotifier<T, P>, T> createAutoDispose<T, P>(
|
2024-03-02 22:53:14 +03:30
|
|
|
String key,
|
|
|
|
|
T defaultValue, {
|
|
|
|
|
T Function(P value)? mapFrom,
|
|
|
|
|
P Function(T value)? mapTo,
|
|
|
|
|
bool Function(T value)? validator,
|
|
|
|
|
T? overrideValue,
|
|
|
|
|
}) =>
|
2024-07-27 23:51:54 +02:00
|
|
|
StateNotifierProvider.autoDispose(
|
|
|
|
|
(ref) => PreferencesNotifier._(
|
|
|
|
|
ref: ref,
|
|
|
|
|
entry: PreferencesEntry<T, P>(
|
|
|
|
|
preferences: ref.read(sharedPreferencesProvider).requireValue,
|
|
|
|
|
key: key,
|
|
|
|
|
defaultValue: defaultValue,
|
|
|
|
|
mapFrom: mapFrom,
|
|
|
|
|
mapTo: mapTo,
|
|
|
|
|
validator: validator,
|
|
|
|
|
),
|
|
|
|
|
overrideValue: overrideValue,
|
|
|
|
|
),
|
|
|
|
|
);
|
2024-03-02 22:53:14 +03:30
|
|
|
|
|
|
|
|
P raw() {
|
|
|
|
|
final value = overrideValue ?? state;
|
|
|
|
|
if (entry.mapTo != null) return entry.mapTo!(value);
|
|
|
|
|
return value as P;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-04 15:58:56 +03:30
|
|
|
Future<void> updateRaw(P input) async {
|
|
|
|
|
final value = await entry.writeRaw(input);
|
|
|
|
|
if (value != null) state = value;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 22:53:14 +03:30
|
|
|
Future<void> update(T value) async {
|
|
|
|
|
if (await entry.write(value)) state = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> reset() async {
|
|
|
|
|
await entry.remove();
|
|
|
|
|
_ref.invalidateSelf();
|
|
|
|
|
}
|
|
|
|
|
}
|