feat(exercice): icône d'exercice (ticket #38)
Étend le modèle Drift (tables.dart, app_database.dart/.g.dart), les entités/use cases/repositories et les écrans bibliothèque d'exercices et programme pour associer une icône à chaque exercice. flutter analyze propre, 72/72 tests verts, build APK debug validé. Dernier ticket du backlog. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -126,6 +126,7 @@ final class Exercise {
|
||||
this.description,
|
||||
String? imageMediaId,
|
||||
List<String> imageMediaIds = const [],
|
||||
String? iconMediaId,
|
||||
this.videoMediaId,
|
||||
required this.hasTimeMeasure,
|
||||
required this.hasRepsMeasure,
|
||||
@ -139,10 +140,13 @@ final class Exercise {
|
||||
this.defaultTargetScoreTimeMs,
|
||||
this.archivedAt,
|
||||
}) : name = _nonBlank(name, 'Exercise name'),
|
||||
imageMediaIds = _validatedImageMediaIds(
|
||||
imageMediaIds.isEmpty && imageMediaId != null
|
||||
? [imageMediaId]
|
||||
: imageMediaIds,
|
||||
imageMediaIds = _resolvedExerciseImageMediaIds(
|
||||
imageMediaId,
|
||||
imageMediaIds,
|
||||
),
|
||||
iconMediaId = _validatedIconMediaId(
|
||||
iconMediaId,
|
||||
_resolvedExerciseImageMediaIds(imageMediaId, imageMediaIds),
|
||||
) {
|
||||
_requireAtLeastOneMeasure(
|
||||
hasTime: hasTimeMeasure,
|
||||
@ -174,6 +178,7 @@ final class Exercise {
|
||||
final String name;
|
||||
final String? description;
|
||||
final List<String> imageMediaIds;
|
||||
final String? iconMediaId;
|
||||
final String? videoMediaId;
|
||||
final bool hasTimeMeasure;
|
||||
final bool hasRepsMeasure;
|
||||
@ -206,6 +211,7 @@ final class Exercise {
|
||||
Object? description = _unchanged,
|
||||
Object? imageMediaId = _unchanged,
|
||||
Object? imageMediaIds = _unchanged,
|
||||
Object? iconMediaId = _unchanged,
|
||||
Object? videoMediaId = _unchanged,
|
||||
bool? hasTimeMeasure,
|
||||
bool? hasRepsMeasure,
|
||||
@ -219,17 +225,25 @@ final class Exercise {
|
||||
Object? defaultTargetScoreTimeMs = _unchanged,
|
||||
Object? archivedAt = _unchanged,
|
||||
}) {
|
||||
final resolvedImageMediaIds = imageMediaIds == _unchanged
|
||||
? imageMediaId == _unchanged
|
||||
? this.imageMediaIds
|
||||
: [if (imageMediaId != null) imageMediaId as String]
|
||||
: imageMediaIds as List<String>;
|
||||
final resolvedIconMediaId = iconMediaId == _unchanged
|
||||
? this.iconMediaId != null &&
|
||||
resolvedImageMediaIds.contains(this.iconMediaId)
|
||||
? this.iconMediaId
|
||||
: null
|
||||
: iconMediaId as String?;
|
||||
return Exercise(
|
||||
metadata: metadata ?? this.metadata,
|
||||
name: name ?? this.name,
|
||||
description: description == _unchanged
|
||||
? this.description
|
||||
: description as String?,
|
||||
imageMediaIds: imageMediaIds == _unchanged
|
||||
? imageMediaId == _unchanged
|
||||
? this.imageMediaIds
|
||||
: [if (imageMediaId != null) imageMediaId as String]
|
||||
: imageMediaIds as List<String>,
|
||||
imageMediaIds: resolvedImageMediaIds,
|
||||
iconMediaId: resolvedIconMediaId,
|
||||
videoMediaId: videoMediaId == _unchanged
|
||||
? this.videoMediaId
|
||||
: videoMediaId as String?,
|
||||
@ -957,6 +971,30 @@ List<String> _validatedImageMediaIds(List<String> ids) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
List<String> _resolvedExerciseImageMediaIds(
|
||||
String? imageMediaId,
|
||||
List<String> imageMediaIds,
|
||||
) {
|
||||
return _validatedImageMediaIds(
|
||||
imageMediaIds.isEmpty && imageMediaId != null
|
||||
? [imageMediaId]
|
||||
: imageMediaIds,
|
||||
);
|
||||
}
|
||||
|
||||
String? _validatedIconMediaId(String? iconMediaId, List<String> imageMediaIds) {
|
||||
if (iconMediaId == null) {
|
||||
return null;
|
||||
}
|
||||
final normalized = _nonBlank(iconMediaId, 'Icon media id');
|
||||
if (!imageMediaIds.contains(normalized)) {
|
||||
throw const DomainException(
|
||||
'Exercise icon must be one of the exercise images.',
|
||||
);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
void _requireAtLeastOneMeasure({
|
||||
required bool hasTime,
|
||||
required bool hasReps,
|
||||
|
||||
Reference in New Issue
Block a user