feat(qt): award pages, reachable from the album sidebar and from each other

Ports the Slint/Tauri award pair: the landing (gold laurel hero, magazine
line, follow heart, "Acclaimed Releases" with its count and See All, the
preview grid and the "Other awards" rail) and the paginated listing with
its client-side search. Routes "award" and "awardalbums", the label pair's
shape — one controller for both, since they share the hero, the follow
state and the identity.

The three rules from the Tauri review are in the module header because they
are the ones a re-implementation gets wrong: the grid is ALWAYS
/award/getAlbums (/award/page is user-scoped and may omit releases, so it
feeds the hero and nothing else); the favorite split is plural-read,
singular-write, because the backend derives `award_ids` from the write
kind; and getAlbums has no has_more, its `total` is a hint, so the only
sound predicate is `total > loaded`.

The album sidebar's AWARDS cards navigate now — with an id directly, and by
NAME through the resolver when Qobuz omits it, which it does. They were
also reading `album.awards` when the field lives on `album.header`, so the
block had rendered EMPTY for every album since it was written: an empty
Repeater draws nothing and reports nothing, and the sidebar's own gate
counted the same empty list. The LABEL card beside it had a "no label view
yet" note and no handler, years after the label view landed.

Beyond the reference: an all-awards dropdown pinned to the page's top-right,
so any award is reachable without first finding an album that won it. It
costs nothing new — the /award/explore crawl already existed to resolve
names to ids.

The award icon was lucide's MICROPHONE under the wrong filename. Search had
routed around it with `crown` and left a note; kiosk was missed and drew a
mic for "Most popular". The asset is the laurel the other two frontends use,
and kiosk now asks for the crown it wanted.

cc
This commit is contained in:
vicrodh committed 2026-08-16 18:56:20 -06:00
1 parent 60bf5b8365
commit dc2496e5c0
18 files changed
+1691 -41

No files matched your search

+7
View File
@@ -348,6 +348,13 @@ ApplicationWindow {
// side. The wizard is the loudest case after QbzBlacklist, because its
// first step ends on "Checking your audio stack…" forever.
QbzDacWizard.boot()
// Offline Cache Manager. Nothing to seed — the view is unmounted until
// the Settings row opens it — but without this line the manager's very
// first publish is dropped and the view sits on its spinner forever:
// `managerLoading` DEFAULTS to true precisely so the empty state does
// not flash, which turns a missing boot() into a permanent spinner
// with nothing logged on either side.
QbzOffline.boot()
// Miniplayer (2026-08-03 miniplayer/tray contract, block B1). Booted
// like every other domain singleton, after QbzSession: `boot()` is what
// registers the Qt-thread hop, and without it `mini_bridge::ui()` is a