fix(watch): finalise correctif sync workoutHistory/exercise et distance live montre (#157)
This commit is contained in:
@ -492,10 +492,14 @@ final class DriftLocalSyncChangeRepository
|
||||
switch (item.resourceType) {
|
||||
case SyncResourceType.exercise:
|
||||
final exercise = _exerciseFromPayload(item);
|
||||
await database
|
||||
.into(database.exercises)
|
||||
.insertOnConflictUpdate(_exerciseCompanion(exercise));
|
||||
await _writeExerciseStarterMetadata(database, exercise);
|
||||
await database.transaction(() async {
|
||||
await database
|
||||
.into(database.exercises)
|
||||
.insertOnConflictUpdate(_exerciseCompanion(exercise));
|
||||
await _writeExerciseStarterMetadata(database, exercise);
|
||||
await _replaceRemoteExerciseImages(exercise);
|
||||
await _replaceRemoteExerciseSteps(exercise);
|
||||
});
|
||||
return true;
|
||||
case SyncResourceType.mediaAsset:
|
||||
await database
|
||||
@ -542,7 +546,11 @@ final class DriftLocalSyncChangeRepository
|
||||
});
|
||||
return true;
|
||||
case SyncResourceType.workoutHistory:
|
||||
return false;
|
||||
await _replaceRemoteWorkoutHistory(
|
||||
_workoutHistoryFromLocalBackupPayload(item),
|
||||
item.clientUpdatedAt,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,7 +612,7 @@ final class DriftLocalSyncChangeRepository
|
||||
}
|
||||
return _LocalSyncSnapshot.fromMetadata(
|
||||
history.metadata,
|
||||
_workoutHistoryPayload(history),
|
||||
_localWorkoutHistoryPayload(history),
|
||||
);
|
||||
}
|
||||
|
||||
@ -627,6 +635,212 @@ final class DriftLocalSyncChangeRepository
|
||||
payload: {'id': id},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _replaceRemoteWorkoutHistory(
|
||||
domain.WorkoutHistory history,
|
||||
DateTime deletedAt,
|
||||
) async {
|
||||
await database.transaction(() async {
|
||||
await database
|
||||
.into(database.workoutHistories)
|
||||
.insertOnConflictUpdate(_workoutHistoryCompanion(history));
|
||||
for (final result in history.results) {
|
||||
await database
|
||||
.into(database.workoutHistorySetResults)
|
||||
.insertOnConflictUpdate(_workoutHistorySetResultCompanion(result));
|
||||
}
|
||||
for (final result in history.stepResults) {
|
||||
await database
|
||||
.into(database.workoutHistoryStepResults)
|
||||
.insertOnConflictUpdate(_workoutHistoryStepResultCompanion(result));
|
||||
}
|
||||
|
||||
final activeResultIds = history.results
|
||||
.map((result) => result.metadata.id)
|
||||
.toSet();
|
||||
await _softDeleteRemoteWorkoutHistoryChildren(
|
||||
tableName: 'workout_history_set_results',
|
||||
historyId: history.metadata.id,
|
||||
keepIds: activeResultIds,
|
||||
deletedAt: deletedAt,
|
||||
);
|
||||
final activeStepResultIds = history.stepResults
|
||||
.map((result) => result.metadata.id)
|
||||
.toSet();
|
||||
await _softDeleteRemoteWorkoutHistoryChildren(
|
||||
tableName: 'workout_history_step_results',
|
||||
historyId: history.metadata.id,
|
||||
keepIds: activeStepResultIds,
|
||||
deletedAt: deletedAt,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _softDeleteRemoteWorkoutHistoryChildren({
|
||||
required String tableName,
|
||||
required String historyId,
|
||||
required Set<String> keepIds,
|
||||
required DateTime deletedAt,
|
||||
}) async {
|
||||
final keepPredicate = keepIds.isEmpty
|
||||
? ''
|
||||
: 'AND id NOT IN (${List.filled(keepIds.length, '?').join(', ')})';
|
||||
await database.customUpdate(
|
||||
'''
|
||||
UPDATE $tableName
|
||||
SET deleted_at = ?, updated_at = ?, sync_state = 'deleted'
|
||||
WHERE workout_history_id = ?
|
||||
AND deleted_at IS NULL
|
||||
$keepPredicate
|
||||
''',
|
||||
variables: [
|
||||
Variable<DateTime>(deletedAt.toUtc()),
|
||||
Variable<DateTime>(deletedAt.toUtc()),
|
||||
Variable<String>(historyId),
|
||||
for (final id in keepIds) Variable<String>(id),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _replaceRemoteExerciseImages(domain.Exercise exercise) async {
|
||||
final rows = await (database.select(
|
||||
database.exerciseImages,
|
||||
)..where((table) => table.exerciseId.equals(exercise.metadata.id))).get();
|
||||
final activeRowsByMediaId = {
|
||||
for (final row in rows)
|
||||
if (row.deletedAt == null) row.mediaAssetId: row,
|
||||
};
|
||||
final desiredIds = exercise.imageMediaIds.toSet();
|
||||
final removedIds = activeRowsByMediaId.values
|
||||
.where((row) => !desiredIds.contains(row.mediaAssetId))
|
||||
.map((row) => row.id)
|
||||
.toList();
|
||||
await _softDeleteRemoteRows(
|
||||
tableName: 'exercise_images',
|
||||
ids: removedIds,
|
||||
deletedAt: exercise.metadata.updatedAt,
|
||||
);
|
||||
|
||||
for (var index = 0; index < exercise.imageMediaIds.length; index++) {
|
||||
final mediaId = exercise.imageMediaIds[index];
|
||||
final existing = activeRowsByMediaId[mediaId];
|
||||
final metadata = existing == null
|
||||
? domain.EntityMetadata(
|
||||
id: _exerciseImageId(exercise.metadata.id, mediaId),
|
||||
createdAt: exercise.metadata.updatedAt,
|
||||
updatedAt: exercise.metadata.updatedAt,
|
||||
syncState: exercise.metadata.syncState,
|
||||
originDeviceId: exercise.metadata.originDeviceId,
|
||||
)
|
||||
: _metadataFromRow(existing).touch(exercise.metadata.updatedAt);
|
||||
await database
|
||||
.into(database.exerciseImages)
|
||||
.insertOnConflictUpdate(
|
||||
db.ExerciseImagesCompanion(
|
||||
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),
|
||||
mediaAssetId: Value(mediaId),
|
||||
position: Value(index),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _replaceRemoteExerciseSteps(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 removedIds = activeRowsByStepId.values
|
||||
.where((row) => !desiredIds.contains(row.id))
|
||||
.map((row) => row.id)
|
||||
.toList();
|
||||
await _softDeleteRemoteRows(
|
||||
tableName: 'exercise_steps',
|
||||
ids: removedIds,
|
||||
deletedAt: 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,
|
||||
syncState: exercise.metadata.syncState,
|
||||
originDeviceId: exercise.metadata.originDeviceId,
|
||||
)
|
||||
: _metadataFromRow(existing).touch(exercise.metadata.updatedAt);
|
||||
await 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),
|
||||
linkedToSeriesScore: Value(step.linkedToSeriesScore),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _softDeleteRemoteRows({
|
||||
required String tableName,
|
||||
required List<String> ids,
|
||||
required DateTime deletedAt,
|
||||
}) async {
|
||||
if (ids.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await database.customUpdate(
|
||||
'''
|
||||
UPDATE $tableName
|
||||
SET deleted_at = ?, updated_at = ?, sync_state = 'deleted'
|
||||
WHERE id IN (${List.filled(ids.length, '?').join(', ')})
|
||||
''',
|
||||
variables: [
|
||||
Variable<DateTime>(deletedAt.toUtc()),
|
||||
Variable<DateTime>(deletedAt.toUtc()),
|
||||
for (final id in ids) Variable<String>(id),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class DriftShareInboxRepository implements ShareInboxRepository {
|
||||
|
||||
Reference in New Issue
Block a user