Add app source and tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
104
lib/Peach/about/internal/application/StepTracker.dart
Normal file
104
lib/Peach/about/internal/application/StepTracker.dart
Normal file
@@ -0,0 +1,104 @@
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user