feat(exécution): dernière performance et meilleur score par exercice (ticket #81)

Ajoute la migration Drift v17 (colonne sourceExerciseIdSnapshot sur
workout_history_set_results et workout_history_step_results, index
associés), le port ExercisePerformanceReferenceRepository et son
implémentation Drift, et le use case
ExercisePerformanceReferenceUseCase (dernière performance + meilleur
score par métrique active pour un exercice donné).

Validé GO par Main : dart analyze propre (1 lint mineur de style),
178 tests (4 échecs préexistants sans rapport avec ce lot).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 07:54:49 +02:00
parent e05405157a
commit ac51c16b51
10 changed files with 1073 additions and 3 deletions

View File

@ -466,6 +466,106 @@ abstract interface class WorkoutHistoryRepository {
Future<void> delete(String id, DateTime deletedAt);
}
final class ActivePerformanceMeasures {
const ActivePerformanceMeasures({
required this.timeEnabled,
required this.repsEnabled,
required this.scoreEnabled,
this.scoreInputMode = ScoreInputMode.manual,
});
final bool timeEnabled;
final bool repsEnabled;
final bool scoreEnabled;
final ScoreInputMode scoreInputMode;
bool get hasAny => timeEnabled || repsEnabled || scoreEnabled;
}
enum PerformanceMetric { score, reps, time }
final class WorkoutHistorySetPerformance {
const WorkoutHistorySetPerformance({
required this.workoutHistoryId,
required this.startedAt,
required this.setIndex,
required this.exerciseNameSnapshot,
required this.scoreInputModeSnapshot,
this.actualTimeMs,
this.actualReps,
this.actualScore,
this.actualScoreTimeMs,
this.completedAt,
});
final String workoutHistoryId;
final DateTime startedAt;
final int setIndex;
final String exerciseNameSnapshot;
final ScoreInputMode scoreInputModeSnapshot;
final int? actualTimeMs;
final int? actualReps;
final double? actualScore;
final int? actualScoreTimeMs;
final DateTime? completedAt;
}
final class WorkoutHistoryMetricPerformance {
const WorkoutHistoryMetricPerformance({
required this.workoutHistoryId,
required this.startedAt,
required this.setIndex,
required this.exerciseNameSnapshot,
required this.metric,
required this.scoreInputModeSnapshot,
this.actualTimeMs,
this.actualReps,
this.actualScore,
this.actualScoreTimeMs,
this.completedAt,
});
final String workoutHistoryId;
final DateTime startedAt;
final int setIndex;
final String exerciseNameSnapshot;
final PerformanceMetric metric;
final ScoreInputMode scoreInputModeSnapshot;
final int? actualTimeMs;
final int? actualReps;
final double? actualScore;
final int? actualScoreTimeMs;
final DateTime? completedAt;
}
final class ExercisePerformanceReference {
const ExercisePerformanceReference({
required this.hasAnyHistoryForExercise,
this.last,
this.record,
});
final bool hasAnyHistoryForExercise;
final WorkoutHistorySetPerformance? last;
final WorkoutHistoryMetricPerformance? record;
}
abstract interface class ExercisePerformanceReferenceRepository {
Future<bool> hasAnyCompletedHistoryForExercise(String exerciseId);
Future<WorkoutHistorySetPerformance?> findLatestSetPerformance({
required String exerciseId,
required ActivePerformanceMeasures activeMeasures,
required int currentSetIndex,
});
Future<WorkoutHistoryMetricPerformance?> findBestMetricPerformance({
required String exerciseId,
required PerformanceMetric metric,
required ScoreInputMode scoreInputMode,
});
}
final class SyncRunSummary {
const SyncRunSummary({
required this.pushedChanges,