fix(core): fusionne la strategie Health Services au niveau seance et nullifie sourceExerciseId au pull si absent

Complement #183 : fusion de la strategie Health Services au niveau seance pour eviter que le premier exercice fige tout le choix de strategie sur la seance.
Fix #185 : nullification defensive de sourceExerciseId au pull quand l'exercice source est absent, pour ne plus casser le chargement des programmes apres suppression d'un exercice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 17:12:03 +02:00
parent c43b93ef99
commit 326bd1893c
4 changed files with 249 additions and 32 deletions

View File

@ -524,12 +524,17 @@ final class DriftLocalSyncChangeRepository
.insertOnConflictUpdate(_programCompanion(program));
await _writeProgramStarterMetadata(database, program);
for (final exercise in program.exercises) {
final exerciseToStore = await _programExerciseWithResolvableSource(
exercise,
);
await database
.into(database.programExercises)
.insertOnConflictUpdate(_programExerciseCompanion(exercise));
.insertOnConflictUpdate(
_programExerciseCompanion(exerciseToStore),
);
await _writeProgramExerciseHealthServicesStrategySnapshot(
database,
exercise,
exerciseToStore,
);
}
});
@ -853,6 +858,53 @@ WHERE id IN (${List.filled(ids.length, '?').join(', ')})
],
);
}
Future<domain.ProgramExercise> _programExerciseWithResolvableSource(
domain.ProgramExercise exercise,
) async {
final sourceExerciseId = exercise.sourceExerciseId;
if (sourceExerciseId == null) {
return exercise;
}
final sourceRow = await (database.select(
database.exercises,
)..where((table) => table.id.equals(sourceExerciseId))).getSingleOrNull();
if (sourceRow != null) {
return exercise;
}
return domain.ProgramExercise(
metadata: exercise.metadata,
programId: exercise.programId,
sourceExerciseId: null,
position: exercise.position,
exerciseNameSnapshot: exercise.exerciseNameSnapshot,
exerciseDescriptionSnapshot: exercise.exerciseDescriptionSnapshot,
exerciseImageMediaIdSnapshot: exercise.exerciseImageMediaIdSnapshot,
exerciseImageMediaIdsSnapshot: exercise.exerciseImageMediaIdsSnapshot,
exerciseVideoMediaIdSnapshot: exercise.exerciseVideoMediaIdSnapshot,
healthServicesExerciseTypeStrategySnapshot:
exercise.healthServicesExerciseTypeStrategySnapshot,
exerciseStepsSnapshot: exercise.exerciseStepsSnapshot,
autoStartNextTimedStepSnapshot: exercise.autoStartNextTimedStepSnapshot,
autoStartNextTimedStepOverride: exercise.autoStartNextTimedStepOverride,
exerciseArchivedSnapshot: exercise.exerciseArchivedSnapshot,
availableTimeSnapshot: exercise.availableTimeSnapshot,
availableRepsSnapshot: exercise.availableRepsSnapshot,
availableScoreSnapshot: exercise.availableScoreSnapshot,
scoreInputModeSnapshot: exercise.scoreInputModeSnapshot,
scoreLabelSnapshot: exercise.scoreLabelSnapshot,
scoreUnitSnapshot: exercise.scoreUnitSnapshot,
setsCount: exercise.setsCount,
timeEnabled: exercise.timeEnabled,
repsEnabled: exercise.repsEnabled,
scoreEnabled: exercise.scoreEnabled,
targetTimeSeconds: exercise.targetTimeSeconds,
targetReps: exercise.targetReps,
targetScore: exercise.targetScore,
targetScoreTimeMs: exercise.targetScoreTimeMs,
restSecondsOverride: exercise.restSecondsOverride,
);
}
}
final class DriftShareInboxRepository implements ShareInboxRepository {