refactor: Improve code formatting and readability in database and note repository

This commit is contained in:
2026-05-19 09:11:52 +02:00
parent 6de318786b
commit bb8caeef93
2 changed files with 95 additions and 42 deletions
+42 -13
View File
@@ -11,8 +11,10 @@ part 'app_database.g.dart';
class Categories extends Table { class Categories extends Table {
TextColumn get uuid => text().unique()(); TextColumn get uuid => text().unique()();
TextColumn get encryptedName => text().named('encrypted_name')(); TextColumn get encryptedName => text().named('encrypted_name')();
IntColumn get serverVersion => integer().named('server_version').withDefault(const Constant(0))(); IntColumn get serverVersion =>
BoolColumn get isDeleted => boolean().named('is_deleted').withDefault(const Constant(false))(); integer().named('server_version').withDefault(const Constant(0))();
BoolColumn get isDeleted =>
boolean().named('is_deleted').withDefault(const Constant(false))();
DateTimeColumn get updatedAt => dateTime().named('updated_at')(); DateTimeColumn get updatedAt => dateTime().named('updated_at')();
@override @override
@@ -28,8 +30,10 @@ class Notes extends Table {
DateTimeColumn get createdAt => dateTime().named('created_at')(); DateTimeColumn get createdAt => dateTime().named('created_at')();
DateTimeColumn get updatedAt => dateTime().named('updated_at')(); DateTimeColumn get updatedAt => dateTime().named('updated_at')();
IntColumn get sortIndex => integer().named('sort_index')(); IntColumn get sortIndex => integer().named('sort_index')();
IntColumn get serverVersion => integer().named('server_version').withDefault(const Constant(0))(); IntColumn get serverVersion =>
BoolColumn get isDeleted => boolean().named('is_deleted').withDefault(const Constant(false))(); integer().named('server_version').withDefault(const Constant(0))();
BoolColumn get isDeleted =>
boolean().named('is_deleted').withDefault(const Constant(false))();
TextColumn get categoryId => text().nullable().named('category_id')(); TextColumn get categoryId => text().nullable().named('category_id')();
} }
@@ -37,7 +41,8 @@ class Notes extends Table {
class AppDatabase extends _$AppDatabase { class AppDatabase extends _$AppDatabase {
@override @override
int get schemaVersion => 1; int get schemaVersion => 1;
AppDatabase({required String encryptionKey}) : super(_openConnection(encryptionKey)); AppDatabase({required String encryptionKey})
: super(_openConnection(encryptionKey));
// ========== Categories ========== // ========== Categories ==========
Future<List<DbCategory>> getAllCategories() { Future<List<DbCategory>> getAllCategories() {
@@ -49,8 +54,9 @@ class AppDatabase extends _$AppDatabase {
} }
Future<void> deleteCategory(String uuid) { Future<void> deleteCategory(String uuid) {
return (update(categories)..where((c) => c.uuid.equals(uuid))) return (update(categories)..where((c) => c.uuid.equals(uuid))).write(
.write(CategoriesCompanion(isDeleted: Value(true))); CategoriesCompanion(isDeleted: Value(true)),
);
} }
// ========== Notes ========== // ========== Notes ==========
@@ -63,7 +69,9 @@ class AppDatabase extends _$AppDatabase {
Future<int> insertNoteAtTop(NotesCompanion note) { Future<int> insertNoteAtTop(NotesCompanion note) {
return transaction(() async { return transaction(() async {
await customStatement('UPDATE notes SET sort_index = sort_index + 1 WHERE is_deleted = 0'); await customStatement(
'UPDATE notes SET sort_index = sort_index + 1 WHERE is_deleted = 0',
);
return into(notes).insert(note.copyWith(sortIndex: const Value<int>(0))); return into(notes).insert(note.copyWith(sortIndex: const Value<int>(0)));
}); });
} }
@@ -83,7 +91,12 @@ class AppDatabase extends _$AppDatabase {
} }
Future<void> deleteNote(int id, int removedIndex) async { Future<void> deleteNote(int id, int removedIndex) async {
await (update(notes)..where((n) => n.id.equals(id))).write(NotesCompanion(isDeleted: Value(true))); await (update(notes)..where((n) => n.id.equals(id))).write(
NotesCompanion(
isDeleted: const Value(true),
updatedAt: Value(DateTime.now()),
),
);
await customStatement( await customStatement(
'UPDATE notes SET sort_index = sort_index - 1 WHERE sort_index > ? AND is_deleted = 0', 'UPDATE notes SET sort_index = sort_index - 1 WHERE sort_index > ? AND is_deleted = 0',
@@ -121,19 +134,35 @@ class AppDatabase extends _$AppDatabase {
} }
await customStatement( await customStatement(
'UPDATE notes SET sort_index = ? WHERE id = ?', 'UPDATE notes SET sort_index = ?, updated_at = ? WHERE id = ?',
[newIndex, id], [newIndex, DateTime.now().toIso8601String(), id],
); );
}); });
} }
Future<List<DbNote>> getNotesChangedSince(DateTime since) {
return (select(
notes,
)..where((n) => n.updatedAt.isBiggerThanValue(since))).get();
}
Future<List<DbCategory>> getCategoriesChangedSince(DateTime since) {
return (select(
categories,
)..where((c) => c.updatedAt.isBiggerThanValue(since))).get();
}
// ========== Sync helpers ========== // ========== Sync helpers ==========
Future<List<DbNote>> getUnsyncedNotes() { Future<List<DbNote>> getUnsyncedNotes() {
return (select(notes)..where((n) => n.isDeleted.equals(true) | n.serverVersion.equals(0))).get(); return (select(notes)
..where((n) => n.isDeleted.equals(true) | n.serverVersion.equals(0)))
.get();
} }
Future<List<DbCategory>> getUnsyncedCategories() { Future<List<DbCategory>> getUnsyncedCategories() {
return (select(categories)..where((c) => c.isDeleted.equals(true) | c.serverVersion.equals(0))).get(); return (select(categories)
..where((c) => c.isDeleted.equals(true) | c.serverVersion.equals(0)))
.get();
} }
} }
+47 -23
View File
@@ -6,6 +6,7 @@ import 'package:notas/models/note.dart';
import 'package:notas/models/category.dart'; import 'package:notas/models/category.dart';
import 'package:notas/data/note_encryption.dart'; import 'package:notas/data/note_encryption.dart';
class NoteRepository { class NoteRepository {
NoteRepository({ NoteRepository({
required AppDatabase database, required AppDatabase database,
@@ -42,7 +43,9 @@ class NoteRepository {
} }
Future<Note> updateNote(Note note) async { Future<Note> updateNote(Note note) async {
final int noteId = note.id ?? (throw ArgumentError('Note id is required to update a note.')); final int noteId =
note.id ??
(throw ArgumentError('Note id is required to update a note.'));
await _database.updateNoteRow( await _database.updateNoteRow(
DbNote( DbNote(
@@ -63,16 +66,17 @@ class NoteRepository {
} }
Future<void> deleteNote(Note note) async { Future<void> deleteNote(Note note) async {
final int noteId = note.id ?? (throw ArgumentError('Note id is required to delete a note.')); final int noteId =
note.id ??
(throw ArgumentError('Note id is required to delete a note.'));
await _database.deleteNoteAndShift( await _database.deleteNoteAndShift(id: noteId, removedIndex: note.index);
id: noteId,
removedIndex: note.index,
);
} }
Future<void> moveNote(Note note, int newIndex) async { Future<void> moveNote(Note note, int newIndex) async {
final int noteId = note.id ?? (throw ArgumentError('Note id is required to reorder a note.')); final int noteId =
note.id ??
(throw ArgumentError('Note id is required to reorder a note.'));
await _database.moveNote( await _database.moveNote(
id: noteId, id: noteId,
@@ -89,19 +93,39 @@ class NoteRepository {
try { try {
// Get last sync timestamp // Get last sync timestamp
final DateTime? lastSync = await _authApi.getLastSyncAt(); final DateTime? lastSync = await _authApi.getLastSyncAt();
final DateTime? lastSyncForRequest = forceFull ? DateTime.utc(1970, 1, 1) : lastSync; final DateTime? lastSyncForRequest = forceFull
? DateTime.utc(1970, 1, 1)
: lastSync;
// Collect pending changes // Collect pending local changes.
final List<DbNote> unsyncedNotes = await _database.getUnsyncedNotes(); // If we already synced at least once, use updatedAt to avoid re-sending
final List<DbCategory> unsyncedCategories = await _database.getUnsyncedCategories(); // old notes that were already uploaded.
final List<DbNote> unsyncedNotes;
final List<DbCategory> unsyncedCategories;
if (forceFull || lastSync == null) {
unsyncedNotes = await _database.getUnsyncedNotes();
unsyncedCategories = await _database.getUnsyncedCategories();
} else {
unsyncedNotes = await _database.getNotesChangedSince(lastSync);
unsyncedCategories = await _database.getCategoriesChangedSince(
lastSync,
);
}
// Build sync request (note: we send encrypted data, but locally we have plaintext) // Build sync request (note: we send encrypted data, but locally we have plaintext)
// Encrypt all notes before sending // Encrypt all notes before sending
final List<SyncNotePayload> encryptedNotesPayload = []; final List<SyncNotePayload> encryptedNotesPayload = [];
for (final dbNote in unsyncedNotes) { for (final dbNote in unsyncedNotes) {
final note = _fromDbNote(dbNote); final note = _fromDbNote(dbNote);
final encryptedTitle = await NoteEncryption.encryptNote(note.title, _masterKey); final encryptedTitle = await NoteEncryption.encryptNote(
final encryptedBody = await NoteEncryption.encryptNote(note.body, _masterKey); note.title,
_masterKey,
);
final encryptedBody = await NoteEncryption.encryptNote(
note.body,
_masterKey,
);
encryptedNotesPayload.add( encryptedNotesPayload.add(
SyncNotePayload.fromNote( SyncNotePayload.fromNote(
note, note,
@@ -112,9 +136,7 @@ class NoteRepository {
} }
final List<SyncCategoryPayload> categoriesPayload = unsyncedCategories final List<SyncCategoryPayload> categoriesPayload = unsyncedCategories
.map((cat) => SyncCategoryPayload.fromCategory( .map((cat) => SyncCategoryPayload.fromCategory(_fromDbCategory(cat)))
_fromDbCategory(cat),
))
.toList(); .toList();
final SyncRequest syncRequest = SyncRequest( final SyncRequest syncRequest = SyncRequest(
@@ -126,8 +148,7 @@ class NoteRepository {
); );
// Call sync API // Call sync API
final Map<String, dynamic> syncResult = final Map<String, dynamic> syncResult = await _authApi.sync(syncRequest);
await _authApi.sync(syncRequest);
if (syncResult['error'] == true) { if (syncResult['error'] == true) {
return {'error': true, 'message': syncResult['body']}; return {'error': true, 'message': syncResult['body']};
@@ -154,7 +175,8 @@ class NoteRepository {
Future<void> _applySyncResponse(SyncResponse response) async { Future<void> _applySyncResponse(SyncResponse response) async {
// Apply categories from server // Apply categories from server
for (final SyncCategoryResponse catResponse in response.changes.categories) { for (final SyncCategoryResponse catResponse
in response.changes.categories) {
await _database.upsertCategory( await _database.upsertCategory(
CategoriesCompanion( CategoriesCompanion(
uuid: Value(catResponse.id), uuid: Value(catResponse.id),
@@ -168,9 +190,9 @@ class NoteRepository {
// Apply notes from server // Apply notes from server
for (final SyncNoteResponse noteResponse in response.changes.notes) { for (final SyncNoteResponse noteResponse in response.changes.notes) {
final existingNote = await (_database.select(_database.notes) final existingNote = await (_database.select(
..where((n) => n.uuid.equals(noteResponse.id))) _database.notes,
.getSingleOrNull(); )..where((n) => n.uuid.equals(noteResponse.id))).getSingleOrNull();
// Decrypt note content // Decrypt note content
String decryptedTitle = 'Encrypted'; String decryptedTitle = 'Encrypted';
@@ -207,7 +229,9 @@ class NoteRepository {
); );
} else { } else {
// Insert new note // Insert new note
await _database.into(_database.notes).insert( await _database
.into(_database.notes)
.insert(
NotesCompanion( NotesCompanion(
uuid: Value(noteResponse.id), uuid: Value(noteResponse.id),
title: Value(decryptedTitle), title: Value(decryptedTitle),