merge(main): icône d'exercice (ticket #38)
Fusionne feature/#38-icone-exercice — analyze propre, 72/72 tests verts, build APK debug validé. Dernier ticket du backlog, clôturé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
issueRef: "#38"
|
issueRef: "#38"
|
||||||
version: 3
|
version: 6
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
updatedAt: 1784330204091
|
updatedAt: 1784377509677
|
||||||
---
|
---
|
||||||
|
Découpage :
|
||||||
|
1. [DevBackend] Ajouter un champ `iconMediaId` (String?, nullable) sur Exercise, distinct de la galerie `imageMediaIds` (ticket #35) — l'icône doit référencer l'un des médias déjà présents dans la galerie de l'exercice (contrainte : iconMediaId, si renseigné, doit faire partie de imageMediaIds). Si iconMediaId est null, l'affichage de la liste retombe sur la première image de la galerie (comportement actuel) ou aucune icône si la galerie est vide. Migration Drift.
|
||||||
|
2. [DevFrontend] Dans le formulaire d'exercice, une fois qu'il y a au moins une image dans la galerie, permettre de désigner l'une d'elles comme icône (ex: tap longue ou bouton "Définir comme icône" sur chaque miniature, avec un indicateur visuel sur celle actuellement choisie). Utiliser cette icône dans ExerciseListTile et partout où l'exercice est affiché en liste compacte (programme, sélection d'exercice).
|
||||||
@ -2,15 +2,15 @@
|
|||||||
id: "51e8769d-7b2b-4b4d-8de1-dc784881e575"
|
id: "51e8769d-7b2b-4b4d-8de1-dc784881e575"
|
||||||
number: 38
|
number: 38
|
||||||
title: "Proposer de mettre une image pour l'icone de l'exercice"
|
title: "Proposer de mettre une image pour l'icone de l'exercice"
|
||||||
status: "open"
|
status: "closed"
|
||||||
priority: "medium"
|
priority: "medium"
|
||||||
sprint: null
|
sprint: null
|
||||||
links: []
|
links: []
|
||||||
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
||||||
createdBy: {"kind":"user"}
|
createdBy: {"kind":"user"}
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
createdAt: 1784330169746
|
createdAt: 1784330169746
|
||||||
updatedAt: 1784330204091
|
updatedAt: 1784377509677
|
||||||
version: 3
|
version: 6
|
||||||
---
|
---
|
||||||
J'aimerais qu'en plus des images que l'on peut ajouter, qu'il soit possible de mettre une image comme icone de l'exercice
|
J'aimerais qu'en plus des images que l'on peut ajouter, qu'il soit possible de mettre une image comme icone de l'exercice
|
||||||
@ -429,13 +429,13 @@
|
|||||||
"issueRef": "#38",
|
"issueRef": "#38",
|
||||||
"path": "38",
|
"path": "38",
|
||||||
"title": "Proposer de mettre une image pour l'icone de l'exercice",
|
"title": "Proposer de mettre une image pour l'icone de l'exercice",
|
||||||
"status": "open",
|
"status": "closed",
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"sprint": null,
|
"sprint": null,
|
||||||
"assignedAgentIds": [
|
"assignedAgentIds": [
|
||||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||||
],
|
],
|
||||||
"updatedAt": 1784330204091
|
"updatedAt": 1784377509677
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"issueRef": "#39",
|
"issueRef": "#39",
|
||||||
|
|||||||
@ -25,6 +25,7 @@ final class ExerciseUseCases {
|
|||||||
String? description,
|
String? description,
|
||||||
String? imageMediaId,
|
String? imageMediaId,
|
||||||
List<String> imageMediaIds = const [],
|
List<String> imageMediaIds = const [],
|
||||||
|
String? iconMediaId,
|
||||||
String? videoMediaId,
|
String? videoMediaId,
|
||||||
required bool hasTimeMeasure,
|
required bool hasTimeMeasure,
|
||||||
required bool hasRepsMeasure,
|
required bool hasRepsMeasure,
|
||||||
@ -53,6 +54,7 @@ final class ExerciseUseCases {
|
|||||||
name: name,
|
name: name,
|
||||||
description: description,
|
description: description,
|
||||||
imageMediaIds: _resolveExerciseImageIds(imageMediaId, imageMediaIds),
|
imageMediaIds: _resolveExerciseImageIds(imageMediaId, imageMediaIds),
|
||||||
|
iconMediaId: iconMediaId,
|
||||||
videoMediaId: videoMediaId,
|
videoMediaId: videoMediaId,
|
||||||
hasTimeMeasure: hasTimeMeasure,
|
hasTimeMeasure: hasTimeMeasure,
|
||||||
hasRepsMeasure: hasRepsMeasure,
|
hasRepsMeasure: hasRepsMeasure,
|
||||||
@ -83,6 +85,7 @@ final class ExerciseUseCases {
|
|||||||
String? description,
|
String? description,
|
||||||
String? imageMediaId,
|
String? imageMediaId,
|
||||||
Object? imageMediaIds = _useCaseUnchanged,
|
Object? imageMediaIds = _useCaseUnchanged,
|
||||||
|
Object? iconMediaId = _useCaseUnchanged,
|
||||||
String? videoMediaId,
|
String? videoMediaId,
|
||||||
required bool hasTimeMeasure,
|
required bool hasTimeMeasure,
|
||||||
required bool hasRepsMeasure,
|
required bool hasRepsMeasure,
|
||||||
@ -123,13 +126,21 @@ final class ExerciseUseCases {
|
|||||||
defaultTargetScore: resolvedDefaultTargetScore,
|
defaultTargetScore: resolvedDefaultTargetScore,
|
||||||
defaultTargetScoreTimeMs: resolvedDefaultTargetScoreTimeMs,
|
defaultTargetScoreTimeMs: resolvedDefaultTargetScoreTimeMs,
|
||||||
);
|
);
|
||||||
|
final resolvedImageMediaIds = imageMediaIds == _useCaseUnchanged
|
||||||
|
? _resolveExerciseImageIds(imageMediaId, exercise.imageMediaIds)
|
||||||
|
: imageMediaIds as List<String>;
|
||||||
|
final resolvedIconMediaId = iconMediaId == _useCaseUnchanged
|
||||||
|
? exercise.iconMediaId != null &&
|
||||||
|
resolvedImageMediaIds.contains(exercise.iconMediaId)
|
||||||
|
? exercise.iconMediaId
|
||||||
|
: null
|
||||||
|
: iconMediaId as String?;
|
||||||
final updated = exercise.copyWith(
|
final updated = exercise.copyWith(
|
||||||
metadata: exercise.metadata.touch(clock.now()),
|
metadata: exercise.metadata.touch(clock.now()),
|
||||||
name: name,
|
name: name,
|
||||||
description: description,
|
description: description,
|
||||||
imageMediaIds: imageMediaIds == _useCaseUnchanged
|
imageMediaIds: resolvedImageMediaIds,
|
||||||
? _resolveExerciseImageIds(imageMediaId, exercise.imageMediaIds)
|
iconMediaId: resolvedIconMediaId,
|
||||||
: imageMediaIds as List<String>,
|
|
||||||
videoMediaId: videoMediaId,
|
videoMediaId: videoMediaId,
|
||||||
hasTimeMeasure: hasTimeMeasure,
|
hasTimeMeasure: hasTimeMeasure,
|
||||||
hasRepsMeasure: hasRepsMeasure,
|
hasRepsMeasure: hasRepsMeasure,
|
||||||
@ -190,6 +201,9 @@ final class ExerciseUseCases {
|
|||||||
imageMediaIds: exercise.imageMediaIds
|
imageMediaIds: exercise.imageMediaIds
|
||||||
.where((imageId) => imageId != mediaAssetId)
|
.where((imageId) => imageId != mediaAssetId)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
iconMediaId: exercise.iconMediaId == mediaAssetId
|
||||||
|
? null
|
||||||
|
: exercise.iconMediaId,
|
||||||
metadata: exercise.metadata.touch(clock.now()),
|
metadata: exercise.metadata.touch(clock.now()),
|
||||||
);
|
);
|
||||||
await repository.save(updated);
|
await repository.save(updated);
|
||||||
@ -217,6 +231,22 @@ final class ExerciseUseCases {
|
|||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Exercise> setIcon({
|
||||||
|
required String exerciseId,
|
||||||
|
required String? mediaAssetId,
|
||||||
|
}) async {
|
||||||
|
final exercise = await repository.findById(exerciseId);
|
||||||
|
if (exercise == null) {
|
||||||
|
throw const DomainException('Exercise not found.');
|
||||||
|
}
|
||||||
|
final updated = exercise.copyWith(
|
||||||
|
iconMediaId: mediaAssetId,
|
||||||
|
metadata: exercise.metadata.touch(clock.now()),
|
||||||
|
);
|
||||||
|
await repository.save(updated);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Exercise> attachVideo({
|
Future<Exercise> attachVideo({
|
||||||
required String exerciseId,
|
required String exerciseId,
|
||||||
required String mediaAssetId,
|
required String mediaAssetId,
|
||||||
|
|||||||
@ -126,6 +126,7 @@ final class Exercise {
|
|||||||
this.description,
|
this.description,
|
||||||
String? imageMediaId,
|
String? imageMediaId,
|
||||||
List<String> imageMediaIds = const [],
|
List<String> imageMediaIds = const [],
|
||||||
|
String? iconMediaId,
|
||||||
this.videoMediaId,
|
this.videoMediaId,
|
||||||
required this.hasTimeMeasure,
|
required this.hasTimeMeasure,
|
||||||
required this.hasRepsMeasure,
|
required this.hasRepsMeasure,
|
||||||
@ -139,10 +140,13 @@ final class Exercise {
|
|||||||
this.defaultTargetScoreTimeMs,
|
this.defaultTargetScoreTimeMs,
|
||||||
this.archivedAt,
|
this.archivedAt,
|
||||||
}) : name = _nonBlank(name, 'Exercise name'),
|
}) : name = _nonBlank(name, 'Exercise name'),
|
||||||
imageMediaIds = _validatedImageMediaIds(
|
imageMediaIds = _resolvedExerciseImageMediaIds(
|
||||||
imageMediaIds.isEmpty && imageMediaId != null
|
imageMediaId,
|
||||||
? [imageMediaId]
|
imageMediaIds,
|
||||||
: imageMediaIds,
|
),
|
||||||
|
iconMediaId = _validatedIconMediaId(
|
||||||
|
iconMediaId,
|
||||||
|
_resolvedExerciseImageMediaIds(imageMediaId, imageMediaIds),
|
||||||
) {
|
) {
|
||||||
_requireAtLeastOneMeasure(
|
_requireAtLeastOneMeasure(
|
||||||
hasTime: hasTimeMeasure,
|
hasTime: hasTimeMeasure,
|
||||||
@ -174,6 +178,7 @@ final class Exercise {
|
|||||||
final String name;
|
final String name;
|
||||||
final String? description;
|
final String? description;
|
||||||
final List<String> imageMediaIds;
|
final List<String> imageMediaIds;
|
||||||
|
final String? iconMediaId;
|
||||||
final String? videoMediaId;
|
final String? videoMediaId;
|
||||||
final bool hasTimeMeasure;
|
final bool hasTimeMeasure;
|
||||||
final bool hasRepsMeasure;
|
final bool hasRepsMeasure;
|
||||||
@ -206,6 +211,7 @@ final class Exercise {
|
|||||||
Object? description = _unchanged,
|
Object? description = _unchanged,
|
||||||
Object? imageMediaId = _unchanged,
|
Object? imageMediaId = _unchanged,
|
||||||
Object? imageMediaIds = _unchanged,
|
Object? imageMediaIds = _unchanged,
|
||||||
|
Object? iconMediaId = _unchanged,
|
||||||
Object? videoMediaId = _unchanged,
|
Object? videoMediaId = _unchanged,
|
||||||
bool? hasTimeMeasure,
|
bool? hasTimeMeasure,
|
||||||
bool? hasRepsMeasure,
|
bool? hasRepsMeasure,
|
||||||
@ -219,17 +225,25 @@ final class Exercise {
|
|||||||
Object? defaultTargetScoreTimeMs = _unchanged,
|
Object? defaultTargetScoreTimeMs = _unchanged,
|
||||||
Object? archivedAt = _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(
|
return Exercise(
|
||||||
metadata: metadata ?? this.metadata,
|
metadata: metadata ?? this.metadata,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
description: description == _unchanged
|
description: description == _unchanged
|
||||||
? this.description
|
? this.description
|
||||||
: description as String?,
|
: description as String?,
|
||||||
imageMediaIds: imageMediaIds == _unchanged
|
imageMediaIds: resolvedImageMediaIds,
|
||||||
? imageMediaId == _unchanged
|
iconMediaId: resolvedIconMediaId,
|
||||||
? this.imageMediaIds
|
|
||||||
: [if (imageMediaId != null) imageMediaId as String]
|
|
||||||
: imageMediaIds as List<String>,
|
|
||||||
videoMediaId: videoMediaId == _unchanged
|
videoMediaId: videoMediaId == _unchanged
|
||||||
? this.videoMediaId
|
? this.videoMediaId
|
||||||
: videoMediaId as String?,
|
: videoMediaId as String?,
|
||||||
@ -957,6 +971,30 @@ List<String> _validatedImageMediaIds(List<String> ids) {
|
|||||||
return normalized;
|
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({
|
void _requireAtLeastOneMeasure({
|
||||||
required bool hasTime,
|
required bool hasTime,
|
||||||
required bool hasReps,
|
required bool hasReps,
|
||||||
|
|||||||
@ -37,7 +37,7 @@ final class AppDatabase extends _$AppDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 6;
|
int get schemaVersion => 7;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
@ -70,6 +70,9 @@ final class AppDatabase extends _$AppDatabase {
|
|||||||
if (from < 6) {
|
if (from < 6) {
|
||||||
await _migrateToSchema6();
|
await _migrateToSchema6();
|
||||||
}
|
}
|
||||||
|
if (from < 7) {
|
||||||
|
await _migrateToSchema7();
|
||||||
|
}
|
||||||
await _createIndexes();
|
await _createIndexes();
|
||||||
},
|
},
|
||||||
beforeOpen: (details) async {
|
beforeOpen: (details) async {
|
||||||
@ -260,4 +263,11 @@ extension on AppDatabase {
|
|||||||
'exercise_image_media_ids_snapshot_json TEXT',
|
'exercise_image_media_ids_snapshot_json TEXT',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _migrateToSchema7() async {
|
||||||
|
await customStatement(
|
||||||
|
'ALTER TABLE exercises ADD COLUMN icon_media_id TEXT '
|
||||||
|
'REFERENCES media_assets(id)',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7959,6 +7959,20 @@ class $ExercisesTable extends Exercises
|
|||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
);
|
);
|
||||||
|
static const VerificationMeta _iconMediaIdMeta = const VerificationMeta(
|
||||||
|
'iconMediaId',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<String> iconMediaId = GeneratedColumn<String>(
|
||||||
|
'icon_media_id',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES media_assets (id)',
|
||||||
|
),
|
||||||
|
);
|
||||||
static const VerificationMeta _videoMediaIdMeta = const VerificationMeta(
|
static const VerificationMeta _videoMediaIdMeta = const VerificationMeta(
|
||||||
'videoMediaId',
|
'videoMediaId',
|
||||||
);
|
);
|
||||||
@ -8119,6 +8133,7 @@ class $ExercisesTable extends Exercises
|
|||||||
remoteRevision,
|
remoteRevision,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
iconMediaId,
|
||||||
videoMediaId,
|
videoMediaId,
|
||||||
hasTimeMeasure,
|
hasTimeMeasure,
|
||||||
hasRepsMeasure,
|
hasRepsMeasure,
|
||||||
@ -8254,6 +8269,15 @@ class $ExercisesTable extends Exercises
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('icon_media_id')) {
|
||||||
|
context.handle(
|
||||||
|
_iconMediaIdMeta,
|
||||||
|
iconMediaId.isAcceptableOrUnknown(
|
||||||
|
data['icon_media_id']!,
|
||||||
|
_iconMediaIdMeta,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (data.containsKey('video_media_id')) {
|
if (data.containsKey('video_media_id')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_videoMediaIdMeta,
|
_videoMediaIdMeta,
|
||||||
@ -8420,6 +8444,10 @@ class $ExercisesTable extends Exercises
|
|||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}description'],
|
data['${effectivePrefix}description'],
|
||||||
),
|
),
|
||||||
|
iconMediaId: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}icon_media_id'],
|
||||||
|
),
|
||||||
videoMediaId: attachedDatabase.typeMapping.read(
|
videoMediaId: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}video_media_id'],
|
data['${effectivePrefix}video_media_id'],
|
||||||
@ -8491,6 +8519,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
final String? remoteRevision;
|
final String? remoteRevision;
|
||||||
final String name;
|
final String name;
|
||||||
final String? description;
|
final String? description;
|
||||||
|
final String? iconMediaId;
|
||||||
final String? videoMediaId;
|
final String? videoMediaId;
|
||||||
final bool hasTimeMeasure;
|
final bool hasTimeMeasure;
|
||||||
final bool hasRepsMeasure;
|
final bool hasRepsMeasure;
|
||||||
@ -8517,6 +8546,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
this.remoteRevision,
|
this.remoteRevision,
|
||||||
required this.name,
|
required this.name,
|
||||||
this.description,
|
this.description,
|
||||||
|
this.iconMediaId,
|
||||||
this.videoMediaId,
|
this.videoMediaId,
|
||||||
required this.hasTimeMeasure,
|
required this.hasTimeMeasure,
|
||||||
required this.hasRepsMeasure,
|
required this.hasRepsMeasure,
|
||||||
@ -8556,6 +8586,9 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
if (!nullToAbsent || description != null) {
|
if (!nullToAbsent || description != null) {
|
||||||
map['description'] = Variable<String>(description);
|
map['description'] = Variable<String>(description);
|
||||||
}
|
}
|
||||||
|
if (!nullToAbsent || iconMediaId != null) {
|
||||||
|
map['icon_media_id'] = Variable<String>(iconMediaId);
|
||||||
|
}
|
||||||
if (!nullToAbsent || videoMediaId != null) {
|
if (!nullToAbsent || videoMediaId != null) {
|
||||||
map['video_media_id'] = Variable<String>(videoMediaId);
|
map['video_media_id'] = Variable<String>(videoMediaId);
|
||||||
}
|
}
|
||||||
@ -8616,6 +8649,9 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
description: description == null && nullToAbsent
|
description: description == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(description),
|
: Value(description),
|
||||||
|
iconMediaId: iconMediaId == null && nullToAbsent
|
||||||
|
? const Value.absent()
|
||||||
|
: Value(iconMediaId),
|
||||||
videoMediaId: videoMediaId == null && nullToAbsent
|
videoMediaId: videoMediaId == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(videoMediaId),
|
: Value(videoMediaId),
|
||||||
@ -8668,6 +8704,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
remoteRevision: serializer.fromJson<String?>(json['remoteRevision']),
|
remoteRevision: serializer.fromJson<String?>(json['remoteRevision']),
|
||||||
name: serializer.fromJson<String>(json['name']),
|
name: serializer.fromJson<String>(json['name']),
|
||||||
description: serializer.fromJson<String?>(json['description']),
|
description: serializer.fromJson<String?>(json['description']),
|
||||||
|
iconMediaId: serializer.fromJson<String?>(json['iconMediaId']),
|
||||||
videoMediaId: serializer.fromJson<String?>(json['videoMediaId']),
|
videoMediaId: serializer.fromJson<String?>(json['videoMediaId']),
|
||||||
hasTimeMeasure: serializer.fromJson<bool>(json['hasTimeMeasure']),
|
hasTimeMeasure: serializer.fromJson<bool>(json['hasTimeMeasure']),
|
||||||
hasRepsMeasure: serializer.fromJson<bool>(json['hasRepsMeasure']),
|
hasRepsMeasure: serializer.fromJson<bool>(json['hasRepsMeasure']),
|
||||||
@ -8705,6 +8742,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
'remoteRevision': serializer.toJson<String?>(remoteRevision),
|
'remoteRevision': serializer.toJson<String?>(remoteRevision),
|
||||||
'name': serializer.toJson<String>(name),
|
'name': serializer.toJson<String>(name),
|
||||||
'description': serializer.toJson<String?>(description),
|
'description': serializer.toJson<String?>(description),
|
||||||
|
'iconMediaId': serializer.toJson<String?>(iconMediaId),
|
||||||
'videoMediaId': serializer.toJson<String?>(videoMediaId),
|
'videoMediaId': serializer.toJson<String?>(videoMediaId),
|
||||||
'hasTimeMeasure': serializer.toJson<bool>(hasTimeMeasure),
|
'hasTimeMeasure': serializer.toJson<bool>(hasTimeMeasure),
|
||||||
'hasRepsMeasure': serializer.toJson<bool>(hasRepsMeasure),
|
'hasRepsMeasure': serializer.toJson<bool>(hasRepsMeasure),
|
||||||
@ -8738,6 +8776,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
Value<String?> remoteRevision = const Value.absent(),
|
Value<String?> remoteRevision = const Value.absent(),
|
||||||
String? name,
|
String? name,
|
||||||
Value<String?> description = const Value.absent(),
|
Value<String?> description = const Value.absent(),
|
||||||
|
Value<String?> iconMediaId = const Value.absent(),
|
||||||
Value<String?> videoMediaId = const Value.absent(),
|
Value<String?> videoMediaId = const Value.absent(),
|
||||||
bool? hasTimeMeasure,
|
bool? hasTimeMeasure,
|
||||||
bool? hasRepsMeasure,
|
bool? hasRepsMeasure,
|
||||||
@ -8768,6 +8807,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
: this.remoteRevision,
|
: this.remoteRevision,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
description: description.present ? description.value : this.description,
|
description: description.present ? description.value : this.description,
|
||||||
|
iconMediaId: iconMediaId.present ? iconMediaId.value : this.iconMediaId,
|
||||||
videoMediaId: videoMediaId.present ? videoMediaId.value : this.videoMediaId,
|
videoMediaId: videoMediaId.present ? videoMediaId.value : this.videoMediaId,
|
||||||
hasTimeMeasure: hasTimeMeasure ?? this.hasTimeMeasure,
|
hasTimeMeasure: hasTimeMeasure ?? this.hasTimeMeasure,
|
||||||
hasRepsMeasure: hasRepsMeasure ?? this.hasRepsMeasure,
|
hasRepsMeasure: hasRepsMeasure ?? this.hasRepsMeasure,
|
||||||
@ -8818,6 +8858,9 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
description: data.description.present
|
description: data.description.present
|
||||||
? data.description.value
|
? data.description.value
|
||||||
: this.description,
|
: this.description,
|
||||||
|
iconMediaId: data.iconMediaId.present
|
||||||
|
? data.iconMediaId.value
|
||||||
|
: this.iconMediaId,
|
||||||
videoMediaId: data.videoMediaId.present
|
videoMediaId: data.videoMediaId.present
|
||||||
? data.videoMediaId.value
|
? data.videoMediaId.value
|
||||||
: this.videoMediaId,
|
: this.videoMediaId,
|
||||||
@ -8871,6 +8914,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
..write('remoteRevision: $remoteRevision, ')
|
..write('remoteRevision: $remoteRevision, ')
|
||||||
..write('name: $name, ')
|
..write('name: $name, ')
|
||||||
..write('description: $description, ')
|
..write('description: $description, ')
|
||||||
|
..write('iconMediaId: $iconMediaId, ')
|
||||||
..write('videoMediaId: $videoMediaId, ')
|
..write('videoMediaId: $videoMediaId, ')
|
||||||
..write('hasTimeMeasure: $hasTimeMeasure, ')
|
..write('hasTimeMeasure: $hasTimeMeasure, ')
|
||||||
..write('hasRepsMeasure: $hasRepsMeasure, ')
|
..write('hasRepsMeasure: $hasRepsMeasure, ')
|
||||||
@ -8902,6 +8946,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
remoteRevision,
|
remoteRevision,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
iconMediaId,
|
||||||
videoMediaId,
|
videoMediaId,
|
||||||
hasTimeMeasure,
|
hasTimeMeasure,
|
||||||
hasRepsMeasure,
|
hasRepsMeasure,
|
||||||
@ -8932,6 +8977,7 @@ class Exercise extends DataClass implements Insertable<Exercise> {
|
|||||||
other.remoteRevision == this.remoteRevision &&
|
other.remoteRevision == this.remoteRevision &&
|
||||||
other.name == this.name &&
|
other.name == this.name &&
|
||||||
other.description == this.description &&
|
other.description == this.description &&
|
||||||
|
other.iconMediaId == this.iconMediaId &&
|
||||||
other.videoMediaId == this.videoMediaId &&
|
other.videoMediaId == this.videoMediaId &&
|
||||||
other.hasTimeMeasure == this.hasTimeMeasure &&
|
other.hasTimeMeasure == this.hasTimeMeasure &&
|
||||||
other.hasRepsMeasure == this.hasRepsMeasure &&
|
other.hasRepsMeasure == this.hasRepsMeasure &&
|
||||||
@ -8960,6 +9006,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
final Value<String?> remoteRevision;
|
final Value<String?> remoteRevision;
|
||||||
final Value<String> name;
|
final Value<String> name;
|
||||||
final Value<String?> description;
|
final Value<String?> description;
|
||||||
|
final Value<String?> iconMediaId;
|
||||||
final Value<String?> videoMediaId;
|
final Value<String?> videoMediaId;
|
||||||
final Value<bool> hasTimeMeasure;
|
final Value<bool> hasTimeMeasure;
|
||||||
final Value<bool> hasRepsMeasure;
|
final Value<bool> hasRepsMeasure;
|
||||||
@ -8987,6 +9034,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
this.remoteRevision = const Value.absent(),
|
this.remoteRevision = const Value.absent(),
|
||||||
this.name = const Value.absent(),
|
this.name = const Value.absent(),
|
||||||
this.description = const Value.absent(),
|
this.description = const Value.absent(),
|
||||||
|
this.iconMediaId = const Value.absent(),
|
||||||
this.videoMediaId = const Value.absent(),
|
this.videoMediaId = const Value.absent(),
|
||||||
this.hasTimeMeasure = const Value.absent(),
|
this.hasTimeMeasure = const Value.absent(),
|
||||||
this.hasRepsMeasure = const Value.absent(),
|
this.hasRepsMeasure = const Value.absent(),
|
||||||
@ -9015,6 +9063,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
this.remoteRevision = const Value.absent(),
|
this.remoteRevision = const Value.absent(),
|
||||||
required String name,
|
required String name,
|
||||||
this.description = const Value.absent(),
|
this.description = const Value.absent(),
|
||||||
|
this.iconMediaId = const Value.absent(),
|
||||||
this.videoMediaId = const Value.absent(),
|
this.videoMediaId = const Value.absent(),
|
||||||
required bool hasTimeMeasure,
|
required bool hasTimeMeasure,
|
||||||
required bool hasRepsMeasure,
|
required bool hasRepsMeasure,
|
||||||
@ -9052,6 +9101,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
Expression<String>? remoteRevision,
|
Expression<String>? remoteRevision,
|
||||||
Expression<String>? name,
|
Expression<String>? name,
|
||||||
Expression<String>? description,
|
Expression<String>? description,
|
||||||
|
Expression<String>? iconMediaId,
|
||||||
Expression<String>? videoMediaId,
|
Expression<String>? videoMediaId,
|
||||||
Expression<bool>? hasTimeMeasure,
|
Expression<bool>? hasTimeMeasure,
|
||||||
Expression<bool>? hasRepsMeasure,
|
Expression<bool>? hasRepsMeasure,
|
||||||
@ -9081,6 +9131,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
if (remoteRevision != null) 'remote_revision': remoteRevision,
|
if (remoteRevision != null) 'remote_revision': remoteRevision,
|
||||||
if (name != null) 'name': name,
|
if (name != null) 'name': name,
|
||||||
if (description != null) 'description': description,
|
if (description != null) 'description': description,
|
||||||
|
if (iconMediaId != null) 'icon_media_id': iconMediaId,
|
||||||
if (videoMediaId != null) 'video_media_id': videoMediaId,
|
if (videoMediaId != null) 'video_media_id': videoMediaId,
|
||||||
if (hasTimeMeasure != null) 'has_time_measure': hasTimeMeasure,
|
if (hasTimeMeasure != null) 'has_time_measure': hasTimeMeasure,
|
||||||
if (hasRepsMeasure != null) 'has_reps_measure': hasRepsMeasure,
|
if (hasRepsMeasure != null) 'has_reps_measure': hasRepsMeasure,
|
||||||
@ -9114,6 +9165,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
Value<String?>? remoteRevision,
|
Value<String?>? remoteRevision,
|
||||||
Value<String>? name,
|
Value<String>? name,
|
||||||
Value<String?>? description,
|
Value<String?>? description,
|
||||||
|
Value<String?>? iconMediaId,
|
||||||
Value<String?>? videoMediaId,
|
Value<String?>? videoMediaId,
|
||||||
Value<bool>? hasTimeMeasure,
|
Value<bool>? hasTimeMeasure,
|
||||||
Value<bool>? hasRepsMeasure,
|
Value<bool>? hasRepsMeasure,
|
||||||
@ -9142,6 +9194,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
remoteRevision: remoteRevision ?? this.remoteRevision,
|
remoteRevision: remoteRevision ?? this.remoteRevision,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
description: description ?? this.description,
|
description: description ?? this.description,
|
||||||
|
iconMediaId: iconMediaId ?? this.iconMediaId,
|
||||||
videoMediaId: videoMediaId ?? this.videoMediaId,
|
videoMediaId: videoMediaId ?? this.videoMediaId,
|
||||||
hasTimeMeasure: hasTimeMeasure ?? this.hasTimeMeasure,
|
hasTimeMeasure: hasTimeMeasure ?? this.hasTimeMeasure,
|
||||||
hasRepsMeasure: hasRepsMeasure ?? this.hasRepsMeasure,
|
hasRepsMeasure: hasRepsMeasure ?? this.hasRepsMeasure,
|
||||||
@ -9204,6 +9257,9 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
if (description.present) {
|
if (description.present) {
|
||||||
map['description'] = Variable<String>(description.value);
|
map['description'] = Variable<String>(description.value);
|
||||||
}
|
}
|
||||||
|
if (iconMediaId.present) {
|
||||||
|
map['icon_media_id'] = Variable<String>(iconMediaId.value);
|
||||||
|
}
|
||||||
if (videoMediaId.present) {
|
if (videoMediaId.present) {
|
||||||
map['video_media_id'] = Variable<String>(videoMediaId.value);
|
map['video_media_id'] = Variable<String>(videoMediaId.value);
|
||||||
}
|
}
|
||||||
@ -9266,6 +9322,7 @@ class ExercisesCompanion extends UpdateCompanion<Exercise> {
|
|||||||
..write('remoteRevision: $remoteRevision, ')
|
..write('remoteRevision: $remoteRevision, ')
|
||||||
..write('name: $name, ')
|
..write('name: $name, ')
|
||||||
..write('description: $description, ')
|
..write('description: $description, ')
|
||||||
|
..write('iconMediaId: $iconMediaId, ')
|
||||||
..write('videoMediaId: $videoMediaId, ')
|
..write('videoMediaId: $videoMediaId, ')
|
||||||
..write('hasTimeMeasure: $hasTimeMeasure, ')
|
..write('hasTimeMeasure: $hasTimeMeasure, ')
|
||||||
..write('hasRepsMeasure: $hasRepsMeasure, ')
|
..write('hasRepsMeasure: $hasRepsMeasure, ')
|
||||||
@ -22861,6 +22918,27 @@ final class $$MediaAssetsTableReferences
|
|||||||
extends BaseReferences<_$AppDatabase, $MediaAssetsTable, MediaAsset> {
|
extends BaseReferences<_$AppDatabase, $MediaAssetsTable, MediaAsset> {
|
||||||
$$MediaAssetsTableReferences(super.$_db, super.$_table, super.$_typedResult);
|
$$MediaAssetsTableReferences(super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
|
static MultiTypedResultKey<$ExercisesTable, List<Exercise>>
|
||||||
|
_exerciseIconReferencesTable(_$AppDatabase db) =>
|
||||||
|
MultiTypedResultKey.fromTable(
|
||||||
|
db.exercises,
|
||||||
|
aliasName: 'media_assets__id__exercises__icon_media_id',
|
||||||
|
);
|
||||||
|
|
||||||
|
$$ExercisesTableProcessedTableManager get exerciseIconReferences {
|
||||||
|
final manager = $$ExercisesTableTableManager(
|
||||||
|
$_db,
|
||||||
|
$_db.exercises,
|
||||||
|
).filter((f) => f.iconMediaId.id.sqlEquals($_itemColumn<String>('id')!));
|
||||||
|
|
||||||
|
final cache = $_typedResult.readTableOrNull(
|
||||||
|
_exerciseIconReferencesTable($_db),
|
||||||
|
);
|
||||||
|
return ProcessedTableManager(
|
||||||
|
manager.$state.copyWith(prefetchedData: cache),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static MultiTypedResultKey<$ExercisesTable, List<Exercise>>
|
static MultiTypedResultKey<$ExercisesTable, List<Exercise>>
|
||||||
_exerciseVideoReferencesTable(_$AppDatabase db) =>
|
_exerciseVideoReferencesTable(_$AppDatabase db) =>
|
||||||
MultiTypedResultKey.fromTable(
|
MultiTypedResultKey.fromTable(
|
||||||
@ -23067,6 +23145,31 @@ class $$MediaAssetsTableFilterComposer
|
|||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Expression<bool> exerciseIconReferences(
|
||||||
|
Expression<bool> Function($$ExercisesTableFilterComposer f) f,
|
||||||
|
) {
|
||||||
|
final $$ExercisesTableFilterComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.id,
|
||||||
|
referencedTable: $db.exercises,
|
||||||
|
getReferencedColumn: (t) => t.iconMediaId,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => $$ExercisesTableFilterComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: $db.exercises,
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return f(composer);
|
||||||
|
}
|
||||||
|
|
||||||
Expression<bool> exerciseVideoReferences(
|
Expression<bool> exerciseVideoReferences(
|
||||||
Expression<bool> Function($$ExercisesTableFilterComposer f) f,
|
Expression<bool> Function($$ExercisesTableFilterComposer f) f,
|
||||||
) {
|
) {
|
||||||
@ -23371,6 +23474,31 @@ class $$MediaAssetsTableAnnotationComposer
|
|||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Expression<T> exerciseIconReferences<T extends Object>(
|
||||||
|
Expression<T> Function($$ExercisesTableAnnotationComposer a) f,
|
||||||
|
) {
|
||||||
|
final $$ExercisesTableAnnotationComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.id,
|
||||||
|
referencedTable: $db.exercises,
|
||||||
|
getReferencedColumn: (t) => t.iconMediaId,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => $$ExercisesTableAnnotationComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: $db.exercises,
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return f(composer);
|
||||||
|
}
|
||||||
|
|
||||||
Expression<T> exerciseVideoReferences<T extends Object>(
|
Expression<T> exerciseVideoReferences<T extends Object>(
|
||||||
Expression<T> Function($$ExercisesTableAnnotationComposer a) f,
|
Expression<T> Function($$ExercisesTableAnnotationComposer a) f,
|
||||||
) {
|
) {
|
||||||
@ -23486,6 +23614,7 @@ class $$MediaAssetsTableTableManager
|
|||||||
(MediaAsset, $$MediaAssetsTableReferences),
|
(MediaAsset, $$MediaAssetsTableReferences),
|
||||||
MediaAsset,
|
MediaAsset,
|
||||||
PrefetchHooks Function({
|
PrefetchHooks Function({
|
||||||
|
bool exerciseIconReferences,
|
||||||
bool exerciseVideoReferences,
|
bool exerciseVideoReferences,
|
||||||
bool exerciseImagesRefs,
|
bool exerciseImagesRefs,
|
||||||
bool programExerciseImageSnapshotReferences,
|
bool programExerciseImageSnapshotReferences,
|
||||||
@ -23609,6 +23738,7 @@ class $$MediaAssetsTableTableManager
|
|||||||
.toList(),
|
.toList(),
|
||||||
prefetchHooksCallback:
|
prefetchHooksCallback:
|
||||||
({
|
({
|
||||||
|
exerciseIconReferences = false,
|
||||||
exerciseVideoReferences = false,
|
exerciseVideoReferences = false,
|
||||||
exerciseImagesRefs = false,
|
exerciseImagesRefs = false,
|
||||||
programExerciseImageSnapshotReferences = false,
|
programExerciseImageSnapshotReferences = false,
|
||||||
@ -23617,6 +23747,7 @@ class $$MediaAssetsTableTableManager
|
|||||||
return PrefetchHooks(
|
return PrefetchHooks(
|
||||||
db: db,
|
db: db,
|
||||||
explicitlyWatchedTables: [
|
explicitlyWatchedTables: [
|
||||||
|
if (exerciseIconReferences) db.exercises,
|
||||||
if (exerciseVideoReferences) db.exercises,
|
if (exerciseVideoReferences) db.exercises,
|
||||||
if (exerciseImagesRefs) db.exerciseImages,
|
if (exerciseImagesRefs) db.exerciseImages,
|
||||||
if (programExerciseImageSnapshotReferences)
|
if (programExerciseImageSnapshotReferences)
|
||||||
@ -23627,6 +23758,27 @@ class $$MediaAssetsTableTableManager
|
|||||||
addJoins: null,
|
addJoins: null,
|
||||||
getPrefetchedDataCallback: (items) async {
|
getPrefetchedDataCallback: (items) async {
|
||||||
return [
|
return [
|
||||||
|
if (exerciseIconReferences)
|
||||||
|
await $_getPrefetchedData<
|
||||||
|
MediaAsset,
|
||||||
|
$MediaAssetsTable,
|
||||||
|
Exercise
|
||||||
|
>(
|
||||||
|
currentTable: table,
|
||||||
|
referencedTable: $$MediaAssetsTableReferences
|
||||||
|
._exerciseIconReferencesTable(db),
|
||||||
|
managerFromTypedResult: (p0) =>
|
||||||
|
$$MediaAssetsTableReferences(
|
||||||
|
db,
|
||||||
|
table,
|
||||||
|
p0,
|
||||||
|
).exerciseIconReferences,
|
||||||
|
referencedItemsForCurrentItem:
|
||||||
|
(item, referencedItems) => referencedItems.where(
|
||||||
|
(e) => e.iconMediaId == item.id,
|
||||||
|
),
|
||||||
|
typedResults: items,
|
||||||
|
),
|
||||||
if (exerciseVideoReferences)
|
if (exerciseVideoReferences)
|
||||||
await $_getPrefetchedData<
|
await $_getPrefetchedData<
|
||||||
MediaAsset,
|
MediaAsset,
|
||||||
@ -23734,6 +23886,7 @@ typedef $$MediaAssetsTableProcessedTableManager =
|
|||||||
(MediaAsset, $$MediaAssetsTableReferences),
|
(MediaAsset, $$MediaAssetsTableReferences),
|
||||||
MediaAsset,
|
MediaAsset,
|
||||||
PrefetchHooks Function({
|
PrefetchHooks Function({
|
||||||
|
bool exerciseIconReferences,
|
||||||
bool exerciseVideoReferences,
|
bool exerciseVideoReferences,
|
||||||
bool exerciseImagesRefs,
|
bool exerciseImagesRefs,
|
||||||
bool programExerciseImageSnapshotReferences,
|
bool programExerciseImageSnapshotReferences,
|
||||||
@ -23755,6 +23908,7 @@ typedef $$ExercisesTableCreateCompanionBuilder =
|
|||||||
Value<String?> remoteRevision,
|
Value<String?> remoteRevision,
|
||||||
required String name,
|
required String name,
|
||||||
Value<String?> description,
|
Value<String?> description,
|
||||||
|
Value<String?> iconMediaId,
|
||||||
Value<String?> videoMediaId,
|
Value<String?> videoMediaId,
|
||||||
required bool hasTimeMeasure,
|
required bool hasTimeMeasure,
|
||||||
required bool hasRepsMeasure,
|
required bool hasRepsMeasure,
|
||||||
@ -23784,6 +23938,7 @@ typedef $$ExercisesTableUpdateCompanionBuilder =
|
|||||||
Value<String?> remoteRevision,
|
Value<String?> remoteRevision,
|
||||||
Value<String> name,
|
Value<String> name,
|
||||||
Value<String?> description,
|
Value<String?> description,
|
||||||
|
Value<String?> iconMediaId,
|
||||||
Value<String?> videoMediaId,
|
Value<String?> videoMediaId,
|
||||||
Value<bool> hasTimeMeasure,
|
Value<bool> hasTimeMeasure,
|
||||||
Value<bool> hasRepsMeasure,
|
Value<bool> hasRepsMeasure,
|
||||||
@ -23803,6 +23958,23 @@ final class $$ExercisesTableReferences
|
|||||||
extends BaseReferences<_$AppDatabase, $ExercisesTable, Exercise> {
|
extends BaseReferences<_$AppDatabase, $ExercisesTable, Exercise> {
|
||||||
$$ExercisesTableReferences(super.$_db, super.$_table, super.$_typedResult);
|
$$ExercisesTableReferences(super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
|
static $MediaAssetsTable _iconMediaIdTable(_$AppDatabase db) =>
|
||||||
|
db.mediaAssets.createAlias('exercises__icon_media_id__media_assets__id');
|
||||||
|
|
||||||
|
$$MediaAssetsTableProcessedTableManager? get iconMediaId {
|
||||||
|
final $_column = $_itemColumn<String>('icon_media_id');
|
||||||
|
if ($_column == null) return null;
|
||||||
|
final manager = $$MediaAssetsTableTableManager(
|
||||||
|
$_db,
|
||||||
|
$_db.mediaAssets,
|
||||||
|
).filter((f) => f.id.sqlEquals($_column));
|
||||||
|
final item = $_typedResult.readTableOrNull(_iconMediaIdTable($_db));
|
||||||
|
if (item == null) return manager;
|
||||||
|
return ProcessedTableManager(
|
||||||
|
manager.$state.copyWith(prefetchedData: [item]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static $MediaAssetsTable _videoMediaIdTable(_$AppDatabase db) =>
|
static $MediaAssetsTable _videoMediaIdTable(_$AppDatabase db) =>
|
||||||
db.mediaAssets.createAlias('exercises__video_media_id__media_assets__id');
|
db.mediaAssets.createAlias('exercises__video_media_id__media_assets__id');
|
||||||
|
|
||||||
@ -23988,6 +24160,29 @@ class $$ExercisesTableFilterComposer
|
|||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$$MediaAssetsTableFilterComposer get iconMediaId {
|
||||||
|
final $$MediaAssetsTableFilterComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.iconMediaId,
|
||||||
|
referencedTable: $db.mediaAssets,
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => $$MediaAssetsTableFilterComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: $db.mediaAssets,
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
|
||||||
$$MediaAssetsTableFilterComposer get videoMediaId {
|
$$MediaAssetsTableFilterComposer get videoMediaId {
|
||||||
final $$MediaAssetsTableFilterComposer composer = $composerBuilder(
|
final $$MediaAssetsTableFilterComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
@ -24191,6 +24386,29 @@ class $$ExercisesTableOrderingComposer
|
|||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$$MediaAssetsTableOrderingComposer get iconMediaId {
|
||||||
|
final $$MediaAssetsTableOrderingComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.iconMediaId,
|
||||||
|
referencedTable: $db.mediaAssets,
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => $$MediaAssetsTableOrderingComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: $db.mediaAssets,
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
|
||||||
$$MediaAssetsTableOrderingComposer get videoMediaId {
|
$$MediaAssetsTableOrderingComposer get videoMediaId {
|
||||||
final $$MediaAssetsTableOrderingComposer composer = $composerBuilder(
|
final $$MediaAssetsTableOrderingComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
@ -24330,6 +24548,29 @@ class $$ExercisesTableAnnotationComposer
|
|||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$$MediaAssetsTableAnnotationComposer get iconMediaId {
|
||||||
|
final $$MediaAssetsTableAnnotationComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.iconMediaId,
|
||||||
|
referencedTable: $db.mediaAssets,
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => $$MediaAssetsTableAnnotationComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: $db.mediaAssets,
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
|
||||||
$$MediaAssetsTableAnnotationComposer get videoMediaId {
|
$$MediaAssetsTableAnnotationComposer get videoMediaId {
|
||||||
final $$MediaAssetsTableAnnotationComposer composer = $composerBuilder(
|
final $$MediaAssetsTableAnnotationComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
@ -24418,6 +24659,7 @@ class $$ExercisesTableTableManager
|
|||||||
(Exercise, $$ExercisesTableReferences),
|
(Exercise, $$ExercisesTableReferences),
|
||||||
Exercise,
|
Exercise,
|
||||||
PrefetchHooks Function({
|
PrefetchHooks Function({
|
||||||
|
bool iconMediaId,
|
||||||
bool videoMediaId,
|
bool videoMediaId,
|
||||||
bool exerciseImagesRefs,
|
bool exerciseImagesRefs,
|
||||||
bool programExercisesRefs,
|
bool programExercisesRefs,
|
||||||
@ -24449,6 +24691,7 @@ class $$ExercisesTableTableManager
|
|||||||
Value<String?> remoteRevision = const Value.absent(),
|
Value<String?> remoteRevision = const Value.absent(),
|
||||||
Value<String> name = const Value.absent(),
|
Value<String> name = const Value.absent(),
|
||||||
Value<String?> description = const Value.absent(),
|
Value<String?> description = const Value.absent(),
|
||||||
|
Value<String?> iconMediaId = const Value.absent(),
|
||||||
Value<String?> videoMediaId = const Value.absent(),
|
Value<String?> videoMediaId = const Value.absent(),
|
||||||
Value<bool> hasTimeMeasure = const Value.absent(),
|
Value<bool> hasTimeMeasure = const Value.absent(),
|
||||||
Value<bool> hasRepsMeasure = const Value.absent(),
|
Value<bool> hasRepsMeasure = const Value.absent(),
|
||||||
@ -24476,6 +24719,7 @@ class $$ExercisesTableTableManager
|
|||||||
remoteRevision: remoteRevision,
|
remoteRevision: remoteRevision,
|
||||||
name: name,
|
name: name,
|
||||||
description: description,
|
description: description,
|
||||||
|
iconMediaId: iconMediaId,
|
||||||
videoMediaId: videoMediaId,
|
videoMediaId: videoMediaId,
|
||||||
hasTimeMeasure: hasTimeMeasure,
|
hasTimeMeasure: hasTimeMeasure,
|
||||||
hasRepsMeasure: hasRepsMeasure,
|
hasRepsMeasure: hasRepsMeasure,
|
||||||
@ -24505,6 +24749,7 @@ class $$ExercisesTableTableManager
|
|||||||
Value<String?> remoteRevision = const Value.absent(),
|
Value<String?> remoteRevision = const Value.absent(),
|
||||||
required String name,
|
required String name,
|
||||||
Value<String?> description = const Value.absent(),
|
Value<String?> description = const Value.absent(),
|
||||||
|
Value<String?> iconMediaId = const Value.absent(),
|
||||||
Value<String?> videoMediaId = const Value.absent(),
|
Value<String?> videoMediaId = const Value.absent(),
|
||||||
required bool hasTimeMeasure,
|
required bool hasTimeMeasure,
|
||||||
required bool hasRepsMeasure,
|
required bool hasRepsMeasure,
|
||||||
@ -24532,6 +24777,7 @@ class $$ExercisesTableTableManager
|
|||||||
remoteRevision: remoteRevision,
|
remoteRevision: remoteRevision,
|
||||||
name: name,
|
name: name,
|
||||||
description: description,
|
description: description,
|
||||||
|
iconMediaId: iconMediaId,
|
||||||
videoMediaId: videoMediaId,
|
videoMediaId: videoMediaId,
|
||||||
hasTimeMeasure: hasTimeMeasure,
|
hasTimeMeasure: hasTimeMeasure,
|
||||||
hasRepsMeasure: hasRepsMeasure,
|
hasRepsMeasure: hasRepsMeasure,
|
||||||
@ -24556,6 +24802,7 @@ class $$ExercisesTableTableManager
|
|||||||
.toList(),
|
.toList(),
|
||||||
prefetchHooksCallback:
|
prefetchHooksCallback:
|
||||||
({
|
({
|
||||||
|
iconMediaId = false,
|
||||||
videoMediaId = false,
|
videoMediaId = false,
|
||||||
exerciseImagesRefs = false,
|
exerciseImagesRefs = false,
|
||||||
programExercisesRefs = false,
|
programExercisesRefs = false,
|
||||||
@ -24582,6 +24829,19 @@ class $$ExercisesTableTableManager
|
|||||||
dynamic
|
dynamic
|
||||||
>
|
>
|
||||||
>(state) {
|
>(state) {
|
||||||
|
if (iconMediaId) {
|
||||||
|
state =
|
||||||
|
state.withJoin(
|
||||||
|
currentTable: table,
|
||||||
|
currentColumn: table.iconMediaId,
|
||||||
|
referencedTable: $$ExercisesTableReferences
|
||||||
|
._iconMediaIdTable(db),
|
||||||
|
referencedColumn: $$ExercisesTableReferences
|
||||||
|
._iconMediaIdTable(db)
|
||||||
|
.id,
|
||||||
|
)
|
||||||
|
as T;
|
||||||
|
}
|
||||||
if (videoMediaId) {
|
if (videoMediaId) {
|
||||||
state =
|
state =
|
||||||
state.withJoin(
|
state.withJoin(
|
||||||
@ -24663,6 +24923,7 @@ typedef $$ExercisesTableProcessedTableManager =
|
|||||||
(Exercise, $$ExercisesTableReferences),
|
(Exercise, $$ExercisesTableReferences),
|
||||||
Exercise,
|
Exercise,
|
||||||
PrefetchHooks Function({
|
PrefetchHooks Function({
|
||||||
|
bool iconMediaId,
|
||||||
bool videoMediaId,
|
bool videoMediaId,
|
||||||
bool exerciseImagesRefs,
|
bool exerciseImagesRefs,
|
||||||
bool programExercisesRefs,
|
bool programExercisesRefs,
|
||||||
|
|||||||
@ -1078,6 +1078,7 @@ db.ExercisesCompanion _exerciseCompanion(domain.Exercise exercise) {
|
|||||||
remoteRevision: values[10] as Value<String?>,
|
remoteRevision: values[10] as Value<String?>,
|
||||||
name: Value(exercise.name),
|
name: Value(exercise.name),
|
||||||
description: Value(exercise.description),
|
description: Value(exercise.description),
|
||||||
|
iconMediaId: Value(exercise.iconMediaId),
|
||||||
videoMediaId: Value(exercise.videoMediaId),
|
videoMediaId: Value(exercise.videoMediaId),
|
||||||
hasTimeMeasure: Value(exercise.hasTimeMeasure),
|
hasTimeMeasure: Value(exercise.hasTimeMeasure),
|
||||||
hasRepsMeasure: Value(exercise.hasRepsMeasure),
|
hasRepsMeasure: Value(exercise.hasRepsMeasure),
|
||||||
@ -1099,6 +1100,7 @@ domain.Exercise _exerciseFromRow(db.Exercise row, List<String> imageMediaIds) {
|
|||||||
name: row.name,
|
name: row.name,
|
||||||
description: row.description,
|
description: row.description,
|
||||||
imageMediaIds: imageMediaIds,
|
imageMediaIds: imageMediaIds,
|
||||||
|
iconMediaId: row.iconMediaId,
|
||||||
videoMediaId: row.videoMediaId,
|
videoMediaId: row.videoMediaId,
|
||||||
hasTimeMeasure: row.hasTimeMeasure,
|
hasTimeMeasure: row.hasTimeMeasure,
|
||||||
hasRepsMeasure: row.hasRepsMeasure,
|
hasRepsMeasure: row.hasRepsMeasure,
|
||||||
|
|||||||
@ -55,6 +55,9 @@ class Exercises extends SyncableTable {
|
|||||||
|
|
||||||
TextColumn get name => text().withLength(min: 1)();
|
TextColumn get name => text().withLength(min: 1)();
|
||||||
TextColumn get description => text().nullable()();
|
TextColumn get description => text().nullable()();
|
||||||
|
@ReferenceName('exerciseIconReferences')
|
||||||
|
TextColumn get iconMediaId =>
|
||||||
|
text().nullable().references(MediaAssets, #id)();
|
||||||
@ReferenceName('exerciseVideoReferences')
|
@ReferenceName('exerciseVideoReferences')
|
||||||
TextColumn get videoMediaId =>
|
TextColumn get videoMediaId =>
|
||||||
text().nullable().references(MediaAssets, #id)();
|
text().nullable().references(MediaAssets, #id)();
|
||||||
|
|||||||
@ -267,15 +267,11 @@ final class ExerciseListTile extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final description = exercise.description?.trim();
|
final description = exercise.description?.trim();
|
||||||
final hasMedia =
|
final iconMediaId = exercise.iconMediaId ?? exercise.imageMediaId;
|
||||||
exercise.imageMediaId != null || exercise.videoMediaId != null;
|
final hasMedia = iconMediaId != null || exercise.videoMediaId != null;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
leading: CircleAvatar(
|
leading: _ExerciseIconAvatar(iconMediaId: iconMediaId),
|
||||||
child: Icon(
|
|
||||||
hasMedia ? Icons.perm_media_outlined : Icons.fitness_center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: Text(exercise.name),
|
title: Text(exercise.name),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -328,6 +324,42 @@ final class ExerciseListTile extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class _ExerciseIconAvatar extends StatelessWidget {
|
||||||
|
const _ExerciseIconAvatar({required this.iconMediaId});
|
||||||
|
|
||||||
|
final String? iconMediaId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final icon = CircleAvatar(
|
||||||
|
child: Icon(
|
||||||
|
iconMediaId == null ? Icons.fitness_center : Icons.perm_media_outlined,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (iconMediaId == null) {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
return Tooltip(
|
||||||
|
message: 'Icône d’exercice $iconMediaId',
|
||||||
|
child: Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
Positioned(
|
||||||
|
right: -2,
|
||||||
|
bottom: -2,
|
||||||
|
child: Icon(
|
||||||
|
Icons.star,
|
||||||
|
size: 16,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class ExerciseFormScreen extends StatefulWidget {
|
final class ExerciseFormScreen extends StatefulWidget {
|
||||||
const ExerciseFormScreen({
|
const ExerciseFormScreen({
|
||||||
required this.exerciseUseCases,
|
required this.exerciseUseCases,
|
||||||
@ -357,6 +389,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
|||||||
late final TextEditingController _defaultScoreController;
|
late final TextEditingController _defaultScoreController;
|
||||||
late final TextEditingController _defaultScoreTimeController;
|
late final TextEditingController _defaultScoreTimeController;
|
||||||
late List<String> _imageMediaIds;
|
late List<String> _imageMediaIds;
|
||||||
|
String? _iconMediaId;
|
||||||
String? _videoMediaId;
|
String? _videoMediaId;
|
||||||
final _selectedImageNamesById = <String, String>{};
|
final _selectedImageNamesById = <String, String>{};
|
||||||
String? _selectedVideoName;
|
String? _selectedVideoName;
|
||||||
@ -395,6 +428,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
_imageMediaIds = List<String>.of(exercise?.imageMediaIds ?? const []);
|
_imageMediaIds = List<String>.of(exercise?.imageMediaIds ?? const []);
|
||||||
|
_iconMediaId = exercise?.iconMediaId;
|
||||||
_videoMediaId = exercise?.videoMediaId;
|
_videoMediaId = exercise?.videoMediaId;
|
||||||
_hasTime = exercise?.hasTimeMeasure ?? true;
|
_hasTime = exercise?.hasTimeMeasure ?? true;
|
||||||
_hasReps = exercise?.hasRepsMeasure ?? false;
|
_hasReps = exercise?.hasRepsMeasure ?? false;
|
||||||
@ -450,9 +484,11 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
|||||||
_ImageGalleryField(
|
_ImageGalleryField(
|
||||||
imageMediaIds: _imageMediaIds,
|
imageMediaIds: _imageMediaIds,
|
||||||
imageNamesById: _selectedImageNamesById,
|
imageNamesById: _selectedImageNamesById,
|
||||||
|
iconMediaId: _iconMediaId,
|
||||||
importing: _importingImage,
|
importing: _importingImage,
|
||||||
onAdd: _importImage,
|
onAdd: _importImage,
|
||||||
onRemove: _removeImage,
|
onRemove: _removeImage,
|
||||||
|
onSetIcon: _setIcon,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_MediaImportField(
|
_MediaImportField(
|
||||||
@ -704,10 +740,17 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
|||||||
_imageMediaIds = _imageMediaIds
|
_imageMediaIds = _imageMediaIds
|
||||||
.where((imageId) => imageId != mediaAssetId)
|
.where((imageId) => imageId != mediaAssetId)
|
||||||
.toList();
|
.toList();
|
||||||
|
if (_iconMediaId == mediaAssetId) {
|
||||||
|
_iconMediaId = null;
|
||||||
|
}
|
||||||
_selectedImageNamesById.remove(mediaAssetId);
|
_selectedImageNamesById.remove(mediaAssetId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _setIcon(String mediaAssetId) {
|
||||||
|
setState(() => _iconMediaId = mediaAssetId);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _importVideo() async {
|
Future<void> _importVideo() async {
|
||||||
final sourcePath = await widget.mediaPicker.pickPath(MediaKind.video);
|
final sourcePath = await widget.mediaPicker.pickPath(MediaKind.video);
|
||||||
if (sourcePath == null) {
|
if (sourcePath == null) {
|
||||||
@ -795,6 +838,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
|||||||
name: _nameController.text.trim(),
|
name: _nameController.text.trim(),
|
||||||
description: _optionalText(_descriptionController),
|
description: _optionalText(_descriptionController),
|
||||||
imageMediaIds: _imageMediaIds,
|
imageMediaIds: _imageMediaIds,
|
||||||
|
iconMediaId: _iconMediaId,
|
||||||
videoMediaId: _videoMediaId,
|
videoMediaId: _videoMediaId,
|
||||||
hasTimeMeasure: _hasTime,
|
hasTimeMeasure: _hasTime,
|
||||||
hasRepsMeasure: _hasReps,
|
hasRepsMeasure: _hasReps,
|
||||||
@ -813,6 +857,7 @@ final class _ExerciseFormScreenState extends State<ExerciseFormScreen> {
|
|||||||
name: _nameController.text.trim(),
|
name: _nameController.text.trim(),
|
||||||
description: _optionalText(_descriptionController),
|
description: _optionalText(_descriptionController),
|
||||||
imageMediaIds: _imageMediaIds,
|
imageMediaIds: _imageMediaIds,
|
||||||
|
iconMediaId: _iconMediaId,
|
||||||
videoMediaId: _videoMediaId,
|
videoMediaId: _videoMediaId,
|
||||||
hasTimeMeasure: _hasTime,
|
hasTimeMeasure: _hasTime,
|
||||||
hasRepsMeasure: _hasReps,
|
hasRepsMeasure: _hasReps,
|
||||||
@ -969,16 +1014,20 @@ final class _ImageGalleryField extends StatelessWidget {
|
|||||||
const _ImageGalleryField({
|
const _ImageGalleryField({
|
||||||
required this.imageMediaIds,
|
required this.imageMediaIds,
|
||||||
required this.imageNamesById,
|
required this.imageNamesById,
|
||||||
|
required this.iconMediaId,
|
||||||
required this.importing,
|
required this.importing,
|
||||||
required this.onAdd,
|
required this.onAdd,
|
||||||
required this.onRemove,
|
required this.onRemove,
|
||||||
|
required this.onSetIcon,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<String> imageMediaIds;
|
final List<String> imageMediaIds;
|
||||||
final Map<String, String> imageNamesById;
|
final Map<String, String> imageNamesById;
|
||||||
|
final String? iconMediaId;
|
||||||
final bool importing;
|
final bool importing;
|
||||||
final VoidCallback onAdd;
|
final VoidCallback onAdd;
|
||||||
final ValueChanged<String> onRemove;
|
final ValueChanged<String> onRemove;
|
||||||
|
final ValueChanged<String> onSetIcon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -998,10 +1047,12 @@ final class _ImageGalleryField extends StatelessWidget {
|
|||||||
for (var index = 0; index < imageMediaIds.length; index++)
|
for (var index = 0; index < imageMediaIds.length; index++)
|
||||||
_ImageThumbnail(
|
_ImageThumbnail(
|
||||||
mediaAssetId: imageMediaIds[index],
|
mediaAssetId: imageMediaIds[index],
|
||||||
|
selectedAsIcon: imageMediaIds[index] == iconMediaId,
|
||||||
label:
|
label:
|
||||||
imageNamesById[imageMediaIds[index]] ??
|
imageNamesById[imageMediaIds[index]] ??
|
||||||
'Image ${index + 1}',
|
'Image ${index + 1}',
|
||||||
onRemove: onRemove,
|
onRemove: onRemove,
|
||||||
|
onSetIcon: onSetIcon,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -1035,44 +1086,78 @@ final class _ImageGalleryField extends StatelessWidget {
|
|||||||
final class _ImageThumbnail extends StatelessWidget {
|
final class _ImageThumbnail extends StatelessWidget {
|
||||||
const _ImageThumbnail({
|
const _ImageThumbnail({
|
||||||
required this.mediaAssetId,
|
required this.mediaAssetId,
|
||||||
|
required this.selectedAsIcon,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.onRemove,
|
required this.onRemove,
|
||||||
|
required this.onSetIcon,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String mediaAssetId;
|
final String mediaAssetId;
|
||||||
|
final bool selectedAsIcon;
|
||||||
final String label;
|
final String label;
|
||||||
final ValueChanged<String> onRemove;
|
final ValueChanged<String> onRemove;
|
||||||
|
final ValueChanged<String> onSetIcon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
final tokens = courtBlazerTokensOf(context);
|
final tokens = courtBlazerTokensOf(context);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 104,
|
width: 132,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 104,
|
height: 154,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
border: Border.all(color: tokens.border),
|
border: Border.all(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: selectedAsIcon
|
||||||
|
? theme.colorScheme.primary
|
||||||
|
: tokens.border,
|
||||||
|
width: selectedAsIcon ? 2 : 1,
|
||||||
|
),
|
||||||
|
color: theme.colorScheme.surface,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.image_outlined),
|
Icon(
|
||||||
|
Icons.image_outlined,
|
||||||
|
color: selectedAsIcon ? theme.colorScheme.primary : null,
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
style: theme.textTheme.labelSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
IconButton(
|
||||||
|
tooltip: 'Définir comme icône',
|
||||||
|
onPressed: selectedAsIcon
|
||||||
|
? null
|
||||||
|
: () => onSetIcon(mediaAssetId),
|
||||||
|
icon: const Icon(Icons.star_outline, size: 18),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (selectedAsIcon)
|
||||||
|
Positioned(
|
||||||
|
top: 4,
|
||||||
|
left: 4,
|
||||||
|
child: Tooltip(
|
||||||
|
message: 'Icône choisie',
|
||||||
|
child: Icon(
|
||||||
|
Icons.star,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 2,
|
top: 2,
|
||||||
right: 2,
|
right: 2,
|
||||||
|
|||||||
@ -897,7 +897,8 @@ final class _ProgramExerciseDraft {
|
|||||||
sourceExerciseId: exercise.metadata.id,
|
sourceExerciseId: exercise.metadata.id,
|
||||||
exerciseNameSnapshot: exercise.name,
|
exerciseNameSnapshot: exercise.name,
|
||||||
exerciseDescriptionSnapshot: exercise.description,
|
exerciseDescriptionSnapshot: exercise.description,
|
||||||
exerciseImageMediaIdSnapshot: exercise.imageMediaId,
|
exerciseImageMediaIdSnapshot:
|
||||||
|
exercise.iconMediaId ?? exercise.imageMediaId,
|
||||||
exerciseImageMediaIdsSnapshot: exercise.imageMediaIds,
|
exerciseImageMediaIdsSnapshot: exercise.imageMediaIds,
|
||||||
exerciseVideoMediaIdSnapshot: exercise.videoMediaId,
|
exerciseVideoMediaIdSnapshot: exercise.videoMediaId,
|
||||||
exerciseArchivedSnapshot: exercise.archivedAt != null,
|
exerciseArchivedSnapshot: exercise.archivedAt != null,
|
||||||
|
|||||||
@ -225,6 +225,72 @@ void main() {
|
|||||||
expect(updated.imageMediaId, 'media-1');
|
expect(updated.imageMediaId, 'media-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Exercise icon can be set to an image from the gallery', () async {
|
||||||
|
final repository = _FakeExerciseRepository()
|
||||||
|
..exercise = Exercise(
|
||||||
|
metadata: _metadata('exercise-1'),
|
||||||
|
name: 'Squat',
|
||||||
|
imageMediaIds: const ['media-1', 'media-2'],
|
||||||
|
hasTimeMeasure: false,
|
||||||
|
hasRepsMeasure: true,
|
||||||
|
hasScoreMeasure: false,
|
||||||
|
defaultTargetReps: 10,
|
||||||
|
);
|
||||||
|
final useCase = _exerciseUseCase(repository);
|
||||||
|
|
||||||
|
final updated = await useCase.setIcon(
|
||||||
|
exerciseId: 'exercise-1',
|
||||||
|
mediaAssetId: 'media-2',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(updated.iconMediaId, 'media-2');
|
||||||
|
expect(repository.saved.single.iconMediaId, 'media-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Exercise icon rejects images outside the gallery', () async {
|
||||||
|
final repository = _FakeExerciseRepository()
|
||||||
|
..exercise = Exercise(
|
||||||
|
metadata: _metadata('exercise-1'),
|
||||||
|
name: 'Squat',
|
||||||
|
imageMediaIds: const ['media-1'],
|
||||||
|
hasTimeMeasure: false,
|
||||||
|
hasRepsMeasure: true,
|
||||||
|
hasScoreMeasure: false,
|
||||||
|
defaultTargetReps: 10,
|
||||||
|
);
|
||||||
|
final useCase = _exerciseUseCase(repository);
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
useCase.setIcon(exerciseId: 'exercise-1', mediaAssetId: 'media-2'),
|
||||||
|
throwsA(isA<DomainException>()),
|
||||||
|
);
|
||||||
|
expect(repository.saved, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Exercise icon is reset when its image is removed', () async {
|
||||||
|
final repository = _FakeExerciseRepository()
|
||||||
|
..exercise = Exercise(
|
||||||
|
metadata: _metadata('exercise-1'),
|
||||||
|
name: 'Squat',
|
||||||
|
imageMediaIds: const ['media-1', 'media-2'],
|
||||||
|
iconMediaId: 'media-2',
|
||||||
|
hasTimeMeasure: false,
|
||||||
|
hasRepsMeasure: true,
|
||||||
|
hasScoreMeasure: false,
|
||||||
|
defaultTargetReps: 10,
|
||||||
|
);
|
||||||
|
final useCase = _exerciseUseCase(repository);
|
||||||
|
|
||||||
|
final updated = await useCase.removeImage(
|
||||||
|
exerciseId: 'exercise-1',
|
||||||
|
mediaAssetId: 'media-2',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(updated.imageMediaIds, ['media-1']);
|
||||||
|
expect(updated.iconMediaId, isNull);
|
||||||
|
expect(repository.saved.single.iconMediaId, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
test('program exercise snapshot keeps the full image gallery', () async {
|
test('program exercise snapshot keeps the full image gallery', () async {
|
||||||
final exerciseRepository = _FakeExerciseRepository()
|
final exerciseRepository = _FakeExerciseRepository()
|
||||||
..exercise = Exercise(
|
..exercise = Exercise(
|
||||||
|
|||||||
@ -216,6 +216,72 @@ void main() {
|
|||||||
expect(find.text('Aucune image sélectionnée'), findsOneWidget);
|
expect(find.text('Aucune image sélectionnée'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('désigner une image comme icône l’indique visuellement', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final exerciseRepository = _FakeExerciseRepository();
|
||||||
|
final storage = _FakeMediaStorage();
|
||||||
|
|
||||||
|
await tester.binding.setSurfaceSize(const Size(400, 1600));
|
||||||
|
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ExerciseFormScreen(
|
||||||
|
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||||
|
mediaUseCases: _mediaUseCases(exerciseRepository, storage: storage),
|
||||||
|
mediaPicker: _FakeMediaPicker(
|
||||||
|
imagePaths: const [
|
||||||
|
'/tmp/imports/photo-1.png',
|
||||||
|
'/tmp/imports/photo-2.png',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.enterText(find.widgetWithText(TextFormField, 'Nom'), 'Squat');
|
||||||
|
await tester.enterText(
|
||||||
|
find.widgetWithText(TextFormField, 'Temps par défaut (s)'),
|
||||||
|
'45',
|
||||||
|
);
|
||||||
|
await tester.tap(find.text('Ajouter une image'));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.tap(find.text('Ajouter une image'));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.byTooltip('Définir comme icône').last);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byTooltip('Icône choisie'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Enregistrer'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(exerciseRepository.saved.single.iconMediaId, 'id-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('ExerciseListTile utilise l’icône choisie', (tester) async {
|
||||||
|
final exercise = Exercise(
|
||||||
|
metadata: _metadata('exercise-1'),
|
||||||
|
name: 'Squat',
|
||||||
|
imageMediaIds: const ['image-1', 'image-2'],
|
||||||
|
iconMediaId: 'image-2',
|
||||||
|
hasTimeMeasure: true,
|
||||||
|
hasRepsMeasure: false,
|
||||||
|
hasScoreMeasure: false,
|
||||||
|
defaultTargetTimeSeconds: 45,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(body: ExerciseListTile(exercise: exercise)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byTooltip('Icône d’exercice image-2'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'supprimer un exercice demande confirmation puis le retire de la liste',
|
'supprimer un exercice demande confirmation puis le retire de la liste',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
|
|||||||
Reference in New Issue
Block a user