import 'package:flutter/foundation.dart'; import 'package:scheduler_core/scheduler_core.dart'; typedef OperationContextFactory = ApplicationOperationContext Function(String operationId); typedef ReadRefresh = Future Function(); sealed class SchedulerCommandState { const SchedulerCommandState(); } class SchedulerCommandIdle extends SchedulerCommandState { const SchedulerCommandIdle(); } class SchedulerCommandRunning extends SchedulerCommandState { const SchedulerCommandRunning(this.label); final String label; } class SchedulerCommandSuccess extends SchedulerCommandState { const SchedulerCommandSuccess(this.message); final String message; } class SchedulerCommandFailure extends SchedulerCommandState { const SchedulerCommandFailure({this.failure, this.error}); final ApplicationFailure? failure; final Object? error; String get codeLabel { final typed = failure; if (typed != null) { return typed.detailCode ?? typed.code.name; } return 'unexpected'; } } class SchedulerCommandController extends ChangeNotifier { SchedulerCommandController({ required this.commands, required this.contextFor, required this.localDate, required this.refreshReads, }); final V1ApplicationCommandUseCases commands; final OperationContextFactory contextFor; final CivilDate localDate; final ReadRefresh refreshReads; var _sequence = 0; SchedulerCommandState _state = const SchedulerCommandIdle(); SchedulerCommandState get state => _state; Future quickCaptureToBacklog(String title) async { await _run( label: 'Capturing task', successMessage: 'Task captured', refreshAfterSuccess: true, action: (operationId) { return commands.quickCaptureToBacklog( context: contextFor(operationId), title: title, projectId: 'home', ); }, ); } Future scheduleBacklogItem({ required String taskId, required int durationMinutes, required DateTime expectedUpdatedAt, }) async { await _run( label: 'Scheduling task', successMessage: 'Task scheduled', refreshAfterSuccess: true, action: (operationId) { return commands.scheduleBacklogItemToNextAvailableSlot( context: contextFor(operationId), localDate: localDate, taskId: taskId, durationMinutes: durationMinutes, expectedUpdatedAt: expectedUpdatedAt, ); }, ); } Future completeFlexibleTask({ required String taskId, DateTime? expectedUpdatedAt, }) async { await _run( label: 'Completing task', successMessage: 'Task completed', refreshAfterSuccess: true, action: (operationId) { return commands.completeFlexibleTask( context: contextFor(operationId), localDate: localDate, taskId: taskId, expectedUpdatedAt: expectedUpdatedAt, ); }, ); } Future _run({ required String label, required String successMessage, required bool refreshAfterSuccess, required Future> Function( String operationId, ) action, }) async { final operationId = _nextOperationId(); _setState(SchedulerCommandRunning(label)); try { final result = await action(operationId); final failure = result.failure; if (failure != null) { _setState(SchedulerCommandFailure(failure: failure)); return; } if (refreshAfterSuccess) { await refreshReads(); } _setState(SchedulerCommandSuccess(successMessage)); } on Object catch (error) { _setState(SchedulerCommandFailure(error: error)); } } String _nextOperationId() { _sequence += 1; return 'ui-command-$_sequence'; } void _setState(SchedulerCommandState next) { _state = next; notifyListeners(); } }