feat(sync): préparation à la synchronisation cloud (ticket #12)
Ajuste les ports et le bootstrap applicatif, ainsi que le mapping Drift des repositories, pour préparer une future synchronisation cloud (invariants documentés dans docs/sync-invariants.md). Corrige au passage un bug de fuseau horaire UTC dans le mapping Drift. flutter analyze propre, 35/35 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -11,6 +11,7 @@ final class AppBootstrap {
|
||||
required this.activeWorkoutSessionUseCases,
|
||||
required this.closeWorkoutSessionUseCase,
|
||||
required this.workoutHistoryUseCases,
|
||||
required this.syncGateway,
|
||||
});
|
||||
|
||||
final AppDatabase database;
|
||||
@ -21,6 +22,7 @@ final class AppBootstrap {
|
||||
final ActiveWorkoutSessionUseCases activeWorkoutSessionUseCases;
|
||||
final CloseWorkoutSessionUseCase closeWorkoutSessionUseCase;
|
||||
final WorkoutHistoryUseCases workoutHistoryUseCases;
|
||||
final SyncGateway syncGateway;
|
||||
|
||||
static Future<AppBootstrap> create() async {
|
||||
final database = AppDatabase.open();
|
||||
@ -83,6 +85,7 @@ final class AppBootstrap {
|
||||
repository: historyRepository,
|
||||
clock: clock,
|
||||
),
|
||||
syncGateway: const NoOpSyncGateway(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -92,3 +92,26 @@ abstract interface class WorkoutHistoryRepository {
|
||||
Future<void> saveSetResult(WorkoutHistorySetResult result);
|
||||
Future<void> delete(String id, DateTime deletedAt);
|
||||
}
|
||||
|
||||
final class SyncRunSummary {
|
||||
const SyncRunSummary({
|
||||
required this.pushedChanges,
|
||||
required this.pulledChanges,
|
||||
});
|
||||
|
||||
final int pushedChanges;
|
||||
final int pulledChanges;
|
||||
}
|
||||
|
||||
abstract interface class SyncGateway {
|
||||
Future<SyncRunSummary> synchronize();
|
||||
}
|
||||
|
||||
final class NoOpSyncGateway implements SyncGateway {
|
||||
const NoOpSyncGateway();
|
||||
|
||||
@override
|
||||
Future<SyncRunSummary> synchronize() async {
|
||||
return const SyncRunSummary(pushedChanges: 0, pulledChanges: 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,9 +45,15 @@ final class DriftExerciseRepository implements ExerciseRepository {
|
||||
|
||||
@override
|
||||
Future<void> save(domain.Exercise exercise) async {
|
||||
await database
|
||||
.into(database.exercises)
|
||||
.insertOnConflictUpdate(_exerciseCompanion(exercise));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'exercises',
|
||||
entityType: 'Exercise',
|
||||
metadata: exercise.metadata,
|
||||
write: () => database
|
||||
.into(database.exercises)
|
||||
.insertOnConflictUpdate(_exerciseCompanion(exercise)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,9 +80,15 @@ final class DriftMediaAssetRepository implements MediaAssetRepository {
|
||||
|
||||
@override
|
||||
Future<void> save(domain.MediaAsset mediaAsset) async {
|
||||
await database
|
||||
.into(database.mediaAssets)
|
||||
.insertOnConflictUpdate(_mediaAssetCompanion(mediaAsset));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'media_assets',
|
||||
entityType: 'MediaAsset',
|
||||
metadata: mediaAsset.metadata,
|
||||
write: () => database
|
||||
.into(database.mediaAssets)
|
||||
.insertOnConflictUpdate(_mediaAssetCompanion(mediaAsset)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,19 +145,33 @@ final class DriftProgramRepository implements ProgramRepository {
|
||||
|
||||
@override
|
||||
Future<void> save(domain.Program program) async {
|
||||
await database
|
||||
.into(database.programs)
|
||||
.insertOnConflictUpdate(_programCompanion(program));
|
||||
for (final exercise in program.exercises) {
|
||||
await saveExercise(exercise);
|
||||
}
|
||||
await database.transaction(() async {
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'programs',
|
||||
entityType: 'Program',
|
||||
metadata: program.metadata,
|
||||
write: () => database
|
||||
.into(database.programs)
|
||||
.insertOnConflictUpdate(_programCompanion(program)),
|
||||
);
|
||||
for (final exercise in program.exercises) {
|
||||
await saveExercise(exercise);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveExercise(domain.ProgramExercise exercise) async {
|
||||
await database
|
||||
.into(database.programExercises)
|
||||
.insertOnConflictUpdate(_programExerciseCompanion(exercise));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'program_exercises',
|
||||
entityType: 'ProgramExercise',
|
||||
metadata: exercise.metadata,
|
||||
write: () => database
|
||||
.into(database.programExercises)
|
||||
.insertOnConflictUpdate(_programExerciseCompanion(exercise)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -158,26 +184,25 @@ final class DriftProgramRepository implements ProgramRepository {
|
||||
final activeIds = program.exercises
|
||||
.map((exercise) => exercise.metadata.id)
|
||||
.toSet();
|
||||
final update = database.update(database.programExercises);
|
||||
final staleQuery = database.select(database.programExercises);
|
||||
if (activeIds.isEmpty) {
|
||||
update.where(
|
||||
staleQuery.where(
|
||||
(table) =>
|
||||
table.programId.equals(program.metadata.id) &
|
||||
table.deletedAt.isNull(),
|
||||
);
|
||||
} else {
|
||||
update.where(
|
||||
staleQuery.where(
|
||||
(table) =>
|
||||
table.programId.equals(program.metadata.id) &
|
||||
table.deletedAt.isNull() &
|
||||
table.id.isNotIn(activeIds),
|
||||
);
|
||||
}
|
||||
await update.write(
|
||||
db.ProgramExercisesCompanion(
|
||||
deletedAt: Value(deletedAt),
|
||||
updatedAt: Value(deletedAt),
|
||||
),
|
||||
await _softDeleteProgramExerciseRows(
|
||||
database,
|
||||
await staleQuery.get(),
|
||||
deletedAt,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -244,33 +269,53 @@ final class DriftWorkoutTemplateRepository
|
||||
|
||||
@override
|
||||
Future<void> save(domain.WorkoutTemplate template) async {
|
||||
await database
|
||||
.into(database.workoutTemplates)
|
||||
.insertOnConflictUpdate(_workoutTemplateCompanion(template));
|
||||
for (final program in template.programs) {
|
||||
await saveProgram(program);
|
||||
}
|
||||
for (final override in template.overrides) {
|
||||
await saveOverride(override);
|
||||
}
|
||||
await database.transaction(() async {
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'workout_templates',
|
||||
entityType: 'WorkoutTemplate',
|
||||
metadata: template.metadata,
|
||||
write: () => database
|
||||
.into(database.workoutTemplates)
|
||||
.insertOnConflictUpdate(_workoutTemplateCompanion(template)),
|
||||
);
|
||||
for (final program in template.programs) {
|
||||
await saveProgram(program);
|
||||
}
|
||||
for (final override in template.overrides) {
|
||||
await saveOverride(override);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveProgram(domain.WorkoutTemplateProgram program) async {
|
||||
await database
|
||||
.into(database.workoutTemplatePrograms)
|
||||
.insertOnConflictUpdate(_workoutTemplateProgramCompanion(program));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'workout_template_programs',
|
||||
entityType: 'WorkoutTemplateProgram',
|
||||
metadata: program.metadata,
|
||||
write: () => database
|
||||
.into(database.workoutTemplatePrograms)
|
||||
.insertOnConflictUpdate(_workoutTemplateProgramCompanion(program)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveOverride(
|
||||
domain.WorkoutTemplateExerciseOverride override,
|
||||
) async {
|
||||
await database
|
||||
.into(database.workoutTemplateExerciseOverrides)
|
||||
.insertOnConflictUpdate(
|
||||
_workoutTemplateExerciseOverrideCompanion(override),
|
||||
);
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'workout_template_exercise_overrides',
|
||||
entityType: 'WorkoutTemplateExerciseOverride',
|
||||
metadata: override.metadata,
|
||||
write: () => database
|
||||
.into(database.workoutTemplateExerciseOverrides)
|
||||
.insertOnConflictUpdate(
|
||||
_workoutTemplateExerciseOverrideCompanion(override),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -293,26 +338,27 @@ final class DriftWorkoutTemplateRepository
|
||||
final activeProgramIds = template.programs
|
||||
.map((program) => program.metadata.id)
|
||||
.toSet();
|
||||
final programUpdate = database.update(database.workoutTemplatePrograms);
|
||||
final staleProgramsQuery = database.select(
|
||||
database.workoutTemplatePrograms,
|
||||
);
|
||||
if (activeProgramIds.isEmpty) {
|
||||
programUpdate.where(
|
||||
staleProgramsQuery.where(
|
||||
(table) =>
|
||||
table.workoutTemplateId.equals(template.metadata.id) &
|
||||
table.deletedAt.isNull(),
|
||||
);
|
||||
} else {
|
||||
programUpdate.where(
|
||||
staleProgramsQuery.where(
|
||||
(table) =>
|
||||
table.workoutTemplateId.equals(template.metadata.id) &
|
||||
table.deletedAt.isNull() &
|
||||
table.id.isNotIn(activeProgramIds),
|
||||
);
|
||||
}
|
||||
await programUpdate.write(
|
||||
db.WorkoutTemplateProgramsCompanion(
|
||||
deletedAt: Value(deletedAt),
|
||||
updatedAt: Value(deletedAt),
|
||||
),
|
||||
await _softDeleteWorkoutTemplateProgramRows(
|
||||
database,
|
||||
await staleProgramsQuery.get(),
|
||||
deletedAt,
|
||||
);
|
||||
|
||||
final activeOverrideIds = template.overrides
|
||||
@ -322,28 +368,27 @@ final class DriftWorkoutTemplateRepository
|
||||
if (relevantProgramIds.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final overrideUpdate = database.update(
|
||||
final staleOverridesQuery = database.select(
|
||||
database.workoutTemplateExerciseOverrides,
|
||||
);
|
||||
if (activeOverrideIds.isEmpty) {
|
||||
overrideUpdate.where(
|
||||
staleOverridesQuery.where(
|
||||
(table) =>
|
||||
table.workoutTemplateProgramId.isIn(relevantProgramIds) &
|
||||
table.deletedAt.isNull(),
|
||||
);
|
||||
} else {
|
||||
overrideUpdate.where(
|
||||
staleOverridesQuery.where(
|
||||
(table) =>
|
||||
table.workoutTemplateProgramId.isIn(relevantProgramIds) &
|
||||
table.deletedAt.isNull() &
|
||||
table.id.isNotIn(activeOverrideIds),
|
||||
);
|
||||
}
|
||||
await overrideUpdate.write(
|
||||
db.WorkoutTemplateExerciseOverridesCompanion(
|
||||
deletedAt: Value(deletedAt),
|
||||
updatedAt: Value(deletedAt),
|
||||
),
|
||||
await _softDeleteWorkoutTemplateExerciseOverrideRows(
|
||||
database,
|
||||
await staleOverridesQuery.get(),
|
||||
deletedAt,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -379,23 +424,41 @@ final class DriftActiveSessionRepository implements ActiveSessionRepository {
|
||||
|
||||
@override
|
||||
Future<void> save(domain.ActiveWorkoutSession session) async {
|
||||
await database
|
||||
.into(database.activeWorkoutSessions)
|
||||
.insertOnConflictUpdate(_activeWorkoutSessionCompanion(session));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'active_workout_sessions',
|
||||
entityType: 'ActiveWorkoutSession',
|
||||
metadata: session.metadata,
|
||||
write: () => database
|
||||
.into(database.activeWorkoutSessions)
|
||||
.insertOnConflictUpdate(_activeWorkoutSessionCompanion(session)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSetResult(domain.ActiveSetResult result) async {
|
||||
await database
|
||||
.into(database.activeSetResults)
|
||||
.insertOnConflictUpdate(_activeSetResultCompanion(result));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'active_set_results',
|
||||
entityType: 'ActiveSetResult',
|
||||
metadata: result.metadata,
|
||||
write: () => database
|
||||
.into(database.activeSetResults)
|
||||
.insertOnConflictUpdate(_activeSetResultCompanion(result)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveRestState(domain.ActiveRestState restState) async {
|
||||
await database
|
||||
.into(database.activeRestStates)
|
||||
.insertOnConflictUpdate(_activeRestStateCompanion(restState));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'active_rest_states',
|
||||
entityType: 'ActiveRestState',
|
||||
metadata: restState.metadata,
|
||||
write: () => database
|
||||
.into(database.activeRestStates)
|
||||
.insertOnConflictUpdate(_activeRestStateCompanion(restState)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -484,71 +547,307 @@ final class DriftWorkoutHistoryRepository implements WorkoutHistoryRepository {
|
||||
|
||||
@override
|
||||
Future<void> save(domain.WorkoutHistory history) async {
|
||||
await database
|
||||
.into(database.workoutHistories)
|
||||
.insertOnConflictUpdate(_workoutHistoryCompanion(history));
|
||||
for (final result in history.results) {
|
||||
await saveSetResult(result);
|
||||
}
|
||||
await database.transaction(() async {
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'workout_history',
|
||||
entityType: 'WorkoutHistory',
|
||||
metadata: history.metadata,
|
||||
write: () => database
|
||||
.into(database.workoutHistories)
|
||||
.insertOnConflictUpdate(_workoutHistoryCompanion(history)),
|
||||
);
|
||||
for (final result in history.results) {
|
||||
await saveSetResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSetResult(domain.WorkoutHistorySetResult result) async {
|
||||
await database
|
||||
.into(database.workoutHistorySetResults)
|
||||
.insertOnConflictUpdate(_workoutHistorySetResultCompanion(result));
|
||||
await _upsertWithChangeLog(
|
||||
database: database,
|
||||
tableName: 'workout_history_set_results',
|
||||
entityType: 'WorkoutHistorySetResult',
|
||||
metadata: result.metadata,
|
||||
write: () => database
|
||||
.into(database.workoutHistorySetResults)
|
||||
.insertOnConflictUpdate(_workoutHistorySetResultCompanion(result)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id, DateTime deletedAt) async {
|
||||
await database.transaction(() async {
|
||||
final history = await (database.select(
|
||||
database.workoutHistories,
|
||||
)..where((table) => table.id.equals(id))).getSingle();
|
||||
final revision = history.localRevision + 1;
|
||||
await (database.update(
|
||||
database.workoutHistories,
|
||||
)..where((table) => table.id.equals(id))).write(
|
||||
db.WorkoutHistoriesCompanion(
|
||||
deletedAt: Value(deletedAt),
|
||||
updatedAt: Value(deletedAt),
|
||||
deletedAt: Value(deletedAt.toUtc()),
|
||||
updatedAt: Value(deletedAt.toUtc()),
|
||||
localRevision: Value(revision),
|
||||
syncState: const Value('deleted'),
|
||||
),
|
||||
);
|
||||
await (database.update(
|
||||
database.workoutHistorySetResults,
|
||||
)..where((table) => table.workoutHistoryId.equals(id))).write(
|
||||
db.WorkoutHistorySetResultsCompanion(
|
||||
deletedAt: Value(deletedAt),
|
||||
updatedAt: Value(deletedAt),
|
||||
),
|
||||
await _writeChangeLog(
|
||||
database: database,
|
||||
entityType: 'WorkoutHistory',
|
||||
entityId: id,
|
||||
operation: 'softDelete',
|
||||
localRevision: revision,
|
||||
originDeviceId: history.originDeviceId,
|
||||
createdAt: deletedAt,
|
||||
);
|
||||
final resultRows =
|
||||
await (database.select(database.workoutHistorySetResults)..where(
|
||||
(table) =>
|
||||
table.workoutHistoryId.equals(id) &
|
||||
table.deletedAt.isNull(),
|
||||
))
|
||||
.get();
|
||||
await _softDeleteWorkoutHistorySetResultRows(
|
||||
database,
|
||||
resultRows,
|
||||
deletedAt,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _upsertWithChangeLog({
|
||||
required db.AppDatabase database,
|
||||
required String tableName,
|
||||
required String entityType,
|
||||
required domain.EntityMetadata metadata,
|
||||
required Future<void> Function() write,
|
||||
}) async {
|
||||
await database.transaction(() async {
|
||||
final operation = await _operationForMutation(
|
||||
database: database,
|
||||
tableName: tableName,
|
||||
entityId: metadata.id,
|
||||
deletedAt: metadata.deletedAt,
|
||||
);
|
||||
await write();
|
||||
await _writeChangeLog(
|
||||
database: database,
|
||||
entityType: entityType,
|
||||
entityId: metadata.id,
|
||||
operation: operation,
|
||||
localRevision: metadata.localRevision,
|
||||
originDeviceId: metadata.originDeviceId,
|
||||
createdAt: metadata.updatedAt,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> _operationForMutation({
|
||||
required db.AppDatabase database,
|
||||
required String tableName,
|
||||
required String entityId,
|
||||
required DateTime? deletedAt,
|
||||
}) async {
|
||||
final existing = await database
|
||||
.customSelect(
|
||||
'SELECT deleted_at FROM $tableName WHERE id = ? LIMIT 1',
|
||||
variables: [Variable<String>(entityId)],
|
||||
)
|
||||
.getSingleOrNull();
|
||||
if (existing == null) {
|
||||
return 'insert';
|
||||
}
|
||||
final wasDeleted = existing.data['deleted_at'] != null;
|
||||
if (deletedAt != null) {
|
||||
return 'softDelete';
|
||||
}
|
||||
if (wasDeleted) {
|
||||
return 'restore';
|
||||
}
|
||||
return 'update';
|
||||
}
|
||||
|
||||
Future<void> _writeChangeLog({
|
||||
required db.AppDatabase database,
|
||||
required String entityType,
|
||||
required String entityId,
|
||||
required String operation,
|
||||
required int localRevision,
|
||||
required String originDeviceId,
|
||||
required DateTime createdAt,
|
||||
}) async {
|
||||
final id = [
|
||||
entityType,
|
||||
entityId,
|
||||
operation,
|
||||
localRevision,
|
||||
createdAt.microsecondsSinceEpoch,
|
||||
].join(':');
|
||||
await database
|
||||
.into(database.changeLogEntries)
|
||||
.insertOnConflictUpdate(
|
||||
db.ChangeLogEntriesCompanion.insert(
|
||||
id: id,
|
||||
entityType: entityType,
|
||||
entityId: entityId,
|
||||
operation: operation,
|
||||
localRevision: localRevision,
|
||||
originDeviceId: originDeviceId,
|
||||
createdAt: createdAt.toUtc(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _softDeleteProgramExerciseRows(
|
||||
db.AppDatabase database,
|
||||
List<db.ProgramExercise> rows,
|
||||
DateTime deletedAt,
|
||||
) async {
|
||||
for (final row in rows) {
|
||||
final revision = row.localRevision + 1;
|
||||
await (database.update(
|
||||
database.programExercises,
|
||||
)..where((table) => table.id.equals(row.id))).write(
|
||||
db.ProgramExercisesCompanion(
|
||||
deletedAt: Value(deletedAt.toUtc()),
|
||||
updatedAt: Value(deletedAt.toUtc()),
|
||||
localRevision: Value(revision),
|
||||
syncState: const Value('deleted'),
|
||||
),
|
||||
);
|
||||
await _writeChangeLog(
|
||||
database: database,
|
||||
entityType: 'ProgramExercise',
|
||||
entityId: row.id,
|
||||
operation: 'softDelete',
|
||||
localRevision: revision,
|
||||
originDeviceId: row.originDeviceId,
|
||||
createdAt: deletedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _softDeleteWorkoutTemplateProgramRows(
|
||||
db.AppDatabase database,
|
||||
List<db.WorkoutTemplateProgram> rows,
|
||||
DateTime deletedAt,
|
||||
) async {
|
||||
for (final row in rows) {
|
||||
final revision = row.localRevision + 1;
|
||||
await (database.update(
|
||||
database.workoutTemplatePrograms,
|
||||
)..where((table) => table.id.equals(row.id))).write(
|
||||
db.WorkoutTemplateProgramsCompanion(
|
||||
deletedAt: Value(deletedAt.toUtc()),
|
||||
updatedAt: Value(deletedAt.toUtc()),
|
||||
localRevision: Value(revision),
|
||||
syncState: const Value('deleted'),
|
||||
),
|
||||
);
|
||||
await _writeChangeLog(
|
||||
database: database,
|
||||
entityType: 'WorkoutTemplateProgram',
|
||||
entityId: row.id,
|
||||
operation: 'softDelete',
|
||||
localRevision: revision,
|
||||
originDeviceId: row.originDeviceId,
|
||||
createdAt: deletedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _softDeleteWorkoutTemplateExerciseOverrideRows(
|
||||
db.AppDatabase database,
|
||||
List<db.WorkoutTemplateExerciseOverride> rows,
|
||||
DateTime deletedAt,
|
||||
) async {
|
||||
for (final row in rows) {
|
||||
final revision = row.localRevision + 1;
|
||||
await (database.update(
|
||||
database.workoutTemplateExerciseOverrides,
|
||||
)..where((table) => table.id.equals(row.id))).write(
|
||||
db.WorkoutTemplateExerciseOverridesCompanion(
|
||||
deletedAt: Value(deletedAt.toUtc()),
|
||||
updatedAt: Value(deletedAt.toUtc()),
|
||||
localRevision: Value(revision),
|
||||
syncState: const Value('deleted'),
|
||||
),
|
||||
);
|
||||
await _writeChangeLog(
|
||||
database: database,
|
||||
entityType: 'WorkoutTemplateExerciseOverride',
|
||||
entityId: row.id,
|
||||
operation: 'softDelete',
|
||||
localRevision: revision,
|
||||
originDeviceId: row.originDeviceId,
|
||||
createdAt: deletedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _softDeleteWorkoutHistorySetResultRows(
|
||||
db.AppDatabase database,
|
||||
List<db.WorkoutHistorySetResult> rows,
|
||||
DateTime deletedAt,
|
||||
) async {
|
||||
for (final row in rows) {
|
||||
final revision = row.localRevision + 1;
|
||||
await (database.update(
|
||||
database.workoutHistorySetResults,
|
||||
)..where((table) => table.id.equals(row.id))).write(
|
||||
db.WorkoutHistorySetResultsCompanion(
|
||||
deletedAt: Value(deletedAt.toUtc()),
|
||||
updatedAt: Value(deletedAt.toUtc()),
|
||||
localRevision: Value(revision),
|
||||
syncState: const Value('deleted'),
|
||||
),
|
||||
);
|
||||
await _writeChangeLog(
|
||||
database: database,
|
||||
entityType: 'WorkoutHistorySetResult',
|
||||
entityId: row.id,
|
||||
operation: 'softDelete',
|
||||
localRevision: revision,
|
||||
originDeviceId: row.originDeviceId,
|
||||
createdAt: deletedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DateTime _utc(DateTime value) => value.toUtc();
|
||||
|
||||
DateTime? _utcOrNull(DateTime? value) => value?.toUtc();
|
||||
|
||||
domain.EntityMetadata _metadataFromRow(dynamic row) {
|
||||
return domain.EntityMetadata(
|
||||
id: row.id as String,
|
||||
createdAt: row.createdAt as DateTime,
|
||||
updatedAt: row.updatedAt as DateTime,
|
||||
deletedAt: row.deletedAt as DateTime?,
|
||||
createdAt: _utc(row.createdAt as DateTime),
|
||||
updatedAt: _utc(row.updatedAt as DateTime),
|
||||
deletedAt: _utcOrNull(row.deletedAt as DateTime?),
|
||||
schemaVersion: row.schemaVersion as int,
|
||||
syncState: _syncStateFromDb(row.syncState as String),
|
||||
localRevision: row.localRevision as int,
|
||||
originDeviceId: row.originDeviceId as String,
|
||||
futureOwnerProfileId: row.futureOwnerProfileId as String?,
|
||||
lastSyncedAt: row.lastSyncedAt as DateTime?,
|
||||
lastSyncedAt: _utcOrNull(row.lastSyncedAt as DateTime?),
|
||||
remoteRevision: row.remoteRevision as String?,
|
||||
);
|
||||
}
|
||||
|
||||
List<dynamic> _metadataValues(domain.EntityMetadata metadata) => [
|
||||
Value(metadata.id),
|
||||
Value(metadata.createdAt),
|
||||
Value(metadata.updatedAt),
|
||||
Value(metadata.deletedAt),
|
||||
Value(metadata.createdAt.toUtc()),
|
||||
Value(metadata.updatedAt.toUtc()),
|
||||
Value(_utcOrNull(metadata.deletedAt)),
|
||||
Value(metadata.schemaVersion),
|
||||
Value(_syncStateToDb(metadata.syncState)),
|
||||
Value(metadata.localRevision),
|
||||
Value(metadata.originDeviceId),
|
||||
Value(metadata.futureOwnerProfileId),
|
||||
Value(metadata.lastSyncedAt),
|
||||
Value(_utcOrNull(metadata.lastSyncedAt)),
|
||||
Value(metadata.remoteRevision),
|
||||
];
|
||||
|
||||
@ -575,7 +874,7 @@ db.ExercisesCompanion _exerciseCompanion(domain.Exercise exercise) {
|
||||
hasScoreMeasure: Value(exercise.hasScoreMeasure),
|
||||
scoreLabel: Value(exercise.scoreLabel),
|
||||
scoreUnit: Value(exercise.scoreUnit),
|
||||
archivedAt: Value(exercise.archivedAt),
|
||||
archivedAt: Value(_utcOrNull(exercise.archivedAt)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -591,7 +890,7 @@ domain.Exercise _exerciseFromRow(db.Exercise row) {
|
||||
hasScoreMeasure: row.hasScoreMeasure,
|
||||
scoreLabel: row.scoreLabel,
|
||||
scoreUnit: row.scoreUnit,
|
||||
archivedAt: row.archivedAt,
|
||||
archivedAt: _utcOrNull(row.archivedAt),
|
||||
);
|
||||
}
|
||||
|
||||
@ -753,7 +1052,7 @@ db.WorkoutTemplatesCompanion _workoutTemplateCompanion(
|
||||
lastSyncedAt: values[9] as Value<DateTime?>,
|
||||
remoteRevision: values[10] as Value<String?>,
|
||||
name: Value(template.name),
|
||||
lastStartedAt: Value(template.lastStartedAt),
|
||||
lastStartedAt: Value(_utcOrNull(template.lastStartedAt)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -765,7 +1064,7 @@ domain.WorkoutTemplate _workoutTemplateFromRow(
|
||||
return domain.WorkoutTemplate(
|
||||
metadata: _metadataFromRow(row),
|
||||
name: row.name,
|
||||
lastStartedAt: row.lastStartedAt,
|
||||
lastStartedAt: _utcOrNull(row.lastStartedAt),
|
||||
programs: programs,
|
||||
overrides: overrides,
|
||||
);
|
||||
@ -868,10 +1167,10 @@ db.ActiveWorkoutSessionsCompanion _activeWorkoutSessionCompanion(
|
||||
remoteRevision: values[10] as Value<String?>,
|
||||
sourceWorkoutTemplateId: Value(session.sourceWorkoutTemplateId),
|
||||
status: Value(_activeStatusToDb(session.status)),
|
||||
startedAt: Value(session.startedAt),
|
||||
pausedAt: Value(session.pausedAt),
|
||||
endedAt: Value(session.endedAt),
|
||||
lastPersistedAt: Value(session.lastPersistedAt),
|
||||
startedAt: Value(session.startedAt.toUtc()),
|
||||
pausedAt: Value(_utcOrNull(session.pausedAt)),
|
||||
endedAt: Value(_utcOrNull(session.endedAt)),
|
||||
lastPersistedAt: Value(session.lastPersistedAt.toUtc()),
|
||||
elapsedActiveMs: Value(session.elapsedActiveMs),
|
||||
currentProgramIndex: Value(session.currentProgramIndex),
|
||||
currentExerciseIndex: Value(session.currentExerciseIndex),
|
||||
@ -887,10 +1186,10 @@ domain.ActiveWorkoutSession _activeWorkoutSessionFromRow(
|
||||
metadata: _metadataFromRow(row),
|
||||
sourceWorkoutTemplateId: row.sourceWorkoutTemplateId,
|
||||
status: _activeStatusFromDb(row.status),
|
||||
startedAt: row.startedAt,
|
||||
pausedAt: row.pausedAt,
|
||||
endedAt: row.endedAt,
|
||||
lastPersistedAt: row.lastPersistedAt,
|
||||
startedAt: _utc(row.startedAt),
|
||||
pausedAt: _utcOrNull(row.pausedAt),
|
||||
endedAt: _utcOrNull(row.endedAt),
|
||||
lastPersistedAt: _utc(row.lastPersistedAt),
|
||||
elapsedActiveMs: row.elapsedActiveMs,
|
||||
currentProgramIndex: row.currentProgramIndex,
|
||||
currentExerciseIndex: row.currentExerciseIndex,
|
||||
@ -921,8 +1220,8 @@ db.ActiveSetResultsCompanion _activeSetResultCompanion(
|
||||
programIndex: Value(result.programIndex),
|
||||
exerciseIndex: Value(result.exerciseIndex),
|
||||
setIndex: Value(result.setIndex),
|
||||
startedAt: Value(result.startedAt),
|
||||
completedAt: Value(result.completedAt),
|
||||
startedAt: Value(_utcOrNull(result.startedAt)),
|
||||
completedAt: Value(_utcOrNull(result.completedAt)),
|
||||
actualTimeMs: Value(result.actualTimeMs),
|
||||
actualReps: Value(result.actualReps),
|
||||
actualScore: Value(result.actualScore),
|
||||
@ -942,8 +1241,8 @@ domain.ActiveSetResult _activeSetResultFromRow(db.ActiveSetResult row) {
|
||||
programIndex: row.programIndex,
|
||||
exerciseIndex: row.exerciseIndex,
|
||||
setIndex: row.setIndex,
|
||||
startedAt: row.startedAt,
|
||||
completedAt: row.completedAt,
|
||||
startedAt: _utcOrNull(row.startedAt),
|
||||
completedAt: _utcOrNull(row.completedAt),
|
||||
actualTimeMs: row.actualTimeMs,
|
||||
actualReps: row.actualReps,
|
||||
actualScore: row.actualScore,
|
||||
@ -976,9 +1275,9 @@ db.ActiveRestStatesCompanion _activeRestStateCompanion(
|
||||
afterSetIndex: Value(restState.afterSetIndex),
|
||||
plannedRestSeconds: Value(restState.plannedRestSeconds),
|
||||
adjustedRestSeconds: Value(restState.adjustedRestSeconds),
|
||||
startedAt: Value(restState.startedAt),
|
||||
endedAt: Value(restState.endedAt),
|
||||
skippedAt: Value(restState.skippedAt),
|
||||
startedAt: Value(restState.startedAt.toUtc()),
|
||||
endedAt: Value(_utcOrNull(restState.endedAt)),
|
||||
skippedAt: Value(_utcOrNull(restState.skippedAt)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -991,9 +1290,9 @@ domain.ActiveRestState _activeRestStateFromRow(db.ActiveRestState row) {
|
||||
afterSetIndex: row.afterSetIndex,
|
||||
plannedRestSeconds: row.plannedRestSeconds,
|
||||
adjustedRestSeconds: row.adjustedRestSeconds,
|
||||
startedAt: row.startedAt,
|
||||
endedAt: row.endedAt,
|
||||
skippedAt: row.skippedAt,
|
||||
startedAt: _utc(row.startedAt),
|
||||
endedAt: _utcOrNull(row.endedAt),
|
||||
skippedAt: _utcOrNull(row.skippedAt),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1016,8 +1315,8 @@ db.WorkoutHistoriesCompanion _workoutHistoryCompanion(
|
||||
sourceWorkoutTemplateId: Value(history.sourceWorkoutTemplateId),
|
||||
sourceActiveWorkoutSessionId: Value(history.sourceActiveWorkoutSessionId),
|
||||
nameSnapshot: Value(history.nameSnapshot),
|
||||
startedAt: Value(history.startedAt),
|
||||
endedAt: Value(history.endedAt),
|
||||
startedAt: Value(history.startedAt.toUtc()),
|
||||
endedAt: Value(history.endedAt.toUtc()),
|
||||
totalActiveMs: Value(history.totalActiveMs),
|
||||
completed: Value(history.completed),
|
||||
historySnapshotJson: Value(history.historySnapshotJson),
|
||||
@ -1059,8 +1358,8 @@ db.WorkoutHistorySetResultsCompanion _workoutHistorySetResultCompanion(
|
||||
actualScore: Value(result.actualScore),
|
||||
scoreLabelSnapshot: Value(result.scoreLabelSnapshot),
|
||||
scoreUnitSnapshot: Value(result.scoreUnitSnapshot),
|
||||
startedAt: Value(result.startedAt),
|
||||
completedAt: Value(result.completedAt),
|
||||
startedAt: Value(_utcOrNull(result.startedAt)),
|
||||
completedAt: Value(_utcOrNull(result.completedAt)),
|
||||
status: Value(_setResultStatusToDb(result.status)),
|
||||
);
|
||||
}
|
||||
@ -1074,8 +1373,8 @@ domain.WorkoutHistory _workoutHistoryFromRow(
|
||||
sourceWorkoutTemplateId: row.sourceWorkoutTemplateId,
|
||||
sourceActiveWorkoutSessionId: row.sourceActiveWorkoutSessionId,
|
||||
nameSnapshot: row.nameSnapshot,
|
||||
startedAt: row.startedAt,
|
||||
endedAt: row.endedAt,
|
||||
startedAt: _utc(row.startedAt),
|
||||
endedAt: _utc(row.endedAt),
|
||||
totalActiveMs: row.totalActiveMs,
|
||||
completed: row.completed,
|
||||
historySnapshotJson: row.historySnapshotJson,
|
||||
@ -1107,8 +1406,8 @@ domain.WorkoutHistorySetResult _workoutHistorySetResultFromRow(
|
||||
actualScore: row.actualScore,
|
||||
scoreLabelSnapshot: row.scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: row.scoreUnitSnapshot,
|
||||
startedAt: row.startedAt,
|
||||
completedAt: row.completedAt,
|
||||
startedAt: _utcOrNull(row.startedAt),
|
||||
completedAt: _utcOrNull(row.completedAt),
|
||||
status: _setResultStatusFromDb(row.status),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user