import "package:flutter/material.dart"; import "package:shared_preferences/shared_preferences.dart"; import "models.dart"; /// App-wide appearance preferences (theme / locale / date format / currency / /// font size), mirroring the web app's prefs.js. Persisted to SharedPreferences /// so the chosen theme survives a restart before /api/me loads, and exposed as a /// ChangeNotifier so MaterialApp and date formatting react to changes. class AppSettings extends ChangeNotifier { static const _kTheme = "cc_theme"; static const _kLocale = "cc_locale"; static const _kDateFormat = "cc_dateFormat"; static const _kTimeFormat = "cc_timeFormat"; static const _kWeekStart = "cc_weekStart"; static const _kCurrency = "cc_currency"; static const _kFontSize = "cc_fontSize"; String theme = "system"; // light | dark | system String locale = "en-US"; // BCP-47 language-REGION String dateFormat = "YMD"; // YMD | DMY_NUM | DMY | MDY /// Which clock times are written on. "auto" is the chosen region's own /// convention, which is what every time in the app read before there was a /// setting; the other two are for the people whose region and habit disagree. String timeFormat = "auto"; // auto | 24 | 12 /// The day a week is drawn as starting on, wherever weekdays are laid out in a /// row — the charging scheduler's day picker today. See format.dart, which /// owns the rule so every such row reads the same. String weekStart = "auto"; // auto | monday | sunday String currency = "USD"; // ISO 4217 code String fontSize = "medium"; // small | medium | large /// Language and region are two controls over the one stored tag, so the pair /// can be mixed freely (English in Poland, say). String get language => locale.split("-").first; String get region => locale.split("-").length > 1 ? locale.split("-")[1] : "US"; Future loadFromStorage() async { final prefs = await SharedPreferences.getInstance(); theme = prefs.getString(_kTheme) ?? theme; locale = prefs.getString(_kLocale) ?? locale; dateFormat = prefs.getString(_kDateFormat) ?? dateFormat; timeFormat = prefs.getString(_kTimeFormat) ?? timeFormat; weekStart = prefs.getString(_kWeekStart) ?? weekStart; currency = prefs.getString(_kCurrency) ?? currency; fontSize = prefs.getString(_kFontSize) ?? fontSize; notifyListeners(); } Future _persist() async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_kTheme, theme); await prefs.setString(_kLocale, locale); await prefs.setString(_kDateFormat, dateFormat); await prefs.setString(_kTimeFormat, timeFormat); await prefs.setString(_kWeekStart, weekStart); await prefs.setString(_kCurrency, currency); await prefs.setString(_kFontSize, fontSize); } /// Sync from a freshly fetched profile (login, boot, settings saves). void applyFromProfile(UserProfile p) { theme = p.theme; locale = p.locale; dateFormat = p.dateFormat; timeFormat = p.timeFormat; weekStart = p.weekStart; currency = p.currency; fontSize = p.fontSize; _persist(); notifyListeners(); } /// Optimistically apply a single changed field (used by Settings so the UI /// reacts instantly while the PATCH is in flight). void patch({ String? theme, String? locale, String? dateFormat, String? timeFormat, String? weekStart, String? currency, String? fontSize, }) { if (theme != null) this.theme = theme; if (locale != null) this.locale = locale; if (dateFormat != null) this.dateFormat = dateFormat; if (timeFormat != null) this.timeFormat = timeFormat; if (weekStart != null) this.weekStart = weekStart; if (currency != null) this.currency = currency; if (fontSize != null) this.fontSize = fontSize; _persist(); notifyListeners(); } ThemeMode get themeMode => switch (theme) { "light" => ThemeMode.light, "dark" => ThemeMode.dark, _ => ThemeMode.system, }; double get textScale => switch (fontSize) { "small" => 0.9375, "large" => 1.125, _ => 1.0, }; }