import 'package:flutter/foundation.dart' show debugPrint; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../library/library_providers.dart'; import '../models/home_data.dart'; /// Pre-warms the drift cache for entities the user is most likely to /// tap. When /api/home returns its sections, fire one albumProvider / /// artistProvider read per top-of-section item in the background. The /// reads trigger the existing cold-cache path, which writes drift. /// By the time the user actually taps a tile, it's a drift hit and /// the detail screen renders instantly. /// /// All reads are unawaited and idempotent — providers that already /// have a cached value short-circuit. Readers/writers don't conflict /// because each provider de-duplicates concurrent subscribers. class MetadataPrefetcher { MetadataPrefetcher(this._ref) { _ref.listen>(homeProvider, (_, next) { next.whenData(_warmHome); }); } final Ref _ref; /// Cap per section so a 50-album recently-added row doesn't fan /// out 50 fetches the moment the screen appears. Top-N covers what /// the user can see without scrolling on a typical phone. static const _topN = 8; void _warmHome(HomeData h) { final albumIds = {}; final artistIds = {}; for (final a in h.recentlyAddedAlbums.take(_topN)) { if (a.id.isNotEmpty) albumIds.add(a.id); if (a.artistId.isNotEmpty) artistIds.add(a.artistId); } for (final a in h.rediscoverAlbums.take(_topN)) { if (a.id.isNotEmpty) albumIds.add(a.id); if (a.artistId.isNotEmpty) artistIds.add(a.artistId); } for (final ar in h.rediscoverArtists.take(_topN)) { if (ar.id.isNotEmpty) artistIds.add(ar.id); } for (final ar in h.lastPlayedArtists.take(_topN)) { if (ar.id.isNotEmpty) artistIds.add(ar.id); } for (final t in h.mostPlayedTracks.take(_topN)) { if (t.albumId.isNotEmpty) albumIds.add(t.albumId); if (t.artistId.isNotEmpty) artistIds.add(t.artistId); } debugPrint( 'metadataPrefetcher: warming ${albumIds.length} albums + ${artistIds.length} artists'); for (final id in albumIds) { _swallow(_ref.read(albumProvider(id).future)); } for (final id in artistIds) { _swallow(_ref.read(artistProvider(id).future)); } } /// Discards the return value and any error from a fire-and-forget /// provider read. We don't care about the value here — we only want /// the side effect of writing drift. void _swallow(Future f) { f.then((_) {}).onError((_, __) {}); } } /// Read once at app start to activate the prefetcher (e.g. wire it /// from a top-level Consumer or main.dart container override). final metadataPrefetcherProvider = Provider((ref) { return MetadataPrefetcher(ref); });