feat(ui): écran Historique des séances

Ajoute l'écran d'historique des séances (presentation/history_screen.dart),
avec les ajustements application/domaine/repositories nécessaires et la
navigation depuis l'accueil. Complète le dernier écran du périmètre UI.
flutter analyze propre, 18/18 tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 19:17:55 +02:00
parent d8cb9b9385
commit 34b3638ce3
11 changed files with 1026 additions and 3 deletions

View File

@ -10,6 +10,7 @@ final class AppBootstrap {
required this.workoutTemplateUseCases,
required this.activeWorkoutSessionUseCases,
required this.closeWorkoutSessionUseCase,
required this.workoutHistoryUseCases,
});
final AppDatabase database;
@ -19,6 +20,7 @@ final class AppBootstrap {
final WorkoutTemplateUseCases workoutTemplateUseCases;
final ActiveWorkoutSessionUseCases activeWorkoutSessionUseCases;
final CloseWorkoutSessionUseCase closeWorkoutSessionUseCase;
final WorkoutHistoryUseCases workoutHistoryUseCases;
static Future<AppBootstrap> create() async {
final database = AppDatabase.open();
@ -77,6 +79,10 @@ final class AppBootstrap {
ids: ids,
originDeviceId: originDeviceId,
),
workoutHistoryUseCases: WorkoutHistoryUseCases(
repository: historyRepository,
clock: clock,
),
);
}

View File

@ -85,6 +85,9 @@ abstract interface class ActiveSessionRepository {
}
abstract interface class WorkoutHistoryRepository {
Future<WorkoutHistory?> findById(String id);
Future<List<WorkoutHistory>> listActive();
Future<void> save(WorkoutHistory history);
Future<void> saveSetResult(WorkoutHistorySetResult result);
Future<void> delete(String id, DateTime deletedAt);
}

View File

@ -638,6 +638,28 @@ final class ActiveWorkoutSessionUseCases {
return session;
}
Future<ActiveWorkoutSession> startFromHistory(WorkoutHistory history) async {
final snapshot =
jsonDecode(history.historySnapshotJson) as Map<String, dynamic>;
final resolvedTemplateSnapshotJson =
snapshot['resolvedTemplateSnapshotJson'] as String;
final now = clock.now();
final session = ActiveWorkoutSession(
metadata: _newMetadata(ids, originDeviceId, now),
sourceWorkoutTemplateId: null,
status: ActiveWorkoutStatus.running,
startedAt: now,
lastPersistedAt: now,
elapsedActiveMs: 0,
currentProgramIndex: 0,
currentExerciseIndex: 0,
currentSetIndex: 0,
resolvedTemplateSnapshotJson: resolvedTemplateSnapshotJson,
);
await sessionRepository.save(session);
return session;
}
Future<ActiveWorkoutSession> pause(String sessionId) async {
final session = await _requiredSession(sessionId);
final paused = session.pause(clock.now());
@ -851,6 +873,19 @@ final class CloseWorkoutSessionUseCase {
}
}
final class WorkoutHistoryUseCases {
const WorkoutHistoryUseCases({required this.repository, required this.clock});
final WorkoutHistoryRepository repository;
final Clock clock;
Future<WorkoutHistory?> findById(String id) => repository.findById(id);
Future<List<WorkoutHistory>> listActive() => repository.listActive();
Future<void> delete(String id) => repository.delete(id, clock.now());
}
EntityMetadata _newMetadata(
IdGenerator ids,
String originDeviceId,