feat: Add color and icon properties to categories, enhance category management in UI

This commit is contained in:
2026-05-20 17:10:44 +02:00
parent def755e1c5
commit 3ff4efb738
8 changed files with 517 additions and 45 deletions
+68 -23
View File
@@ -12,6 +12,7 @@ import 'package:notas/models/category.dart';
import 'package:notas/data/note_encryption.dart';
import 'package:notas/widgets/sync_status.dart';
import 'package:flutter/foundation.dart' show debugPrint;
class NoteRepository {
NoteRepository({
@@ -34,6 +35,56 @@ class NoteRepository {
return _loadDeletedNotesFromDatabase();
}
Future<List<Category>> loadCategories() async {
final List<DbCategory> dbCategories = await _database.getAllCategories();
final List<Category> categories = [];
for (final DbCategory row in dbCategories) {
try {
final String decryptedName = await NoteEncryption.decryptNote(
row.encryptedName,
_masterKey,
);
categories.add(
Category(
id: row.id,
name: decryptedName,
serverVersion: row.serverVersion,
isDeleted: row.isDeleted,
updatedAt: row.updatedAt,
isDirty: row.isDirty,
colorValue: row.colorValue,
iconCodePoint: row.iconCodePoint,
),
);
} catch (e) {
debugPrint('Error al desencriptar categoría: $e');
}
}
return categories;
}
Future<void> createCategory(Category category) async {
debugPrint('createCategory called with: ${category.name}');
final String encryptedName = await NoteEncryption.encryptNote(
category.name,
_masterKey,
);
debugPrint('Category name encrypted');
await _database.upsertCategory(
CategoriesCompanion.insert(
id: category.id,
encryptedName: encryptedName,
updatedAt: category.updatedAt,
serverVersion: const Value(0),
isDeleted: const Value(false),
isDirty: const Value(true),
colorValue: Value<int?>(category.colorValue),
iconCodePoint: Value<int?>(category.iconCodePoint),
),
);
debugPrint('Category inserted to database');
}
Future<Note> createNote(Note note) async {
await _database.insertNoteAtTop(
NotesCompanion.insert(
@@ -60,7 +111,7 @@ class NoteRepository {
final DbNote row =
existingNote ??
(throw ArgumentError('Note not found for id ${note.id}.'));
(throw ArgumentError('Note not found for id ${note.id}.'));
await _database.updateNoteRow(
DbNote(
@@ -92,7 +143,7 @@ class NoteRepository {
final DbNote row =
existingNote ??
(throw ArgumentError('Note not found for id ${note.id}.'));
(throw ArgumentError('Note not found for id ${note.id}.'));
if (row.isDeleted || note.isDeleted || note.isPermanentlyDeleted) {
await _database.permanentlyDeleteNote(row.id);
@@ -111,7 +162,7 @@ class NoteRepository {
final DbNote row =
existingNote ??
(throw ArgumentError('Note not found for id ${note.id}.'));
(throw ArgumentError('Note not found for id ${note.id}.'));
await _database.moveNote(
id: row.id,
@@ -212,10 +263,7 @@ class NoteRepository {
details.add('StackTrace: ${stackTrace.toString()}');
}
return {
'error': true,
'message': details.join('\n\n'),
};
return {'error': true, 'message': details.join('\n\n')};
}
final SyncResponse response = syncResult['data'] as SyncResponse;
@@ -249,10 +297,7 @@ class NoteRepository {
'categoriesCount': response.changes.categories.length,
};
} catch (e, st) {
return {
'error': true,
'message': '$e\n\nStackTrace: $st',
};
return {'error': true, 'message': '$e\n\nStackTrace: $st'};
}
}
@@ -263,21 +308,18 @@ class NoteRepository {
// Apply categories from server
for (final SyncCategoryResponse catResponse
in response.changes.categories) {
final String categoryName =
catResponse.isDeleted || catResponse.encryptedName.isEmpty
? ''
: await NoteEncryption.decryptNote(
catResponse.encryptedName,
_masterKey,
);
// Store the encrypted blob received from the server directly in the DB.
// Decryption is only performed when loading categories for display.
final String encryptedBlob = catResponse.encryptedName;
await _database.upsertCategory(
CategoriesCompanion(
id: Value(catResponse.id),
encryptedName: Value(categoryName),
encryptedName: Value(encryptedBlob),
serverVersion: Value(catResponse.serverVersion),
isDeleted: Value(catResponse.isDeleted),
colorValue: Value<int?>(catResponse.colorValue),
iconCodePoint: Value<int?>(catResponse.iconCodePoint),
updatedAt: Value(catResponse.updatedAt),
isDirty: const Value(false),
),
@@ -439,9 +481,10 @@ Future<List<SyncCategoryPayload>> _encryptCategories(
final List<SyncCategoryPayload> payloads = [];
for (final DbCategory row in categories) {
final String encryptedName = row.encryptedName.isEmpty
? ''
: await NoteEncryption.encryptNote(row.encryptedName, masterKey);
// The DB already stores the encrypted name blob in `encryptedName`.
// Use it directly when building the sync payload and preserve
// color/icon values from the DB row so they are sent to the server.
final String encryptedName = row.encryptedName;
payloads.add(
SyncCategoryPayload.fromCategory(
@@ -452,6 +495,8 @@ Future<List<SyncCategoryPayload>> _encryptCategories(
isDeleted: row.isDeleted,
updatedAt: row.updatedAt,
isDirty: row.isDirty,
colorValue: row.colorValue,
iconCodePoint: row.iconCodePoint,
),
encryptedName: encryptedName,
),