feat(qt): windowed View-all artwork, a real per-rail default, Play later, MyQBZ tabs

VIEW-ALL PAGES GOT THE LIBRARY > ALL TREATMENT, in both halves.

PlaylistBrowse windowed nothing: every card mounted, and a View-all of
playlists runs to hundreds, each a PlaylistCard carrying a four-tile collage.
AlbumCollection had solved that for the album pages and this one never got it.
The band is sampled on a timer rather than bound to contentY — a direct binding
re-evaluates every delegate on every scroll frame — and out-of-band cards keep
their footprint as bare Items, so the scroll geometry and the infinite-scroll
trigger never move.

Covers arrived in ONE batch: Rust downloaded every missing cover of the page and
then republished the whole document to attach the paths, so a page had no
artwork until it had all of it, and the republish rebuilt every delegate on
arrival. Both browse pages now ask for what is near the viewport and take the
answers one key at a time through the existing sidebarArtworkWindow ->
libraryArtworkReady seam. `attach_card_art` stays — it is a pure disk-cache
lookup, so anything already downloaded still renders on the first frame with no
request. The two now-dead republish helpers are gone with it.

The AlbumCollection change is ADDITIVE for its nine hosts: those that pass no
`flick` never report, the map stays empty and `artOf` falls through to the
`artPath` they already publish.

ITEMS PER CAROUSEL now has a real default instead of a lie. The first entry read
"All", which was wrong twice over: an "all" in Qobuz terms is what the View-all
page holds, and /discover/index hands each rail a different count anyway —
measured on a live response, new_releases 26, ideal_discography 26, playlists 20,
most_streamed 15, and qobuzissims / album_of_the_week / press_awards 10. No
single label could be honest about all of them, so the default became a real
uniform cap of ten and the entry says so. That is a behaviour change and not
only a label one: the rails that used to arrive at 26 now arrive at 10, and the
select is how a rail gets more. Ten therefore stops being a separate option
because it IS the first one, and the number comes from Rust so the label and the
cap cannot drift. The select also went 82 -> 124 wide: "Default (10)" elided to
"Defaul…" in both the closed control and the popup.

PLAY LATER on the album page's track rows. It was off because
`enqueue_album_track` had only two arms — "next" and "everything else appends" —
so "later" and "queue" both plain-appended and offering both would have been two
menu entries doing one thing. The third arm lands on the manual block's tail via
`add_track_later`, which is the whole distinction, and the QConnect route stops
collapsing "later" into "queue" (route_track_to_peer has had a real later arm
all along). Only then does the entry turn on.

MIXTAPES <-> COLLECTIONS as a tab bar, the Discover/Library pattern where the
tabs stand in for the page heading. They are separate ROUTES sharing one
component, so a tab click navigates rather than flipping a local property: the
sidebar highlight, the back stack and the `kind` Binding then agree on where the
user is, and the page transition is the one every route change gets.

Eight locales. Release build clean, offscreen smoke green, 421/421 and 216/216.

cc
This commit is contained in:
vicrodh committed 2026-08-19 13:53:30 -06:00
1 parent e9f09a59a4
commit 730ae6998e
18 files changed
+386 -116

No files matched your search

+14 -16
View File
@@ -432,24 +432,22 @@ pub fn reconcile_list(persisted: Option<&Vec<Value>>, fallback: &[SectionPref])
// SQLite store
// ---------------------------------------------------------------------------
/// The rail sizes the UI offers, IN THE ORDER THE UI OFFERS THEM — a selector
/// sends back an index into this array, so reordering it silently changes what
/// every existing user has selected. Append, never reorder.
/// The rail sizes the UI offers, IN THE ORDER THE UI OFFERS THEM — the selector
/// sends back an index into this array. Reordering is safe ONLY because what is
/// STORED is the size and never the index (`save_rail_size` takes the count,
/// `rail_size_index_of` derives the index back), so a stored 15 keeps meaning
/// 15 whatever position it sits in. Keep that invariant or this comment becomes
/// a trap.
///
/// `0` means "no cap" and is the DEFAULT on purpose: before this setting
/// existed the editorial rails were not truncated at all
/// (`home_qt::order_by_prefs` pushed every item through), so any other default
/// would shrink the Discover page of every user who upgrades without ever
/// asking them.
///
/// The size is PER RAIL, which is what Tauri offered
/// (`HomeSettingsModal.svelte` over `homeSettingsStore.ts`) — a single global
/// number was this port's simplification and it lost the point of the feature:
/// a 25-wide "New Releases" and a 10-wide "Recently Played" are the reason
/// anyone touches it.
pub const RAIL_SIZE_PRESETS: [i64; 5] = [10, 15, 20, 25, 0];
/// There is no "uncapped" entry, and that is deliberate. It used to read "All",
/// which was a lie twice over: an "all" in Qobuz terms is what the View-all
/// page holds, and /discover/index hands each rail a DIFFERENT number anyway —
/// measured on a live response, new_releases 26, ideal_discography 26,
/// playlists 20, most_streamed 15, and qobuzissims / album_of_the_week /
/// press_awards 10. One label could not be honest about all of them. So the
/// default is now a real, uniform cap of ten and the entry says so.
pub const RAIL_SIZE_PRESETS: [i64; 4] = [10, 15, 20, 25];
/// The default rail size — `0`, i.e. today's uncapped behaviour.
pub const DEFAULT_RAIL_SIZE: i64 = 0;
pub struct DiscoverPrefsStore {