feat(ui): écran Bibliothèque d'exercices
Ajoute l'écran de bibliothèque d'exercices (presentation/exercise_library_screen.dart) et ajuste la couche application/domaine (bootstrap, ports, use cases, entités, repositories Drift) pour l'alimenter. flutter analyze propre, 9/9 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,15 +1,43 @@
|
||||
import '../infrastructure/local/app_database.dart';
|
||||
import '../infrastructure/local/local.dart';
|
||||
import 'application.dart';
|
||||
|
||||
final class AppBootstrap {
|
||||
AppBootstrap._({required this.database});
|
||||
AppBootstrap._({
|
||||
required this.database,
|
||||
required this.exerciseUseCases,
|
||||
required this.mediaUseCases,
|
||||
});
|
||||
|
||||
final AppDatabase database;
|
||||
final ExerciseUseCases exerciseUseCases;
|
||||
final MediaUseCases mediaUseCases;
|
||||
|
||||
static Future<AppBootstrap> create() async {
|
||||
final database = AppDatabase.open();
|
||||
await database.customSelect('SELECT 1').get();
|
||||
final exerciseRepository = DriftExerciseRepository(database);
|
||||
final mediaRepository = DriftMediaAssetRepository(database);
|
||||
final ids = LocalIdGenerator();
|
||||
const clock = SystemClock();
|
||||
const originDeviceId = 'local-device';
|
||||
|
||||
return AppBootstrap._(database: database);
|
||||
return AppBootstrap._(
|
||||
database: database,
|
||||
exerciseUseCases: ExerciseUseCases(
|
||||
repository: exerciseRepository,
|
||||
clock: clock,
|
||||
ids: ids,
|
||||
originDeviceId: originDeviceId,
|
||||
),
|
||||
mediaUseCases: MediaUseCases(
|
||||
mediaRepository: mediaRepository,
|
||||
exerciseRepository: exerciseRepository,
|
||||
storage: const PathProviderLocalMediaStorage(),
|
||||
clock: clock,
|
||||
ids: ids,
|
||||
originDeviceId: originDeviceId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> dispose() => database.close();
|
||||
|
||||
@ -18,6 +18,7 @@ abstract interface class IdGenerator {
|
||||
abstract interface class ExerciseRepository {
|
||||
Future<Exercise?> findById(String id);
|
||||
Future<List<Exercise>> listActive();
|
||||
Future<bool> isReferencedByProgram(String id);
|
||||
Future<void> save(Exercise exercise);
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,44 @@ final class ExerciseUseCases {
|
||||
|
||||
Future<void> save(Exercise exercise) => repository.save(exercise);
|
||||
|
||||
Future<List<Exercise>> listActive() => repository.listActive();
|
||||
|
||||
Future<bool> isUsedInPrograms(String id) {
|
||||
return repository.isReferencedByProgram(id);
|
||||
}
|
||||
|
||||
Future<Exercise> update({
|
||||
required String id,
|
||||
required String name,
|
||||
String? description,
|
||||
String? imageMediaId,
|
||||
String? videoMediaId,
|
||||
required bool hasTimeMeasure,
|
||||
required bool hasRepsMeasure,
|
||||
required bool hasScoreMeasure,
|
||||
String? scoreLabel,
|
||||
String? scoreUnit,
|
||||
}) async {
|
||||
final exercise = await repository.findById(id);
|
||||
if (exercise == null) {
|
||||
throw const DomainException('Exercise not found.');
|
||||
}
|
||||
final updated = exercise.copyWith(
|
||||
metadata: exercise.metadata.touch(clock.now()),
|
||||
name: name,
|
||||
description: description,
|
||||
imageMediaId: imageMediaId,
|
||||
videoMediaId: videoMediaId,
|
||||
hasTimeMeasure: hasTimeMeasure,
|
||||
hasRepsMeasure: hasRepsMeasure,
|
||||
hasScoreMeasure: hasScoreMeasure,
|
||||
scoreLabel: scoreLabel,
|
||||
scoreUnit: scoreUnit,
|
||||
);
|
||||
await repository.save(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
Future<Exercise> attachImage({
|
||||
required String exerciseId,
|
||||
required String mediaAssetId,
|
||||
|
||||
Reference in New Issue
Block a user