focus-flow/apps/focus_flow_flutter/lib/controllers/today_screen_controller.dart

106 lines
3.1 KiB
Dart

/// Coordinates Today Screen Controller state for the FocusFlow Flutter UI.
library;
import 'package:flutter/foundation.dart';
import 'package:scheduler_core/scheduler_core.dart';
import '../models/today_screen_models.dart';
/// Reads Today state from the scheduler application layer.
typedef TodayStateReader = Future<ApplicationResult<TodayState>> Function();
/// Base state for the compact Today screen.
sealed class TodayScreenState {
/// Creates a Today screen state.
const TodayScreenState();
}
/// Indicates that the Today screen is loading.
class TodayScreenLoading extends TodayScreenState {
/// Creates a loading Today screen state.
const TodayScreenLoading();
}
/// Indicates that the Today screen has renderable timeline data.
class TodayScreenReady extends TodayScreenState {
/// Creates a ready state with mapped screen [data].
const TodayScreenReady(this.data);
/// Presentation model for the Today screen.
final TodayScreenData data;
}
/// Indicates that the Today screen loaded successfully with no cards.
class TodayScreenEmpty extends TodayScreenState {
/// Creates an empty Today screen state.
const TodayScreenEmpty();
}
/// Indicates that the Today screen failed to load.
class TodayScreenFailure extends TodayScreenState {
/// Creates a failure state with a displayable [message].
const TodayScreenFailure(this.message);
/// User-facing failure message or code.
final String message;
}
/// Loads Today screen data and tracks selected timeline cards.
class TodayScreenController extends ChangeNotifier {
/// Creates a Today screen controller from a read callback.
TodayScreenController({required this.read});
/// Callback used to read Today state.
final TodayStateReader read;
TodayScreenState _state = const TodayScreenLoading();
TimelineCardModel? _selectedCard;
/// Current Today screen loading/data/failure state.
TodayScreenState get state => _state;
/// Currently selected timeline card, if a modal should be visible.
TimelineCardModel? get selectedCard => _selectedCard;
/// Loads Today state and maps it into presentation data.
Future<void> load() async {
_state = const TodayScreenLoading();
notifyListeners();
try {
final result = await read();
final failure = result.failure;
if (failure != null) {
_state = TodayScreenFailure(failure.detailCode ?? failure.code.name);
notifyListeners();
return;
}
final data = TodayScreenData.fromTodayState(result.requireValue);
_state = data.cards.isEmpty
? const TodayScreenEmpty()
: TodayScreenReady(data);
notifyListeners();
} on Object catch (error) {
_state = TodayScreenFailure(error.toString());
notifyListeners();
}
}
/// Selects [card] when the card allows selection.
void selectCard(TimelineCardModel card) {
if (!card.isSelectable) {
return;
}
_selectedCard = card;
notifyListeners();
}
/// Clears the selected timeline card.
void clearSelection() {
if (_selectedCard == null) {
return;
}
_selectedCard = null;
notifyListeners();
}
}