From 947d4401a6dfdd350363d5c905c9a2ee84c8e45a Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 10 May 2026 11:18:59 -0400 Subject: [PATCH] feat(flutter/cache): migrate artistProvider to drift-first (#357 plan C) artistProvider becomes a StreamProvider backed by drift's cached_artists watch(). Empty + online triggers REST fallback; populates drift; watch re-emits. Empty + offline yields a sentinel ArtistRef(id: '', name: ''); surfaces should already check state.value?.id.isNotEmpty. AsyncValue shape unchanged so .when callsites keep working. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../lib/library/library_providers.dart | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/flutter_client/lib/library/library_providers.dart b/flutter_client/lib/library/library_providers.dart index 5152171a..b18cf932 100644 --- a/flutter_client/lib/library/library_providers.dart +++ b/flutter_client/lib/library/library_providers.dart @@ -1,9 +1,15 @@ import 'package:dio/dio.dart'; +import 'package:drift/drift.dart' as drift; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/client.dart'; import '../api/endpoints/library.dart'; import '../auth/auth_provider.dart'; +import '../cache/adapters.dart'; +import '../cache/audio_cache_manager.dart' show appDbProvider; +import '../cache/cache_first.dart'; +import '../cache/connectivity_provider.dart'; +import '../cache/db.dart'; import '../models/album.dart'; import '../models/artist.dart'; import '../models/home_data.dart'; @@ -37,9 +43,26 @@ final homeProvider = FutureProvider((ref) async { return (await ref.watch(libraryApiProvider.future)).getHome(); }); +/// Drift-first per #357 plan C. Watches cached_artists for the row; +/// when empty + online, fetches via REST + populates drift, which +/// re-emits via watch(). final artistProvider = - FutureProvider.family((ref, id) async { - return (await ref.watch(libraryApiProvider.future)).getArtist(id); + StreamProvider.family((ref, id) { + final db = ref.watch(appDbProvider); + return cacheFirst( + driftStream: (db.select(db.cachedArtists)..where((t) => t.id.equals(id))) + .watch(), + fetchAndPopulate: () async { + final api = await ref.read(libraryApiProvider.future); + final fresh = await api.getArtist(id); + await db.into(db.cachedArtists).insertOnConflictUpdate(fresh.toDrift()); + }, + toResult: (rows) => rows.isEmpty + ? const ArtistRef(id: '', name: '') + : rows.first.toRef(), + isOnline: () async => + (await ref.read(connectivityProvider.future)), + ); }); final artistAlbumsProvider =