feat(studio): show what a running update is doing, not just that it runs
Applying an update downloads and swaps a binary and takes minutes; the doctor showed a spinner inside a disabled button for the whole time. SystemActions gained a streaming CLI runner that hands each line to the caller as it arrives, with CHAIN_PLAIN=1 set for the child so the CLI emits one line per transition instead of its redraw-in-place block. The update card now shows an indeterminate bar labelled with the running step and an elapsed counter on its own timer, so the counter keeps moving between lines that can be minutes apart. No invented percentage: the CLI reports steps, not a measurable total. The streaming path still defers to debugRunFaiOverride, so tests stay hermetic and never spawn a process; three guards pin that. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
1549334767
commit
14f9217007
3 changed files with 160 additions and 5 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
|
|
@ -1357,15 +1359,48 @@ class _UpdateBannerState extends State<_UpdateBanner> {
|
|||
bool _applying = false;
|
||||
String? _applyOutput;
|
||||
|
||||
/// Last line the CLI printed, shown as the bar's label.
|
||||
String? _applyLine;
|
||||
|
||||
/// When the apply started, for the elapsed counter.
|
||||
DateTime? _applySince;
|
||||
|
||||
/// Redraws the elapsed counter between CLI lines, which can be
|
||||
/// minutes apart during a download.
|
||||
Timer? _applyClock;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_applyClock?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _applyUpdate() async {
|
||||
setState(() {
|
||||
_applying = true;
|
||||
_applyOutput = null;
|
||||
_applyLine = null;
|
||||
_applySince = DateTime.now();
|
||||
});
|
||||
final r = await SystemActions.chainUpdateApply(widget.status.channel);
|
||||
_applyClock?.cancel();
|
||||
_applyClock = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||
if (mounted && _applying) setState(() {});
|
||||
});
|
||||
final r = await SystemActions.chainUpdateApply(
|
||||
widget.status.channel,
|
||||
// Each line the CLI prints becomes the label under the bar, so
|
||||
// the operator sees which step is running rather than a spinner
|
||||
// that could mean anything.
|
||||
onLine: (line) {
|
||||
if (mounted) setState(() => _applyLine = line);
|
||||
},
|
||||
);
|
||||
_applyClock?.cancel();
|
||||
_applyClock = null;
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_applying = false;
|
||||
_applyLine = null;
|
||||
_applyOutput = r.ok
|
||||
? AppLocalizations.of(context)!.doctorApplyDone
|
||||
: (r.stderr.isEmpty ? r.stdout : r.stderr).trim();
|
||||
|
|
@ -1473,6 +1508,21 @@ class _UpdateBannerState extends State<_UpdateBanner> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (_applying) ...[
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
// No percentage to be had: the CLI reports steps, not a
|
||||
// measurable total. So the bar runs indeterminate and
|
||||
// carries the running step plus an elapsed counter, which
|
||||
// is what separates "slow" from "stuck".
|
||||
ChainProgressBar(
|
||||
stage: _applyLine ?? l.doctorApplying,
|
||||
detail: _applySince == null
|
||||
? null
|
||||
: l.installElapsed(
|
||||
DateTime.now().difference(_applySince!).inSeconds,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (_applyOutput != null) ...[
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
ChainErrorBox(text: _applyOutput!, maxHeight: 240),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue