import '../../core/exceptions.dart'; import '../api/notes_api.dart'; import '../local/database.dart'; import '../models/note.dart'; /// Notes repository with read-through caching for Tier 2 offline support. /// /// Reads attempt the network first; on success the response is written to /// the local Drift cache. On `NetworkException` the read falls back to the /// cache (rethrowing if the cache is empty so the UI can show its /// fresh-install empty state). Writes (create / update / delete) hit the /// server then sync the cache; offline write queueing is Phase 3 work and /// not yet wired here — write methods will throw on `NetworkException` in /// the meantime. /// /// `AuthStatus.offline` is owned by `AuthNotifier.verify()` (a periodic /// heartbeat). This repository deliberately does not poke that state — its /// only job is to serve what's cached when the network is unreachable. class NotesRepository { final NotesApi _api; final FabledDatabase _db; const NotesRepository(this._api, this._db); Future> getAll() async { try { final notes = await _api.getAll(); await _db.replaceAllNotes(notes); return notes; } on NetworkException { final cached = await _db.getAllNotes(); if (cached.isEmpty) rethrow; return cached; } } Future getOne(int id) async { try { final note = await _api.getOne(id); await _db.upsertNote(note); return note; } on NetworkException { final cached = await _db.getNote(id); if (cached == null) rethrow; return cached; } } Future create( String title, String body, { List tags = const [], int? projectId, String noteType = 'note', }) async { final note = await _api.create( title, body, tags: tags, projectId: projectId, noteType: noteType, ); await _db.upsertNote(note); return note; } Future update( int id, String title, String body, { List tags = const [], int? projectId, bool clearProject = false, String noteType = 'note', }) async { final updated = await _api.update( id, title, body, tags: tags, projectId: projectId, clearProject: clearProject, noteType: noteType, ); await _db.upsertNote(updated); return updated; } Future delete(int id) async { await _api.delete(id); await _db.deleteNote(id); } /// Last successful `getAll` sync timestamp. Surfaced by the OfflineBanner /// when the network is unreachable so the user knows how stale the /// cached notes list is. Future lastSyncedAt() => _db.getLastSync(kSyncDomainNotes); }