import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../library/library_providers.dart'; import '../models/home_data.dart'; /// Pre-warms the drift cache for likely-tap targets. Conservative on /// purpose: only warms artistProvider rows (single row, single round /// trip per id) and only ever fires once per id per session. Album /// detail is NOT prewarmed — the albumProvider auto-fetches its track /// list when missing, and pre-warming N albums fans out N parallel /// "fetch tracks" round trips that compete with the user's actual /// playback request for bandwidth. class MetadataPrefetcher { MetadataPrefetcher(this._ref) { _ref.listen>(homeProvider, (_, next) { next.whenData(_warmHome); }); } final Ref _ref; /// Per-session dedupe so re-rendering a screen (every UI rebuild /// fires the data: callback) doesn't trigger N more fetches for /// ids we've already warmed. final Set _warmedArtists = {}; /// Cap per section. Covers what fits on screen without scrolling. static const _topN = 8; /// Pre-warm artists. Albums are intentionally not pre-warmed — /// see class comment. void warmArtists(Iterable ids) { var n = 0; for (final id in ids) { if (id.isEmpty) continue; if (!_warmedArtists.add(id)) continue; // already warmed if (n++ >= _topN) break; _swallow(_ref.read(artistProvider(id).future)); } } void _warmHome(HomeData h) { final artistIds = {}; for (final a in h.recentlyAddedAlbums.take(_topN)) { if (a.artistId.isNotEmpty) artistIds.add(a.artistId); } for (final a in h.rediscoverAlbums.take(_topN)) { 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.artistId.isNotEmpty) artistIds.add(t.artistId); } warmArtists(artistIds); } /// 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); });