import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'api_client_provider.dart'; const _kServerUrl = 'server_url'; const _kThemeMode = 'theme_mode'; const _kForgejoRepoUrl = 'forgejo_repo_url'; const _kHasEverLoggedIn = 'has_ever_logged_in'; final sharedPreferencesProvider = Provider((ref) { throw UnimplementedError('Override in ProviderScope'); }); final cookiesPathProvider = Provider((ref) { throw UnimplementedError('Override in ProviderScope'); }); final themeModeProvider = NotifierProvider(ThemeModeNotifier.new); class ThemeModeNotifier extends Notifier { @override ThemeMode build() { final prefs = ref.watch(sharedPreferencesProvider); return _fromString(prefs.getString(_kThemeMode)); } static ThemeMode _fromString(String? value) => switch (value) { 'light' => ThemeMode.light, 'dark' => ThemeMode.dark, _ => ThemeMode.system, }; Future setMode(ThemeMode mode) async { final value = switch (mode) { ThemeMode.light => 'light', ThemeMode.dark => 'dark', _ => 'system', }; await ref.read(sharedPreferencesProvider).setString(_kThemeMode, value); state = mode; } } final forgejoRepoUrlProvider = NotifierProvider( ForgejoRepoUrlNotifier.new); class ForgejoRepoUrlNotifier extends Notifier { @override String? build() { final prefs = ref.watch(sharedPreferencesProvider); return prefs.getString(_kForgejoRepoUrl) ?? 'https://git.fabledsword.com/bvandeusen/FabledApp'; } Future setUrl(String url) async { final clean = url.endsWith('/') ? url.substring(0, url.length - 1) : url; await ref.read(sharedPreferencesProvider).setString(_kForgejoRepoUrl, clean); state = clean; } } final serverUrlProvider = NotifierProvider(ServerUrlNotifier.new); class ServerUrlNotifier extends Notifier { @override String? build() { final prefs = ref.watch(sharedPreferencesProvider); return prefs.getString(_kServerUrl); } Future setUrl(String url) async { // Strip trailing slash final clean = url.endsWith('/') ? url.substring(0, url.length - 1) : url; await ref.read(sharedPreferencesProvider).setString(_kServerUrl, clean); state = clean; } Future clear() async { await ref.read(sharedPreferencesProvider).remove(_kServerUrl); state = null; } } /// Tracks whether this install has ever completed a successful login. /// Used so offline users who've logged in before land on the briefing (with a /// banner) instead of being dumped onto the login screen as if freshly installed. final hasEverLoggedInProvider = NotifierProvider(HasEverLoggedInNotifier.new); class HasEverLoggedInNotifier extends Notifier { @override bool build() { final prefs = ref.watch(sharedPreferencesProvider); return prefs.getBool(_kHasEverLoggedIn) ?? false; } Future markLoggedIn() async { if (state) return; await ref.read(sharedPreferencesProvider).setBool(_kHasEverLoggedIn, true); state = true; } Future clear() async { await ref.read(sharedPreferencesProvider).remove(_kHasEverLoggedIn); state = false; } } final serverSettingsProvider = AsyncNotifierProvider>( ServerSettingsNotifier.new); class ServerSettingsNotifier extends AsyncNotifier> { @override Future> build() async { try { return await ref.read(settingsApiProvider).getAll(); } catch (_) { return {}; } } bool get rssEnabled { final data = state.value ?? {}; return data['rss_enabled']?.toString().toLowerCase() == 'true'; } Future refresh() async { state = AsyncData(await ref.read(settingsApiProvider).getAll()); } } final rssEnabledProvider = Provider((ref) { final settings = ref.watch(serverSettingsProvider).value ?? {}; return settings['rss_enabled']?.toString().toLowerCase() == 'true'; });