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:
@ -4160,6 +4160,10 @@ final class WatchSessionProjectionProjector {
|
||||
currentStep: currentStep,
|
||||
timers: allTimers,
|
||||
);
|
||||
final healthServicesExerciseTypeStrategy =
|
||||
_sessionHealthServicesExerciseTypeStrategy(
|
||||
session.resolvedTemplateSnapshotJson,
|
||||
);
|
||||
|
||||
return WatchSessionProjection(
|
||||
deviceSessionId: session.metadata.id,
|
||||
@ -4220,8 +4224,7 @@ final class WatchSessionProjectionProjector {
|
||||
manualScoreTargetLabel: manualScoreProjection?.targetLabel,
|
||||
manualScoreRepsTargetValue: manualScoreProjection?.repsTargetValue,
|
||||
manualScoreScope: manualScoreProjection?.scope,
|
||||
healthServicesExerciseTypeStrategy:
|
||||
snapshot.healthServicesExerciseTypeStrategy,
|
||||
healthServicesExerciseTypeStrategy: healthServicesExerciseTypeStrategy,
|
||||
);
|
||||
}
|
||||
|
||||
@ -7216,14 +7219,73 @@ _ResolvedExerciseSnapshot? _findExerciseSnapshot({
|
||||
scoreUnitSnapshot: exercise['scoreUnitSnapshot'] as String?,
|
||||
setsCount: exercise['setsCount'] as int? ?? 0,
|
||||
restSeconds: exercise['restSecondsOverride'] as int? ?? 0,
|
||||
healthServicesExerciseTypeStrategy: _stringListFromSnapshot(
|
||||
exercise['healthServicesExerciseTypeStrategy'],
|
||||
),
|
||||
steps: _exerciseStepsFromSnapshot(exercise['exerciseStepsSnapshot']),
|
||||
autoStartNextTimedStepEffective: autoStartNextTimedStepEffective,
|
||||
);
|
||||
}
|
||||
|
||||
const _legacyHealthServicesExerciseTypeStrategy = [
|
||||
'RUNNING',
|
||||
'WALKING',
|
||||
'HIGH_INTENSITY_INTERVAL_TRAINING',
|
||||
'WORKOUT',
|
||||
];
|
||||
|
||||
const _knownHealthServicesExerciseTypeNames = {
|
||||
'RUNNING',
|
||||
'WALKING',
|
||||
'HIGH_INTENSITY_INTERVAL_TRAINING',
|
||||
'WORKOUT',
|
||||
};
|
||||
|
||||
List<String> _sessionHealthServicesExerciseTypeStrategy(
|
||||
String resolvedTemplateSnapshotJson,
|
||||
) {
|
||||
try {
|
||||
final decoded =
|
||||
jsonDecode(resolvedTemplateSnapshotJson) as Map<String, dynamic>;
|
||||
final programs = (decoded['programs'] as List<dynamic>? ?? const []);
|
||||
final output = <String>[];
|
||||
void add(String type) {
|
||||
if (_knownHealthServicesExerciseTypeNames.contains(type) &&
|
||||
!output.contains(type)) {
|
||||
output.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
for (final rawProgram in programs) {
|
||||
if (rawProgram is! Map) {
|
||||
continue;
|
||||
}
|
||||
final program = Map<String, dynamic>.from(rawProgram);
|
||||
final programSnapshotJson = program['programSnapshotJson'] as String?;
|
||||
if (programSnapshotJson == null) {
|
||||
continue;
|
||||
}
|
||||
final programSnapshot =
|
||||
jsonDecode(programSnapshotJson) as Map<String, dynamic>;
|
||||
final exercises =
|
||||
(programSnapshot['exercises'] as List<dynamic>? ?? const []);
|
||||
for (final rawExercise in exercises) {
|
||||
if (rawExercise is! Map) {
|
||||
continue;
|
||||
}
|
||||
final exercise = Map<String, dynamic>.from(rawExercise);
|
||||
for (final type in _stringListFromSnapshot(
|
||||
exercise['healthServicesExerciseTypeStrategy'],
|
||||
)) {
|
||||
add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output.isEmpty
|
||||
? _legacyHealthServicesExerciseTypeStrategy
|
||||
: List.unmodifiable(output);
|
||||
} on Object {
|
||||
return _legacyHealthServicesExerciseTypeStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
bool? _templateStepChainingOverride(
|
||||
Map<String, dynamic> resolvedTemplateSnapshot, {
|
||||
required String workoutTemplateProgramId,
|
||||
@ -7439,9 +7501,6 @@ Map<String, _ResolvedExerciseSnapshot> _exerciseSnapshotsById(
|
||||
scoreUnitSnapshot: exercise['scoreUnitSnapshot'] as String?,
|
||||
setsCount: exercise['setsCount'] as int? ?? 0,
|
||||
restSeconds: exercise['restSecondsOverride'] as int? ?? 0,
|
||||
healthServicesExerciseTypeStrategy: _stringListFromSnapshot(
|
||||
exercise['healthServicesExerciseTypeStrategy'],
|
||||
),
|
||||
steps: _exerciseStepsFromSnapshot(exercise['exerciseStepsSnapshot']),
|
||||
autoStartNextTimedStepEffective:
|
||||
(exercise['autoStartNextTimedStepOverride'] as bool?) ??
|
||||
@ -7472,7 +7531,6 @@ final class _ResolvedExerciseSnapshot {
|
||||
this.scoreUnitSnapshot,
|
||||
required this.setsCount,
|
||||
required this.restSeconds,
|
||||
this.healthServicesExerciseTypeStrategy = const [],
|
||||
this.steps = const [],
|
||||
this.autoStartNextTimedStepEffective = true,
|
||||
});
|
||||
@ -7494,7 +7552,6 @@ final class _ResolvedExerciseSnapshot {
|
||||
final String? scoreUnitSnapshot;
|
||||
final int setsCount;
|
||||
final int restSeconds;
|
||||
final List<String> healthServicesExerciseTypeStrategy;
|
||||
final List<ExerciseStep> steps;
|
||||
final bool autoStartNextTimedStepEffective;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user