// SPDX-FileCopyrightText: 2026 FocusFlow contributors // SPDX-License-Identifier: AGPL-3.0-only /// Coordinates Scheduler Read Controller state for the FocusFlow Flutter UI. library; import 'package:flutter/foundation.dart'; import 'package:scheduler_core/scheduler_core.dart'; /// Reads an application value for a UI surface. typedef ApplicationReader = Future> Function(); /// Determines whether a successful read should be rendered as empty state. typedef EmptyPredicate = bool Function(T value); /// Shared interface for read controllers consumed by UI widgets. abstract class UiReadController extends ChangeNotifier { /// Current read state. SchedulerReadState get state; /// Loads the backing value. Future load(); /// Retries the latest read. Future retry(); } /// Base state for scheduler read operations. sealed class SchedulerReadState { /// Creates a scheduler read state. const SchedulerReadState(); } /// Indicates that a scheduler read is in progress. class SchedulerReadLoading extends SchedulerReadState { /// Creates a loading read state. const SchedulerReadLoading(); } /// Indicates that a scheduler read returned renderable data. class SchedulerReadData extends SchedulerReadState { /// Creates a data state from [value]. const SchedulerReadData(this.value); /// Value returned by the read operation. final T value; } /// Indicates that a scheduler read succeeded but has no primary content. class SchedulerReadEmpty extends SchedulerReadState { /// Creates an empty state that still carries the read [value]. const SchedulerReadEmpty(this.value); /// Value returned by the read operation. final T value; } /// Indicates that a scheduler read failed. class SchedulerReadFailure extends SchedulerReadState { /// Creates a failure state from an application [failure] or unexpected [error]. const SchedulerReadFailure({this.failure, this.error}); /// Typed application failure returned by the scheduler core. final ApplicationFailure? failure; /// Unexpected error thrown while running the read. final Object? error; /// Stable label for display and diagnostics. String get codeLabel { final typed = failure; if (typed != null) { return typed.detailCode ?? typed.code.name; } return 'unexpected'; } } /// Default read controller backed by an [ApplicationReader]. class ApplicationReadController extends UiReadController { /// Creates a controller from a read callback and empty-state predicate. ApplicationReadController({required this.read, required this.isEmpty}); /// Callback used to load the application value. final ApplicationReader read; /// Predicate used to classify a successful value as empty or populated. final EmptyPredicate isEmpty; /// Private state stored as `_state` for this implementation. /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior. SchedulerReadState _state = SchedulerReadLoading(); /// Current read state. @override SchedulerReadState get state => _state; /// Loads the current value and updates [state]. @override Future load() async { _setState(SchedulerReadLoading()); try { final result = await read(); final failure = result.failure; if (failure != null) { _setState(SchedulerReadFailure(failure: failure)); return; } final value = result.requireValue; _setState( isEmpty(value) ? SchedulerReadEmpty(value) : SchedulerReadData(value), ); } on Object catch (error) { _setState(SchedulerReadFailure(error: error)); } } /// Retries by running [load] again. @override Future retry() => load(); /// Runs the `_setState` helper used inside this library. /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read. void _setState(SchedulerReadState next) { _state = next; notifyListeners(); } }