diff --git a/lib/application/use_cases.dart b/lib/application/use_cases.dart index e3d9fa3..f612def 100644 --- a/lib/application/use_cases.dart +++ b/lib/application/use_cases.dart @@ -37,7 +37,9 @@ final class ExerciseUseCases { int? defaultTargetReps, double? defaultTargetScore, int? defaultTargetScoreTimeMs, + List steps = const [], }) async { + _validateExerciseSteps(steps); _validateExerciseDefaultTargets( hasTimeMeasure: hasTimeMeasure, hasRepsMeasure: hasRepsMeasure, @@ -97,6 +99,7 @@ final class ExerciseUseCases { Object? defaultTargetReps = _useCaseUnchanged, Object? defaultTargetScore = _useCaseUnchanged, Object? defaultTargetScoreTimeMs = _useCaseUnchanged, + Object? steps = _useCaseUnchanged, }) async { final exercise = await repository.findById(id); if (exercise == null) { @@ -126,6 +129,10 @@ final class ExerciseUseCases { defaultTargetScore: resolvedDefaultTargetScore, defaultTargetScoreTimeMs: resolvedDefaultTargetScoreTimeMs, ); + final resolvedSteps = steps == _useCaseUnchanged + ? exercise.steps + : steps as List; + _validateExerciseSteps(resolvedSteps); final resolvedImageMediaIds = imageMediaIds == _useCaseUnchanged ? _resolveExerciseImageIds(imageMediaId, exercise.imageMediaIds) : imageMediaIds as List; @@ -152,6 +159,7 @@ final class ExerciseUseCases { defaultTargetReps: resolvedDefaultTargetReps, defaultTargetScore: resolvedDefaultTargetScore, defaultTargetScoreTimeMs: resolvedDefaultTargetScoreTimeMs, + steps: resolvedSteps, ); await repository.save(updated); return updated; @@ -475,6 +483,7 @@ final class ProgramUseCases { exerciseImageMediaIdSnapshot: input.exerciseImageMediaIdSnapshot, exerciseImageMediaIdsSnapshot: input.exerciseImageMediaIdsSnapshot, exerciseVideoMediaIdSnapshot: input.exerciseVideoMediaIdSnapshot, + exerciseStepsSnapshot: input.exerciseStepsSnapshot, exerciseArchivedSnapshot: input.exerciseArchivedSnapshot, availableTimeSnapshot: input.availableTimeSnapshot, availableRepsSnapshot: input.availableRepsSnapshot, @@ -592,6 +601,7 @@ final class ProgramExerciseConfig { this.exerciseImageMediaIdSnapshot, this.exerciseImageMediaIdsSnapshot = const [], this.exerciseVideoMediaIdSnapshot, + this.exerciseStepsSnapshot = const [], this.exerciseArchivedSnapshot = false, required this.availableTimeSnapshot, required this.availableRepsSnapshot, @@ -615,6 +625,7 @@ final class ProgramExerciseConfig { final String? exerciseImageMediaIdSnapshot; final List exerciseImageMediaIdsSnapshot; final String? exerciseVideoMediaIdSnapshot; + final List exerciseStepsSnapshot; final bool exerciseArchivedSnapshot; final bool availableTimeSnapshot; final bool availableRepsSnapshot; @@ -1560,6 +1571,7 @@ List _repositionProgramExercises( exercises[index].exerciseImageMediaIdsSnapshot, exerciseVideoMediaIdSnapshot: exercises[index].exerciseVideoMediaIdSnapshot, + exerciseStepsSnapshot: exercises[index].exerciseStepsSnapshot, exerciseArchivedSnapshot: exercises[index].exerciseArchivedSnapshot, availableTimeSnapshot: exercises[index].availableTimeSnapshot, availableRepsSnapshot: exercises[index].availableRepsSnapshot, @@ -1625,6 +1637,82 @@ bool _sameImageSet(List left, List right) { return left.toSet().containsAll(right) && right.toSet().containsAll(left); } +void _validateExerciseSteps(List steps) { + if (steps.length > 8) { + throw const DomainException('An exercise cannot have more than 8 steps.'); + } + for (var index = 0; index < steps.length; index++) { + final step = steps[index]; + if (step.position != index) { + throw const DomainException( + 'Exercise step positions must be contiguous and ordered.', + ); + } + if (step.name.trim().isEmpty) { + throw const DomainException('Exercise step name must not be blank.'); + } + if (step.defaultTargetValue <= 0) { + throw const DomainException( + 'Exercise step default target value must be positive.', + ); + } + if (!step.hasScore) { + if (step.scoreLabel != null || + step.scoreUnit != null || + step.defaultTargetScore != null || + step.defaultTargetScoreTimeMs != null) { + throw const DomainException( + 'Disabled exercise step score must not define score values.', + ); + } + continue; + } + if (step.defaultTargetScore != null && + step.defaultTargetScoreTimeMs != null) { + throw const DomainException( + 'Manual and stopwatch step score targets cannot both be set.', + ); + } + switch (step.scoreInputMode) { + case ScoreInputMode.manual: + if (step.scoreLabel == null || step.scoreLabel!.trim().isEmpty) { + throw const DomainException( + 'Exercise step score label must not be blank.', + ); + } + if (step.scoreUnit == null || step.scoreUnit!.trim().isEmpty) { + throw const DomainException( + 'Exercise step score unit must not be blank.', + ); + } + if (step.defaultTargetScore != null && step.defaultTargetScore! < 0) { + throw const DomainException( + 'Exercise step default target score must not be negative.', + ); + } + if (step.defaultTargetScoreTimeMs != null) { + throw const DomainException( + 'Manual exercise step score cannot define stopwatch target.', + ); + } + case ScoreInputMode.stopwatch: + if (step.scoreLabel != null || + step.scoreUnit != null || + step.defaultTargetScore != null) { + throw const DomainException( + 'Stopwatch exercise step score cannot define manual score values.', + ); + } + if (step.defaultTargetScoreTimeMs != null && + step.defaultTargetScoreTimeMs! <= 0) { + throw const DomainException( + 'Exercise step default target score time ms must be positive.', + ); + } + } + } +} + void _validateExerciseDefaultTargets({ required bool hasTimeMeasure, required bool hasRepsMeasure, diff --git a/lib/domain/entities.dart b/lib/domain/entities.dart index f23e779..a8e8501 100644 --- a/lib/domain/entities.dart +++ b/lib/domain/entities.dart @@ -8,6 +8,8 @@ enum WorkoutMeasure { time, reps, score } enum ScoreInputMode { manual, stopwatch } +enum ExerciseStepType { time, reps } + enum ActiveWorkoutStatus { running, paused, savedExit, completed, abandoned } enum SetResultStatus { completed, skipped } @@ -138,6 +140,7 @@ final class Exercise { this.defaultTargetReps, this.defaultTargetScore, this.defaultTargetScoreTimeMs, + List steps = const [], this.archivedAt, }) : name = _nonBlank(name, 'Exercise name'), imageMediaIds = _resolvedExerciseImageMediaIds( @@ -147,7 +150,8 @@ final class Exercise { iconMediaId = _validatedIconMediaId( iconMediaId, _resolvedExerciseImageMediaIds(imageMediaId, imageMediaIds), - ) { + ), + steps = _validatedExerciseSteps(steps) { _requireAtLeastOneMeasure( hasTime: hasTimeMeasure, hasReps: hasRepsMeasure, @@ -193,6 +197,7 @@ final class Exercise { final int? defaultTargetReps; final double? defaultTargetScore; final int? defaultTargetScoreTimeMs; + final List steps; final DateTime? archivedAt; Set get availableMeasures => { @@ -226,6 +231,7 @@ final class Exercise { Object? defaultTargetReps = _unchanged, Object? defaultTargetScore = _unchanged, Object? defaultTargetScoreTimeMs = _unchanged, + Object? steps = _unchanged, Object? archivedAt = _unchanged, }) { final resolvedImageMediaIds = imageMediaIds == _unchanged @@ -272,6 +278,7 @@ final class Exercise { defaultTargetScoreTimeMs: defaultTargetScoreTimeMs == _unchanged ? this.defaultTargetScoreTimeMs : defaultTargetScoreTimeMs as int?, + steps: steps == _unchanged ? this.steps : steps as List, archivedAt: archivedAt == _unchanged ? this.archivedAt : archivedAt as DateTime?, @@ -279,6 +286,60 @@ final class Exercise { } } +final class ExerciseStep { + ExerciseStep({ + required String id, + required this.position, + required String name, + required this.type, + required this.defaultTargetValue, + this.hasScore = false, + this.scoreInputMode = ScoreInputMode.manual, + this.scoreLabel, + this.scoreUnit, + this.defaultTargetScore, + this.defaultTargetScoreTimeMs, + }) : id = _nonBlank(id, 'Exercise step id'), + name = _nonBlank(name, 'Exercise step name') { + _requireNonNegative(position, 'Exercise step position'); + _requirePositive(defaultTargetValue, 'Exercise step default target value'); + _validateExerciseStepScoreShape( + hasScore: hasScore, + scoreInputMode: scoreInputMode, + scoreLabel: scoreLabel, + scoreUnit: scoreUnit, + defaultTargetScore: defaultTargetScore, + defaultTargetScoreTimeMs: defaultTargetScoreTimeMs, + ); + } + + final String id; + final int position; + final String name; + final ExerciseStepType type; + final int defaultTargetValue; + final bool hasScore; + final ScoreInputMode scoreInputMode; + final String? scoreLabel; + final String? scoreUnit; + final double? defaultTargetScore; + final int? defaultTargetScoreTimeMs; + + Map toSnapshotJson() => { + 'id': id, + 'position': position, + 'name': name, + 'type': type.name, + 'defaultTargetValue': defaultTargetValue, + 'hasScore': hasScore, + 'scoreInputMode': scoreInputMode.name, + 'scoreLabel': scoreLabel, + 'scoreUnit': scoreUnit, + 'defaultTargetScore': defaultTargetScore, + 'defaultTargetScoreTimeMs': defaultTargetScoreTimeMs, + }; +} + const Object _unchanged = Object(); final class Program { @@ -322,6 +383,7 @@ final class ProgramExercise { this.exerciseImageMediaIdSnapshot, List exerciseImageMediaIdsSnapshot = const [], this.exerciseVideoMediaIdSnapshot, + List exerciseStepsSnapshot = const [], this.exerciseArchivedSnapshot = false, required this.availableTimeSnapshot, required this.availableRepsSnapshot, @@ -347,7 +409,8 @@ final class ProgramExercise { exerciseImageMediaIdSnapshot != null ? [exerciseImageMediaIdSnapshot] : exerciseImageMediaIdsSnapshot, - ) { + ), + exerciseStepsSnapshot = _validatedExerciseSteps(exerciseStepsSnapshot) { _requireNonNegative(position, 'Position'); _requirePositive(setsCount, 'Sets count'); _requireAtLeastOneMeasure( @@ -386,6 +449,7 @@ final class ProgramExercise { final String? exerciseImageMediaIdSnapshot; final List exerciseImageMediaIdsSnapshot; final String? exerciseVideoMediaIdSnapshot; + final List exerciseStepsSnapshot; final bool exerciseArchivedSnapshot; final bool availableTimeSnapshot; final bool availableRepsSnapshot; @@ -432,6 +496,7 @@ final class ProgramExercise { exerciseImageMediaIdSnapshot: exercise.imageMediaId, exerciseImageMediaIdsSnapshot: exercise.imageMediaIds, exerciseVideoMediaIdSnapshot: exercise.videoMediaId, + exerciseStepsSnapshot: exercise.steps, exerciseArchivedSnapshot: exercise.archivedAt != null, availableTimeSnapshot: exercise.hasTimeMeasure, availableRepsSnapshot: exercise.hasRepsMeasure, @@ -461,6 +526,9 @@ final class ProgramExercise { 'exerciseImageMediaIdsSnapshot': exerciseImageMediaIdsSnapshot, 'imageMediaIdsSnapshot': exerciseImageMediaIdsSnapshot, 'exerciseVideoMediaIdSnapshot': exerciseVideoMediaIdSnapshot, + 'exerciseStepsSnapshot': exerciseStepsSnapshot + .map((step) => step.toSnapshotJson()) + .toList(), 'exerciseArchivedSnapshot': exerciseArchivedSnapshot, 'availableTimeSnapshot': availableTimeSnapshot, 'availableRepsSnapshot': availableRepsSnapshot, @@ -998,6 +1066,24 @@ String? _validatedIconMediaId(String? iconMediaId, List imageMediaIds) { return normalized; } +List _validatedExerciseSteps(List steps) { + if (steps.length > 8) { + throw const DomainException('An exercise cannot have more than 8 steps.'); + } + for (var index = 0; index < steps.length; index++) { + if (steps[index].position != index) { + throw const DomainException( + 'Exercise step positions must be contiguous and ordered.', + ); + } + } + final ids = steps.map((step) => _nonBlank(step.id, 'Exercise step id')); + if (ids.toSet().length != steps.length) { + throw const DomainException('Exercise step ids must be unique.'); + } + return List.unmodifiable(steps); +} + void _requireAtLeastOneMeasure({ required bool hasTime, required bool hasReps, @@ -1063,6 +1149,58 @@ void _requireScoreTargetShape({ } } +void _validateExerciseStepScoreShape({ + required bool hasScore, + required ScoreInputMode scoreInputMode, + required String? scoreLabel, + required String? scoreUnit, + required double? defaultTargetScore, + required int? defaultTargetScoreTimeMs, +}) { + if (!hasScore) { + if (scoreLabel != null || + scoreUnit != null || + defaultTargetScore != null || + defaultTargetScoreTimeMs != null) { + throw const DomainException( + 'Disabled exercise step score must not define score values.', + ); + } + return; + } + if (defaultTargetScore != null && defaultTargetScoreTimeMs != null) { + throw const DomainException( + 'Manual and stopwatch step score targets cannot both be set.', + ); + } + switch (scoreInputMode) { + case ScoreInputMode.manual: + _nonBlank(scoreLabel, 'Exercise step score label'); + _nonBlank(scoreUnit, 'Exercise step score unit'); + _requireNullableNonNegativeDouble( + defaultTargetScore, + 'Exercise step default target score', + ); + if (defaultTargetScoreTimeMs != null) { + throw const DomainException( + 'Manual exercise step score cannot define stopwatch target.', + ); + } + case ScoreInputMode.stopwatch: + if (scoreLabel != null || + scoreUnit != null || + defaultTargetScore != null) { + throw const DomainException( + 'Stopwatch exercise step score cannot define manual score values.', + ); + } + _requireNullablePositive( + defaultTargetScoreTimeMs, + 'Exercise step default target score time ms', + ); + } +} + void _requireScoreResultShape({ required ScoreInputMode scoreInputMode, required double? actualScore, diff --git a/lib/infrastructure/local/app_database.dart b/lib/infrastructure/local/app_database.dart index fb1f104..65328d8 100644 --- a/lib/infrastructure/local/app_database.dart +++ b/lib/infrastructure/local/app_database.dart @@ -14,6 +14,7 @@ part 'app_database.g.dart'; ChangeLogEntries, Exercises, ExerciseImages, + ExerciseSteps, MediaAssets, ProgramExercises, Programs, @@ -37,7 +38,7 @@ final class AppDatabase extends _$AppDatabase { } @override - int get schemaVersion => 7; + int get schemaVersion => 8; @override MigrationStrategy get migration { @@ -73,6 +74,9 @@ final class AppDatabase extends _$AppDatabase { if (from < 7) { await _migrateToSchema7(); } + if (from < 8) { + await _migrateToSchema8(migrator); + } await _createIndexes(); }, beforeOpen: (details) async { @@ -109,6 +113,10 @@ final class AppDatabase extends _$AppDatabase { 'CREATE INDEX IF NOT EXISTS idx_exercise_images_exercise_id ' 'ON exercise_images (exercise_id)', ); + await customStatement( + 'CREATE INDEX IF NOT EXISTS idx_exercise_steps_exercise_id_position ' + 'ON exercise_steps (exercise_id, position)', + ); await customStatement( 'CREATE INDEX IF NOT EXISTS idx_workout_template_programs_template_id ' 'ON workout_template_programs (workout_template_id)', @@ -162,6 +170,7 @@ const _syncableTableNames = [ 'active_workout_sessions', 'exercises', 'exercise_images', + 'exercise_steps', 'media_assets', 'program_exercises', 'programs', @@ -270,4 +279,12 @@ extension on AppDatabase { 'REFERENCES media_assets(id)', ); } + + Future _migrateToSchema8(Migrator migrator) async { + await migrator.createTable(exerciseSteps); + await customStatement( + 'ALTER TABLE program_exercises ADD COLUMN ' + 'exercise_steps_snapshot_json TEXT', + ); + } } diff --git a/lib/infrastructure/local/app_database.g.dart b/lib/infrastructure/local/app_database.g.dart index 6415a96..5bf1c04 100644 --- a/lib/infrastructure/local/app_database.g.dart +++ b/lib/infrastructure/local/app_database.g.dart @@ -10199,6 +10199,1286 @@ class ExerciseImagesCompanion extends UpdateCompanion { } } +class $ExerciseStepsTable extends ExerciseSteps + with TableInfo<$ExerciseStepsTable, ExerciseStep> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $ExerciseStepsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _createdAtMeta = const VerificationMeta( + 'createdAt', + ); + @override + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const VerificationMeta _updatedAtMeta = const VerificationMeta( + 'updatedAt', + ); + @override + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const VerificationMeta _deletedAtMeta = const VerificationMeta( + 'deletedAt', + ); + @override + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const VerificationMeta _schemaVersionMeta = const VerificationMeta( + 'schemaVersion', + ); + @override + late final GeneratedColumn schemaVersion = GeneratedColumn( + 'schema_version', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(1), + ); + static const VerificationMeta _syncStateMeta = const VerificationMeta( + 'syncState', + ); + @override + late final GeneratedColumn syncState = GeneratedColumn( + 'sync_state', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL CHECK (sync_state IN (\'localOnly\', \'dirty\', \'synced\', \'deleted\'))', + ); + static const VerificationMeta _localRevisionMeta = const VerificationMeta( + 'localRevision', + ); + @override + late final GeneratedColumn localRevision = GeneratedColumn( + 'local_revision', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL CHECK (local_revision >= 0)', + ); + static const VerificationMeta _originDeviceIdMeta = const VerificationMeta( + 'originDeviceId', + ); + @override + late final GeneratedColumn originDeviceId = GeneratedColumn( + 'origin_device_id', + aliasedName, + false, + additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _futureOwnerProfileIdMeta = + const VerificationMeta('futureOwnerProfileId'); + @override + late final GeneratedColumn futureOwnerProfileId = + GeneratedColumn( + 'future_owner_profile_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _lastSyncedAtMeta = const VerificationMeta( + 'lastSyncedAt', + ); + @override + late final GeneratedColumn lastSyncedAt = GeneratedColumn( + 'last_synced_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const VerificationMeta _remoteRevisionMeta = const VerificationMeta( + 'remoteRevision', + ); + @override + late final GeneratedColumn remoteRevision = GeneratedColumn( + 'remote_revision', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _exerciseIdMeta = const VerificationMeta( + 'exerciseId', + ); + @override + late final GeneratedColumn exerciseId = GeneratedColumn( + 'exercise_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES exercises (id)', + ), + ); + static const VerificationMeta _positionMeta = const VerificationMeta( + 'position', + ); + @override + late final GeneratedColumn position = GeneratedColumn( + 'position', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _typeMeta = const VerificationMeta('type'); + @override + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _defaultTargetValueMeta = + const VerificationMeta('defaultTargetValue'); + @override + late final GeneratedColumn defaultTargetValue = GeneratedColumn( + 'default_target_value', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _hasScoreMeta = const VerificationMeta( + 'hasScore', + ); + @override + late final GeneratedColumn hasScore = GeneratedColumn( + 'has_score', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_score" IN (0, 1))', + ), + ); + static const VerificationMeta _scoreInputModeMeta = const VerificationMeta( + 'scoreInputMode', + ); + @override + late final GeneratedColumn scoreInputMode = GeneratedColumn( + 'score_input_mode', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _scoreLabelMeta = const VerificationMeta( + 'scoreLabel', + ); + @override + late final GeneratedColumn scoreLabel = GeneratedColumn( + 'score_label', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _scoreUnitMeta = const VerificationMeta( + 'scoreUnit', + ); + @override + late final GeneratedColumn scoreUnit = GeneratedColumn( + 'score_unit', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _defaultTargetScoreMeta = + const VerificationMeta('defaultTargetScore'); + @override + late final GeneratedColumn defaultTargetScore = + GeneratedColumn( + 'default_target_score', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + static const VerificationMeta _defaultTargetScoreTimeMsMeta = + const VerificationMeta('defaultTargetScoreTimeMs'); + @override + late final GeneratedColumn defaultTargetScoreTimeMs = + GeneratedColumn( + 'default_target_score_time_ms', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + schemaVersion, + syncState, + localRevision, + originDeviceId, + futureOwnerProfileId, + lastSyncedAt, + remoteRevision, + exerciseId, + position, + name, + type, + defaultTargetValue, + hasScore, + scoreInputMode, + scoreLabel, + scoreUnit, + defaultTargetScore, + defaultTargetScoreTimeMs, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'exercise_steps'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } else if (isInserting) { + context.missing(_createdAtMeta); + } + if (data.containsKey('updated_at')) { + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); + } else if (isInserting) { + context.missing(_updatedAtMeta); + } + if (data.containsKey('deleted_at')) { + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); + } + if (data.containsKey('schema_version')) { + context.handle( + _schemaVersionMeta, + schemaVersion.isAcceptableOrUnknown( + data['schema_version']!, + _schemaVersionMeta, + ), + ); + } + if (data.containsKey('sync_state')) { + context.handle( + _syncStateMeta, + syncState.isAcceptableOrUnknown(data['sync_state']!, _syncStateMeta), + ); + } else if (isInserting) { + context.missing(_syncStateMeta); + } + if (data.containsKey('local_revision')) { + context.handle( + _localRevisionMeta, + localRevision.isAcceptableOrUnknown( + data['local_revision']!, + _localRevisionMeta, + ), + ); + } else if (isInserting) { + context.missing(_localRevisionMeta); + } + if (data.containsKey('origin_device_id')) { + context.handle( + _originDeviceIdMeta, + originDeviceId.isAcceptableOrUnknown( + data['origin_device_id']!, + _originDeviceIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_originDeviceIdMeta); + } + if (data.containsKey('future_owner_profile_id')) { + context.handle( + _futureOwnerProfileIdMeta, + futureOwnerProfileId.isAcceptableOrUnknown( + data['future_owner_profile_id']!, + _futureOwnerProfileIdMeta, + ), + ); + } + if (data.containsKey('last_synced_at')) { + context.handle( + _lastSyncedAtMeta, + lastSyncedAt.isAcceptableOrUnknown( + data['last_synced_at']!, + _lastSyncedAtMeta, + ), + ); + } + if (data.containsKey('remote_revision')) { + context.handle( + _remoteRevisionMeta, + remoteRevision.isAcceptableOrUnknown( + data['remote_revision']!, + _remoteRevisionMeta, + ), + ); + } + if (data.containsKey('exercise_id')) { + context.handle( + _exerciseIdMeta, + exerciseId.isAcceptableOrUnknown(data['exercise_id']!, _exerciseIdMeta), + ); + } else if (isInserting) { + context.missing(_exerciseIdMeta); + } + if (data.containsKey('position')) { + context.handle( + _positionMeta, + position.isAcceptableOrUnknown(data['position']!, _positionMeta), + ); + } else if (isInserting) { + context.missing(_positionMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, + type.isAcceptableOrUnknown(data['type']!, _typeMeta), + ); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('default_target_value')) { + context.handle( + _defaultTargetValueMeta, + defaultTargetValue.isAcceptableOrUnknown( + data['default_target_value']!, + _defaultTargetValueMeta, + ), + ); + } else if (isInserting) { + context.missing(_defaultTargetValueMeta); + } + if (data.containsKey('has_score')) { + context.handle( + _hasScoreMeta, + hasScore.isAcceptableOrUnknown(data['has_score']!, _hasScoreMeta), + ); + } else if (isInserting) { + context.missing(_hasScoreMeta); + } + if (data.containsKey('score_input_mode')) { + context.handle( + _scoreInputModeMeta, + scoreInputMode.isAcceptableOrUnknown( + data['score_input_mode']!, + _scoreInputModeMeta, + ), + ); + } + if (data.containsKey('score_label')) { + context.handle( + _scoreLabelMeta, + scoreLabel.isAcceptableOrUnknown(data['score_label']!, _scoreLabelMeta), + ); + } + if (data.containsKey('score_unit')) { + context.handle( + _scoreUnitMeta, + scoreUnit.isAcceptableOrUnknown(data['score_unit']!, _scoreUnitMeta), + ); + } + if (data.containsKey('default_target_score')) { + context.handle( + _defaultTargetScoreMeta, + defaultTargetScore.isAcceptableOrUnknown( + data['default_target_score']!, + _defaultTargetScoreMeta, + ), + ); + } + if (data.containsKey('default_target_score_time_ms')) { + context.handle( + _defaultTargetScoreTimeMsMeta, + defaultTargetScoreTimeMs.isAcceptableOrUnknown( + data['default_target_score_time_ms']!, + _defaultTargetScoreTimeMsMeta, + ), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + ExerciseStep map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ExerciseStep( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + schemaVersion: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}schema_version'], + )!, + syncState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}sync_state'], + )!, + localRevision: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}local_revision'], + )!, + originDeviceId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}origin_device_id'], + )!, + futureOwnerProfileId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}future_owner_profile_id'], + ), + lastSyncedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}last_synced_at'], + ), + remoteRevision: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}remote_revision'], + ), + exerciseId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exercise_id'], + )!, + position: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}position'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + defaultTargetValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}default_target_value'], + )!, + hasScore: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_score'], + )!, + scoreInputMode: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}score_input_mode'], + ), + scoreLabel: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}score_label'], + ), + scoreUnit: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}score_unit'], + ), + defaultTargetScore: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}default_target_score'], + ), + defaultTargetScoreTimeMs: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}default_target_score_time_ms'], + ), + ); + } + + @override + $ExerciseStepsTable createAlias(String alias) { + return $ExerciseStepsTable(attachedDatabase, alias); + } +} + +class ExerciseStep extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final int schemaVersion; + final String syncState; + final int localRevision; + final String originDeviceId; + final String? futureOwnerProfileId; + final DateTime? lastSyncedAt; + final String? remoteRevision; + final String exerciseId; + final int position; + final String name; + final String type; + final int defaultTargetValue; + final bool hasScore; + final String? scoreInputMode; + final String? scoreLabel; + final String? scoreUnit; + final double? defaultTargetScore; + final int? defaultTargetScoreTimeMs; + const ExerciseStep({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.schemaVersion, + required this.syncState, + required this.localRevision, + required this.originDeviceId, + this.futureOwnerProfileId, + this.lastSyncedAt, + this.remoteRevision, + required this.exerciseId, + required this.position, + required this.name, + required this.type, + required this.defaultTargetValue, + required this.hasScore, + this.scoreInputMode, + this.scoreLabel, + this.scoreUnit, + this.defaultTargetScore, + this.defaultTargetScoreTimeMs, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['schema_version'] = Variable(schemaVersion); + map['sync_state'] = Variable(syncState); + map['local_revision'] = Variable(localRevision); + map['origin_device_id'] = Variable(originDeviceId); + if (!nullToAbsent || futureOwnerProfileId != null) { + map['future_owner_profile_id'] = Variable(futureOwnerProfileId); + } + if (!nullToAbsent || lastSyncedAt != null) { + map['last_synced_at'] = Variable(lastSyncedAt); + } + if (!nullToAbsent || remoteRevision != null) { + map['remote_revision'] = Variable(remoteRevision); + } + map['exercise_id'] = Variable(exerciseId); + map['position'] = Variable(position); + map['name'] = Variable(name); + map['type'] = Variable(type); + map['default_target_value'] = Variable(defaultTargetValue); + map['has_score'] = Variable(hasScore); + if (!nullToAbsent || scoreInputMode != null) { + map['score_input_mode'] = Variable(scoreInputMode); + } + if (!nullToAbsent || scoreLabel != null) { + map['score_label'] = Variable(scoreLabel); + } + if (!nullToAbsent || scoreUnit != null) { + map['score_unit'] = Variable(scoreUnit); + } + if (!nullToAbsent || defaultTargetScore != null) { + map['default_target_score'] = Variable(defaultTargetScore); + } + if (!nullToAbsent || defaultTargetScoreTimeMs != null) { + map['default_target_score_time_ms'] = Variable( + defaultTargetScoreTimeMs, + ); + } + return map; + } + + ExerciseStepsCompanion toCompanion(bool nullToAbsent) { + return ExerciseStepsCompanion( + id: Value(id), + createdAt: Value(createdAt), + updatedAt: Value(updatedAt), + deletedAt: deletedAt == null && nullToAbsent + ? const Value.absent() + : Value(deletedAt), + schemaVersion: Value(schemaVersion), + syncState: Value(syncState), + localRevision: Value(localRevision), + originDeviceId: Value(originDeviceId), + futureOwnerProfileId: futureOwnerProfileId == null && nullToAbsent + ? const Value.absent() + : Value(futureOwnerProfileId), + lastSyncedAt: lastSyncedAt == null && nullToAbsent + ? const Value.absent() + : Value(lastSyncedAt), + remoteRevision: remoteRevision == null && nullToAbsent + ? const Value.absent() + : Value(remoteRevision), + exerciseId: Value(exerciseId), + position: Value(position), + name: Value(name), + type: Value(type), + defaultTargetValue: Value(defaultTargetValue), + hasScore: Value(hasScore), + scoreInputMode: scoreInputMode == null && nullToAbsent + ? const Value.absent() + : Value(scoreInputMode), + scoreLabel: scoreLabel == null && nullToAbsent + ? const Value.absent() + : Value(scoreLabel), + scoreUnit: scoreUnit == null && nullToAbsent + ? const Value.absent() + : Value(scoreUnit), + defaultTargetScore: defaultTargetScore == null && nullToAbsent + ? const Value.absent() + : Value(defaultTargetScore), + defaultTargetScoreTimeMs: defaultTargetScoreTimeMs == null && nullToAbsent + ? const Value.absent() + : Value(defaultTargetScoreTimeMs), + ); + } + + factory ExerciseStep.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ExerciseStep( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + schemaVersion: serializer.fromJson(json['schemaVersion']), + syncState: serializer.fromJson(json['syncState']), + localRevision: serializer.fromJson(json['localRevision']), + originDeviceId: serializer.fromJson(json['originDeviceId']), + futureOwnerProfileId: serializer.fromJson( + json['futureOwnerProfileId'], + ), + lastSyncedAt: serializer.fromJson(json['lastSyncedAt']), + remoteRevision: serializer.fromJson(json['remoteRevision']), + exerciseId: serializer.fromJson(json['exerciseId']), + position: serializer.fromJson(json['position']), + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + defaultTargetValue: serializer.fromJson(json['defaultTargetValue']), + hasScore: serializer.fromJson(json['hasScore']), + scoreInputMode: serializer.fromJson(json['scoreInputMode']), + scoreLabel: serializer.fromJson(json['scoreLabel']), + scoreUnit: serializer.fromJson(json['scoreUnit']), + defaultTargetScore: serializer.fromJson( + json['defaultTargetScore'], + ), + defaultTargetScoreTimeMs: serializer.fromJson( + json['defaultTargetScoreTimeMs'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'schemaVersion': serializer.toJson(schemaVersion), + 'syncState': serializer.toJson(syncState), + 'localRevision': serializer.toJson(localRevision), + 'originDeviceId': serializer.toJson(originDeviceId), + 'futureOwnerProfileId': serializer.toJson(futureOwnerProfileId), + 'lastSyncedAt': serializer.toJson(lastSyncedAt), + 'remoteRevision': serializer.toJson(remoteRevision), + 'exerciseId': serializer.toJson(exerciseId), + 'position': serializer.toJson(position), + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'defaultTargetValue': serializer.toJson(defaultTargetValue), + 'hasScore': serializer.toJson(hasScore), + 'scoreInputMode': serializer.toJson(scoreInputMode), + 'scoreLabel': serializer.toJson(scoreLabel), + 'scoreUnit': serializer.toJson(scoreUnit), + 'defaultTargetScore': serializer.toJson(defaultTargetScore), + 'defaultTargetScoreTimeMs': serializer.toJson( + defaultTargetScoreTimeMs, + ), + }; + } + + ExerciseStep copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + int? schemaVersion, + String? syncState, + int? localRevision, + String? originDeviceId, + Value futureOwnerProfileId = const Value.absent(), + Value lastSyncedAt = const Value.absent(), + Value remoteRevision = const Value.absent(), + String? exerciseId, + int? position, + String? name, + String? type, + int? defaultTargetValue, + bool? hasScore, + Value scoreInputMode = const Value.absent(), + Value scoreLabel = const Value.absent(), + Value scoreUnit = const Value.absent(), + Value defaultTargetScore = const Value.absent(), + Value defaultTargetScoreTimeMs = const Value.absent(), + }) => ExerciseStep( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + schemaVersion: schemaVersion ?? this.schemaVersion, + syncState: syncState ?? this.syncState, + localRevision: localRevision ?? this.localRevision, + originDeviceId: originDeviceId ?? this.originDeviceId, + futureOwnerProfileId: futureOwnerProfileId.present + ? futureOwnerProfileId.value + : this.futureOwnerProfileId, + lastSyncedAt: lastSyncedAt.present ? lastSyncedAt.value : this.lastSyncedAt, + remoteRevision: remoteRevision.present + ? remoteRevision.value + : this.remoteRevision, + exerciseId: exerciseId ?? this.exerciseId, + position: position ?? this.position, + name: name ?? this.name, + type: type ?? this.type, + defaultTargetValue: defaultTargetValue ?? this.defaultTargetValue, + hasScore: hasScore ?? this.hasScore, + scoreInputMode: scoreInputMode.present + ? scoreInputMode.value + : this.scoreInputMode, + scoreLabel: scoreLabel.present ? scoreLabel.value : this.scoreLabel, + scoreUnit: scoreUnit.present ? scoreUnit.value : this.scoreUnit, + defaultTargetScore: defaultTargetScore.present + ? defaultTargetScore.value + : this.defaultTargetScore, + defaultTargetScoreTimeMs: defaultTargetScoreTimeMs.present + ? defaultTargetScoreTimeMs.value + : this.defaultTargetScoreTimeMs, + ); + ExerciseStep copyWithCompanion(ExerciseStepsCompanion data) { + return ExerciseStep( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + schemaVersion: data.schemaVersion.present + ? data.schemaVersion.value + : this.schemaVersion, + syncState: data.syncState.present ? data.syncState.value : this.syncState, + localRevision: data.localRevision.present + ? data.localRevision.value + : this.localRevision, + originDeviceId: data.originDeviceId.present + ? data.originDeviceId.value + : this.originDeviceId, + futureOwnerProfileId: data.futureOwnerProfileId.present + ? data.futureOwnerProfileId.value + : this.futureOwnerProfileId, + lastSyncedAt: data.lastSyncedAt.present + ? data.lastSyncedAt.value + : this.lastSyncedAt, + remoteRevision: data.remoteRevision.present + ? data.remoteRevision.value + : this.remoteRevision, + exerciseId: data.exerciseId.present + ? data.exerciseId.value + : this.exerciseId, + position: data.position.present ? data.position.value : this.position, + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + defaultTargetValue: data.defaultTargetValue.present + ? data.defaultTargetValue.value + : this.defaultTargetValue, + hasScore: data.hasScore.present ? data.hasScore.value : this.hasScore, + scoreInputMode: data.scoreInputMode.present + ? data.scoreInputMode.value + : this.scoreInputMode, + scoreLabel: data.scoreLabel.present + ? data.scoreLabel.value + : this.scoreLabel, + scoreUnit: data.scoreUnit.present ? data.scoreUnit.value : this.scoreUnit, + defaultTargetScore: data.defaultTargetScore.present + ? data.defaultTargetScore.value + : this.defaultTargetScore, + defaultTargetScoreTimeMs: data.defaultTargetScoreTimeMs.present + ? data.defaultTargetScoreTimeMs.value + : this.defaultTargetScoreTimeMs, + ); + } + + @override + String toString() { + return (StringBuffer('ExerciseStep(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('schemaVersion: $schemaVersion, ') + ..write('syncState: $syncState, ') + ..write('localRevision: $localRevision, ') + ..write('originDeviceId: $originDeviceId, ') + ..write('futureOwnerProfileId: $futureOwnerProfileId, ') + ..write('lastSyncedAt: $lastSyncedAt, ') + ..write('remoteRevision: $remoteRevision, ') + ..write('exerciseId: $exerciseId, ') + ..write('position: $position, ') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('defaultTargetValue: $defaultTargetValue, ') + ..write('hasScore: $hasScore, ') + ..write('scoreInputMode: $scoreInputMode, ') + ..write('scoreLabel: $scoreLabel, ') + ..write('scoreUnit: $scoreUnit, ') + ..write('defaultTargetScore: $defaultTargetScore, ') + ..write('defaultTargetScoreTimeMs: $defaultTargetScoreTimeMs') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + id, + createdAt, + updatedAt, + deletedAt, + schemaVersion, + syncState, + localRevision, + originDeviceId, + futureOwnerProfileId, + lastSyncedAt, + remoteRevision, + exerciseId, + position, + name, + type, + defaultTargetValue, + hasScore, + scoreInputMode, + scoreLabel, + scoreUnit, + defaultTargetScore, + defaultTargetScoreTimeMs, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ExerciseStep && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.schemaVersion == this.schemaVersion && + other.syncState == this.syncState && + other.localRevision == this.localRevision && + other.originDeviceId == this.originDeviceId && + other.futureOwnerProfileId == this.futureOwnerProfileId && + other.lastSyncedAt == this.lastSyncedAt && + other.remoteRevision == this.remoteRevision && + other.exerciseId == this.exerciseId && + other.position == this.position && + other.name == this.name && + other.type == this.type && + other.defaultTargetValue == this.defaultTargetValue && + other.hasScore == this.hasScore && + other.scoreInputMode == this.scoreInputMode && + other.scoreLabel == this.scoreLabel && + other.scoreUnit == this.scoreUnit && + other.defaultTargetScore == this.defaultTargetScore && + other.defaultTargetScoreTimeMs == this.defaultTargetScoreTimeMs); +} + +class ExerciseStepsCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value schemaVersion; + final Value syncState; + final Value localRevision; + final Value originDeviceId; + final Value futureOwnerProfileId; + final Value lastSyncedAt; + final Value remoteRevision; + final Value exerciseId; + final Value position; + final Value name; + final Value type; + final Value defaultTargetValue; + final Value hasScore; + final Value scoreInputMode; + final Value scoreLabel; + final Value scoreUnit; + final Value defaultTargetScore; + final Value defaultTargetScoreTimeMs; + final Value rowid; + const ExerciseStepsCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.schemaVersion = const Value.absent(), + this.syncState = const Value.absent(), + this.localRevision = const Value.absent(), + this.originDeviceId = const Value.absent(), + this.futureOwnerProfileId = const Value.absent(), + this.lastSyncedAt = const Value.absent(), + this.remoteRevision = const Value.absent(), + this.exerciseId = const Value.absent(), + this.position = const Value.absent(), + this.name = const Value.absent(), + this.type = const Value.absent(), + this.defaultTargetValue = const Value.absent(), + this.hasScore = const Value.absent(), + this.scoreInputMode = const Value.absent(), + this.scoreLabel = const Value.absent(), + this.scoreUnit = const Value.absent(), + this.defaultTargetScore = const Value.absent(), + this.defaultTargetScoreTimeMs = const Value.absent(), + this.rowid = const Value.absent(), + }); + ExerciseStepsCompanion.insert({ + required String id, + required DateTime createdAt, + required DateTime updatedAt, + this.deletedAt = const Value.absent(), + this.schemaVersion = const Value.absent(), + required String syncState, + required int localRevision, + required String originDeviceId, + this.futureOwnerProfileId = const Value.absent(), + this.lastSyncedAt = const Value.absent(), + this.remoteRevision = const Value.absent(), + required String exerciseId, + required int position, + required String name, + required String type, + required int defaultTargetValue, + required bool hasScore, + this.scoreInputMode = const Value.absent(), + this.scoreLabel = const Value.absent(), + this.scoreUnit = const Value.absent(), + this.defaultTargetScore = const Value.absent(), + this.defaultTargetScoreTimeMs = const Value.absent(), + this.rowid = const Value.absent(), + }) : id = Value(id), + createdAt = Value(createdAt), + updatedAt = Value(updatedAt), + syncState = Value(syncState), + localRevision = Value(localRevision), + originDeviceId = Value(originDeviceId), + exerciseId = Value(exerciseId), + position = Value(position), + name = Value(name), + type = Value(type), + defaultTargetValue = Value(defaultTargetValue), + hasScore = Value(hasScore); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? schemaVersion, + Expression? syncState, + Expression? localRevision, + Expression? originDeviceId, + Expression? futureOwnerProfileId, + Expression? lastSyncedAt, + Expression? remoteRevision, + Expression? exerciseId, + Expression? position, + Expression? name, + Expression? type, + Expression? defaultTargetValue, + Expression? hasScore, + Expression? scoreInputMode, + Expression? scoreLabel, + Expression? scoreUnit, + Expression? defaultTargetScore, + Expression? defaultTargetScoreTimeMs, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (schemaVersion != null) 'schema_version': schemaVersion, + if (syncState != null) 'sync_state': syncState, + if (localRevision != null) 'local_revision': localRevision, + if (originDeviceId != null) 'origin_device_id': originDeviceId, + if (futureOwnerProfileId != null) + 'future_owner_profile_id': futureOwnerProfileId, + if (lastSyncedAt != null) 'last_synced_at': lastSyncedAt, + if (remoteRevision != null) 'remote_revision': remoteRevision, + if (exerciseId != null) 'exercise_id': exerciseId, + if (position != null) 'position': position, + if (name != null) 'name': name, + if (type != null) 'type': type, + if (defaultTargetValue != null) + 'default_target_value': defaultTargetValue, + if (hasScore != null) 'has_score': hasScore, + if (scoreInputMode != null) 'score_input_mode': scoreInputMode, + if (scoreLabel != null) 'score_label': scoreLabel, + if (scoreUnit != null) 'score_unit': scoreUnit, + if (defaultTargetScore != null) + 'default_target_score': defaultTargetScore, + if (defaultTargetScoreTimeMs != null) + 'default_target_score_time_ms': defaultTargetScoreTimeMs, + if (rowid != null) 'rowid': rowid, + }); + } + + ExerciseStepsCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? schemaVersion, + Value? syncState, + Value? localRevision, + Value? originDeviceId, + Value? futureOwnerProfileId, + Value? lastSyncedAt, + Value? remoteRevision, + Value? exerciseId, + Value? position, + Value? name, + Value? type, + Value? defaultTargetValue, + Value? hasScore, + Value? scoreInputMode, + Value? scoreLabel, + Value? scoreUnit, + Value? defaultTargetScore, + Value? defaultTargetScoreTimeMs, + Value? rowid, + }) { + return ExerciseStepsCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + schemaVersion: schemaVersion ?? this.schemaVersion, + syncState: syncState ?? this.syncState, + localRevision: localRevision ?? this.localRevision, + originDeviceId: originDeviceId ?? this.originDeviceId, + futureOwnerProfileId: futureOwnerProfileId ?? this.futureOwnerProfileId, + lastSyncedAt: lastSyncedAt ?? this.lastSyncedAt, + remoteRevision: remoteRevision ?? this.remoteRevision, + exerciseId: exerciseId ?? this.exerciseId, + position: position ?? this.position, + name: name ?? this.name, + type: type ?? this.type, + defaultTargetValue: defaultTargetValue ?? this.defaultTargetValue, + hasScore: hasScore ?? this.hasScore, + scoreInputMode: scoreInputMode ?? this.scoreInputMode, + scoreLabel: scoreLabel ?? this.scoreLabel, + scoreUnit: scoreUnit ?? this.scoreUnit, + defaultTargetScore: defaultTargetScore ?? this.defaultTargetScore, + defaultTargetScoreTimeMs: + defaultTargetScoreTimeMs ?? this.defaultTargetScoreTimeMs, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (schemaVersion.present) { + map['schema_version'] = Variable(schemaVersion.value); + } + if (syncState.present) { + map['sync_state'] = Variable(syncState.value); + } + if (localRevision.present) { + map['local_revision'] = Variable(localRevision.value); + } + if (originDeviceId.present) { + map['origin_device_id'] = Variable(originDeviceId.value); + } + if (futureOwnerProfileId.present) { + map['future_owner_profile_id'] = Variable( + futureOwnerProfileId.value, + ); + } + if (lastSyncedAt.present) { + map['last_synced_at'] = Variable(lastSyncedAt.value); + } + if (remoteRevision.present) { + map['remote_revision'] = Variable(remoteRevision.value); + } + if (exerciseId.present) { + map['exercise_id'] = Variable(exerciseId.value); + } + if (position.present) { + map['position'] = Variable(position.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (defaultTargetValue.present) { + map['default_target_value'] = Variable(defaultTargetValue.value); + } + if (hasScore.present) { + map['has_score'] = Variable(hasScore.value); + } + if (scoreInputMode.present) { + map['score_input_mode'] = Variable(scoreInputMode.value); + } + if (scoreLabel.present) { + map['score_label'] = Variable(scoreLabel.value); + } + if (scoreUnit.present) { + map['score_unit'] = Variable(scoreUnit.value); + } + if (defaultTargetScore.present) { + map['default_target_score'] = Variable(defaultTargetScore.value); + } + if (defaultTargetScoreTimeMs.present) { + map['default_target_score_time_ms'] = Variable( + defaultTargetScoreTimeMs.value, + ); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ExerciseStepsCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('schemaVersion: $schemaVersion, ') + ..write('syncState: $syncState, ') + ..write('localRevision: $localRevision, ') + ..write('originDeviceId: $originDeviceId, ') + ..write('futureOwnerProfileId: $futureOwnerProfileId, ') + ..write('lastSyncedAt: $lastSyncedAt, ') + ..write('remoteRevision: $remoteRevision, ') + ..write('exerciseId: $exerciseId, ') + ..write('position: $position, ') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('defaultTargetValue: $defaultTargetValue, ') + ..write('hasScore: $hasScore, ') + ..write('scoreInputMode: $scoreInputMode, ') + ..write('scoreLabel: $scoreLabel, ') + ..write('scoreUnit: $scoreUnit, ') + ..write('defaultTargetScore: $defaultTargetScore, ') + ..write('defaultTargetScoreTimeMs: $defaultTargetScoreTimeMs, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + class $ProgramsTable extends Programs with TableInfo<$ProgramsTable, Program> { @override final GeneratedDatabase attachedDatabase; @@ -11216,6 +12496,17 @@ class $ProgramExercisesTable extends ProgramExercises type: DriftSqlType.string, requiredDuringInsert: false, ); + static const VerificationMeta _exerciseStepsSnapshotJsonMeta = + const VerificationMeta('exerciseStepsSnapshotJson'); + @override + late final GeneratedColumn exerciseStepsSnapshotJson = + GeneratedColumn( + 'exercise_steps_snapshot_json', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); static const VerificationMeta _exerciseVideoMediaIdSnapshotMeta = const VerificationMeta('exerciseVideoMediaIdSnapshot'); @override @@ -11449,6 +12740,7 @@ class $ProgramExercisesTable extends ProgramExercises exerciseDescriptionSnapshot, exerciseImageMediaIdSnapshot, exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot, exerciseArchivedSnapshot, availableTimeSnapshot, @@ -11635,6 +12927,15 @@ class $ProgramExercisesTable extends ProgramExercises ), ); } + if (data.containsKey('exercise_steps_snapshot_json')) { + context.handle( + _exerciseStepsSnapshotJsonMeta, + exerciseStepsSnapshotJson.isAcceptableOrUnknown( + data['exercise_steps_snapshot_json']!, + _exerciseStepsSnapshotJsonMeta, + ), + ); + } if (data.containsKey('exercise_video_media_id_snapshot')) { context.handle( _exerciseVideoMediaIdSnapshotMeta, @@ -11877,6 +13178,10 @@ class $ProgramExercisesTable extends ProgramExercises DriftSqlType.string, data['${effectivePrefix}exercise_image_media_ids_snapshot_json'], ), + exerciseStepsSnapshotJson: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exercise_steps_snapshot_json'], + ), exerciseVideoMediaIdSnapshot: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}exercise_video_media_id_snapshot'], @@ -11973,6 +13278,7 @@ class ProgramExercise extends DataClass implements Insertable { final String? exerciseDescriptionSnapshot; final String? exerciseImageMediaIdSnapshot; final String? exerciseImageMediaIdsSnapshotJson; + final String? exerciseStepsSnapshotJson; final String? exerciseVideoMediaIdSnapshot; final bool exerciseArchivedSnapshot; final bool availableTimeSnapshot; @@ -12009,6 +13315,7 @@ class ProgramExercise extends DataClass implements Insertable { this.exerciseDescriptionSnapshot, this.exerciseImageMediaIdSnapshot, this.exerciseImageMediaIdsSnapshotJson, + this.exerciseStepsSnapshotJson, this.exerciseVideoMediaIdSnapshot, required this.exerciseArchivedSnapshot, required this.availableTimeSnapshot, @@ -12070,6 +13377,11 @@ class ProgramExercise extends DataClass implements Insertable { exerciseImageMediaIdsSnapshotJson, ); } + if (!nullToAbsent || exerciseStepsSnapshotJson != null) { + map['exercise_steps_snapshot_json'] = Variable( + exerciseStepsSnapshotJson, + ); + } if (!nullToAbsent || exerciseVideoMediaIdSnapshot != null) { map['exercise_video_media_id_snapshot'] = Variable( exerciseVideoMediaIdSnapshot, @@ -12149,6 +13461,10 @@ class ProgramExercise extends DataClass implements Insertable { exerciseImageMediaIdsSnapshotJson == null && nullToAbsent ? const Value.absent() : Value(exerciseImageMediaIdsSnapshotJson), + exerciseStepsSnapshotJson: + exerciseStepsSnapshotJson == null && nullToAbsent + ? const Value.absent() + : Value(exerciseStepsSnapshotJson), exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot == null && nullToAbsent ? const Value.absent() @@ -12220,6 +13536,9 @@ class ProgramExercise extends DataClass implements Insertable { exerciseImageMediaIdsSnapshotJson: serializer.fromJson( json['exerciseImageMediaIdsSnapshotJson'], ), + exerciseStepsSnapshotJson: serializer.fromJson( + json['exerciseStepsSnapshotJson'], + ), exerciseVideoMediaIdSnapshot: serializer.fromJson( json['exerciseVideoMediaIdSnapshot'], ), @@ -12285,6 +13604,9 @@ class ProgramExercise extends DataClass implements Insertable { 'exerciseImageMediaIdsSnapshotJson': serializer.toJson( exerciseImageMediaIdsSnapshotJson, ), + 'exerciseStepsSnapshotJson': serializer.toJson( + exerciseStepsSnapshotJson, + ), 'exerciseVideoMediaIdSnapshot': serializer.toJson( exerciseVideoMediaIdSnapshot, ), @@ -12330,6 +13652,7 @@ class ProgramExercise extends DataClass implements Insertable { Value exerciseDescriptionSnapshot = const Value.absent(), Value exerciseImageMediaIdSnapshot = const Value.absent(), Value exerciseImageMediaIdsSnapshotJson = const Value.absent(), + Value exerciseStepsSnapshotJson = const Value.absent(), Value exerciseVideoMediaIdSnapshot = const Value.absent(), bool? exerciseArchivedSnapshot, bool? availableTimeSnapshot, @@ -12378,6 +13701,9 @@ class ProgramExercise extends DataClass implements Insertable { exerciseImageMediaIdsSnapshotJson: exerciseImageMediaIdsSnapshotJson.present ? exerciseImageMediaIdsSnapshotJson.value : this.exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson: exerciseStepsSnapshotJson.present + ? exerciseStepsSnapshotJson.value + : this.exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot.present ? exerciseVideoMediaIdSnapshot.value : this.exerciseVideoMediaIdSnapshot, @@ -12454,6 +13780,9 @@ class ProgramExercise extends DataClass implements Insertable { data.exerciseImageMediaIdsSnapshotJson.present ? data.exerciseImageMediaIdsSnapshotJson.value : this.exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson: data.exerciseStepsSnapshotJson.present + ? data.exerciseStepsSnapshotJson.value + : this.exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot: data.exerciseVideoMediaIdSnapshot.present ? data.exerciseVideoMediaIdSnapshot.value : this.exerciseVideoMediaIdSnapshot, @@ -12531,6 +13860,7 @@ class ProgramExercise extends DataClass implements Insertable { ..write( 'exerciseImageMediaIdsSnapshotJson: $exerciseImageMediaIdsSnapshotJson, ', ) + ..write('exerciseStepsSnapshotJson: $exerciseStepsSnapshotJson, ') ..write( 'exerciseVideoMediaIdSnapshot: $exerciseVideoMediaIdSnapshot, ', ) @@ -12574,6 +13904,7 @@ class ProgramExercise extends DataClass implements Insertable { exerciseDescriptionSnapshot, exerciseImageMediaIdSnapshot, exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot, exerciseArchivedSnapshot, availableTimeSnapshot, @@ -12617,6 +13948,7 @@ class ProgramExercise extends DataClass implements Insertable { this.exerciseImageMediaIdSnapshot && other.exerciseImageMediaIdsSnapshotJson == this.exerciseImageMediaIdsSnapshotJson && + other.exerciseStepsSnapshotJson == this.exerciseStepsSnapshotJson && other.exerciseVideoMediaIdSnapshot == this.exerciseVideoMediaIdSnapshot && other.exerciseArchivedSnapshot == this.exerciseArchivedSnapshot && @@ -12656,6 +13988,7 @@ class ProgramExercisesCompanion extends UpdateCompanion { final Value exerciseDescriptionSnapshot; final Value exerciseImageMediaIdSnapshot; final Value exerciseImageMediaIdsSnapshotJson; + final Value exerciseStepsSnapshotJson; final Value exerciseVideoMediaIdSnapshot; final Value exerciseArchivedSnapshot; final Value availableTimeSnapshot; @@ -12693,6 +14026,7 @@ class ProgramExercisesCompanion extends UpdateCompanion { this.exerciseDescriptionSnapshot = const Value.absent(), this.exerciseImageMediaIdSnapshot = const Value.absent(), this.exerciseImageMediaIdsSnapshotJson = const Value.absent(), + this.exerciseStepsSnapshotJson = const Value.absent(), this.exerciseVideoMediaIdSnapshot = const Value.absent(), this.exerciseArchivedSnapshot = const Value.absent(), this.availableTimeSnapshot = const Value.absent(), @@ -12731,6 +14065,7 @@ class ProgramExercisesCompanion extends UpdateCompanion { this.exerciseDescriptionSnapshot = const Value.absent(), this.exerciseImageMediaIdSnapshot = const Value.absent(), this.exerciseImageMediaIdsSnapshotJson = const Value.absent(), + this.exerciseStepsSnapshotJson = const Value.absent(), this.exerciseVideoMediaIdSnapshot = const Value.absent(), this.exerciseArchivedSnapshot = const Value.absent(), required bool availableTimeSnapshot, @@ -12784,6 +14119,7 @@ class ProgramExercisesCompanion extends UpdateCompanion { Expression? exerciseDescriptionSnapshot, Expression? exerciseImageMediaIdSnapshot, Expression? exerciseImageMediaIdsSnapshotJson, + Expression? exerciseStepsSnapshotJson, Expression? exerciseVideoMediaIdSnapshot, Expression? exerciseArchivedSnapshot, Expression? availableTimeSnapshot, @@ -12828,6 +14164,8 @@ class ProgramExercisesCompanion extends UpdateCompanion { if (exerciseImageMediaIdsSnapshotJson != null) 'exercise_image_media_ids_snapshot_json': exerciseImageMediaIdsSnapshotJson, + if (exerciseStepsSnapshotJson != null) + 'exercise_steps_snapshot_json': exerciseStepsSnapshotJson, if (exerciseVideoMediaIdSnapshot != null) 'exercise_video_media_id_snapshot': exerciseVideoMediaIdSnapshot, if (exerciseArchivedSnapshot != null) @@ -12876,6 +14214,7 @@ class ProgramExercisesCompanion extends UpdateCompanion { Value? exerciseDescriptionSnapshot, Value? exerciseImageMediaIdSnapshot, Value? exerciseImageMediaIdsSnapshotJson, + Value? exerciseStepsSnapshotJson, Value? exerciseVideoMediaIdSnapshot, Value? exerciseArchivedSnapshot, Value? availableTimeSnapshot, @@ -12918,6 +14257,8 @@ class ProgramExercisesCompanion extends UpdateCompanion { exerciseImageMediaIdsSnapshotJson: exerciseImageMediaIdsSnapshotJson ?? this.exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson: + exerciseStepsSnapshotJson ?? this.exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot ?? this.exerciseVideoMediaIdSnapshot, exerciseArchivedSnapshot: @@ -13012,6 +14353,11 @@ class ProgramExercisesCompanion extends UpdateCompanion { exerciseImageMediaIdsSnapshotJson.value, ); } + if (exerciseStepsSnapshotJson.present) { + map['exercise_steps_snapshot_json'] = Variable( + exerciseStepsSnapshotJson.value, + ); + } if (exerciseVideoMediaIdSnapshot.present) { map['exercise_video_media_id_snapshot'] = Variable( exerciseVideoMediaIdSnapshot.value, @@ -13106,6 +14452,7 @@ class ProgramExercisesCompanion extends UpdateCompanion { ..write( 'exerciseImageMediaIdsSnapshotJson: $exerciseImageMediaIdsSnapshotJson, ', ) + ..write('exerciseStepsSnapshotJson: $exerciseStepsSnapshotJson, ') ..write( 'exerciseVideoMediaIdSnapshot: $exerciseVideoMediaIdSnapshot, ', ) @@ -18629,6 +19976,7 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final $MediaAssetsTable mediaAssets = $MediaAssetsTable(this); late final $ExercisesTable exercises = $ExercisesTable(this); late final $ExerciseImagesTable exerciseImages = $ExerciseImagesTable(this); + late final $ExerciseStepsTable exerciseSteps = $ExerciseStepsTable(this); late final $ProgramsTable programs = $ProgramsTable(this); late final $ProgramExercisesTable programExercises = $ProgramExercisesTable( this, @@ -18658,6 +20006,7 @@ abstract class _$AppDatabase extends GeneratedDatabase { mediaAssets, exercises, exerciseImages, + exerciseSteps, programs, programExercises, workoutHistories, @@ -24010,6 +25359,24 @@ final class $$ExercisesTableReferences ); } + static MultiTypedResultKey<$ExerciseStepsTable, List> + _exerciseStepsRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.exerciseSteps, + aliasName: 'exercises__id__exercise_steps__exercise_id', + ); + + $$ExerciseStepsTableProcessedTableManager get exerciseStepsRefs { + final manager = $$ExerciseStepsTableTableManager( + $_db, + $_db.exerciseSteps, + ).filter((f) => f.exerciseId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_exerciseStepsRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + static MultiTypedResultKey<$ProgramExercisesTable, List> _programExercisesRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( db.programExercises, @@ -24231,6 +25598,31 @@ class $$ExercisesTableFilterComposer return f(composer); } + Expression exerciseStepsRefs( + Expression Function($$ExerciseStepsTableFilterComposer f) f, + ) { + final $$ExerciseStepsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.exerciseSteps, + getReferencedColumn: (t) => t.exerciseId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ExerciseStepsTableFilterComposer( + $db: $db, + $table: $db.exerciseSteps, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + Expression programExercisesRefs( Expression Function($$ProgramExercisesTableFilterComposer f) f, ) { @@ -24619,6 +26011,31 @@ class $$ExercisesTableAnnotationComposer return f(composer); } + Expression exerciseStepsRefs( + Expression Function($$ExerciseStepsTableAnnotationComposer a) f, + ) { + final $$ExerciseStepsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.exerciseSteps, + getReferencedColumn: (t) => t.exerciseId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ExerciseStepsTableAnnotationComposer( + $db: $db, + $table: $db.exerciseSteps, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + Expression programExercisesRefs( Expression Function($$ProgramExercisesTableAnnotationComposer a) f, ) { @@ -24662,6 +26079,7 @@ class $$ExercisesTableTableManager bool iconMediaId, bool videoMediaId, bool exerciseImagesRefs, + bool exerciseStepsRefs, bool programExercisesRefs, }) > { @@ -24805,12 +26223,14 @@ class $$ExercisesTableTableManager iconMediaId = false, videoMediaId = false, exerciseImagesRefs = false, + exerciseStepsRefs = false, programExercisesRefs = false, }) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (exerciseImagesRefs) db.exerciseImages, + if (exerciseStepsRefs) db.exerciseSteps, if (programExercisesRefs) db.programExercises, ], addJoins: @@ -24881,6 +26301,27 @@ class $$ExercisesTableTableManager ), typedResults: items, ), + if (exerciseStepsRefs) + await $_getPrefetchedData< + Exercise, + $ExercisesTable, + ExerciseStep + >( + currentTable: table, + referencedTable: $$ExercisesTableReferences + ._exerciseStepsRefsTable(db), + managerFromTypedResult: (p0) => + $$ExercisesTableReferences( + db, + table, + p0, + ).exerciseStepsRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.exerciseId == item.id, + ), + typedResults: items, + ), if (programExercisesRefs) await $_getPrefetchedData< Exercise, @@ -24926,6 +26367,7 @@ typedef $$ExercisesTableProcessedTableManager = bool iconMediaId, bool videoMediaId, bool exerciseImagesRefs, + bool exerciseStepsRefs, bool programExercisesRefs, }) >; @@ -25524,6 +26966,672 @@ typedef $$ExerciseImagesTableProcessedTableManager = ExerciseImage, PrefetchHooks Function({bool exerciseId, bool mediaAssetId}) >; +typedef $$ExerciseStepsTableCreateCompanionBuilder = + ExerciseStepsCompanion Function({ + required String id, + required DateTime createdAt, + required DateTime updatedAt, + Value deletedAt, + Value schemaVersion, + required String syncState, + required int localRevision, + required String originDeviceId, + Value futureOwnerProfileId, + Value lastSyncedAt, + Value remoteRevision, + required String exerciseId, + required int position, + required String name, + required String type, + required int defaultTargetValue, + required bool hasScore, + Value scoreInputMode, + Value scoreLabel, + Value scoreUnit, + Value defaultTargetScore, + Value defaultTargetScoreTimeMs, + Value rowid, + }); +typedef $$ExerciseStepsTableUpdateCompanionBuilder = + ExerciseStepsCompanion Function({ + Value id, + Value createdAt, + Value updatedAt, + Value deletedAt, + Value schemaVersion, + Value syncState, + Value localRevision, + Value originDeviceId, + Value futureOwnerProfileId, + Value lastSyncedAt, + Value remoteRevision, + Value exerciseId, + Value position, + Value name, + Value type, + Value defaultTargetValue, + Value hasScore, + Value scoreInputMode, + Value scoreLabel, + Value scoreUnit, + Value defaultTargetScore, + Value defaultTargetScoreTimeMs, + Value rowid, + }); + +final class $$ExerciseStepsTableReferences + extends BaseReferences<_$AppDatabase, $ExerciseStepsTable, ExerciseStep> { + $$ExerciseStepsTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $ExercisesTable _exerciseIdTable(_$AppDatabase db) => + db.exercises.createAlias('exercise_steps__exercise_id__exercises__id'); + + $$ExercisesTableProcessedTableManager get exerciseId { + final $_column = $_itemColumn('exercise_id')!; + + final manager = $$ExercisesTableTableManager( + $_db, + $_db.exercises, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_exerciseIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$ExerciseStepsTableFilterComposer + extends Composer<_$AppDatabase, $ExerciseStepsTable> { + $$ExerciseStepsTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get deletedAt => $composableBuilder( + column: $table.deletedAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get schemaVersion => $composableBuilder( + column: $table.schemaVersion, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get syncState => $composableBuilder( + column: $table.syncState, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get localRevision => $composableBuilder( + column: $table.localRevision, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get originDeviceId => $composableBuilder( + column: $table.originDeviceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get futureOwnerProfileId => $composableBuilder( + column: $table.futureOwnerProfileId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get lastSyncedAt => $composableBuilder( + column: $table.lastSyncedAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get remoteRevision => $composableBuilder( + column: $table.remoteRevision, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get position => $composableBuilder( + column: $table.position, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get defaultTargetValue => $composableBuilder( + column: $table.defaultTargetValue, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get hasScore => $composableBuilder( + column: $table.hasScore, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get scoreInputMode => $composableBuilder( + column: $table.scoreInputMode, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get scoreLabel => $composableBuilder( + column: $table.scoreLabel, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get scoreUnit => $composableBuilder( + column: $table.scoreUnit, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get defaultTargetScore => $composableBuilder( + column: $table.defaultTargetScore, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get defaultTargetScoreTimeMs => $composableBuilder( + column: $table.defaultTargetScoreTimeMs, + builder: (column) => ColumnFilters(column), + ); + + $$ExercisesTableFilterComposer get exerciseId { + final $$ExercisesTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.exerciseId, + referencedTable: $db.exercises, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ExercisesTableFilterComposer( + $db: $db, + $table: $db.exercises, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$ExerciseStepsTableOrderingComposer + extends Composer<_$AppDatabase, $ExerciseStepsTable> { + $$ExerciseStepsTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get deletedAt => $composableBuilder( + column: $table.deletedAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get schemaVersion => $composableBuilder( + column: $table.schemaVersion, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get syncState => $composableBuilder( + column: $table.syncState, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get localRevision => $composableBuilder( + column: $table.localRevision, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get originDeviceId => $composableBuilder( + column: $table.originDeviceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get futureOwnerProfileId => $composableBuilder( + column: $table.futureOwnerProfileId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get lastSyncedAt => $composableBuilder( + column: $table.lastSyncedAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get remoteRevision => $composableBuilder( + column: $table.remoteRevision, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get position => $composableBuilder( + column: $table.position, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get defaultTargetValue => $composableBuilder( + column: $table.defaultTargetValue, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get hasScore => $composableBuilder( + column: $table.hasScore, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get scoreInputMode => $composableBuilder( + column: $table.scoreInputMode, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get scoreLabel => $composableBuilder( + column: $table.scoreLabel, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get scoreUnit => $composableBuilder( + column: $table.scoreUnit, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get defaultTargetScore => $composableBuilder( + column: $table.defaultTargetScore, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get defaultTargetScoreTimeMs => $composableBuilder( + column: $table.defaultTargetScoreTimeMs, + builder: (column) => ColumnOrderings(column), + ); + + $$ExercisesTableOrderingComposer get exerciseId { + final $$ExercisesTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.exerciseId, + referencedTable: $db.exercises, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ExercisesTableOrderingComposer( + $db: $db, + $table: $db.exercises, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$ExerciseStepsTableAnnotationComposer + extends Composer<_$AppDatabase, $ExerciseStepsTable> { + $$ExerciseStepsTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + GeneratedColumn get deletedAt => + $composableBuilder(column: $table.deletedAt, builder: (column) => column); + + GeneratedColumn get schemaVersion => $composableBuilder( + column: $table.schemaVersion, + builder: (column) => column, + ); + + GeneratedColumn get syncState => + $composableBuilder(column: $table.syncState, builder: (column) => column); + + GeneratedColumn get localRevision => $composableBuilder( + column: $table.localRevision, + builder: (column) => column, + ); + + GeneratedColumn get originDeviceId => $composableBuilder( + column: $table.originDeviceId, + builder: (column) => column, + ); + + GeneratedColumn get futureOwnerProfileId => $composableBuilder( + column: $table.futureOwnerProfileId, + builder: (column) => column, + ); + + GeneratedColumn get lastSyncedAt => $composableBuilder( + column: $table.lastSyncedAt, + builder: (column) => column, + ); + + GeneratedColumn get remoteRevision => $composableBuilder( + column: $table.remoteRevision, + builder: (column) => column, + ); + + GeneratedColumn get position => + $composableBuilder(column: $table.position, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + GeneratedColumn get defaultTargetValue => $composableBuilder( + column: $table.defaultTargetValue, + builder: (column) => column, + ); + + GeneratedColumn get hasScore => + $composableBuilder(column: $table.hasScore, builder: (column) => column); + + GeneratedColumn get scoreInputMode => $composableBuilder( + column: $table.scoreInputMode, + builder: (column) => column, + ); + + GeneratedColumn get scoreLabel => $composableBuilder( + column: $table.scoreLabel, + builder: (column) => column, + ); + + GeneratedColumn get scoreUnit => + $composableBuilder(column: $table.scoreUnit, builder: (column) => column); + + GeneratedColumn get defaultTargetScore => $composableBuilder( + column: $table.defaultTargetScore, + builder: (column) => column, + ); + + GeneratedColumn get defaultTargetScoreTimeMs => $composableBuilder( + column: $table.defaultTargetScoreTimeMs, + builder: (column) => column, + ); + + $$ExercisesTableAnnotationComposer get exerciseId { + final $$ExercisesTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.exerciseId, + referencedTable: $db.exercises, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ExercisesTableAnnotationComposer( + $db: $db, + $table: $db.exercises, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$ExerciseStepsTableTableManager + extends + RootTableManager< + _$AppDatabase, + $ExerciseStepsTable, + ExerciseStep, + $$ExerciseStepsTableFilterComposer, + $$ExerciseStepsTableOrderingComposer, + $$ExerciseStepsTableAnnotationComposer, + $$ExerciseStepsTableCreateCompanionBuilder, + $$ExerciseStepsTableUpdateCompanionBuilder, + (ExerciseStep, $$ExerciseStepsTableReferences), + ExerciseStep, + PrefetchHooks Function({bool exerciseId}) + > { + $$ExerciseStepsTableTableManager(_$AppDatabase db, $ExerciseStepsTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$ExerciseStepsTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$ExerciseStepsTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$ExerciseStepsTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value createdAt = const Value.absent(), + Value updatedAt = const Value.absent(), + Value deletedAt = const Value.absent(), + Value schemaVersion = const Value.absent(), + Value syncState = const Value.absent(), + Value localRevision = const Value.absent(), + Value originDeviceId = const Value.absent(), + Value futureOwnerProfileId = const Value.absent(), + Value lastSyncedAt = const Value.absent(), + Value remoteRevision = const Value.absent(), + Value exerciseId = const Value.absent(), + Value position = const Value.absent(), + Value name = const Value.absent(), + Value type = const Value.absent(), + Value defaultTargetValue = const Value.absent(), + Value hasScore = const Value.absent(), + Value scoreInputMode = const Value.absent(), + Value scoreLabel = const Value.absent(), + Value scoreUnit = const Value.absent(), + Value defaultTargetScore = const Value.absent(), + Value defaultTargetScoreTimeMs = const Value.absent(), + Value rowid = const Value.absent(), + }) => ExerciseStepsCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + schemaVersion: schemaVersion, + syncState: syncState, + localRevision: localRevision, + originDeviceId: originDeviceId, + futureOwnerProfileId: futureOwnerProfileId, + lastSyncedAt: lastSyncedAt, + remoteRevision: remoteRevision, + exerciseId: exerciseId, + position: position, + name: name, + type: type, + defaultTargetValue: defaultTargetValue, + hasScore: hasScore, + scoreInputMode: scoreInputMode, + scoreLabel: scoreLabel, + scoreUnit: scoreUnit, + defaultTargetScore: defaultTargetScore, + defaultTargetScoreTimeMs: defaultTargetScoreTimeMs, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String id, + required DateTime createdAt, + required DateTime updatedAt, + Value deletedAt = const Value.absent(), + Value schemaVersion = const Value.absent(), + required String syncState, + required int localRevision, + required String originDeviceId, + Value futureOwnerProfileId = const Value.absent(), + Value lastSyncedAt = const Value.absent(), + Value remoteRevision = const Value.absent(), + required String exerciseId, + required int position, + required String name, + required String type, + required int defaultTargetValue, + required bool hasScore, + Value scoreInputMode = const Value.absent(), + Value scoreLabel = const Value.absent(), + Value scoreUnit = const Value.absent(), + Value defaultTargetScore = const Value.absent(), + Value defaultTargetScoreTimeMs = const Value.absent(), + Value rowid = const Value.absent(), + }) => ExerciseStepsCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + schemaVersion: schemaVersion, + syncState: syncState, + localRevision: localRevision, + originDeviceId: originDeviceId, + futureOwnerProfileId: futureOwnerProfileId, + lastSyncedAt: lastSyncedAt, + remoteRevision: remoteRevision, + exerciseId: exerciseId, + position: position, + name: name, + type: type, + defaultTargetValue: defaultTargetValue, + hasScore: hasScore, + scoreInputMode: scoreInputMode, + scoreLabel: scoreLabel, + scoreUnit: scoreUnit, + defaultTargetScore: defaultTargetScore, + defaultTargetScoreTimeMs: defaultTargetScoreTimeMs, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$ExerciseStepsTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({exerciseId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (exerciseId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.exerciseId, + referencedTable: $$ExerciseStepsTableReferences + ._exerciseIdTable(db), + referencedColumn: $$ExerciseStepsTableReferences + ._exerciseIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$ExerciseStepsTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $ExerciseStepsTable, + ExerciseStep, + $$ExerciseStepsTableFilterComposer, + $$ExerciseStepsTableOrderingComposer, + $$ExerciseStepsTableAnnotationComposer, + $$ExerciseStepsTableCreateCompanionBuilder, + $$ExerciseStepsTableUpdateCompanionBuilder, + (ExerciseStep, $$ExerciseStepsTableReferences), + ExerciseStep, + PrefetchHooks Function({bool exerciseId}) + >; typedef $$ProgramsTableCreateCompanionBuilder = ProgramsCompanion Function({ required String id, @@ -26130,6 +28238,7 @@ typedef $$ProgramExercisesTableCreateCompanionBuilder = Value exerciseDescriptionSnapshot, Value exerciseImageMediaIdSnapshot, Value exerciseImageMediaIdsSnapshotJson, + Value exerciseStepsSnapshotJson, Value exerciseVideoMediaIdSnapshot, Value exerciseArchivedSnapshot, required bool availableTimeSnapshot, @@ -26169,6 +28278,7 @@ typedef $$ProgramExercisesTableUpdateCompanionBuilder = Value exerciseDescriptionSnapshot, Value exerciseImageMediaIdSnapshot, Value exerciseImageMediaIdsSnapshotJson, + Value exerciseStepsSnapshotJson, Value exerciseVideoMediaIdSnapshot, Value exerciseArchivedSnapshot, Value availableTimeSnapshot, @@ -26363,6 +28473,11 @@ class $$ProgramExercisesTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get exerciseStepsSnapshotJson => $composableBuilder( + column: $table.exerciseStepsSnapshotJson, + builder: (column) => ColumnFilters(column), + ); + ColumnFilters get exerciseArchivedSnapshot => $composableBuilder( column: $table.exerciseArchivedSnapshot, builder: (column) => ColumnFilters(column), @@ -26621,6 +28736,11 @@ class $$ProgramExercisesTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get exerciseStepsSnapshotJson => $composableBuilder( + column: $table.exerciseStepsSnapshotJson, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get exerciseArchivedSnapshot => $composableBuilder( column: $table.exerciseArchivedSnapshot, builder: (column) => ColumnOrderings(column), @@ -26867,6 +28987,11 @@ class $$ProgramExercisesTableAnnotationComposer builder: (column) => column, ); + GeneratedColumn get exerciseStepsSnapshotJson => $composableBuilder( + column: $table.exerciseStepsSnapshotJson, + builder: (column) => column, + ); + GeneratedColumn get exerciseArchivedSnapshot => $composableBuilder( column: $table.exerciseArchivedSnapshot, builder: (column) => column, @@ -27094,6 +29219,7 @@ class $$ProgramExercisesTableTableManager const Value.absent(), Value exerciseImageMediaIdsSnapshotJson = const Value.absent(), + Value exerciseStepsSnapshotJson = const Value.absent(), Value exerciseVideoMediaIdSnapshot = const Value.absent(), Value exerciseArchivedSnapshot = const Value.absent(), @@ -27133,6 +29259,7 @@ class $$ProgramExercisesTableTableManager exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot, exerciseImageMediaIdsSnapshotJson: exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson: exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot, exerciseArchivedSnapshot: exerciseArchivedSnapshot, availableTimeSnapshot: availableTimeSnapshot, @@ -27175,6 +29302,7 @@ class $$ProgramExercisesTableTableManager const Value.absent(), Value exerciseImageMediaIdsSnapshotJson = const Value.absent(), + Value exerciseStepsSnapshotJson = const Value.absent(), Value exerciseVideoMediaIdSnapshot = const Value.absent(), Value exerciseArchivedSnapshot = const Value.absent(), @@ -27214,6 +29342,7 @@ class $$ProgramExercisesTableTableManager exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot, exerciseImageMediaIdsSnapshotJson: exerciseImageMediaIdsSnapshotJson, + exerciseStepsSnapshotJson: exerciseStepsSnapshotJson, exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot, exerciseArchivedSnapshot: exerciseArchivedSnapshot, availableTimeSnapshot: availableTimeSnapshot, @@ -30660,6 +32789,8 @@ class $AppDatabaseManager { $$ExercisesTableTableManager(_db, _db.exercises); $$ExerciseImagesTableTableManager get exerciseImages => $$ExerciseImagesTableTableManager(_db, _db.exerciseImages); + $$ExerciseStepsTableTableManager get exerciseSteps => + $$ExerciseStepsTableTableManager(_db, _db.exerciseSteps); $$ProgramsTableTableManager get programs => $$ProgramsTableTableManager(_db, _db.programs); $$ProgramExercisesTableTableManager get programExercises => diff --git a/lib/infrastructure/local/drift_repositories.dart b/lib/infrastructure/local/drift_repositories.dart index b220b6f..0089e8f 100644 --- a/lib/infrastructure/local/drift_repositories.dart +++ b/lib/infrastructure/local/drift_repositories.dart @@ -18,7 +18,11 @@ final class DriftExerciseRepository implements ExerciseRepository { )..where((table) => table.id.equals(id))).getSingleOrNull(); return row == null ? null - : _exerciseFromRow(row, await _imageMediaIdsForExercise(id)); + : _exerciseFromRow( + row, + await _imageMediaIdsForExercise(id), + await _stepsForExercise(id), + ); } @override @@ -33,7 +37,11 @@ final class DriftExerciseRepository implements ExerciseRepository { final exercises = []; for (final row in rows) { exercises.add( - _exerciseFromRow(row, await _imageMediaIdsForExercise(row.id)), + _exerciseFromRow( + row, + await _imageMediaIdsForExercise(row.id), + await _stepsForExercise(row.id), + ), ); } return exercises; @@ -66,6 +74,7 @@ final class DriftExerciseRepository implements ExerciseRepository { .insertOnConflictUpdate(_exerciseCompanion(exercise)), ); await _replaceExerciseImages(database, exercise); + await _replaceExerciseSteps(database, exercise); }); } @@ -81,6 +90,19 @@ final class DriftExerciseRepository implements ExerciseRepository { .get(); return rows.map((row) => row.mediaAssetId).toList(); } + + Future> _stepsForExercise(String exerciseId) async { + final rows = + await (database.select(database.exerciseSteps) + ..where( + (table) => + table.exerciseId.equals(exerciseId) & + table.deletedAt.isNull(), + ) + ..orderBy([(table) => OrderingTerm.asc(table.position)])) + .get(); + return rows.map(_exerciseStepFromRow).toList(); + } } final class DriftMediaAssetRepository implements MediaAssetRepository { @@ -823,6 +845,76 @@ Future _replaceExerciseImages( } } +Future _replaceExerciseSteps( + db.AppDatabase database, + domain.Exercise exercise, +) async { + final rows = await (database.select( + database.exerciseSteps, + )..where((table) => table.exerciseId.equals(exercise.metadata.id))).get(); + final activeRowsByStepId = { + for (final row in rows) + if (row.deletedAt == null) row.id: row, + }; + final desiredIds = exercise.steps.map((step) => step.id).toSet(); + final removedRows = activeRowsByStepId.values + .where((row) => !desiredIds.contains(row.id)) + .toList(); + await _softDeleteExerciseStepRows( + database, + removedRows, + exercise.metadata.updatedAt, + ); + + for (final step in exercise.steps) { + final existing = activeRowsByStepId[step.id]; + final metadata = existing == null + ? domain.EntityMetadata( + id: step.id, + createdAt: exercise.metadata.updatedAt, + updatedAt: exercise.metadata.updatedAt, + originDeviceId: exercise.metadata.originDeviceId, + ) + : _metadataFromRow(existing).touch(exercise.metadata.updatedAt); + await _upsertWithChangeLog( + database: database, + tableName: 'exercise_steps', + entityType: 'ExerciseStep', + metadata: metadata, + write: () => database + .into(database.exerciseSteps) + .insertOnConflictUpdate( + db.ExerciseStepsCompanion( + id: Value(metadata.id), + createdAt: Value(metadata.createdAt.toUtc()), + updatedAt: Value(metadata.updatedAt.toUtc()), + deletedAt: Value(_utcOrNull(metadata.deletedAt)), + schemaVersion: Value(metadata.schemaVersion), + syncState: Value(_syncStateToDb(metadata.syncState)), + localRevision: Value(metadata.localRevision), + originDeviceId: Value(metadata.originDeviceId), + futureOwnerProfileId: Value(metadata.futureOwnerProfileId), + lastSyncedAt: Value(_utcOrNull(metadata.lastSyncedAt)), + remoteRevision: Value(metadata.remoteRevision), + exerciseId: Value(exercise.metadata.id), + position: Value(step.position), + name: Value(step.name), + type: Value(_exerciseStepTypeToDb(step.type)), + defaultTargetValue: Value(step.defaultTargetValue), + hasScore: Value(step.hasScore), + scoreInputMode: Value( + step.hasScore ? _scoreInputModeToDb(step.scoreInputMode) : null, + ), + scoreLabel: Value(step.scoreLabel), + scoreUnit: Value(step.scoreUnit), + defaultTargetScore: Value(step.defaultTargetScore), + defaultTargetScoreTimeMs: Value(step.defaultTargetScoreTimeMs), + ), + ), + ); + } +} + Future _softDeleteExerciseImageRows( db.AppDatabase database, List rows, @@ -852,6 +944,35 @@ Future _softDeleteExerciseImageRows( } } +Future _softDeleteExerciseStepRows( + db.AppDatabase database, + List rows, + DateTime deletedAt, +) async { + for (final row in rows) { + final revision = row.localRevision + 1; + await (database.update( + database.exerciseSteps, + )..where((table) => table.id.equals(row.id))).write( + db.ExerciseStepsCompanion( + deletedAt: Value(deletedAt.toUtc()), + updatedAt: Value(deletedAt.toUtc()), + localRevision: Value(revision), + syncState: const Value('deleted'), + ), + ); + await _writeChangeLog( + database: database, + entityType: 'ExerciseStep', + entityId: row.id, + operation: 'softDelete', + localRevision: revision, + originDeviceId: row.originDeviceId, + createdAt: deletedAt, + ); + } +} + String _exerciseImageId(String exerciseId, String mediaId) { return 'exercise-image:$exerciseId:$mediaId'; } @@ -1094,7 +1215,11 @@ db.ExercisesCompanion _exerciseCompanion(domain.Exercise exercise) { ); } -domain.Exercise _exerciseFromRow(db.Exercise row, List imageMediaIds) { +domain.Exercise _exerciseFromRow( + db.Exercise row, + List imageMediaIds, + List steps, +) { return domain.Exercise( metadata: _metadataFromRow(row), name: row.name, @@ -1112,10 +1237,29 @@ domain.Exercise _exerciseFromRow(db.Exercise row, List imageMediaIds) { defaultTargetReps: row.defaultTargetReps, defaultTargetScore: row.defaultTargetScore, defaultTargetScoreTimeMs: row.defaultTargetScoreTimeMs, + steps: steps, archivedAt: _utcOrNull(row.archivedAt), ); } +domain.ExerciseStep _exerciseStepFromRow(db.ExerciseStep row) { + return domain.ExerciseStep( + id: row.id, + position: row.position, + name: row.name, + type: _exerciseStepTypeFromDb(row.type), + defaultTargetValue: row.defaultTargetValue, + hasScore: row.hasScore, + scoreInputMode: row.scoreInputMode == null + ? domain.ScoreInputMode.manual + : _scoreInputModeFromDb(row.scoreInputMode!), + scoreLabel: row.scoreLabel, + scoreUnit: row.scoreUnit, + defaultTargetScore: row.defaultTargetScore, + defaultTargetScoreTimeMs: row.defaultTargetScoreTimeMs, + ); +} + db.MediaAssetsCompanion _mediaAssetCompanion(domain.MediaAsset asset) { final values = _metadataValues(asset.metadata); return db.MediaAssetsCompanion( @@ -1215,6 +1359,9 @@ db.ProgramExercisesCompanion _programExerciseCompanion( exerciseImageMediaIdsSnapshotJson: Value( _encodeImageMediaIdsSnapshot(exercise.exerciseImageMediaIdsSnapshot), ), + exerciseStepsSnapshotJson: Value( + _encodeExerciseStepsSnapshot(exercise.exerciseStepsSnapshot), + ), exerciseVideoMediaIdSnapshot: Value(exercise.exerciseVideoMediaIdSnapshot), exerciseArchivedSnapshot: Value(exercise.exerciseArchivedSnapshot), availableTimeSnapshot: Value(exercise.availableTimeSnapshot), @@ -1250,6 +1397,9 @@ domain.ProgramExercise _programExerciseFromRow(db.ProgramExercise row) { row.exerciseImageMediaIdsSnapshotJson, row.exerciseImageMediaIdSnapshot, ), + exerciseStepsSnapshot: _decodeExerciseStepsSnapshot( + row.exerciseStepsSnapshotJson, + ), exerciseVideoMediaIdSnapshot: row.exerciseVideoMediaIdSnapshot, exerciseArchivedSnapshot: row.exerciseArchivedSnapshot, availableTimeSnapshot: row.availableTimeSnapshot, @@ -1751,6 +1901,18 @@ domain.ScoreInputMode _scoreInputModeFromDb(String value) => switch (value) { _ => throw domain.DomainException('Unknown score input mode: $value'), }; +String _exerciseStepTypeToDb(domain.ExerciseStepType type) => switch (type) { + domain.ExerciseStepType.time => 'time', + domain.ExerciseStepType.reps => 'reps', +}; + +domain.ExerciseStepType _exerciseStepTypeFromDb(String value) => + switch (value) { + 'time' => domain.ExerciseStepType.time, + 'reps' => domain.ExerciseStepType.reps, + _ => throw domain.DomainException('Unknown exercise step type: $value'), + }; + String _setResultStatusToDb(domain.SetResultStatus status) => switch (status) { domain.SetResultStatus.completed => 'completed', domain.SetResultStatus.skipped => 'skipped', @@ -1800,6 +1962,90 @@ List _decodeImageMediaIdsSnapshot( .toList(growable: false); } +String? _encodeExerciseStepsSnapshot(List steps) { + return steps.isEmpty + ? null + : jsonEncode(steps.map((step) => step.toSnapshotJson()).toList()); +} + +List _decodeExerciseStepsSnapshot(String? encoded) { + if (encoded == null || encoded.trim().isEmpty) { + return const []; + } + final decoded = jsonDecode(encoded); + if (decoded is! List) { + throw const domain.DomainException('Invalid exercise steps snapshot.'); + } + return decoded + .map((item) { + if (item is! Map) { + throw const domain.DomainException('Invalid exercise step snapshot.'); + } + final json = Map.from(item); + return domain.ExerciseStep( + id: _requiredString(json, 'id'), + position: _requiredInt(json, 'position'), + name: _requiredString(json, 'name'), + type: _exerciseStepTypeFromDb(_requiredString(json, 'type')), + defaultTargetValue: _requiredInt(json, 'defaultTargetValue'), + hasScore: _requiredBool(json, 'hasScore'), + scoreInputMode: _scoreInputModeFromDb( + _optionalString(json, 'scoreInputMode') ?? 'manual', + ), + scoreLabel: _optionalString(json, 'scoreLabel'), + scoreUnit: _optionalString(json, 'scoreUnit'), + defaultTargetScore: _optionalDouble(json, 'defaultTargetScore'), + defaultTargetScoreTimeMs: _optionalInt( + json, + 'defaultTargetScoreTimeMs', + ), + ); + }) + .toList(growable: false); +} + +String _requiredString(Map json, String key) { + final value = json[key]; + if (value is String && value.trim().isNotEmpty) { + return value; + } + throw domain.DomainException('$key must be a non-empty string.'); +} + +String? _optionalString(Map json, String key) { + final value = json[key]; + if (value == null) { + return null; + } + return value is String ? value : null; +} + +int _requiredInt(Map json, String key) { + final value = json[key]; + if (value is int) { + return value; + } + throw domain.DomainException('$key must be an integer.'); +} + +int? _optionalInt(Map json, String key) { + final value = json[key]; + return value is int ? value : null; +} + +double? _optionalDouble(Map json, String key) { + final value = json[key]; + return (value as num?)?.toDouble(); +} + +bool _requiredBool(Map json, String key) { + final value = json[key]; + if (value is bool) { + return value; + } + throw domain.DomainException('$key must be a boolean.'); +} + domain.ActiveWorkoutStatus _activeStatusFromDb(String value) => switch (value) { 'running' => domain.ActiveWorkoutStatus.running, 'paused' => domain.ActiveWorkoutStatus.paused, diff --git a/lib/infrastructure/local/tables.dart b/lib/infrastructure/local/tables.dart index af9cf36..eeee536 100644 --- a/lib/infrastructure/local/tables.dart +++ b/lib/infrastructure/local/tables.dart @@ -107,6 +107,50 @@ class ExerciseImages extends SyncableTable { ]; } +class ExerciseSteps extends SyncableTable { + @override + String get tableName => 'exercise_steps'; + + TextColumn get exerciseId => text().references(Exercises, #id)(); + IntColumn get position => integer()(); + TextColumn get name => text().withLength(min: 1)(); + TextColumn get type => text()(); + IntColumn get defaultTargetValue => integer()(); + BoolColumn get hasScore => boolean()(); + TextColumn get scoreInputMode => text().nullable()(); + TextColumn get scoreLabel => text().nullable()(); + TextColumn get scoreUnit => text().nullable()(); + RealColumn get defaultTargetScore => real().nullable()(); + IntColumn get defaultTargetScoreTimeMs => integer().nullable()(); + + @override + List get customConstraints => [ + "CHECK (type IN ('time', 'reps'))", + 'CHECK (position >= 0 AND position < 8)', + 'CHECK (default_target_value > 0)', + "CHECK (score_input_mode IS NULL OR score_input_mode IN ('manual', " + "'stopwatch'))", + 'CHECK (has_score OR (score_input_mode IS NULL ' + 'AND score_label IS NULL AND score_unit IS NULL ' + 'AND default_target_score IS NULL ' + 'AND default_target_score_time_ms IS NULL))', + 'CHECK (NOT has_score OR score_input_mode IS NOT NULL)', + "CHECK (NOT has_score OR score_input_mode != 'manual' OR " + '(score_label IS NOT NULL AND length(trim(score_label)) > 0 ' + 'AND score_unit IS NOT NULL AND length(trim(score_unit)) > 0))', + 'CHECK (default_target_score IS NULL OR default_target_score >= 0)', + 'CHECK (default_target_score_time_ms IS NULL OR ' + 'default_target_score_time_ms > 0)', + "CHECK (score_input_mode != 'manual' OR " + 'default_target_score_time_ms IS NULL)', + "CHECK (score_input_mode != 'stopwatch' OR " + '(score_label IS NULL AND score_unit IS NULL ' + 'AND default_target_score IS NULL))', + 'CHECK (default_target_score IS NULL OR ' + 'default_target_score_time_ms IS NULL)', + ]; +} + class Programs extends SyncableTable { @override String get tableName => 'programs'; @@ -132,6 +176,7 @@ class ProgramExercises extends SyncableTable { TextColumn get exerciseImageMediaIdSnapshot => text().nullable().references(MediaAssets, #id)(); TextColumn get exerciseImageMediaIdsSnapshotJson => text().nullable()(); + TextColumn get exerciseStepsSnapshotJson => text().nullable()(); @ReferenceName('programExerciseVideoSnapshotReferences') TextColumn get exerciseVideoMediaIdSnapshot => diff --git a/test/application/use_cases_test.dart b/test/application/use_cases_test.dart index 33ebcbc..bb02d5f 100644 --- a/test/application/use_cases_test.dart +++ b/test/application/use_cases_test.dart @@ -177,6 +177,113 @@ void main() { ); }); + test('Exercise use case rejects more than eight steps', () async { + final useCase = _exerciseUseCase(_FakeExerciseRepository()); + + await expectLater( + useCase.create( + name: 'Complexe', + hasTimeMeasure: false, + hasRepsMeasure: true, + hasScoreMeasure: false, + defaultTargetReps: 1, + steps: [ + for (var index = 0; index < 9; index++) + _step(id: 'step-$index', position: index), + ], + ), + throwsA(isA()), + ); + }); + + test('Exercise use case rejects non-contiguous step positions', () async { + final useCase = _exerciseUseCase(_FakeExerciseRepository()); + + await expectLater( + useCase.create( + name: 'Complexe', + hasTimeMeasure: false, + hasRepsMeasure: true, + hasScoreMeasure: false, + defaultTargetReps: 1, + steps: [ + _step(id: 'step-1', position: 1), + _step(id: 'step-0', position: 0), + ], + ), + throwsA(isA()), + ); + }); + + test('Exercise step validation rejects invalid basic fields', () { + expect(() => _step(name: ''), throwsA(isA())); + expect(() => _step(defaultTargetValue: 0), throwsA(isA())); + }); + + test('Exercise step validation rejects disabled score values', () { + expect( + () => _step(hasScore: false, scoreLabel: 'Charge'), + throwsA(isA()), + ); + expect( + () => _step(hasScore: false, defaultTargetScore: 0), + throwsA(isA()), + ); + }); + + test( + 'Exercise step manual score validation accepts zero and rejects negative', + () { + final step = _step( + hasScore: true, + scoreLabel: 'Charge', + scoreUnit: 'kg', + defaultTargetScore: 0, + ); + + expect(step.defaultTargetScore, 0); + expect( + () => _step( + hasScore: true, + scoreLabel: 'Charge', + scoreUnit: 'kg', + defaultTargetScore: -1, + ), + throwsA(isA()), + ); + expect( + () => _step(hasScore: true, scoreLabel: 'Charge'), + throwsA(isA()), + ); + }, + ); + + test('Exercise step stopwatch score validation rejects manual fields', () { + final step = _step( + hasScore: true, + scoreInputMode: ScoreInputMode.stopwatch, + defaultTargetScoreTimeMs: 12000, + ); + + expect(step.defaultTargetScoreTimeMs, 12000); + expect( + () => _step( + hasScore: true, + scoreInputMode: ScoreInputMode.stopwatch, + defaultTargetScoreTimeMs: 0, + ), + throwsA(isA()), + ); + expect( + () => _step( + hasScore: true, + scoreInputMode: ScoreInputMode.stopwatch, + scoreLabel: 'Charge', + ), + throwsA(isA()), + ); + }); + test('Exercise image gallery rejects a sixth image', () async { final repository = _FakeExerciseRepository() ..exercise = Exercise( @@ -959,6 +1066,34 @@ void main() { }); } +ExerciseStep _step({ + String id = 'step-1', + int position = 0, + String name = 'Phase', + ExerciseStepType type = ExerciseStepType.time, + int defaultTargetValue = 30, + bool hasScore = false, + ScoreInputMode scoreInputMode = ScoreInputMode.manual, + String? scoreLabel, + String? scoreUnit, + double? defaultTargetScore, + int? defaultTargetScoreTimeMs, +}) { + return ExerciseStep( + id: id, + position: position, + name: name, + type: type, + defaultTargetValue: defaultTargetValue, + hasScore: hasScore, + scoreInputMode: scoreInputMode, + scoreLabel: scoreLabel, + scoreUnit: scoreUnit, + defaultTargetScore: defaultTargetScore, + defaultTargetScoreTimeMs: defaultTargetScoreTimeMs, + ); +} + EntityMetadata _metadata(String id) { return EntityMetadata( id: id, diff --git a/test/infrastructure/drift_repositories_test.dart b/test/infrastructure/drift_repositories_test.dart index 0d4d836..c1b3f2b 100644 --- a/test/infrastructure/drift_repositories_test.dart +++ b/test/infrastructure/drift_repositories_test.dart @@ -79,6 +79,158 @@ void main() { expect(changes.every((change) => change.entityType == 'Exercise'), isTrue); }); + test('exercise repository round-trips configured steps', () async { + final now = DateTime.utc(2026, 7, 17, 12); + final exercise = Exercise( + metadata: _metadata('exercise-steps-1', now), + name: 'Burpee complexe', + hasTimeMeasure: false, + hasRepsMeasure: true, + hasScoreMeasure: false, + defaultTargetReps: 1, + steps: [ + ExerciseStep( + id: 'step-1', + position: 0, + name: 'Planche', + type: ExerciseStepType.time, + defaultTargetValue: 20, + ), + ExerciseStep( + id: 'step-2', + position: 1, + name: 'Sauts', + type: ExerciseStepType.reps, + defaultTargetValue: 8, + hasScore: true, + scoreLabel: 'Amplitude', + scoreUnit: 'pts', + defaultTargetScore: 0, + ), + ExerciseStep( + id: 'step-3', + position: 2, + name: 'Sprint final', + type: ExerciseStepType.time, + defaultTargetValue: 10, + hasScore: true, + scoreInputMode: ScoreInputMode.stopwatch, + defaultTargetScoreTimeMs: 12000, + ), + ], + ); + + await exerciseRepository.save(exercise); + + final restored = await exerciseRepository.findById(exercise.metadata.id); + + expect(restored, isNotNull); + expect(restored!.steps, hasLength(3)); + expect(restored.steps.map((step) => step.name), [ + 'Planche', + 'Sauts', + 'Sprint final', + ]); + expect(restored.steps[1].defaultTargetScore, 0); + expect(restored.steps[2].scoreInputMode, ScoreInputMode.stopwatch); + expect(restored.steps[2].defaultTargetScoreTimeMs, 12000); + }); + + test( + 'program exercise step snapshot is preserved when source exercise changes', + () async { + final now = DateTime.utc(2026, 7, 17, 12); + final source = Exercise( + metadata: _metadata('exercise-source-1', now), + name: 'Complexe original', + hasTimeMeasure: false, + hasRepsMeasure: true, + hasScoreMeasure: false, + defaultTargetReps: 1, + steps: [ + ExerciseStep( + id: 'step-original-1', + position: 0, + name: 'Phase originale A', + type: ExerciseStepType.time, + defaultTargetValue: 30, + ), + ExerciseStep( + id: 'step-original-2', + position: 1, + name: 'Phase originale B', + type: ExerciseStepType.reps, + defaultTargetValue: 12, + ), + ], + ); + await exerciseRepository.save(source); + await programRepository.save( + Program( + metadata: _metadata('program-steps-1', now), + name: 'Programme étapes', + defaultRestSeconds: 60, + ), + ); + final useCase = ProgramUseCases( + programRepository: programRepository, + exerciseRepository: exerciseRepository, + templateRepository: templateRepository, + clock: _FakeClock(now), + ids: _FakeIds(), + originDeviceId: 'device-1', + ); + + final snapshot = await useCase.addExercise( + programId: 'program-steps-1', + exerciseId: source.metadata.id, + position: 0, + setsCount: 1, + enabledMeasures: const {WorkoutMeasure.reps}, + ); + await exerciseRepository.save( + source.copyWith( + metadata: _metadata( + source.metadata.id, + now.add(const Duration(minutes: 1)), + 1, + ), + steps: [ + ExerciseStep( + id: 'step-updated-1', + position: 0, + name: 'Phase modifiée', + type: ExerciseStepType.time, + defaultTargetValue: 45, + ), + ], + ), + ); + + final restoredProgram = await programRepository.findById( + 'program-steps-1', + ); + final restoredSnapshot = restoredProgram!.exercises.single; + final snapshotJson = snapshot.toSnapshotJson(); + + expect(restoredSnapshot.exerciseStepsSnapshot, hasLength(2)); + expect( + restoredSnapshot.exerciseStepsSnapshot.first.name, + 'Phase originale A', + ); + expect( + restoredSnapshot.exerciseStepsSnapshot.last.type, + ExerciseStepType.reps, + ); + expect( + (snapshotJson['exerciseStepsSnapshot'] as List).map( + (step) => (step as Map)['name'], + ), + ['Phase originale A', 'Phase originale B'], + ); + }, + ); + test( 'removing a program exercise writes a soft delete change log entry', () async {