import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logging/logging.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../core/state.dart'; import '../oath/menu_actions.dart'; import 'models.dart'; final _log = Logger('app.state'); // Default implementation is always focused, override with platform specific version. final windowStateProvider = Provider( (ref) => WindowState(focused: true, visible: true, active: true), ); final themeModeProvider = StateNotifierProvider( (ref) => ThemeModeNotifier(ref.watch(prefProvider))); class ThemeModeNotifier extends StateNotifier { static const String _key = 'APP_STATE_THEME'; final SharedPreferences _prefs; ThemeModeNotifier(this._prefs) : super(_fromName(_prefs.getString(_key))); void setThemeMode(ThemeMode mode) { _log.config('Set theme to $mode'); state = mode; _prefs.setString(_key, mode.name); } static ThemeMode _fromName(String? name) { switch (name) { case 'light': return ThemeMode.light; case 'dark': return ThemeMode.dark; default: return ThemeMode.system; } } } final searchProvider = StateNotifierProvider((ref) => SearchNotifier()); class SearchNotifier extends StateNotifier { SearchNotifier() : super(''); setFilter(String value) { state = value; } } // Override with platform implementation final attachedDevicesProvider = Provider>( (ref) => [], ); // Override with platform implementation final currentDeviceDataProvider = Provider( (ref) => null, ); final currentDeviceProvider = StateNotifierProvider((ref) { final provider = CurrentDeviceNotifier(ref.watch(prefProvider)); ref.listen(attachedDevicesProvider, provider._updateAttachedDevices); return provider; }); class CurrentDeviceNotifier extends StateNotifier { static const String _lastDevice = 'APP_STATE_LAST_DEVICE'; final SharedPreferences _prefs; CurrentDeviceNotifier(this._prefs) : super(null); _updateAttachedDevices(List? previous, List devices) { if (!devices.contains(state)) { final lastDevice = _prefs.getString(_lastDevice) ?? ''; try { state = devices.firstWhere( (dev) => dev.when( usbYubiKey: (path, name, pid, info) => lastDevice == 'serial:${info.serial}', nfcReader: (path, name) => lastDevice == 'name:$name', ), orElse: () => devices.whereType().first); } on StateError { state = null; } } } setCurrentDevice(DeviceNode device) { state = device; device.when( usbYubiKey: (path, name, pid, info) { final serial = info.serial; if (serial != null) { _prefs.setString(_lastDevice, 'serial:$serial'); } }, nfcReader: (path, name) { _prefs.setString(_lastDevice, 'name:$name'); }, ); } } final subPageProvider = StateNotifierProvider( (ref) => SubPageNotifier(SubPage.oath)); class SubPageNotifier extends StateNotifier { SubPageNotifier(SubPage state) : super(state); void setSubPage(SubPage page) { state = page; } } final menuActionsProvider = Provider.autoDispose>((ref) { switch (ref.watch(subPageProvider)) { case SubPage.oath: return buildOathMenuActions(ref); // TODO: Handle other cases. default: return []; } }); abstract class QrScanner { Future scanQr(); } final qrScannerProvider = Provider( (ref) => null, );