feat: add local backup export import core
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import '../domain/domain.dart';
|
||||
|
||||
abstract interface class Clock {
|
||||
@ -34,6 +36,213 @@ final class TagUsage {
|
||||
final int count;
|
||||
}
|
||||
|
||||
enum LocalBackupImportMode { merge, replaceAll }
|
||||
|
||||
enum LocalBackupValidationError {
|
||||
invalidFile,
|
||||
incompatibleFormat,
|
||||
newerVersion,
|
||||
corrupted,
|
||||
activeWorkoutInProgress,
|
||||
importFailedNoMutation,
|
||||
}
|
||||
|
||||
final class LocalBackupException implements Exception {
|
||||
const LocalBackupException(this.error);
|
||||
|
||||
final LocalBackupValidationError error;
|
||||
|
||||
@override
|
||||
String toString() => error.name;
|
||||
}
|
||||
|
||||
final class LocalBackupDocument {
|
||||
const LocalBackupDocument({required this.fileName, required this.bytes});
|
||||
|
||||
final String fileName;
|
||||
final Uint8List bytes;
|
||||
}
|
||||
|
||||
final class LocalBackupCounts {
|
||||
const LocalBackupCounts({
|
||||
required this.exercises,
|
||||
required this.programs,
|
||||
required this.workoutTemplates,
|
||||
required this.workoutHistories,
|
||||
required this.mediaAssets,
|
||||
required this.embeddedMediaFiles,
|
||||
});
|
||||
|
||||
final int exercises;
|
||||
final int programs;
|
||||
final int workoutTemplates;
|
||||
final int workoutHistories;
|
||||
final int mediaAssets;
|
||||
final int embeddedMediaFiles;
|
||||
}
|
||||
|
||||
final class LocalBackupPreview {
|
||||
const LocalBackupPreview({
|
||||
required this.exportedAt,
|
||||
required this.counts,
|
||||
required this.hasEmbeddedMedia,
|
||||
required this.missingMediaCount,
|
||||
required this.hasNameDuplicates,
|
||||
});
|
||||
|
||||
final DateTime exportedAt;
|
||||
final LocalBackupCounts counts;
|
||||
final bool hasEmbeddedMedia;
|
||||
final int missingMediaCount;
|
||||
final bool hasNameDuplicates;
|
||||
}
|
||||
|
||||
final class LocalBackupImportResult {
|
||||
const LocalBackupImportResult({
|
||||
required this.insertedCount,
|
||||
required this.updatedCount,
|
||||
required this.ignoredOlderCount,
|
||||
required this.deletedByReplaceCount,
|
||||
required this.missingMediaCount,
|
||||
});
|
||||
|
||||
final int insertedCount;
|
||||
final int updatedCount;
|
||||
final int ignoredOlderCount;
|
||||
final int deletedByReplaceCount;
|
||||
final int missingMediaCount;
|
||||
|
||||
LocalBackupImportResult copyWith({int? missingMediaCount}) {
|
||||
return LocalBackupImportResult(
|
||||
insertedCount: insertedCount,
|
||||
updatedCount: updatedCount,
|
||||
ignoredOlderCount: ignoredOlderCount,
|
||||
deletedByReplaceCount: deletedByReplaceCount,
|
||||
missingMediaCount: missingMediaCount ?? this.missingMediaCount,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class LocalBackupResource {
|
||||
const LocalBackupResource({
|
||||
required this.id,
|
||||
required this.updatedAt,
|
||||
required this.payload,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final DateTime updatedAt;
|
||||
final Map<String, Object?> payload;
|
||||
|
||||
LocalBackupResource copyWith({Map<String, Object?>? payload}) {
|
||||
return LocalBackupResource(
|
||||
id: id,
|
||||
updatedAt: updatedAt,
|
||||
payload: payload ?? this.payload,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class LocalDataExportSnapshot {
|
||||
const LocalDataExportSnapshot({
|
||||
required this.exportedAt,
|
||||
required this.appSchemaVersion,
|
||||
required this.originDeviceId,
|
||||
required this.mediaAssets,
|
||||
required this.exercises,
|
||||
required this.programs,
|
||||
required this.workoutTemplates,
|
||||
required this.workoutHistories,
|
||||
this.mediaFiles = const [],
|
||||
});
|
||||
|
||||
final DateTime exportedAt;
|
||||
final int appSchemaVersion;
|
||||
final String originDeviceId;
|
||||
final List<LocalBackupResource> mediaAssets;
|
||||
final List<LocalBackupResource> exercises;
|
||||
final List<LocalBackupResource> programs;
|
||||
final List<LocalBackupResource> workoutTemplates;
|
||||
final List<LocalBackupResource> workoutHistories;
|
||||
final List<EmbeddedBackupMediaFile> mediaFiles;
|
||||
|
||||
LocalBackupCounts get counts => LocalBackupCounts(
|
||||
exercises: exercises.length,
|
||||
programs: programs.length,
|
||||
workoutTemplates: workoutTemplates.length,
|
||||
workoutHistories: workoutHistories.length,
|
||||
mediaAssets: mediaAssets.length,
|
||||
embeddedMediaFiles: mediaFiles.length,
|
||||
);
|
||||
|
||||
LocalDataExportSnapshot copyWith({
|
||||
List<LocalBackupResource>? mediaAssets,
|
||||
List<EmbeddedBackupMediaFile>? mediaFiles,
|
||||
}) {
|
||||
return LocalDataExportSnapshot(
|
||||
exportedAt: exportedAt,
|
||||
appSchemaVersion: appSchemaVersion,
|
||||
originDeviceId: originDeviceId,
|
||||
mediaAssets: mediaAssets ?? this.mediaAssets,
|
||||
exercises: exercises,
|
||||
programs: programs,
|
||||
workoutTemplates: workoutTemplates,
|
||||
workoutHistories: workoutHistories,
|
||||
mediaFiles: mediaFiles ?? this.mediaFiles,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class EmbeddedBackupMediaFile {
|
||||
const EmbeddedBackupMediaFile({
|
||||
required this.mediaAssetId,
|
||||
required this.role,
|
||||
required this.fileName,
|
||||
required this.mimeType,
|
||||
required this.sizeBytes,
|
||||
required this.base64,
|
||||
});
|
||||
|
||||
final String mediaAssetId;
|
||||
final String role;
|
||||
final String fileName;
|
||||
final String? mimeType;
|
||||
final int sizeBytes;
|
||||
final String base64;
|
||||
}
|
||||
|
||||
final class RestoredMediaFile {
|
||||
const RestoredMediaFile({
|
||||
required this.mediaAssetId,
|
||||
required this.localUri,
|
||||
required this.sizeBytes,
|
||||
});
|
||||
|
||||
final String mediaAssetId;
|
||||
final String localUri;
|
||||
final int sizeBytes;
|
||||
}
|
||||
|
||||
abstract interface class LocalDataBackupRepository {
|
||||
Future<LocalDataExportSnapshot> readExportSnapshot(DateTime exportedAt);
|
||||
Future<bool> hasOpenActiveWorkoutSession();
|
||||
Future<bool> hasAnyUserData();
|
||||
Future<LocalBackupImportResult> applyImportSnapshot({
|
||||
required LocalDataExportSnapshot snapshot,
|
||||
required LocalBackupImportMode mode,
|
||||
required DateTime importedAt,
|
||||
});
|
||||
}
|
||||
|
||||
abstract interface class LocalBackupMediaStore {
|
||||
Future<List<EmbeddedBackupMediaFile>> readEmbeddableFiles(
|
||||
List<LocalBackupResource> mediaAssets,
|
||||
);
|
||||
Future<Map<String, RestoredMediaFile>> restoreEmbeddedFiles(
|
||||
List<EmbeddedBackupMediaFile> files,
|
||||
);
|
||||
}
|
||||
|
||||
enum ProgressionPeriod { fourWeeks, threeMonths, all }
|
||||
|
||||
enum ProgressionMeasure { manualScore, stopwatchScore, reps, time }
|
||||
|
||||
Reference in New Issue
Block a user