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>
This commit is contained in:
@@ -18,6 +18,7 @@ import '../data/api/tasks_api.dart';
|
||||
import '../data/local/database.dart';
|
||||
import '../data/repositories/auth_repository.dart';
|
||||
import '../data/repositories/chat_repository.dart';
|
||||
import '../data/repositories/events_repository.dart';
|
||||
import '../data/repositories/knowledge_repository.dart';
|
||||
import '../data/repositories/voice_repository.dart';
|
||||
import '../data/repositories/milestones_repository.dart';
|
||||
@@ -82,15 +83,24 @@ final notesRepositoryProvider = Provider<NotesRepository>((ref) {
|
||||
});
|
||||
|
||||
final tasksRepositoryProvider = Provider<TasksRepository>((ref) {
|
||||
return TasksRepository(ref.watch(tasksApiProvider));
|
||||
return TasksRepository(
|
||||
ref.watch(tasksApiProvider),
|
||||
ref.watch(fabledDatabaseProvider),
|
||||
);
|
||||
});
|
||||
|
||||
final chatRepositoryProvider = Provider<ChatRepository>((ref) {
|
||||
return ChatRepository(ref.watch(chatApiProvider));
|
||||
return ChatRepository(
|
||||
ref.watch(chatApiProvider),
|
||||
ref.watch(fabledDatabaseProvider),
|
||||
);
|
||||
});
|
||||
|
||||
final projectsRepositoryProvider = Provider<ProjectsRepository>((ref) {
|
||||
return ProjectsRepository(ref.watch(projectsApiProvider));
|
||||
return ProjectsRepository(
|
||||
ref.watch(projectsApiProvider),
|
||||
ref.watch(fabledDatabaseProvider),
|
||||
);
|
||||
});
|
||||
|
||||
final milestonesApiProvider = Provider<MilestonesApi>((ref) {
|
||||
@@ -98,7 +108,10 @@ final milestonesApiProvider = Provider<MilestonesApi>((ref) {
|
||||
});
|
||||
|
||||
final milestonesRepositoryProvider = Provider<MilestonesRepository>((ref) {
|
||||
return MilestonesRepository(ref.watch(milestonesApiProvider));
|
||||
return MilestonesRepository(
|
||||
ref.watch(milestonesApiProvider),
|
||||
ref.watch(fabledDatabaseProvider),
|
||||
);
|
||||
});
|
||||
|
||||
final knowledgeApiProvider = Provider<KnowledgeApi>((ref) {
|
||||
@@ -128,3 +141,10 @@ final voiceRepositoryProvider = Provider<VoiceRepository>((ref) {
|
||||
final eventsApiProvider = Provider<EventsApi>((ref) {
|
||||
return EventsApi(ref.watch(dioProvider));
|
||||
});
|
||||
|
||||
final eventsRepositoryProvider = Provider<EventsRepository>((ref) {
|
||||
return EventsRepository(
|
||||
ref.watch(eventsApiProvider),
|
||||
ref.watch(fabledDatabaseProvider),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ class CalendarNotifier extends AsyncNotifier<CalendarState> {
|
||||
// Fetch current month ± 1 month as the initial window.
|
||||
final from = DateTime(now.year, now.month - 1, 1);
|
||||
final to = DateTime(now.year, now.month + 2, 0, 23, 59, 59);
|
||||
final events = await ref.watch(eventsApiProvider).getEvents(from, to);
|
||||
final events = await ref.watch(eventsRepositoryProvider).getEvents(from, to);
|
||||
return CalendarState(
|
||||
eventsByDay: _groupByDay(events),
|
||||
selectedDay: today,
|
||||
@@ -60,7 +60,7 @@ class CalendarNotifier extends AsyncNotifier<CalendarState> {
|
||||
Future<void> refresh() async {
|
||||
final current = state.value;
|
||||
if (current == null) return;
|
||||
final events = await ref.read(eventsApiProvider).getEvents(
|
||||
final events = await ref.read(eventsRepositoryProvider).getEvents(
|
||||
current.loadedRange.start,
|
||||
current.loadedRange.end,
|
||||
);
|
||||
@@ -93,7 +93,7 @@ class CalendarNotifier extends AsyncNotifier<CalendarState> {
|
||||
try {
|
||||
final from = DateTime(month.year, month.month, 1);
|
||||
final to = DateTime(month.year, month.month + 1, 0, 23, 59, 59);
|
||||
final events = await ref.read(eventsApiProvider).getEvents(from, to);
|
||||
final events = await ref.read(eventsRepositoryProvider).getEvents(from, to);
|
||||
final s = state.value!;
|
||||
final merged = Map<DateTime, List<CalendarEvent>>.from(s.eventsByDay);
|
||||
for (final e in events) {
|
||||
|
||||
Reference in New Issue
Block a user