Files
PeachDeployments/lib/Peach/about/internal/application/StepTracker.dart
alvocool 38881d1fe1 Add app source and tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:15:30 +03:00

105 lines
2.4 KiB
Dart

import 'dart:async';
enum StepState { pending, active, done, failed, skipped }
class DeployStep {
final String key;
final String label;
StepState state;
double? progress; // 0..1 within this step, when meaningful
DeployStep(
this.key,
this.label, {
this.state = StepState.pending,
this.progress,
});
}
/// Tracks an ordered list of steps for a build or deploy operation and streams
/// updates so the UI can render a live stepper with per-step progress.
class StepTracker {
final _controller = StreamController<List<DeployStep>>.broadcast();
Stream<List<DeployStep>> get stream => _controller.stream;
final List<DeployStep> steps = [];
void reset(List<(String, String)> defs) {
steps
..clear()
..addAll(defs.map((d) => DeployStep(d.$1, d.$2)));
_emit();
}
DeployStep? _find(String key) {
for (final s in steps) {
if (s.key == key) return s;
}
return null;
}
void active(String key) {
final s = _find(key);
if (s == null) return;
// mark earlier active/pending steps done as we move forward
for (final step in steps) {
if (step == s) break;
if (step.state == StepState.active) step.state = StepState.done;
}
s.state = StepState.active;
s.progress = null;
_emit();
}
void progress(String key, double? p) {
final s = _find(key);
if (s == null) return;
s.progress = p;
_emit();
}
void done(String key) => _set(key, StepState.done);
void fail(String key) => _set(key, StepState.failed);
void skip(String key) => _set(key, StepState.skipped);
void _set(String key, StepState state) {
final s = _find(key);
if (s == null) return;
s.state = state;
s.progress = null;
_emit();
}
/// Marks the currently-active step done (used before moving on).
void completeActive() {
for (final s in steps) {
if (s.state == StepState.active) s.state = StepState.done;
}
_emit();
}
/// Any still-pending/active steps become failed (on an error).
void failRemaining() {
for (final s in steps) {
if (s.state == StepState.active) {
s.state = StepState.failed;
} else if (s.state == StepState.pending) {
s.state = StepState.skipped;
}
}
_emit();
}
void clear() {
steps.clear();
_emit();
}
void _emit() {
if (!_controller.isClosed) {
_controller.add(List.unmodifiable(steps));
}
}
void dispose() => _controller.close();
}