130 lines
4.1 KiB
Dart
130 lines
4.1 KiB
Dart
// 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<T> = Future<ApplicationResult<T>> Function();
|
|
|
|
/// Determines whether a successful read should be rendered as empty state.
|
|
typedef EmptyPredicate<T> = bool Function(T value);
|
|
|
|
/// Shared interface for read controllers consumed by UI widgets.
|
|
abstract class UiReadController<T> extends ChangeNotifier {
|
|
/// Current read state.
|
|
SchedulerReadState<T> get state;
|
|
|
|
/// Loads the backing value.
|
|
Future<void> load();
|
|
|
|
/// Retries the latest read.
|
|
Future<void> retry();
|
|
}
|
|
|
|
/// Base state for scheduler read operations.
|
|
sealed class SchedulerReadState<T> {
|
|
/// Creates a scheduler read state.
|
|
const SchedulerReadState();
|
|
}
|
|
|
|
/// Indicates that a scheduler read is in progress.
|
|
class SchedulerReadLoading<T> extends SchedulerReadState<T> {
|
|
/// Creates a loading read state.
|
|
const SchedulerReadLoading();
|
|
}
|
|
|
|
/// Indicates that a scheduler read returned renderable data.
|
|
class SchedulerReadData<T> extends SchedulerReadState<T> {
|
|
/// 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<T> extends SchedulerReadState<T> {
|
|
/// 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<T> extends SchedulerReadState<T> {
|
|
/// 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<T> extends UiReadController<T> {
|
|
/// 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<T> read;
|
|
|
|
/// Predicate used to classify a successful value as empty or populated.
|
|
final EmptyPredicate<T> 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<T> _state = SchedulerReadLoading<T>();
|
|
|
|
/// Current read state.
|
|
@override
|
|
SchedulerReadState<T> get state => _state;
|
|
|
|
/// Loads the current value and updates [state].
|
|
@override
|
|
Future<void> load() async {
|
|
_setState(SchedulerReadLoading<T>());
|
|
try {
|
|
final result = await read();
|
|
final failure = result.failure;
|
|
if (failure != null) {
|
|
_setState(SchedulerReadFailure<T>(failure: failure));
|
|
return;
|
|
}
|
|
final value = result.requireValue;
|
|
_setState(
|
|
isEmpty(value)
|
|
? SchedulerReadEmpty<T>(value)
|
|
: SchedulerReadData<T>(value),
|
|
);
|
|
} on Object catch (error) {
|
|
_setState(SchedulerReadFailure<T>(error: error));
|
|
}
|
|
}
|
|
|
|
/// Retries by running [load] again.
|
|
@override
|
|
Future<void> 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<T> next) {
|
|
_state = next;
|
|
notifyListeners();
|
|
}
|
|
}
|