Files
notas/lib/data/sync_models.dart
T
Marcos def755e1c5 refactor: Update Note and Category models to use 'id' instead of 'uuid', and adjust related database operations
- Changed 'uuid' to 'id' in Note and Category models for consistency.
- Updated database operations in NoteRepository to reflect the new 'id' field.
- Modified sync models to accommodate changes in Note and Category structures.
- Adjusted the handling of notes and categories during synchronization.
- Refactored the note editor and home screen to use the new 'id' field.
- Ensured that the 'isDirty' flag is properly set and utilized across models.
2026-05-20 11:05:30 +02:00

298 lines
7.7 KiB
Dart

import 'package:notas/models/note.dart';
import 'package:notas/models/category.dart';
import 'dart:convert';
// DTOs para sincronización con el servidor
class SyncRequest {
SyncRequest({DateTime? lastSyncAt, required this.changes})
: lastSyncAt = lastSyncAt ?? DateTime.utc(1970, 1, 1);
final DateTime lastSyncAt;
final SyncChanges changes;
Map<String, dynamic> toJson() {
return {
'lastSyncAt': lastSyncAt.toIso8601String(),
'changes': changes.toJson(),
};
}
}
class SyncChanges {
const SyncChanges({this.categories = const [], this.notes = const []});
final List<SyncCategoryPayload> categories;
final List<SyncNotePayload> notes;
Map<String, dynamic> toJson() {
return {
if (categories.isNotEmpty)
'categories': categories.map((c) => c.toJson()).toList(),
if (notes.isNotEmpty) 'notes': notes.map((n) => n.toJson()).toList(),
};
}
}
class SyncChangesResponse {
const SyncChangesResponse({
this.categories = const [],
this.notes = const [],
});
final List<SyncCategoryResponse> categories;
final List<SyncNoteResponse> notes;
factory SyncChangesResponse.fromJson(Map<String, dynamic> json) {
final List<dynamic> categoriesJson =
json['categories'] as List<dynamic>? ?? [];
final List<dynamic> notesJson = json['notes'] as List<dynamic>? ?? [];
return SyncChangesResponse(
categories: categoriesJson
.map((c) => SyncCategoryResponse.fromJson(c as Map<String, dynamic>))
.toList(),
notes: notesJson
.map((n) => SyncNoteResponse.fromJson(n as Map<String, dynamic>))
.toList(),
);
}
}
String _readStringValue(dynamic value) {
if (value is String) {
return value;
}
if (value == null) {
throw FormatException('Expected String value but found null');
}
return jsonEncode(value);
}
String _readOptionalStringValue(dynamic value) {
if (value == null) {
return '';
}
return _readStringValue(value);
}
int _readIntValue(dynamic value) {
if (value is int) {
return value;
}
if (value is String) {
final int? parsed = int.tryParse(value);
if (parsed != null) {
return parsed;
}
}
throw FormatException('Expected int value but found $value');
}
class SyncCategoryPayload {
const SyncCategoryPayload({
required this.id,
required this.encryptedName,
required this.serverVersion,
this.isDeleted = false,
required this.updatedAt,
});
final String id;
final String encryptedName;
final int serverVersion;
final bool isDeleted;
final DateTime updatedAt;
factory SyncCategoryPayload.fromCategory(
Category category, {
required String encryptedName,
}) {
return SyncCategoryPayload(
id: category.id,
encryptedName: encryptedName,
serverVersion: category.serverVersion,
isDeleted: category.isDeleted,
updatedAt: category.updatedAt,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'encrypted_name': encryptedName,
'serverVersion': serverVersion,
'isDeleted': isDeleted,
'updatedAt': updatedAt.toIso8601String(),
};
}
}
class SyncNotePayload {
const SyncNotePayload({
required this.id,
this.categoryId,
required this.encryptedTitle,
required this.encryptedBody,
required this.serverVersion,
this.position = 0.0,
this.isDeleted = false,
this.isPermanentlyDeleted = false,
required this.updatedAt,
});
final String id;
final String? categoryId;
final String encryptedTitle;
final String encryptedBody;
final int serverVersion;
final double position;
final bool isDeleted;
final bool isPermanentlyDeleted;
final DateTime updatedAt;
factory SyncNotePayload.fromNote(
Note note, {
required String encryptedTitle,
required String encryptedBody,
bool isPermanentlyDeleted = false,
}) {
return SyncNotePayload(
id: note.id,
categoryId: note.categoryId,
encryptedTitle: encryptedTitle,
encryptedBody: encryptedBody,
serverVersion: note.serverVersion,
position: note.position,
isDeleted: note.isDeleted,
isPermanentlyDeleted: isPermanentlyDeleted,
updatedAt: note.updatedAt,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
if (categoryId != null) 'categoryId': categoryId,
'encrypted_title': encryptedTitle,
'encrypted_body': encryptedBody,
'serverVersion': serverVersion,
if (position != 0) 'position': position,
if (isDeleted) 'isDeleted': isDeleted,
if (isPermanentlyDeleted) 'isPermanentlyDeleted': isPermanentlyDeleted,
'updatedAt': updatedAt.toIso8601String(),
};
}
}
class SyncResponse {
const SyncResponse({
required this.serverTimestamp,
required this.synced,
required this.changes,
});
final DateTime serverTimestamp;
final bool synced;
final SyncChangesResponse changes;
factory SyncResponse.fromJson(Map<String, dynamic> json) {
return SyncResponse(
serverTimestamp: DateTime.parse(json['serverTimestamp'] as String),
synced: json['synced'] as bool? ?? false,
changes: SyncChangesResponse.fromJson(
json['changes'] as Map<String, dynamic>? ?? {},
),
);
}
}
class SyncCategoryResponse {
const SyncCategoryResponse({
required this.id,
required this.encryptedName,
required this.serverVersion,
this.isDeleted = false,
required this.updatedAt,
});
final String id;
final String encryptedName;
final int serverVersion;
final bool isDeleted;
final DateTime updatedAt;
factory SyncCategoryResponse.fromJson(Map<String, dynamic> json) {
return SyncCategoryResponse(
id: _readStringValue(json['id']),
encryptedName: _readStringValue(json['encrypted_name']),
serverVersion: _readIntValue(json['serverVersion']),
isDeleted: json['isDeleted'] as bool? ?? false,
updatedAt: DateTime.parse(json['updatedAt'] as String),
);
}
Category toCategory({required String name}) {
return Category(
id: id,
name: name,
serverVersion: serverVersion,
isDeleted: isDeleted,
updatedAt: updatedAt,
);
}
}
class SyncNoteResponse {
const SyncNoteResponse({
required this.id,
this.categoryId,
required this.encryptedTitle,
required this.encryptedBody,
required this.serverVersion,
this.position = 0.0,
this.isDeleted = false,
this.isPermanentlyDeleted = false,
required this.updatedAt,
});
final String id;
final String? categoryId;
final String encryptedTitle;
final String encryptedBody;
final int serverVersion;
final double position;
final bool isDeleted;
final bool isPermanentlyDeleted;
final DateTime updatedAt;
factory SyncNoteResponse.fromJson(Map<String, dynamic> json) {
return SyncNoteResponse(
id: _readStringValue(json['id']),
categoryId: json['categoryId'] == null
? null
: _readStringValue(json['categoryId']),
encryptedTitle: _readOptionalStringValue(json['encrypted_title']),
encryptedBody: _readOptionalStringValue(json['encrypted_body']),
serverVersion: _readIntValue(json['serverVersion']),
position: (json['position'] as num?)?.toDouble() ?? 0,
isDeleted: json['isDeleted'] as bool? ?? false,
isPermanentlyDeleted: json['isPermanentlyDeleted'] as bool? ?? false,
updatedAt: DateTime.parse(json['updatedAt'] as String),
);
}
Note toNote() {
return Note(
id: id,
title: isPermanentlyDeleted ? '' : 'Encrypted',
body: isPermanentlyDeleted ? '' : 'Encrypted',
createdAt: updatedAt,
updatedAt: updatedAt,
position: position,
serverVersion: serverVersion,
isDeleted: isDeleted,
categoryId: categoryId,
isDirty: false,
);
}
}