4dc80047b2
Goal is UI responsiveness: the History tab was fetch-on-visit and showed a spinner on every open. Now it paints instantly from a cached snapshot and refreshes underneath — the native mirror of Flutter's cacheFirst _historyProvider (alwaysRefresh over the cached_history_ snapshot drift row). Native implementation (idiomatic, not a drift transliteration): * CachedHistorySnapshotEntity — single-row table (id=1) holding the raw /api/me/history wire JSON + updatedAt. AppDatabase v5→6 (fallbackToDestructiveMigration rebuilds; cache refills from server). * CachedHistorySnapshotDao.observe() Flow + upsert; DatabaseModule @Provides bridge (the AudioCacheIndexDao lesson — every @Inject dep needs a provider). * HistoryRepository.observeHistory(): Flow<HistoryPage?> decodes the blob; refresh() fetches + overwrites the snapshot. Whole page as one JSON blob (read-only timestamp-keyed snapshot — no per-row bookkeeping for no UX gain), matching Flutter's snapshot shape. * HistoryTabViewModel: uiState = combine(observeHistory, refreshError) — cache-first, SWR, and Error only when there's no cache to show. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>