feat: Refactor sync status handling and improve synchronization feedback in the app

This commit is contained in:
2026-05-19 09:23:38 +02:00
parent bb8caeef93
commit a5ab223e1f
6 changed files with 388 additions and 128 deletions
+60 -5
View File
@@ -6,6 +6,7 @@ import 'package:notas/models/note.dart';
import 'package:notas/models/category.dart';
import 'package:notas/data/note_encryption.dart';
import 'package:notas/widgets/sync_status.dart';
class NoteRepository {
NoteRepository({
@@ -89,8 +90,17 @@ class NoteRepository {
/// Sincroniza notas con el servidor.
/// Requiere que el usuario esté autenticado (token válido).
Future<Map<String, dynamic>> performSync({bool forceFull = false}) async {
Future<Map<String, dynamic>> performSync({
bool forceFull = false,
void Function(SyncStatus status, {double? progress, String? message})?
onProgress,
}) async {
try {
onProgress?.call(
SyncStatus.preparing,
message: 'Preparando sincronización...',
);
// Get last sync timestamp
final DateTime? lastSync = await _authApi.getLastSyncAt();
final DateTime? lastSyncForRequest = forceFull
@@ -113,10 +123,21 @@ class NoteRepository {
);
}
final int totalNotesToEncrypt = unsyncedNotes.length;
// Build sync request (note: we send encrypted data, but locally we have plaintext)
// Encrypt all notes before sending
final List<SyncNotePayload> encryptedNotesPayload = [];
for (final dbNote in unsyncedNotes) {
if (totalNotesToEncrypt == 0) {
onProgress?.call(
SyncStatus.encrypting,
progress: 1.0,
message: 'No hay notas pendientes de encriptar.',
);
}
for (var index = 0; index < unsyncedNotes.length; index += 1) {
final DbNote dbNote = unsyncedNotes[index];
final note = _fromDbNote(dbNote);
final encryptedTitle = await NoteEncryption.encryptNote(
note.title,
@@ -133,6 +154,13 @@ class NoteRepository {
encryptedBody: encryptedBody,
),
);
onProgress?.call(
SyncStatus.encrypting,
progress: (index + 1) / totalNotesToEncrypt,
message:
'Encriptando notas para subir: ${index + 1} de $totalNotesToEncrypt',
);
}
final List<SyncCategoryPayload> categoriesPayload = unsyncedCategories
@@ -148,6 +176,10 @@ class NoteRepository {
);
// Call sync API
onProgress?.call(
SyncStatus.uploading,
message: 'Subiendo datos al servidor...',
);
final Map<String, dynamic> syncResult = await _authApi.sync(syncRequest);
if (syncResult['error'] == true) {
@@ -157,7 +189,23 @@ class NoteRepository {
final SyncResponse response = syncResult['data'] as SyncResponse;
// Apply server changes to local database
await _applySyncResponse(response);
onProgress?.call(
SyncStatus.waitingResponse,
message: 'Esperando respuesta del servidor...',
);
await _applySyncResponse(
response,
onDecryptProgress: (int processed, int total) {
onProgress?.call(
SyncStatus.decrypting,
progress: total == 0 ? 1.0 : processed / total,
message: total == 0
? 'Desencriptando datos recibidos...'
: 'Desencriptando respuesta: $processed de $total',
);
},
);
// Update lastSyncAt
await _authApi.setLastSyncAt(response.serverTimestamp);
@@ -173,7 +221,10 @@ class NoteRepository {
}
}
Future<void> _applySyncResponse(SyncResponse response) async {
Future<void> _applySyncResponse(
SyncResponse response, {
void Function(int processed, int total)? onDecryptProgress,
}) async {
// Apply categories from server
for (final SyncCategoryResponse catResponse
in response.changes.categories) {
@@ -189,7 +240,9 @@ class NoteRepository {
}
// Apply notes from server
for (final SyncNoteResponse noteResponse in response.changes.notes) {
final int totalNotesToDecrypt = response.changes.notes.length;
for (var index = 0; index < response.changes.notes.length; index += 1) {
final SyncNoteResponse noteResponse = response.changes.notes[index];
final existingNote = await (_database.select(
_database.notes,
)..where((n) => n.uuid.equals(noteResponse.id))).getSingleOrNull();
@@ -245,6 +298,8 @@ class NoteRepository {
),
);
}
onDecryptProgress?.call(index + 1, totalNotesToDecrypt);
}
}