Implement annotation syncing to and from server
This commit is contained in:
118
lib/core/services/annotation_sync_service.dart
Normal file
118
lib/core/services/annotation_sync_service.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'api_client.dart';
|
||||
import 'storage_service.dart';
|
||||
|
||||
/// Service for synchronizing annotations between local storage and server.
|
||||
///
|
||||
/// Handles downloading annotations on sheet open and uploading on save,
|
||||
/// comparing timestamps to determine which version is newer.
|
||||
class AnnotationSyncService {
|
||||
final _log = Logger('AnnotationSyncService');
|
||||
final ApiClient _apiClient;
|
||||
final StorageService _storageService;
|
||||
|
||||
AnnotationSyncService({
|
||||
required ApiClient apiClient,
|
||||
required StorageService storageService,
|
||||
}) : _apiClient = apiClient,
|
||||
_storageService = storageService;
|
||||
|
||||
/// Downloads annotations from server and merges with local storage.
|
||||
///
|
||||
/// For each page, compares server's lastModified with local lastModified.
|
||||
/// If server is newer, overwrites local. Local annotations that are newer
|
||||
/// are preserved.
|
||||
Future<void> syncFromServer(String sheetUuid) async {
|
||||
try {
|
||||
_log.info('Syncing annotations from server for sheet $sheetUuid');
|
||||
|
||||
// Fetch all annotations from server
|
||||
final serverAnnotations = await _apiClient.fetchAnnotations(sheetUuid);
|
||||
|
||||
// Get all local annotations with metadata
|
||||
final localAnnotations = await _storageService
|
||||
.readAllAnnotationsWithMetadata(sheetUuid);
|
||||
|
||||
int updatedCount = 0;
|
||||
|
||||
// Process each server annotation
|
||||
for (final serverAnnotation in serverAnnotations) {
|
||||
final page = serverAnnotation.page;
|
||||
final localAnnotation = localAnnotations[page];
|
||||
|
||||
bool shouldUpdate = false;
|
||||
|
||||
if (localAnnotation == null) {
|
||||
// No local annotation - use server version
|
||||
shouldUpdate = true;
|
||||
_log.fine('Page $page: No local annotation, using server version');
|
||||
} else if (serverAnnotation.lastModified.isAfter(
|
||||
localAnnotation.lastModified,
|
||||
)) {
|
||||
// Server is newer - overwrite local
|
||||
shouldUpdate = true;
|
||||
_log.fine(
|
||||
'Page $page: Server is newer '
|
||||
'(server: ${serverAnnotation.lastModified}, '
|
||||
'local: ${localAnnotation.lastModified})',
|
||||
);
|
||||
} else {
|
||||
_log.fine(
|
||||
'Page $page: Local is newer or same, keeping local version',
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldUpdate) {
|
||||
await _storageService.writeAnnotationsWithMetadata(
|
||||
sheetUuid,
|
||||
page,
|
||||
serverAnnotation.annotationsJson,
|
||||
serverAnnotation.lastModified,
|
||||
);
|
||||
updatedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
_log.info(
|
||||
'Sync complete: $updatedCount pages updated from server '
|
||||
'(${serverAnnotations.length} total on server)',
|
||||
);
|
||||
} on ApiException catch (e) {
|
||||
_log.warning('Failed to sync annotations from server: $e');
|
||||
} catch (e) {
|
||||
_log.warning('Unexpected error syncing annotations: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Uploads a single page's annotation to the server.
|
||||
///
|
||||
/// Called when annotations are saved (e.g., exiting paint mode).
|
||||
/// Silently fails if upload fails (allows offline usage).
|
||||
Future<bool> uploadAnnotation({
|
||||
required String sheetUuid,
|
||||
required int page,
|
||||
required String annotationsJson,
|
||||
required DateTime lastModified,
|
||||
}) async {
|
||||
try {
|
||||
_log.info('Uploading annotation for sheet $sheetUuid page $page');
|
||||
|
||||
await _apiClient.uploadAnnotation(
|
||||
sheetUuid: sheetUuid,
|
||||
page: page,
|
||||
lastModified: lastModified,
|
||||
annotationsJson: annotationsJson,
|
||||
);
|
||||
|
||||
_log.info('Upload successful');
|
||||
return true;
|
||||
} on ApiException catch (e) {
|
||||
_log.warning('Failed to upload annotation: $e');
|
||||
return false;
|
||||
} catch (e) {
|
||||
_log.warning('Unexpected error uploading annotation: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user