feat(bibliotheque): tags et duplication profonde
Ticket #83 B1-B3 : tags JSON dénormalisés, migration Drift v19, mappings sync, helpers de filtre/suggestions, duplication profonde Program/WorkoutTemplate, tests associés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -2816,6 +2816,7 @@ db.ExercisesCompanion _exerciseCompanion(domain.Exercise exercise) {
|
||||
defaultTargetScore: Value(exercise.defaultTargetScore),
|
||||
defaultTargetScoreTimeMs: Value(exercise.defaultTargetScoreTimeMs),
|
||||
autoStartNextTimedStep: Value(exercise.autoStartNextTimedStep),
|
||||
tagsJson: Value(_encodeTags(exercise.tags)),
|
||||
archivedAt: Value(_utcOrNull(exercise.archivedAt)),
|
||||
);
|
||||
}
|
||||
@ -2846,6 +2847,7 @@ domain.Exercise _exerciseFromRow(
|
||||
autoStartNextTimedStep: row.autoStartNextTimedStep,
|
||||
category: starterMetadata.category,
|
||||
isExample: starterMetadata.isExample,
|
||||
tags: _decodeTags(row.tagsJson),
|
||||
steps: steps,
|
||||
archivedAt: _utcOrNull(row.archivedAt),
|
||||
);
|
||||
@ -3028,6 +3030,7 @@ db.ProgramsCompanion _programCompanion(domain.Program program) {
|
||||
remoteRevision: values[10] as Value<String?>,
|
||||
name: Value(program.name),
|
||||
defaultRestSeconds: Value(program.defaultRestSeconds),
|
||||
tagsJson: Value(_encodeTags(program.tags)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -3041,6 +3044,7 @@ domain.Program _programFromRow(
|
||||
name: row.name,
|
||||
defaultRestSeconds: row.defaultRestSeconds,
|
||||
isExample: isExample,
|
||||
tags: _decodeTags(row.tagsJson),
|
||||
exercises: exercises,
|
||||
);
|
||||
}
|
||||
@ -3157,6 +3161,7 @@ db.WorkoutTemplatesCompanion _workoutTemplateCompanion(
|
||||
remoteRevision: values[10] as Value<String?>,
|
||||
name: Value(template.name),
|
||||
lastStartedAt: Value(_utcOrNull(template.lastStartedAt)),
|
||||
tagsJson: Value(_encodeTags(template.tags)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -3171,6 +3176,7 @@ domain.WorkoutTemplate _workoutTemplateFromRow(
|
||||
name: row.name,
|
||||
lastStartedAt: _utcOrNull(row.lastStartedAt),
|
||||
isExample: isExample,
|
||||
tags: _decodeTags(row.tagsJson),
|
||||
programs: programs,
|
||||
overrides: overrides,
|
||||
);
|
||||
@ -4077,6 +4083,7 @@ Map<String, Object?> _exercisePayload(domain.Exercise exercise) => {
|
||||
'autoStartNextTimedStep': exercise.autoStartNextTimedStep,
|
||||
'category': exercise.category.name,
|
||||
'isExample': exercise.isExample,
|
||||
'tags': exercise.tags,
|
||||
'steps': exercise.steps.map((step) => step.toSnapshotJson()).toList(),
|
||||
'archivedAt': exercise.archivedAt?.toUtc().toIso8601String(),
|
||||
};
|
||||
@ -4102,6 +4109,7 @@ Map<String, Object?> _programPayload(domain.Program program) => {
|
||||
'name': program.name,
|
||||
'defaultRestSeconds': program.defaultRestSeconds,
|
||||
'isExample': program.isExample,
|
||||
'tags': program.tags,
|
||||
'exercises': program.exercises
|
||||
.map((exercise) => exercise.toSnapshotJson())
|
||||
.toList(),
|
||||
@ -4114,6 +4122,7 @@ Map<String, Object?> _workoutTemplatePayload(domain.WorkoutTemplate template) =>
|
||||
'name': template.name,
|
||||
'lastStartedAt': template.lastStartedAt?.toUtc().toIso8601String(),
|
||||
'isExample': template.isExample,
|
||||
'tags': template.tags,
|
||||
'programs': template.programs
|
||||
.map(
|
||||
(program) => {
|
||||
@ -4183,6 +4192,7 @@ domain.Exercise _exerciseFromPayload(RemoteSyncedItem item) {
|
||||
payload['category'] as String? ?? 'uncategorized',
|
||||
),
|
||||
isExample: payload['isExample'] as bool? ?? false,
|
||||
tags: _stringListFromPayload(payload['tags']),
|
||||
steps: _stepsFromPayload(payload['steps']),
|
||||
archivedAt: _dateTimeFromPayload(payload['archivedAt']),
|
||||
);
|
||||
@ -4213,6 +4223,7 @@ domain.Program _programFromPayload(RemoteSyncedItem item) {
|
||||
name: _stringFromPayload(payload, 'name', item.clientId),
|
||||
defaultRestSeconds: payload['defaultRestSeconds'] as int? ?? 0,
|
||||
isExample: payload['isExample'] as bool? ?? false,
|
||||
tags: _stringListFromPayload(payload['tags']),
|
||||
exercises: _programExercisesFromPayload(payload['exercises'], metadata),
|
||||
);
|
||||
}
|
||||
@ -4225,6 +4236,7 @@ domain.WorkoutTemplate _workoutTemplateFromPayload(RemoteSyncedItem item) {
|
||||
name: _stringFromPayload(payload, 'name', item.clientId),
|
||||
lastStartedAt: _dateTimeFromPayload(payload['lastStartedAt']),
|
||||
isExample: payload['isExample'] as bool? ?? false,
|
||||
tags: _stringListFromPayload(payload['tags']),
|
||||
programs: _workoutTemplateProgramsFromPayload(
|
||||
payload['programs'],
|
||||
metadata,
|
||||
@ -4422,6 +4434,23 @@ List<String> _stringListFromPayload(Object? value) {
|
||||
return value.whereType<String>().toList(growable: false);
|
||||
}
|
||||
|
||||
String _encodeTags(List<String> tags) => jsonEncode(domain.normalizeTags(tags));
|
||||
|
||||
List<String> _decodeTags(String? encoded) {
|
||||
if (encoded == null || encoded.trim().isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
try {
|
||||
final decoded = jsonDecode(encoded);
|
||||
if (decoded is! List) {
|
||||
return const [];
|
||||
}
|
||||
return domain.normalizeTags(decoded.whereType<String>());
|
||||
} on Object {
|
||||
return const [];
|
||||
}
|
||||
}
|
||||
|
||||
List<domain.ExerciseStep> _stepsFromPayload(Object? value) {
|
||||
if (value is! List) {
|
||||
return const [];
|
||||
|
||||
Reference in New Issue
Block a user