chore(wip): lot #165-169 validé QA + rework foreground #163 (KO - preuve device/toolchain insuffisante)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 17:59:43 +02:00
parent 917777e18b
commit bc533d6c45
32 changed files with 898 additions and 123 deletions

View File

@ -77,6 +77,7 @@ final class _WatchSessionScreenState extends State<WatchSessionScreen> {
onTogglePause: widget.viewModel.sendPrimaryAction,
onIncrementScore: widget.viewModel.incrementScore,
onDecrementScore: widget.viewModel.decrementScore,
onCompleteStep: widget.viewModel.completeCurrentStep,
),
),
_RoundScaffold(
@ -399,6 +400,7 @@ final class _SessionMainView extends StatelessWidget {
required this.onTogglePause,
required this.onIncrementScore,
required this.onDecrementScore,
required this.onCompleteStep,
});
final WatchSessionUiState state;
@ -407,6 +409,7 @@ final class _SessionMainView extends StatelessWidget {
final VoidCallback onTogglePause;
final VoidCallback onIncrementScore;
final VoidCallback onDecrementScore;
final VoidCallback onCompleteStep;
@override
Widget build(BuildContext context) {
@ -456,6 +459,7 @@ final class _SessionMainView extends StatelessWidget {
onTogglePause: canToggleTimer ? onTogglePause : null,
onIncrementScore: onIncrementScore,
onDecrementScore: onDecrementScore,
onCompleteStep: onCompleteStep,
),
),
),
@ -703,12 +707,14 @@ final class _ActiveContent extends StatelessWidget {
required this.onTogglePause,
required this.onIncrementScore,
required this.onDecrementScore,
required this.onCompleteStep,
});
final WatchSessionUiState state;
final VoidCallback? onTogglePause;
final VoidCallback onIncrementScore;
final VoidCallback onDecrementScore;
final VoidCallback onCompleteStep;
@override
Widget build(BuildContext context) {
@ -726,10 +732,15 @@ final class _ActiveContent extends StatelessWidget {
projection,
primaryTimer: timer,
);
final repsTarget = _repsStepTarget(projection);
final dominantValue = timer == null
? _seriesValue(projection)
: _timerText(timer);
final dominantLabel = timer == null ? 'SÉRIE' : timer.label;
final controlsEnabled =
!state.connectionLost &&
projection.phoneReachable &&
!state.commandPending;
return _ScaledContent(
child: Column(
mainAxisSize: MainAxisSize.min,
@ -737,9 +748,15 @@ final class _ActiveContent extends StatelessWidget {
_ExerciseName(projection.exerciseName),
_StepNameBand(projection.stepName),
const SizedBox(height: 2),
_SmallLabel(dominantLabel),
_SmallLabel(repsTarget == null ? dominantLabel : 'Répétitions'),
const SizedBox(height: 2),
if (timer == null)
if (repsTarget != null)
_DominantRepsLine(
value: repsTarget.toString(),
pending: state.commandPending,
onComplete: controlsEnabled ? onCompleteStep : null,
)
else if (timer == null)
_DominantValue(dominantValue)
else
_DominantTimerLine(
@ -770,6 +787,62 @@ final class _ActiveContent extends StatelessWidget {
}
}
final class _DominantRepsLine extends StatelessWidget {
const _DominantRepsLine({
required this.value,
required this.pending,
required this.onComplete,
});
final String value;
final bool pending;
final VoidCallback? onComplete;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 52,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(child: _DominantValue(value)),
const SizedBox(width: 6),
_CompleteStepButton(pending: pending, onPressed: onComplete),
],
),
);
}
}
final class _CompleteStepButton extends StatelessWidget {
const _CompleteStepButton({required this.pending, required this.onPressed});
final bool pending;
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return SizedBox.square(
dimension: 48,
child: IconButton(
onPressed: onPressed,
tooltip: 'Valider létape',
visualDensity: VisualDensity.compact,
iconSize: 24,
color: const Color(0xFFC9A24A),
disabledColor: const Color(0xFF414754),
style: IconButton.styleFrom(
backgroundColor: const Color(0xFF141824),
side: const BorderSide(color: Color(0xFFD72638)),
),
icon: pending
? const _PendingDot(key: ValueKey('complete-step-pending-dot'))
: const Icon(Icons.check),
),
);
}
}
final class _DominantTimerLine extends StatelessWidget {
const _DominantTimerLine({
required this.value,
@ -901,7 +974,7 @@ final class _ManualScoreContent extends StatelessWidget {
SizedBox(
height: 6,
child: state.scoreWaitingForPhone
? const _PendingDot()
? const _PendingDot(key: ValueKey('score-pending-dot'))
: const SizedBox.shrink(),
),
_MainHeartRateLine(sample: state.sensorSample),
@ -1465,6 +1538,14 @@ String _seriesValue(WatchSessionProjection projection) {
return '${projection.seriesIndex}/${projection.seriesTotal}';
}
int? _repsStepTarget(WatchSessionProjection projection) {
final target = projection.stepTargetValue;
if (projection.stepType != WatchStepType.reps || target == null) {
return null;
}
return target > 0 ? target : null;
}
String _scoreText(double value) {
if (value == value.roundToDouble()) {
return value.toInt().toString();