Files
minstrel/flutter_client/lib/cache/metadata_prefetcher.dart
T
bvandeusen 1646b02ca2 refactor(flutter): drop legacy home snapshot path (#406)
The home screen renders solely from the per-item cached_home_index
path (proven on devices for many releases); the legacy snapshot stack
was dead weight.

- library_providers: remove homeProvider + _encodeHomeData +
  _albumToJson/_artistToJson/_trackToJson; drop now-unused dart:convert
  and models/home_data.dart imports
- metadata_prefetcher: re-point off homeIndexProvider/HomeIndex —
  pre-warm artistProvider from the rediscover/last-played artist
  sections (album/track tiles hydrate their own artist on render)
- live_events_dispatcher: homeProvider -> homeIndexProvider so live
  events still refresh the home screen
- db.dart: drop CachedHomeSnapshot (table class + @DriftDatabase
  entry); schemaVersion 10->11; from<3 createTable -> raw
  customStatement so the historical step compiles without the
  generated symbol; from<11 DROP TABLE cached_home_snapshot

No test references the removed symbols. db.g.dart regenerated by CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:04:43 -04:00

68 lines
2.4 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../library/library_providers.dart';
import '../models/home_index.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.
///
/// Driven off the per-item home path (homeIndexProvider). Only the
/// artist-typed sections (rediscover / last-played artists) carry
/// artist ids directly; album/track tiles hydrate their own artist on
/// render via the per-item path, so warming those here is unnecessary.
class MetadataPrefetcher {
MetadataPrefetcher(this._ref) {
_ref.listen<AsyncValue<HomeIndex>>(homeIndexProvider, (_, next) {
next.whenData(_warmIndex);
});
}
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<String> _warmedArtists = {};
/// Cap warmed per emission. 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<String> 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 _warmIndex(HomeIndex h) {
warmArtists(<String>{
...h.rediscoverArtists.take(_topN),
...h.lastPlayedArtists.take(_topN),
});
}
/// 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<Object?> f) {
f.then<void>((_) {}).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<MetadataPrefetcher>((ref) {
return MetadataPrefetcher(ref);
});