import 'package:hiddify/data/data_providers.dart'; 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 { const Pref( this.prefs, this.key, this.defaultValue, { this.mapFrom, this.mapTo, }); final SharedPreferences prefs; final String key; final T defaultValue; final T Function(String value)? mapFrom; final String Function(T value)? mapTo; /// 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; } } } class PrefNotifier extends AutoDisposeNotifier with _Prefs, InfraLogger { PrefNotifier( 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 AutoDisposeNotifierProvider, T> provider( String key, T defaultValue, { T Function(String value)? mapFrom, String 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); 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; } } }