feat(flutter): drift-first homeProvider (#357 deferred follow-up)

Home screen is the first surface on app open; without a local cache
the cold-start blocks on /api/home, which dominates felt latency on
slow or remote connections. This commit caches the last successful
HomeData as a single-row JSON blob in drift, so subsequent app opens
yield content immediately and revalidate in the background.

Schema:
  - New CachedHomeSnapshot table (single row: id=1, json TEXT,
    updated_at). schemaVersion bumped 2 → 3 with a forward migration
    that calls m.createTable(cachedHomeSnapshot). Codegen regenerated
    via build_runner; the *.g.dart files are gitignored and rebuilt
    by the CI Codegen step.

Provider rewrite:
  - homeProvider: FutureProvider<HomeData> → StreamProvider<HomeData>
    using the existing cacheFirst<CachedHomeSnapshotData, HomeData>
    pattern (alwaysRefresh: true for SWR). On cold cache the first
    /api/home fetch populates the row. On warm cache the cached
    HomeData is yielded immediately and a background REST fetch
    overwrites the row, which drift's watch() picks up.

  - Encoder helpers (_albumToJson / _artistToJson / _trackToJson) so
    HomeData survives the JSON round-trip into and out of drift.
    Field names match the server's /api/home wire shape exactly so
    HomeData.fromJson handles both fresh server responses and cached
    drift rows.

Callers untouched: home_screen.dart's ref.watch + ref.refresh +
metadata_prefetcher's ref.listen all keep working with the
StreamProvider shape (AsyncValue<HomeData> stays the surface type).

Test fix: 4 homeProvider.overrideWith sites in home_screen_test.dart
switched from `(ref) async => _emptyHome` (FutureProvider form) to
`(ref) => Stream.value(_emptyHome)` (StreamProvider form).

For #357. Completes the user-visible deferred follow-up. Remaining
deferred items: library_changes server-side retention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 15:40:43 -04:00
parent efa52484d4
commit 22bd06a578
3 changed files with 118 additions and 7 deletions
@@ -26,7 +26,7 @@ void main() {
(tester) async {
await tester.pumpWidget(ProviderScope(
overrides: [
homeProvider.overrideWith((ref) async => _emptyHome),
homeProvider.overrideWith((ref) => Stream.value(_emptyHome)),
playlistsListProvider('all').overrideWith(
(ref) => Stream.value(PlaylistsList.empty()),
),
@@ -46,7 +46,7 @@ void main() {
(tester) async {
await tester.pumpWidget(ProviderScope(
overrides: [
homeProvider.overrideWith((ref) async => _emptyHome),
homeProvider.overrideWith((ref) => Stream.value(_emptyHome)),
playlistsListProvider('all').overrideWith(
(ref) => Stream.value(PlaylistsList.empty()),
),
@@ -87,7 +87,7 @@ void main() {
);
await tester.pumpWidget(ProviderScope(
overrides: [
homeProvider.overrideWith((ref) async => _emptyHome),
homeProvider.overrideWith((ref) => Stream.value(_emptyHome)),
playlistsListProvider('all').overrideWith(
(ref) => Stream.value(
const PlaylistsList(owned: [forYou], public: [])),
@@ -118,7 +118,7 @@ void main() {
);
await tester.pumpWidget(ProviderScope(
overrides: [
homeProvider.overrideWith((ref) async => home),
homeProvider.overrideWith((ref) => Stream.value(home)),
playlistsListProvider('all').overrideWith(
(ref) => Stream.value(PlaylistsList.empty()),
),