feat(flutter): home screen on per-item rendering (Slice C)
End-to-end pilot of the per-item architecture. Home now reads from the new homeIndexProvider (drift-first over CachedHomeIndex with /api/home/index discovery + SWR), then each tile is a small ConsumerWidget watching its own albumTileProvider/artistTileProvider/ trackTileProvider. Tiles render a matched-dimension skeleton while their entity is still hydrating, and swap in the real card once drift emits the populated row. Track hydration is wired up — /api/tracks/:id already existed so the queue's case 'track' just calls api.getTrack(id) and persists. The visible behavior: * Cold visit: small /api/home/index round-trip (IDs only, ~10× smaller than /api/home), then sections appear shaped with skeleton tiles; each tile materializes as the hydration queue drains. No more "30s blank → everything pops in at once." * Warm visit: drift index emits instantly, drift entity rows emit instantly, no network. Page paints fully in the first frame. * Mid-state: scrolling through a partially-hydrated section sees real cards next to skeleton cards. Layout doesn't shift because skeletons match real-card dimensions exactly. CachedHomeSnapshot (and the legacy homeProvider) stay in place but unconsumed by Flutter — left in for now so revert is cheap if the new path needs reworking. Cleanup follow-up in a later slice. Old /api/home endpoint untouched, so the web client keeps working unchanged.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
/// Mirrors internal/api/types.go HomeIndexPayload. Five flat slices of
|
||||
/// entity ID strings — the per-item rendering variant of HomeData.
|
||||
/// Section name implies entity type; no per-entry type tag is needed.
|
||||
///
|
||||
/// Slices are non-null after fromJson so callers can branch on `length`
|
||||
/// instead of dealing with null sections.
|
||||
class HomeIndex {
|
||||
const HomeIndex({
|
||||
required this.recentlyAddedAlbums,
|
||||
required this.rediscoverAlbums,
|
||||
required this.rediscoverArtists,
|
||||
required this.mostPlayedTracks,
|
||||
required this.lastPlayedArtists,
|
||||
});
|
||||
|
||||
final List<String> recentlyAddedAlbums;
|
||||
final List<String> rediscoverAlbums;
|
||||
final List<String> rediscoverArtists;
|
||||
final List<String> mostPlayedTracks;
|
||||
final List<String> lastPlayedArtists;
|
||||
|
||||
static const empty = HomeIndex(
|
||||
recentlyAddedAlbums: [],
|
||||
rediscoverAlbums: [],
|
||||
rediscoverArtists: [],
|
||||
mostPlayedTracks: [],
|
||||
lastPlayedArtists: [],
|
||||
);
|
||||
|
||||
factory HomeIndex.fromJson(Map<String, dynamic> j) {
|
||||
List<String> ids(String key) =>
|
||||
((j[key] as List?) ?? const []).map((e) => e.toString()).toList();
|
||||
return HomeIndex(
|
||||
recentlyAddedAlbums: ids('recently_added_albums'),
|
||||
rediscoverAlbums: ids('rediscover_albums'),
|
||||
rediscoverArtists: ids('rediscover_artists'),
|
||||
mostPlayedTracks: ids('most_played_tracks'),
|
||||
lastPlayedArtists: ids('last_played_artists'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user