feat(exercice): exécution persistante des étapes et éditeur d'étapes (tickets #58, #59)

Ajoute la persistance de l'exécution par étapes avec résultats par
passage (ports, use cases, entités, modèle Drift avec migration) et
l'éditeur d'exercice avec séquence d'étapes sur
exercise_library_screen.dart. Développés dans le même worktree
partagé par DevBackend et DevFrontend ; commit combiné plutôt que
séparé car les fakes de repository de test corrigés (nouvelles
interfaces #58) touchent plusieurs écrans sans lien avec l'éditeur
lui-même (historique, accueil, exécution, séance-modèle). Corrige au
passage un ListTile sans ancêtre Material dans le nouvel éditeur
d'étapes (Material(type: MaterialType.transparency)). flutter pub get
OK, build_runner OK, analyze propre (mêmes infos préexistantes),
93/93 tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 19:52:43 +02:00
parent 4731a257a1
commit ebba22db56
14 changed files with 11371 additions and 2 deletions

View File

@ -1085,6 +1085,8 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
final results = <ActiveSetResult>[];
final restStates = <ActiveRestState>[];
final scoreStopwatchStates = <ActiveScoreStopwatchState>[];
final stepProgressStates = <ActiveExerciseStepProgressState>[];
final stepResults = <ActiveExerciseStepResult>[];
@override
Future<ActiveWorkoutSession?> findById(String id) async {
@ -1123,6 +1125,25 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
return null;
}
@override
Future<ActiveExerciseStepProgressState?> findExerciseStepProgressState({
required String sessionId,
required int programIndex,
required int exerciseIndex,
required int setIndex,
}) async {
for (final state in stepProgressStates) {
if (state.activeWorkoutSessionId == sessionId &&
state.programIndex == programIndex &&
state.exerciseIndex == exerciseIndex &&
state.setIndex == setIndex &&
state.metadata.deletedAt == null) {
return state;
}
}
return null;
}
@override
Future<List<ActiveRestState>> listRestStates(String sessionId) async {
return restStates
@ -1144,6 +1165,24 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
return results;
}
@override
Future<List<ActiveExerciseStepProgressState>> listExerciseStepProgressStates(
String sessionId,
) async {
return stepProgressStates
.where((state) => state.activeWorkoutSessionId == sessionId)
.toList();
}
@override
Future<List<ActiveExerciseStepResult>> listExerciseStepResults(
String sessionId,
) async {
return stepResults
.where((result) => result.activeWorkoutSessionId == sessionId)
.toList();
}
@override
Future<void> save(ActiveWorkoutSession session) async {
this.session = session;
@ -1173,6 +1212,44 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
}
}
@override
Future<void> saveExerciseStepProgressState(
ActiveExerciseStepProgressState state,
) async {
final index = stepProgressStates.indexWhere(
(saved) =>
saved.activeWorkoutSessionId == state.activeWorkoutSessionId &&
saved.programIndex == state.programIndex &&
saved.exerciseIndex == state.exerciseIndex &&
saved.setIndex == state.setIndex,
);
if (index == -1) {
stepProgressStates.add(state);
} else {
stepProgressStates[index] = state;
}
}
@override
Future<void> saveExerciseStepResult(
ActiveExerciseStepResult result,
) async {
final index = stepResults.indexWhere(
(saved) =>
saved.activeWorkoutSessionId == result.activeWorkoutSessionId &&
saved.programIndex == result.programIndex &&
saved.exerciseIndex == result.exerciseIndex &&
saved.setIndex == result.setIndex &&
saved.passageIndex == result.passageIndex &&
saved.stepIndex == result.stepIndex,
);
if (index == -1) {
stepResults.add(result);
} else {
stepResults[index] = result;
}
}
@override
Future<void> deleteScoreStopwatchState({
required String sessionId,
@ -1246,6 +1323,9 @@ final class _FakeWorkoutHistoryRepository implements WorkoutHistoryRepository {
@override
Future<void> saveSetResult(WorkoutHistorySetResult result) async {}
@override
Future<void> saveStepResult(WorkoutHistoryStepResult result) async {}
}
final class _FakeProgramRepository implements ProgramRepository {