Integrate the Kiosk profile hardening round: 12 dedicated Kiosk views plus 22 shared opt-in hosts, shared navigation history with Back/Forward, Albums as the default library tab, native paged Local Library (artists/tracks), lightweight MyQBZ grid and detail, Ephemeral lifecycle, bounded lists and artwork sized to the physical frame, static skeletons, 64px transport bar, Settings touch reflow and a persistent Immersive exit. Desktop keeps its prior behavior: every shared component defaults to the desktop path and only opts into Kiosk behavior through explicit properties (origin-relative ScrollMemory, viewport-bounded artwork requests). Regression coverage: kiosk navigation and artwork Node tests added to the runtime gate, scroll-origin fixtures for fixed and grouped rows, i18n strings for all eight locales. Verified by 750 release tests, six QML audits, Kiosk smokes at 800x480 and 1280x800 and a Desktop A/B with seven pixel-identical captures. cc
225 lines
7.9 KiB
QML
225 lines
7.9 KiB
QML
// Award "See all" listing — QML port of award/AwardAlbumsView.slint. Route
|
|
// "awardalbums".
|
|
//
|
|
// A search box over the LOADED set, the award name as the page title, and an
|
|
// explicitly paginated grid over `/award/getAlbums`. Same four exclusive body
|
|
// branches as the landing
|
|
// page, in the reference's order: loading · error+retry · no-results (a search
|
|
// that matched nothing) · the grid.
|
|
//
|
|
// The search is CLIENT-SIDE over what has been paginated in — 1:1 with the
|
|
// reference, which does not re-query for this box either. That is also why
|
|
// load-more is suppressed while a query is active: appending pages the user
|
|
// cannot see, to filter them out again, is work with no visible result.
|
|
//
|
|
// Every number, filter and page in the document comes from src/award_qt.rs;
|
|
// this file renders and reports scroll, nothing else.
|
|
|
|
import QtQuick
|
|
import com.blitzfc.qbz
|
|
import "../controls"
|
|
import "../theme"
|
|
|
|
Rectangle {
|
|
id: root
|
|
property bool kioskHost: false
|
|
|
|
readonly property bool ambientOn: theme.ambientOn
|
|
color: root.ambientOn ? "transparent" : theme.surfaceMain
|
|
radius: 12
|
|
|
|
QbzTheme { id: theme }
|
|
|
|
readonly property var doc: {
|
|
try {
|
|
return JSON.parse(QbzHome.awardAlbumsJson)
|
|
} catch (e) {
|
|
return ({})
|
|
}
|
|
}
|
|
readonly property var albums: root.doc.albums || []
|
|
readonly property bool loading: QbzHome.awardAlbumsLoading === true
|
|
readonly property bool loadError: root.doc.loadError === true
|
|
readonly property string query: root.doc.searchQuery || ""
|
|
|
|
Flickable {
|
|
id: page
|
|
anchors.fill: parent
|
|
contentWidth: width
|
|
contentHeight: col.implicitHeight
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
clip: true
|
|
|
|
Column {
|
|
id: col
|
|
width: page.width - 64
|
|
x: 32
|
|
topPadding: 11
|
|
bottomPadding: 100
|
|
spacing: 0
|
|
|
|
Item { width: 1; height: 22 }
|
|
|
|
// ---- Search ------------------------------------------------------
|
|
QbzLineEdit {
|
|
kioskHost: root.kioskHost
|
|
width: 320
|
|
searchMode: true
|
|
placeholder: QbzSession.tr("Search albums & tracks…", QbzSession.trRev)
|
|
text: root.query
|
|
onEdited: function (v) { QbzHome.awardAlbumsSearch(v) }
|
|
}
|
|
|
|
Item { width: 1; height: 20 }
|
|
|
|
// ---- Title -------------------------------------------------------
|
|
Text {
|
|
width: parent.width
|
|
text: root.doc.name || ""
|
|
color: theme.textPrimary
|
|
font.pixelSize: theme.fontSection
|
|
font.weight: theme.weightBold
|
|
elide: Text.ElideRight
|
|
}
|
|
Item { width: 1; height: 4 }
|
|
Row {
|
|
spacing: 10
|
|
Text {
|
|
text: QbzSession.tr("Acclaimed Releases", QbzSession.trRev)
|
|
color: theme.textSecondary
|
|
font.pixelSize: theme.fontBody
|
|
}
|
|
Text {
|
|
visible: (root.doc.total || 0) > 0
|
|
text: root.doc.total || 0
|
|
color: theme.textMuted
|
|
font.pixelSize: theme.fontBody
|
|
font.weight: theme.weightMedium
|
|
}
|
|
}
|
|
|
|
Item { width: 1; height: 16 }
|
|
|
|
// ---- Body: loading / error+retry / no-results / grid ------------
|
|
Item {
|
|
visible: root.loading
|
|
width: parent.width
|
|
height: visible ? 280 : 0
|
|
Column {
|
|
anchors.centerIn: parent
|
|
spacing: 18
|
|
QbzSpinner {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
size: 36
|
|
}
|
|
Text {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
text: QbzSession.tr("Loading…", QbzSession.trRev)
|
|
color: theme.textMuted
|
|
font.pixelSize: 13
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
visible: !root.loading && root.loadError
|
|
width: parent.width
|
|
height: visible ? 240 : 0
|
|
Column {
|
|
anchors.centerIn: parent
|
|
spacing: 16
|
|
Text {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
text: QbzSession.tr("Failed to load Library", QbzSession.trRev)
|
|
color: theme.textMuted
|
|
font.pixelSize: 14
|
|
}
|
|
SettingsButton {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
text: QbzSession.tr("Retry", QbzSession.trRev)
|
|
onClicked: QbzHome.awardRetry()
|
|
}
|
|
}
|
|
}
|
|
|
|
// A search that matched nothing is NOT the same as an award with
|
|
// no releases, and the reference keeps them as two strings.
|
|
Item {
|
|
visible: !root.loading && !root.loadError
|
|
&& root.albums.length === 0 && root.query !== ""
|
|
width: parent.width
|
|
height: visible ? 200 : 0
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: QbzSession.tr("No results found.", QbzSession.trRev)
|
|
color: theme.textMuted
|
|
font.pixelSize: 14
|
|
}
|
|
}
|
|
Item {
|
|
visible: !root.loading && !root.loadError
|
|
&& root.albums.length === 0 && root.query === ""
|
|
width: parent.width
|
|
height: visible ? 200 : 0
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: QbzSession.tr("No award-winning releases yet.", QbzSession.trRev)
|
|
color: theme.textMuted
|
|
font.pixelSize: 14
|
|
}
|
|
}
|
|
|
|
AlbumCollection {
|
|
kioskHost: root.kioskHost
|
|
id: collection
|
|
visible: !root.loading && !root.loadError && root.albums.length > 0
|
|
width: parent.width
|
|
collectionKey: root.doc.id || ""
|
|
albums: root.albums
|
|
viewMode: "grid"
|
|
isGrouped: false
|
|
cardWidth: 200
|
|
cardHeight: 266
|
|
cardGap: 24
|
|
flick: page
|
|
contentOffset: root.kioskHost ? y : (collection.y)
|
|
}
|
|
|
|
Item {
|
|
visible: !root.loading && !root.loadError
|
|
&& root.albums.length > 0
|
|
&& root.doc.hasMore === true
|
|
&& root.query === ""
|
|
width: parent.width
|
|
height: visible ? loadMore.height : 0
|
|
|
|
QbzLoadMore {
|
|
id: loadMore
|
|
width: parent.width
|
|
buttonHeight: root.kioskHost ? 64 : 32
|
|
busy: root.doc.loadingMore === true
|
|
skeleton: "cards"
|
|
cellW: 224
|
|
cellH: 290
|
|
onClicked: {
|
|
collection.armTailFade()
|
|
QbzHome.awardAlbumsLoadMore()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Back/forward scroll memory (controls/ScrollMemory.qml): reports
|
|
// this container's offset while it is the live page, and restores it
|
|
// when a back/forward step arms this route.
|
|
ScrollMemory { target: page; scope: "awardalbums" }
|
|
QbzScrollBar {
|
|
target: page
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 4
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
}
|
|
}
|
|
}
|