/// App preferences beyond theme (Look ahead, Overview favourites, Home widgets). library; import 'package:flutter/foundation.dart'; import '../models/home_filter_widget.dart'; import '../models/overview_favourite.dart'; import '../services/storage_service.dart'; class PreferencesController extends ChangeNotifier { PreferencesController(this._storage) { _lookAheadMaxYears = _storage.lookAheadMaxYears; _overviewFavourites = _storage.loadOverviewFavourites(); _homeFilterWidgets = _storage.loadHomeFilterWidgets(); } final StorageService _storage; late int _lookAheadMaxYears; late List _overviewFavourites; late List _homeFilterWidgets; /// Fired after favourites are persisted (for cloud push debounce). VoidCallback? onFavouritesPersisted; /// Fired after Home widgets are persisted (for cloud push debounce). VoidCallback? onHomeWidgetsPersisted; /// Years ahead Look ahead can navigate (1–30, default 5). int get lookAheadMaxYears => _lookAheadMaxYears; List get overviewFavourites => List.unmodifiable(_overviewFavourites); List get displayedOverviewFavourites => [ for (final fav in _overviewFavourites) if (fav.display) fav, ]; bool get hasDisplayedOverviewFavourites => _overviewFavourites.any((f) => f.display); List get homeFilterWidgets => List.unmodifiable(_homeFilterWidgets); List get displayedHomeFilterWidgets => [ for (final w in _homeFilterWidgets) if (w.display) w, ]; bool get hasDisplayedHomeFilterWidgets => _homeFilterWidgets.any((w) => w.display); Future setLookAheadMaxYears(int years) async { final next = years.clamp(1, 30); if (next == _lookAheadMaxYears) return; _lookAheadMaxYears = next; notifyListeners(); await _storage.setLookAheadMaxYears(next); } Future addOverviewFavourite(OverviewFavourite favourite) async { _overviewFavourites = [..._overviewFavourites, favourite]; notifyListeners(); await _persistFavourites(); } Future updateOverviewFavourite(OverviewFavourite favourite) async { final index = _overviewFavourites.indexWhere((f) => f.id == favourite.id); if (index < 0) return; final next = [..._overviewFavourites]; next[index] = favourite; _overviewFavourites = next; notifyListeners(); await _persistFavourites(); } Future removeOverviewFavourite(String id) async { final next = [ for (final fav in _overviewFavourites) if (fav.id != id) fav, ]; if (next.length == _overviewFavourites.length) return; _overviewFavourites = next; notifyListeners(); await _persistFavourites(); } Future setOverviewFavouriteDisplay(String id, bool display) async { final index = _overviewFavourites.indexWhere((f) => f.id == id); if (index < 0) return; final current = _overviewFavourites[index]; if (current.display == display) return; await updateOverviewFavourite(current.copyWith(display: display)); } Future replaceOverviewFavourites( List favourites, { bool notify = true, bool schedulePush = false, }) async { _overviewFavourites = List.of(favourites); if (notify) notifyListeners(); await _storage.saveOverviewFavourites(_overviewFavourites); if (schedulePush) onFavouritesPersisted?.call(); } Future addHomeFilterWidget(HomeFilterWidget widget) async { final order = _homeFilterWidgets.isEmpty ? 0 : _homeFilterWidgets .map((w) => w.sortOrder) .reduce((a, b) => a > b ? a : b) + 1; _homeFilterWidgets = [ ..._homeFilterWidgets, widget.copyWith(sortOrder: widget.sortOrder == 0 ? order : widget.sortOrder), ]; notifyListeners(); await _persistHomeWidgets(); } Future updateHomeFilterWidget(HomeFilterWidget widget) async { final index = _homeFilterWidgets.indexWhere((w) => w.id == widget.id); if (index < 0) return; final next = [..._homeFilterWidgets]; next[index] = widget; next.sort((a, b) => a.sortOrder.compareTo(b.sortOrder)); _homeFilterWidgets = next; notifyListeners(); await _persistHomeWidgets(); } Future removeHomeFilterWidget(String id) async { final next = [ for (final w in _homeFilterWidgets) if (w.id != id) w, ]; if (next.length == _homeFilterWidgets.length) return; _homeFilterWidgets = next; notifyListeners(); await _persistHomeWidgets(); } Future setHomeFilterWidgetDisplay(String id, bool display) async { final index = _homeFilterWidgets.indexWhere((w) => w.id == id); if (index < 0) return; final current = _homeFilterWidgets[index]; if (current.display == display) return; await updateHomeFilterWidget(current.copyWith(display: display)); } Future replaceHomeFilterWidgets( List widgets, { bool notify = true, bool schedulePush = false, }) async { final next = List.of(widgets) ..sort((a, b) => a.sortOrder.compareTo(b.sortOrder)); _homeFilterWidgets = next; if (notify) notifyListeners(); await _storage.saveHomeFilterWidgets(_homeFilterWidgets); if (schedulePush) onHomeWidgetsPersisted?.call(); } Future _persistFavourites() async { await _storage.saveOverviewFavourites(_overviewFavourites); onFavouritesPersisted?.call(); } Future _persistHomeWidgets() async { await _storage.saveHomeFilterWidgets(_homeFilterWidgets); onHomeWidgetsPersisted?.call(); } }