71 lines
2.4 KiB
Dart
71 lines
2.4 KiB
Dart
import "package:flutter/material.dart";
|
|
import "package:shared_preferences/shared_preferences.dart";
|
|
|
|
import "models.dart";
|
|
|
|
/// App-wide appearance preferences (theme / locale / date format / 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 _kFontSize = "cc_fontSize";
|
|
|
|
String theme = "system"; // light | dark | system
|
|
String locale = "en-US";
|
|
String dateFormat = "YMD"; // YMD | DMY_NUM | DMY | MDY
|
|
String fontSize = "medium"; // small | medium | large
|
|
|
|
Future<void> loadFromStorage() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
theme = prefs.getString(_kTheme) ?? theme;
|
|
locale = prefs.getString(_kLocale) ?? locale;
|
|
dateFormat = prefs.getString(_kDateFormat) ?? dateFormat;
|
|
fontSize = prefs.getString(_kFontSize) ?? fontSize;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> _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(_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;
|
|
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? fontSize}) {
|
|
if (theme != null) this.theme = theme;
|
|
if (locale != null) this.locale = locale;
|
|
if (dateFormat != null) this.dateFormat = dateFormat;
|
|
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,
|
|
};
|
|
}
|