feat(exercice): affichage des médias pendant l'exécution (ticket #36)
Expose la galerie d'images d'exercice (backend du ticket #35) pendant l'exécution de séance sur workout_execution_screen.dart et program_screen.dart, avec les ajustements domain/use cases/repositories nécessaires. flutter analyze propre, 67/67 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -439,6 +439,7 @@ final class ProgramUseCases {
|
||||
exerciseNameSnapshot: input.exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: input.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: input.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: input.exerciseImageMediaIdsSnapshot,
|
||||
exerciseVideoMediaIdSnapshot: input.exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: input.exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: input.availableTimeSnapshot,
|
||||
@ -555,6 +556,7 @@ final class ProgramExerciseConfig {
|
||||
required this.exerciseNameSnapshot,
|
||||
this.exerciseDescriptionSnapshot,
|
||||
this.exerciseImageMediaIdSnapshot,
|
||||
this.exerciseImageMediaIdsSnapshot = const [],
|
||||
this.exerciseVideoMediaIdSnapshot,
|
||||
this.exerciseArchivedSnapshot = false,
|
||||
required this.availableTimeSnapshot,
|
||||
@ -577,6 +579,7 @@ final class ProgramExerciseConfig {
|
||||
final String exerciseNameSnapshot;
|
||||
final String? exerciseDescriptionSnapshot;
|
||||
final String? exerciseImageMediaIdSnapshot;
|
||||
final List<String> exerciseImageMediaIdsSnapshot;
|
||||
final String? exerciseVideoMediaIdSnapshot;
|
||||
final bool exerciseArchivedSnapshot;
|
||||
final bool availableTimeSnapshot;
|
||||
@ -1501,6 +1504,8 @@ List<ProgramExercise> _repositionProgramExercises(
|
||||
exercises[index].exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot:
|
||||
exercises[index].exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot:
|
||||
exercises[index].exerciseImageMediaIdsSnapshot,
|
||||
exerciseVideoMediaIdSnapshot:
|
||||
exercises[index].exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exercises[index].exerciseArchivedSnapshot,
|
||||
|
||||
@ -303,6 +303,7 @@ final class ProgramExercise {
|
||||
required String exerciseNameSnapshot,
|
||||
this.exerciseDescriptionSnapshot,
|
||||
this.exerciseImageMediaIdSnapshot,
|
||||
List<String> exerciseImageMediaIdsSnapshot = const [],
|
||||
this.exerciseVideoMediaIdSnapshot,
|
||||
this.exerciseArchivedSnapshot = false,
|
||||
required this.availableTimeSnapshot,
|
||||
@ -323,6 +324,12 @@ final class ProgramExercise {
|
||||
}) : exerciseNameSnapshot = _nonBlank(
|
||||
exerciseNameSnapshot,
|
||||
'Exercise snapshot name',
|
||||
),
|
||||
exerciseImageMediaIdsSnapshot = _validatedImageMediaIds(
|
||||
exerciseImageMediaIdsSnapshot.isEmpty &&
|
||||
exerciseImageMediaIdSnapshot != null
|
||||
? [exerciseImageMediaIdSnapshot]
|
||||
: exerciseImageMediaIdsSnapshot,
|
||||
) {
|
||||
_requireNonNegative(position, 'Position');
|
||||
_requirePositive(setsCount, 'Sets count');
|
||||
@ -360,6 +367,7 @@ final class ProgramExercise {
|
||||
final String exerciseNameSnapshot;
|
||||
final String? exerciseDescriptionSnapshot;
|
||||
final String? exerciseImageMediaIdSnapshot;
|
||||
final List<String> exerciseImageMediaIdsSnapshot;
|
||||
final String? exerciseVideoMediaIdSnapshot;
|
||||
final bool exerciseArchivedSnapshot;
|
||||
final bool availableTimeSnapshot;
|
||||
@ -405,6 +413,7 @@ final class ProgramExercise {
|
||||
exerciseNameSnapshot: exercise.name,
|
||||
exerciseDescriptionSnapshot: exercise.description,
|
||||
exerciseImageMediaIdSnapshot: exercise.imageMediaId,
|
||||
exerciseImageMediaIdsSnapshot: exercise.imageMediaIds,
|
||||
exerciseVideoMediaIdSnapshot: exercise.videoMediaId,
|
||||
exerciseArchivedSnapshot: exercise.archivedAt != null,
|
||||
availableTimeSnapshot: exercise.hasTimeMeasure,
|
||||
@ -432,6 +441,8 @@ final class ProgramExercise {
|
||||
'exerciseNameSnapshot': exerciseNameSnapshot,
|
||||
'exerciseDescriptionSnapshot': exerciseDescriptionSnapshot,
|
||||
'exerciseImageMediaIdSnapshot': exerciseImageMediaIdSnapshot,
|
||||
'exerciseImageMediaIdsSnapshot': exerciseImageMediaIdsSnapshot,
|
||||
'imageMediaIdsSnapshot': exerciseImageMediaIdsSnapshot,
|
||||
'exerciseVideoMediaIdSnapshot': exerciseVideoMediaIdSnapshot,
|
||||
'exerciseArchivedSnapshot': exerciseArchivedSnapshot,
|
||||
'availableTimeSnapshot': availableTimeSnapshot,
|
||||
|
||||
@ -37,7 +37,7 @@ final class AppDatabase extends _$AppDatabase {
|
||||
}
|
||||
|
||||
@override
|
||||
int get schemaVersion => 5;
|
||||
int get schemaVersion => 6;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@ -67,6 +67,9 @@ final class AppDatabase extends _$AppDatabase {
|
||||
if (from < 5) {
|
||||
await _migrateToSchema5(migrator);
|
||||
}
|
||||
if (from < 6) {
|
||||
await _migrateToSchema6();
|
||||
}
|
||||
await _createIndexes();
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
@ -250,4 +253,11 @@ extension on AppDatabase {
|
||||
'WHERE image_media_id IS NOT NULL',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _migrateToSchema6() async {
|
||||
await customStatement(
|
||||
'ALTER TABLE program_exercises ADD COLUMN '
|
||||
'exercise_image_media_ids_snapshot_json TEXT',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11148,6 +11148,17 @@ class $ProgramExercisesTable extends ProgramExercises
|
||||
'REFERENCES media_assets (id)',
|
||||
),
|
||||
);
|
||||
static const VerificationMeta _exerciseImageMediaIdsSnapshotJsonMeta =
|
||||
const VerificationMeta('exerciseImageMediaIdsSnapshotJson');
|
||||
@override
|
||||
late final GeneratedColumn<String> exerciseImageMediaIdsSnapshotJson =
|
||||
GeneratedColumn<String>(
|
||||
'exercise_image_media_ids_snapshot_json',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _exerciseVideoMediaIdSnapshotMeta =
|
||||
const VerificationMeta('exerciseVideoMediaIdSnapshot');
|
||||
@override
|
||||
@ -11380,6 +11391,7 @@ class $ProgramExercisesTable extends ProgramExercises
|
||||
exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot,
|
||||
@ -11557,6 +11569,15 @@ class $ProgramExercisesTable extends ProgramExercises
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('exercise_image_media_ids_snapshot_json')) {
|
||||
context.handle(
|
||||
_exerciseImageMediaIdsSnapshotJsonMeta,
|
||||
exerciseImageMediaIdsSnapshotJson.isAcceptableOrUnknown(
|
||||
data['exercise_image_media_ids_snapshot_json']!,
|
||||
_exerciseImageMediaIdsSnapshotJsonMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('exercise_video_media_id_snapshot')) {
|
||||
context.handle(
|
||||
_exerciseVideoMediaIdSnapshotMeta,
|
||||
@ -11795,6 +11816,10 @@ class $ProgramExercisesTable extends ProgramExercises
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}exercise_image_media_id_snapshot'],
|
||||
),
|
||||
exerciseImageMediaIdsSnapshotJson: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}exercise_image_media_ids_snapshot_json'],
|
||||
),
|
||||
exerciseVideoMediaIdSnapshot: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}exercise_video_media_id_snapshot'],
|
||||
@ -11890,6 +11915,7 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
final String exerciseNameSnapshot;
|
||||
final String? exerciseDescriptionSnapshot;
|
||||
final String? exerciseImageMediaIdSnapshot;
|
||||
final String? exerciseImageMediaIdsSnapshotJson;
|
||||
final String? exerciseVideoMediaIdSnapshot;
|
||||
final bool exerciseArchivedSnapshot;
|
||||
final bool availableTimeSnapshot;
|
||||
@ -11925,6 +11951,7 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
required this.exerciseNameSnapshot,
|
||||
this.exerciseDescriptionSnapshot,
|
||||
this.exerciseImageMediaIdSnapshot,
|
||||
this.exerciseImageMediaIdsSnapshotJson,
|
||||
this.exerciseVideoMediaIdSnapshot,
|
||||
required this.exerciseArchivedSnapshot,
|
||||
required this.availableTimeSnapshot,
|
||||
@ -11981,6 +12008,11 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
exerciseImageMediaIdSnapshot,
|
||||
);
|
||||
}
|
||||
if (!nullToAbsent || exerciseImageMediaIdsSnapshotJson != null) {
|
||||
map['exercise_image_media_ids_snapshot_json'] = Variable<String>(
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
);
|
||||
}
|
||||
if (!nullToAbsent || exerciseVideoMediaIdSnapshot != null) {
|
||||
map['exercise_video_media_id_snapshot'] = Variable<String>(
|
||||
exerciseVideoMediaIdSnapshot,
|
||||
@ -12056,6 +12088,10 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
exerciseImageMediaIdSnapshot == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(exerciseImageMediaIdSnapshot),
|
||||
exerciseImageMediaIdsSnapshotJson:
|
||||
exerciseImageMediaIdsSnapshotJson == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(exerciseImageMediaIdsSnapshotJson),
|
||||
exerciseVideoMediaIdSnapshot:
|
||||
exerciseVideoMediaIdSnapshot == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
@ -12124,6 +12160,9 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
exerciseImageMediaIdSnapshot: serializer.fromJson<String?>(
|
||||
json['exerciseImageMediaIdSnapshot'],
|
||||
),
|
||||
exerciseImageMediaIdsSnapshotJson: serializer.fromJson<String?>(
|
||||
json['exerciseImageMediaIdsSnapshotJson'],
|
||||
),
|
||||
exerciseVideoMediaIdSnapshot: serializer.fromJson<String?>(
|
||||
json['exerciseVideoMediaIdSnapshot'],
|
||||
),
|
||||
@ -12186,6 +12225,9 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
'exerciseImageMediaIdSnapshot': serializer.toJson<String?>(
|
||||
exerciseImageMediaIdSnapshot,
|
||||
),
|
||||
'exerciseImageMediaIdsSnapshotJson': serializer.toJson<String?>(
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
),
|
||||
'exerciseVideoMediaIdSnapshot': serializer.toJson<String?>(
|
||||
exerciseVideoMediaIdSnapshot,
|
||||
),
|
||||
@ -12230,6 +12272,7 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
String? exerciseNameSnapshot,
|
||||
Value<String?> exerciseDescriptionSnapshot = const Value.absent(),
|
||||
Value<String?> exerciseImageMediaIdSnapshot = const Value.absent(),
|
||||
Value<String?> exerciseImageMediaIdsSnapshotJson = const Value.absent(),
|
||||
Value<String?> exerciseVideoMediaIdSnapshot = const Value.absent(),
|
||||
bool? exerciseArchivedSnapshot,
|
||||
bool? availableTimeSnapshot,
|
||||
@ -12275,6 +12318,9 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot.present
|
||||
? exerciseImageMediaIdSnapshot.value
|
||||
: this.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson: exerciseImageMediaIdsSnapshotJson.present
|
||||
? exerciseImageMediaIdsSnapshotJson.value
|
||||
: this.exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot.present
|
||||
? exerciseVideoMediaIdSnapshot.value
|
||||
: this.exerciseVideoMediaIdSnapshot,
|
||||
@ -12347,6 +12393,10 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
exerciseImageMediaIdSnapshot: data.exerciseImageMediaIdSnapshot.present
|
||||
? data.exerciseImageMediaIdSnapshot.value
|
||||
: this.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson:
|
||||
data.exerciseImageMediaIdsSnapshotJson.present
|
||||
? data.exerciseImageMediaIdsSnapshotJson.value
|
||||
: this.exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot: data.exerciseVideoMediaIdSnapshot.present
|
||||
? data.exerciseVideoMediaIdSnapshot.value
|
||||
: this.exerciseVideoMediaIdSnapshot,
|
||||
@ -12421,6 +12471,9 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
..write(
|
||||
'exerciseImageMediaIdSnapshot: $exerciseImageMediaIdSnapshot, ',
|
||||
)
|
||||
..write(
|
||||
'exerciseImageMediaIdsSnapshotJson: $exerciseImageMediaIdsSnapshotJson, ',
|
||||
)
|
||||
..write(
|
||||
'exerciseVideoMediaIdSnapshot: $exerciseVideoMediaIdSnapshot, ',
|
||||
)
|
||||
@ -12463,6 +12516,7 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot,
|
||||
@ -12504,6 +12558,8 @@ class ProgramExercise extends DataClass implements Insertable<ProgramExercise> {
|
||||
this.exerciseDescriptionSnapshot &&
|
||||
other.exerciseImageMediaIdSnapshot ==
|
||||
this.exerciseImageMediaIdSnapshot &&
|
||||
other.exerciseImageMediaIdsSnapshotJson ==
|
||||
this.exerciseImageMediaIdsSnapshotJson &&
|
||||
other.exerciseVideoMediaIdSnapshot ==
|
||||
this.exerciseVideoMediaIdSnapshot &&
|
||||
other.exerciseArchivedSnapshot == this.exerciseArchivedSnapshot &&
|
||||
@ -12542,6 +12598,7 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
final Value<String> exerciseNameSnapshot;
|
||||
final Value<String?> exerciseDescriptionSnapshot;
|
||||
final Value<String?> exerciseImageMediaIdSnapshot;
|
||||
final Value<String?> exerciseImageMediaIdsSnapshotJson;
|
||||
final Value<String?> exerciseVideoMediaIdSnapshot;
|
||||
final Value<bool> exerciseArchivedSnapshot;
|
||||
final Value<bool> availableTimeSnapshot;
|
||||
@ -12578,6 +12635,7 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
this.exerciseNameSnapshot = const Value.absent(),
|
||||
this.exerciseDescriptionSnapshot = const Value.absent(),
|
||||
this.exerciseImageMediaIdSnapshot = const Value.absent(),
|
||||
this.exerciseImageMediaIdsSnapshotJson = const Value.absent(),
|
||||
this.exerciseVideoMediaIdSnapshot = const Value.absent(),
|
||||
this.exerciseArchivedSnapshot = const Value.absent(),
|
||||
this.availableTimeSnapshot = const Value.absent(),
|
||||
@ -12615,6 +12673,7 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
required String exerciseNameSnapshot,
|
||||
this.exerciseDescriptionSnapshot = const Value.absent(),
|
||||
this.exerciseImageMediaIdSnapshot = const Value.absent(),
|
||||
this.exerciseImageMediaIdsSnapshotJson = const Value.absent(),
|
||||
this.exerciseVideoMediaIdSnapshot = const Value.absent(),
|
||||
this.exerciseArchivedSnapshot = const Value.absent(),
|
||||
required bool availableTimeSnapshot,
|
||||
@ -12667,6 +12726,7 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
Expression<String>? exerciseNameSnapshot,
|
||||
Expression<String>? exerciseDescriptionSnapshot,
|
||||
Expression<String>? exerciseImageMediaIdSnapshot,
|
||||
Expression<String>? exerciseImageMediaIdsSnapshotJson,
|
||||
Expression<String>? exerciseVideoMediaIdSnapshot,
|
||||
Expression<bool>? exerciseArchivedSnapshot,
|
||||
Expression<bool>? availableTimeSnapshot,
|
||||
@ -12708,6 +12768,9 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
'exercise_description_snapshot': exerciseDescriptionSnapshot,
|
||||
if (exerciseImageMediaIdSnapshot != null)
|
||||
'exercise_image_media_id_snapshot': exerciseImageMediaIdSnapshot,
|
||||
if (exerciseImageMediaIdsSnapshotJson != null)
|
||||
'exercise_image_media_ids_snapshot_json':
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
if (exerciseVideoMediaIdSnapshot != null)
|
||||
'exercise_video_media_id_snapshot': exerciseVideoMediaIdSnapshot,
|
||||
if (exerciseArchivedSnapshot != null)
|
||||
@ -12755,6 +12818,7 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
Value<String>? exerciseNameSnapshot,
|
||||
Value<String?>? exerciseDescriptionSnapshot,
|
||||
Value<String?>? exerciseImageMediaIdSnapshot,
|
||||
Value<String?>? exerciseImageMediaIdsSnapshotJson,
|
||||
Value<String?>? exerciseVideoMediaIdSnapshot,
|
||||
Value<bool>? exerciseArchivedSnapshot,
|
||||
Value<bool>? availableTimeSnapshot,
|
||||
@ -12794,6 +12858,9 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
exerciseDescriptionSnapshot ?? this.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot:
|
||||
exerciseImageMediaIdSnapshot ?? this.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson:
|
||||
exerciseImageMediaIdsSnapshotJson ??
|
||||
this.exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot:
|
||||
exerciseVideoMediaIdSnapshot ?? this.exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot:
|
||||
@ -12883,6 +12950,11 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
exerciseImageMediaIdSnapshot.value,
|
||||
);
|
||||
}
|
||||
if (exerciseImageMediaIdsSnapshotJson.present) {
|
||||
map['exercise_image_media_ids_snapshot_json'] = Variable<String>(
|
||||
exerciseImageMediaIdsSnapshotJson.value,
|
||||
);
|
||||
}
|
||||
if (exerciseVideoMediaIdSnapshot.present) {
|
||||
map['exercise_video_media_id_snapshot'] = Variable<String>(
|
||||
exerciseVideoMediaIdSnapshot.value,
|
||||
@ -12974,6 +13046,9 @@ class ProgramExercisesCompanion extends UpdateCompanion<ProgramExercise> {
|
||||
..write(
|
||||
'exerciseImageMediaIdSnapshot: $exerciseImageMediaIdSnapshot, ',
|
||||
)
|
||||
..write(
|
||||
'exerciseImageMediaIdsSnapshotJson: $exerciseImageMediaIdsSnapshotJson, ',
|
||||
)
|
||||
..write(
|
||||
'exerciseVideoMediaIdSnapshot: $exerciseVideoMediaIdSnapshot, ',
|
||||
)
|
||||
@ -25793,6 +25868,7 @@ typedef $$ProgramExercisesTableCreateCompanionBuilder =
|
||||
required String exerciseNameSnapshot,
|
||||
Value<String?> exerciseDescriptionSnapshot,
|
||||
Value<String?> exerciseImageMediaIdSnapshot,
|
||||
Value<String?> exerciseImageMediaIdsSnapshotJson,
|
||||
Value<String?> exerciseVideoMediaIdSnapshot,
|
||||
Value<bool> exerciseArchivedSnapshot,
|
||||
required bool availableTimeSnapshot,
|
||||
@ -25831,6 +25907,7 @@ typedef $$ProgramExercisesTableUpdateCompanionBuilder =
|
||||
Value<String> exerciseNameSnapshot,
|
||||
Value<String?> exerciseDescriptionSnapshot,
|
||||
Value<String?> exerciseImageMediaIdSnapshot,
|
||||
Value<String?> exerciseImageMediaIdsSnapshotJson,
|
||||
Value<String?> exerciseVideoMediaIdSnapshot,
|
||||
Value<bool> exerciseArchivedSnapshot,
|
||||
Value<bool> availableTimeSnapshot,
|
||||
@ -26019,6 +26096,12 @@ class $$ProgramExercisesTableFilterComposer
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get exerciseImageMediaIdsSnapshotJson =>
|
||||
$composableBuilder(
|
||||
column: $table.exerciseImageMediaIdsSnapshotJson,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get exerciseArchivedSnapshot => $composableBuilder(
|
||||
column: $table.exerciseArchivedSnapshot,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
@ -26271,6 +26354,12 @@ class $$ProgramExercisesTableOrderingComposer
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get exerciseImageMediaIdsSnapshotJson =>
|
||||
$composableBuilder(
|
||||
column: $table.exerciseImageMediaIdsSnapshotJson,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get exerciseArchivedSnapshot => $composableBuilder(
|
||||
column: $table.exerciseArchivedSnapshot,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
@ -26511,6 +26600,12 @@ class $$ProgramExercisesTableAnnotationComposer
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get exerciseImageMediaIdsSnapshotJson =>
|
||||
$composableBuilder(
|
||||
column: $table.exerciseImageMediaIdsSnapshotJson,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<bool> get exerciseArchivedSnapshot => $composableBuilder(
|
||||
column: $table.exerciseArchivedSnapshot,
|
||||
builder: (column) => column,
|
||||
@ -26736,6 +26831,8 @@ class $$ProgramExercisesTableTableManager
|
||||
const Value.absent(),
|
||||
Value<String?> exerciseImageMediaIdSnapshot =
|
||||
const Value.absent(),
|
||||
Value<String?> exerciseImageMediaIdsSnapshotJson =
|
||||
const Value.absent(),
|
||||
Value<String?> exerciseVideoMediaIdSnapshot =
|
||||
const Value.absent(),
|
||||
Value<bool> exerciseArchivedSnapshot = const Value.absent(),
|
||||
@ -26773,6 +26870,8 @@ class $$ProgramExercisesTableTableManager
|
||||
exerciseNameSnapshot: exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson:
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
@ -26813,6 +26912,8 @@ class $$ProgramExercisesTableTableManager
|
||||
const Value.absent(),
|
||||
Value<String?> exerciseImageMediaIdSnapshot =
|
||||
const Value.absent(),
|
||||
Value<String?> exerciseImageMediaIdsSnapshotJson =
|
||||
const Value.absent(),
|
||||
Value<String?> exerciseVideoMediaIdSnapshot =
|
||||
const Value.absent(),
|
||||
Value<bool> exerciseArchivedSnapshot = const Value.absent(),
|
||||
@ -26850,6 +26951,8 @@ class $$ProgramExercisesTableTableManager
|
||||
exerciseNameSnapshot: exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshotJson:
|
||||
exerciseImageMediaIdsSnapshotJson,
|
||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
import '../../application/application.dart';
|
||||
@ -1208,6 +1210,9 @@ db.ProgramExercisesCompanion _programExerciseCompanion(
|
||||
exerciseNameSnapshot: Value(exercise.exerciseNameSnapshot),
|
||||
exerciseDescriptionSnapshot: Value(exercise.exerciseDescriptionSnapshot),
|
||||
exerciseImageMediaIdSnapshot: Value(exercise.exerciseImageMediaIdSnapshot),
|
||||
exerciseImageMediaIdsSnapshotJson: Value(
|
||||
_encodeImageMediaIdsSnapshot(exercise.exerciseImageMediaIdsSnapshot),
|
||||
),
|
||||
exerciseVideoMediaIdSnapshot: Value(exercise.exerciseVideoMediaIdSnapshot),
|
||||
exerciseArchivedSnapshot: Value(exercise.exerciseArchivedSnapshot),
|
||||
availableTimeSnapshot: Value(exercise.availableTimeSnapshot),
|
||||
@ -1239,6 +1244,10 @@ domain.ProgramExercise _programExerciseFromRow(db.ProgramExercise row) {
|
||||
exerciseNameSnapshot: row.exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: row.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: row.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: _decodeImageMediaIdsSnapshot(
|
||||
row.exerciseImageMediaIdsSnapshotJson,
|
||||
row.exerciseImageMediaIdSnapshot,
|
||||
),
|
||||
exerciseVideoMediaIdSnapshot: row.exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: row.exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: row.availableTimeSnapshot,
|
||||
@ -1766,6 +1775,29 @@ domain.ActiveScoreStopwatchStatus _scoreStopwatchStatusFromDb(String value) =>
|
||||
),
|
||||
};
|
||||
|
||||
String? _encodeImageMediaIdsSnapshot(List<String> imageMediaIds) {
|
||||
return imageMediaIds.isEmpty ? null : jsonEncode(imageMediaIds);
|
||||
}
|
||||
|
||||
List<String> _decodeImageMediaIdsSnapshot(
|
||||
String? encoded,
|
||||
String? fallbackImageMediaId,
|
||||
) {
|
||||
if (encoded == null || encoded.trim().isEmpty) {
|
||||
return [if (fallbackImageMediaId != null) fallbackImageMediaId];
|
||||
}
|
||||
final decoded = jsonDecode(encoded);
|
||||
if (decoded is! List) {
|
||||
throw const domain.DomainException(
|
||||
'Invalid exercise image media ids snapshot.',
|
||||
);
|
||||
}
|
||||
return decoded
|
||||
.whereType<String>()
|
||||
.where((id) => id.trim().isNotEmpty)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
domain.ActiveWorkoutStatus _activeStatusFromDb(String value) => switch (value) {
|
||||
'running' => domain.ActiveWorkoutStatus.running,
|
||||
'paused' => domain.ActiveWorkoutStatus.paused,
|
||||
|
||||
@ -128,6 +128,7 @@ class ProgramExercises extends SyncableTable {
|
||||
@ReferenceName('programExerciseImageSnapshotReferences')
|
||||
TextColumn get exerciseImageMediaIdSnapshot =>
|
||||
text().nullable().references(MediaAssets, #id)();
|
||||
TextColumn get exerciseImageMediaIdsSnapshotJson => text().nullable()();
|
||||
|
||||
@ReferenceName('programExerciseVideoSnapshotReferences')
|
||||
TextColumn get exerciseVideoMediaIdSnapshot =>
|
||||
|
||||
@ -870,6 +870,7 @@ final class _ProgramExerciseDraft {
|
||||
required this.exerciseNameSnapshot,
|
||||
this.exerciseDescriptionSnapshot,
|
||||
this.exerciseImageMediaIdSnapshot,
|
||||
this.exerciseImageMediaIdsSnapshot = const [],
|
||||
this.exerciseVideoMediaIdSnapshot,
|
||||
this.exerciseArchivedSnapshot = false,
|
||||
required this.availableTimeSnapshot,
|
||||
@ -897,6 +898,7 @@ final class _ProgramExerciseDraft {
|
||||
exerciseNameSnapshot: exercise.name,
|
||||
exerciseDescriptionSnapshot: exercise.description,
|
||||
exerciseImageMediaIdSnapshot: exercise.imageMediaId,
|
||||
exerciseImageMediaIdsSnapshot: exercise.imageMediaIds,
|
||||
exerciseVideoMediaIdSnapshot: exercise.videoMediaId,
|
||||
exerciseArchivedSnapshot: exercise.archivedAt != null,
|
||||
availableTimeSnapshot: exercise.hasTimeMeasure,
|
||||
@ -927,6 +929,7 @@ final class _ProgramExerciseDraft {
|
||||
exerciseNameSnapshot: exercise.exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: exercise.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: exercise.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: exercise.exerciseImageMediaIdsSnapshot,
|
||||
exerciseVideoMediaIdSnapshot: exercise.exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exercise.exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: exercise.availableTimeSnapshot,
|
||||
@ -955,6 +958,7 @@ final class _ProgramExerciseDraft {
|
||||
final String exerciseNameSnapshot;
|
||||
final String? exerciseDescriptionSnapshot;
|
||||
final String? exerciseImageMediaIdSnapshot;
|
||||
final List<String> exerciseImageMediaIdsSnapshot;
|
||||
final String? exerciseVideoMediaIdSnapshot;
|
||||
final bool exerciseArchivedSnapshot;
|
||||
final bool availableTimeSnapshot;
|
||||
@ -979,6 +983,7 @@ final class _ProgramExerciseDraft {
|
||||
exerciseNameSnapshot: exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: exerciseImageMediaIdsSnapshot,
|
||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
@ -1006,6 +1011,7 @@ final class _ProgramExerciseDraft {
|
||||
exerciseNameSnapshot: exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: exerciseImageMediaIdsSnapshot,
|
||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
|
||||
@ -130,6 +130,20 @@ final class _WorkoutExecutionScreenState extends State<WorkoutExecutionScreen> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(_exercise.name, style: Theme.of(context).textTheme.headlineMedium),
|
||||
if (_exercise.hasMedia) ...[
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Tooltip(
|
||||
message: 'Voir les médias de l’exercice',
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _openExerciseMedia(_exercise),
|
||||
icon: const Icon(Icons.perm_media_outlined),
|
||||
label: const Text('Voir médias'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
SetMeasureInput(
|
||||
exercise: _exercise,
|
||||
@ -183,6 +197,19 @@ final class _WorkoutExecutionScreenState extends State<WorkoutExecutionScreen> {
|
||||
fontSize: 30,
|
||||
),
|
||||
),
|
||||
if (nextExercise.hasMedia) ...[
|
||||
const SizedBox(height: 12),
|
||||
Center(
|
||||
child: Tooltip(
|
||||
message: 'Voir les médias de l’exercice',
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _openExerciseMedia(nextExercise),
|
||||
icon: const Icon(Icons.perm_media_outlined),
|
||||
label: const Text('Voir médias'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
const Spacer(),
|
||||
Row(
|
||||
@ -282,6 +309,23 @@ final class _WorkoutExecutionScreenState extends State<WorkoutExecutionScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openExerciseMedia(ExecutionExercise exercise) async {
|
||||
if (!exercise.hasMedia) {
|
||||
return;
|
||||
}
|
||||
await showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (context) => _ExerciseMediaSheet(
|
||||
exercise: exercise,
|
||||
restRemainingSeconds: _mode == WorkoutExecutionMode.rest
|
||||
? _remainingRestSeconds
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool get _canPauseFromNavigation {
|
||||
return _mode == WorkoutExecutionMode.active ||
|
||||
_mode == WorkoutExecutionMode.rest;
|
||||
@ -816,6 +860,228 @@ final class _RestPlanBanner extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
enum _MediaSheetSection { images, video }
|
||||
|
||||
final class _ExerciseMediaSheet extends StatefulWidget {
|
||||
const _ExerciseMediaSheet({
|
||||
required this.exercise,
|
||||
this.restRemainingSeconds,
|
||||
});
|
||||
|
||||
final ExecutionExercise exercise;
|
||||
final int? restRemainingSeconds;
|
||||
|
||||
@override
|
||||
State<_ExerciseMediaSheet> createState() => _ExerciseMediaSheetState();
|
||||
}
|
||||
|
||||
final class _ExerciseMediaSheetState extends State<_ExerciseMediaSheet> {
|
||||
late final PageController _pageController;
|
||||
late _MediaSheetSection _section;
|
||||
var _imageIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pageController = PageController();
|
||||
_section = widget.exercise.imageMediaIds.isNotEmpty
|
||||
? _MediaSheetSection.images
|
||||
: _MediaSheetSection.video;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final exercise = widget.exercise;
|
||||
final hasImages = exercise.imageMediaIds.isNotEmpty;
|
||||
final hasVideo = exercise.videoMediaId != null;
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.95,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Médias de l’exercice'),
|
||||
Text(exercise.name, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: 'Fermer',
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
if (widget.restRemainingSeconds != null) ...[
|
||||
_RestPlanBanner(remainingSeconds: widget.restRemainingSeconds!),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
if (hasImages && hasVideo) ...[
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
ChoiceChip(
|
||||
label: const Text('Images'),
|
||||
selected: _section == _MediaSheetSection.images,
|
||||
onSelected: (_) {
|
||||
setState(() => _section = _MediaSheetSection.images);
|
||||
},
|
||||
),
|
||||
ChoiceChip(
|
||||
label: const Text('Vidéo'),
|
||||
selected: _section == _MediaSheetSection.video,
|
||||
onSelected: (_) {
|
||||
setState(() => _section = _MediaSheetSection.video);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
if (_section == _MediaSheetSection.images && hasImages)
|
||||
_ImageMediaGallery(
|
||||
imageMediaIds: exercise.imageMediaIds,
|
||||
pageController: _pageController,
|
||||
currentIndex: _imageIndex,
|
||||
onPageChanged: (index) => setState(() => _imageIndex = index),
|
||||
)
|
||||
else if (hasVideo)
|
||||
_VideoMediaPanel(videoMediaId: exercise.videoMediaId!),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class _ImageMediaGallery extends StatelessWidget {
|
||||
const _ImageMediaGallery({
|
||||
required this.imageMediaIds,
|
||||
required this.pageController,
|
||||
required this.currentIndex,
|
||||
required this.onPageChanged,
|
||||
});
|
||||
|
||||
final List<String> imageMediaIds;
|
||||
final PageController pageController;
|
||||
final int currentIndex;
|
||||
final ValueChanged<int> onPageChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CourtBlazerAccentPanel(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'${currentIndex + 1}/${imageMediaIds.length}',
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 320,
|
||||
child: PageView.builder(
|
||||
controller: pageController,
|
||||
itemCount: imageMediaIds.length,
|
||||
onPageChanged: onPageChanged,
|
||||
itemBuilder: (context, index) {
|
||||
return _ImageMediaPage(
|
||||
mediaAssetId: imageMediaIds[index],
|
||||
index: index,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class _ImageMediaPage extends StatelessWidget {
|
||||
const _ImageMediaPage({required this.mediaAssetId, required this.index});
|
||||
|
||||
final String mediaAssetId;
|
||||
final int index;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = courtBlazerTokensOf(context);
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: tokens.border),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.image_outlined,
|
||||
size: 64,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Image ${index + 1}',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(mediaAssetId, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class _VideoMediaPanel extends StatelessWidget {
|
||||
const _VideoMediaPanel({required this.videoMediaId});
|
||||
|
||||
final String videoMediaId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CourtBlazerAccentPanel(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: SizedBox(
|
||||
height: 260,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.play_circle_outline,
|
||||
size: 64,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('Vidéo', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 4),
|
||||
Text(videoMediaId, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class _WorkoutPlanSetTile extends StatelessWidget {
|
||||
const _WorkoutPlanSetTile({
|
||||
required this.position,
|
||||
@ -1364,6 +1630,8 @@ final class ExecutionExercise {
|
||||
required this.repsEnabled,
|
||||
required this.scoreEnabled,
|
||||
required this.restSeconds,
|
||||
this.imageMediaIds = const [],
|
||||
this.videoMediaId,
|
||||
this.scoreInputMode = ScoreInputMode.manual,
|
||||
this.scoreLabel,
|
||||
this.targetTimeSeconds,
|
||||
@ -1386,6 +1654,10 @@ final class ExecutionExercise {
|
||||
repsEnabled: exercise['repsEnabled'] as bool,
|
||||
scoreEnabled: exercise['scoreEnabled'] as bool,
|
||||
restSeconds: exercise['restSecondsOverride'] as int? ?? 0,
|
||||
imageMediaIds: _imageMediaIdsFromSnapshot(exercise),
|
||||
videoMediaId:
|
||||
exercise['videoMediaIdSnapshot'] as String? ??
|
||||
exercise['exerciseVideoMediaIdSnapshot'] as String?,
|
||||
scoreInputMode: _scoreInputModeFromSnapshot(
|
||||
exercise['scoreInputModeSnapshot'],
|
||||
),
|
||||
@ -1410,6 +1682,8 @@ final class ExecutionExercise {
|
||||
final bool repsEnabled;
|
||||
final bool scoreEnabled;
|
||||
final int restSeconds;
|
||||
final List<String> imageMediaIds;
|
||||
final String? videoMediaId;
|
||||
final ScoreInputMode scoreInputMode;
|
||||
final String? scoreLabel;
|
||||
final int? targetTimeSeconds;
|
||||
@ -1420,6 +1694,26 @@ final class ExecutionExercise {
|
||||
bool get manualScoreEnabled {
|
||||
return scoreEnabled && scoreInputMode == ScoreInputMode.manual;
|
||||
}
|
||||
|
||||
bool get hasMedia {
|
||||
return imageMediaIds.isNotEmpty || videoMediaId != null;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _imageMediaIdsFromSnapshot(Map<String, dynamic> exercise) {
|
||||
final imageMediaIds =
|
||||
exercise['imageMediaIdsSnapshot'] ??
|
||||
exercise['exerciseImageMediaIdsSnapshot'];
|
||||
if (imageMediaIds is List) {
|
||||
return imageMediaIds
|
||||
.whereType<String>()
|
||||
.where((id) => id.trim().isNotEmpty)
|
||||
.toList(growable: false);
|
||||
}
|
||||
final imageMediaId = exercise['exerciseImageMediaIdSnapshot'] as String?;
|
||||
return [
|
||||
if (imageMediaId != null && imageMediaId.trim().isNotEmpty) imageMediaId,
|
||||
];
|
||||
}
|
||||
|
||||
ScoreInputMode _scoreInputModeFromSnapshot(Object? value) {
|
||||
|
||||
Reference in New Issue
Block a user