This repository has been archived on 2026-06-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FabledApp/lib/data/repositories/notes_repository.dart
T
bvandeusen fe10067761 feat(offline): tier 2 phase 2 — extend cache to tasks/projects/milestones/events/conversations
Phase 1 cached only notes. Phase 2 brings the read-through pattern to every
domain the app reads in bulk:

- Drift schema v2: 5 new cached_* tables + onUpgrade migration. Calendar
  events use range-scoped read/write (replaceEventsInRange) so disjoint
  month fetches don't clobber each other; milestones are per-project.
- Wrap TasksRepository, ProjectsRepository, MilestonesRepository, and
  ChatRepository (conversation list only) with NetworkException fallback.
  Writes hit the API then sync the cache.
- New EventsRepository wrapping EventsApi; calendar_provider and
  event_form_sheet repointed at the repository.
- OfflineBanner now surfaces getLatestSync() — most-recent timestamp
  across all domains — so the hint reads sensibly regardless of screen.

flutter analyze clean; existing tests pass. Phase 3 (offline write queue)
and Phase 4 (read-only UI indicators) still to come.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 21:22:06 -04:00

94 lines
2.4 KiB
Dart

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<List<Note>> 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<Note> 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<Note> create(
String title,
String body, {
List<String> 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<Note> update(
int id,
String title,
String body, {
List<String> 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<void> delete(int id) async {
await _api.delete(id);
await _db.deleteNote(id);
}
}