fix(execution): série qu'on ne pouvait pas terminer (ticket #29)
Corrige le blocage empêchant de terminer une série sur workout_execution_screen.dart. flutter analyze propre, 42/42 tests verts, build APK debug validé. Ticket laissé en QA : validation manuelle sur APK par l'utilisateur avant clôture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -354,48 +354,60 @@ final class _WorkoutExecutionScreenState extends State<WorkoutExecutionScreen> {
|
||||
}
|
||||
|
||||
Future<void> _finishSet({required bool skipped}) async {
|
||||
final score = double.tryParse(_scoreController.text.trim());
|
||||
final actualTimeMs = DateTime.now()
|
||||
.toUtc()
|
||||
.difference(_seriesStartedAt)
|
||||
.inMilliseconds;
|
||||
await widget.activeUseCases.recordCurrentSetResult(
|
||||
sessionId: _session.metadata.id,
|
||||
programSnapshotId: _plan.programAt(_position).id,
|
||||
exerciseSnapshotId: _exercise.id,
|
||||
programIndex: _position.programIndex,
|
||||
exerciseIndex: _position.exerciseIndex,
|
||||
setIndex: _position.setIndex,
|
||||
actualReps: skipped ? null : (_exercise.repsEnabled ? _reps : null),
|
||||
actualScore: skipped ? null : (_exercise.scoreEnabled ? score : null),
|
||||
scoreUnitSnapshot: _exercise.scoreUnit,
|
||||
actualTimeMs: skipped
|
||||
? null
|
||||
: (_exercise.timeEnabled ? actualTimeMs : null),
|
||||
);
|
||||
|
||||
final next = _plan.nextPosition(_position);
|
||||
if (next == null) {
|
||||
await _complete();
|
||||
return;
|
||||
}
|
||||
final shouldRest = _plan.shouldShowRestAfter(_position);
|
||||
if (shouldRest) {
|
||||
final rest = await widget.activeUseCases.startRestAfterSet(
|
||||
try {
|
||||
final score = double.tryParse(_scoreController.text.trim());
|
||||
final actualTimeMs = DateTime.now()
|
||||
.toUtc()
|
||||
.difference(_seriesStartedAt)
|
||||
.inMilliseconds;
|
||||
await widget.activeUseCases.recordCurrentSetResult(
|
||||
sessionId: _session.metadata.id,
|
||||
afterProgramIndex: _position.programIndex,
|
||||
afterExerciseIndex: _position.exerciseIndex,
|
||||
afterSetIndex: _position.setIndex,
|
||||
plannedRestSeconds: _exercise.restSeconds,
|
||||
programSnapshotId: _plan.programAt(_position).id,
|
||||
exerciseSnapshotId: _exercise.id,
|
||||
programIndex: _position.programIndex,
|
||||
exerciseIndex: _position.exerciseIndex,
|
||||
setIndex: _position.setIndex,
|
||||
actualReps: skipped ? null : (_exercise.repsEnabled ? _reps : null),
|
||||
actualScore: skipped
|
||||
? null
|
||||
: (_exercise.manualScoreEnabled ? score : null),
|
||||
scoreInputModeSnapshot: _exercise.scoreInputMode,
|
||||
scoreLabelSnapshot: _exercise.scoreLabel,
|
||||
scoreUnitSnapshot: _exercise.scoreUnit,
|
||||
actualTimeMs: skipped
|
||||
? null
|
||||
: (_exercise.timeEnabled ? actualTimeMs : null),
|
||||
);
|
||||
|
||||
final next = _plan.nextPosition(_position);
|
||||
if (next == null) {
|
||||
await _complete();
|
||||
return;
|
||||
}
|
||||
final shouldRest = _plan.shouldShowRestAfter(_position);
|
||||
if (shouldRest) {
|
||||
final rest = await widget.activeUseCases.startRestAfterSet(
|
||||
sessionId: _session.metadata.id,
|
||||
afterProgramIndex: _position.programIndex,
|
||||
afterExerciseIndex: _position.exerciseIndex,
|
||||
afterSetIndex: _position.setIndex,
|
||||
plannedRestSeconds: _exercise.restSeconds,
|
||||
);
|
||||
if (!mounted) return;
|
||||
_activeRestStateId = rest.metadata.id;
|
||||
_remainingRestSeconds = _exercise.restSeconds;
|
||||
_startRestTicker(next);
|
||||
setState(() => _mode = WorkoutExecutionMode.rest);
|
||||
return;
|
||||
}
|
||||
await _moveTo(next);
|
||||
} on Exception catch (error) {
|
||||
if (!mounted) return;
|
||||
_activeRestStateId = rest.metadata.id;
|
||||
_remainingRestSeconds = _exercise.restSeconds;
|
||||
_startRestTicker(next);
|
||||
setState(() => _mode = WorkoutExecutionMode.rest);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Impossible de terminer la série : $error')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
await _moveTo(next);
|
||||
}
|
||||
|
||||
void _startRestTicker(ExecutionPosition next) {
|
||||
@ -1344,6 +1356,8 @@ final class ExecutionExercise {
|
||||
required this.repsEnabled,
|
||||
required this.scoreEnabled,
|
||||
required this.restSeconds,
|
||||
this.scoreInputMode = ScoreInputMode.manual,
|
||||
this.scoreLabel,
|
||||
this.targetTimeSeconds,
|
||||
this.targetReps,
|
||||
this.targetScore,
|
||||
@ -1364,6 +1378,10 @@ final class ExecutionExercise {
|
||||
repsEnabled: exercise['repsEnabled'] as bool,
|
||||
scoreEnabled: exercise['scoreEnabled'] as bool,
|
||||
restSeconds: exercise['restSecondsOverride'] as int? ?? 0,
|
||||
scoreInputMode: _scoreInputModeFromSnapshot(
|
||||
exercise['scoreInputModeSnapshot'],
|
||||
),
|
||||
scoreLabel: exercise['scoreLabelSnapshot'] as String?,
|
||||
targetTimeSeconds:
|
||||
(override?['targetTimeSecondsOverride'] as int?) ??
|
||||
(exercise['targetTimeSeconds'] as int?),
|
||||
@ -1384,10 +1402,23 @@ final class ExecutionExercise {
|
||||
final bool repsEnabled;
|
||||
final bool scoreEnabled;
|
||||
final int restSeconds;
|
||||
final ScoreInputMode scoreInputMode;
|
||||
final String? scoreLabel;
|
||||
final int? targetTimeSeconds;
|
||||
final int? targetReps;
|
||||
final double? targetScore;
|
||||
final String? scoreUnit;
|
||||
|
||||
bool get manualScoreEnabled {
|
||||
return scoreEnabled && scoreInputMode == ScoreInputMode.manual;
|
||||
}
|
||||
}
|
||||
|
||||
ScoreInputMode _scoreInputModeFromSnapshot(Object? value) {
|
||||
return switch (value) {
|
||||
'stopwatch' => ScoreInputMode.stopwatch,
|
||||
_ => ScoreInputMode.manual,
|
||||
};
|
||||
}
|
||||
|
||||
final class ExecutionPosition {
|
||||
|
||||
Reference in New Issue
Block a user