The port had a 67-line settings row showing two counters and a header saying the Manage chevron was "NOT shipped — this port has no Blacklist Manager view for it to open". The Slint has 1,114 lines of manager plus the consumer surfaces that make the feature mean anything. - src/artist_blacklist.rs is a 1:1 port of the reference's per-user lifecycle, including the fail-open asymmetry (is_enabled reads TRUE when there is no session, everything else false/empty) and the byte-identical NO_SESSION_ERR. src/reco_dismiss_qt.rs carries the reco-dismissal store. Both are bound in ALL THREE session-activation paths — the offline one included, because the reference records that Tauri never bound it offline and blacklisted artists leaked into offline surfaces — and torn down on logout, or the next account inherits them. - The manager view: three tabs (bordered, ADR-008 — no pills), search, sort, the enable toggle, the disabled warning banner, the four body branches per tab in their load-bearing order, windowed lists, and the clear-all confirm. Reached from Settings only, exactly like the Slint; no sidebar entry. - Every one of the ten store mutations publishes TWICE — its own document and publish_settings() — because the Settings counters live in a different document and nav_qt::back() re-runs no per-view load. Without it, Manage -> unblock -> Back showed pre-mutation counters. The Slint gets this free from a shared global. - The search field is driven imperatively, not by a binding: QbzLineEdit's X used to destroy the binding, after which the box went blank while the list stayed filtered. - Consumer surfaces, which is where the feature actually lives: the album card and album list row gained "Block this album", the artist card gained the 3-arg "Not interested", the album header menu gained Block/Unblock swapping on state, and the artist page gained the Show/Blacklist flip plus the amber hidden banner with its "Show artist" button. album_qt and artist_qt now seed isAlbumBlocked / isBlacklisted at document build time — without the seed an already-blocked album offered "Block this album" and the first click UN-blocked it, the same lie the pin glyph told before isPinned existed. - search_qt passed &Default::default() for both blacklist snapshots, so nothing was filtered anywhere and a greyed row still played. It now passes the real snapshots at both call sites. - The 17 toasts the spec had dropped for want of a toast host are wired: that premise stopped being true when toast_qt landed, and a Clear All that failed with NO_SESSION_ERR was completely silent. Their msgids are in all eight catalogues; the five album-axis ones existed only in es and nl. Also fixed in passing, pre-existing on this branch and unrelated to the blacklist: ArtistView's library tab read `libTracks` unqualified from a child of the object that declares it, which QML resolves against the component root, not the enclosing object — so the "Tracks" heading threw a ReferenceError and never rendered. Found by forcing the artist route in the offscreen smoke, which no earlier gate did. cc
105 lines
4.3 KiB
QML
105 lines
4.3 KiB
QML
// Settings > Blacklist — the QML port of crates/qbz-ui/ui/settings/
|
|
// ContentFilteringSettings.slint: ONE 64px row (blind-eye glyph + "Blacklist" +
|
|
// "{n} artists, {m} albums blocked", with a "(disabled)" suffix when the
|
|
// feature is off) and a right-aligned "Manage" button that opens the full-page
|
|
// Blacklist Manager. There is no enable toggle here in the Slint either
|
|
// (:13-16) — the switch lives inside the manager.
|
|
//
|
|
// Layout, verbatim from the reference (:66-115): outer spacing 4; a 64px row
|
|
// with spacing 24; the left group horizontal-stretch 1 / spacing 12 with the
|
|
// blind-eye 18x18 tint secondary, then a text column at spacing 3 holding
|
|
// "Blacklist" (text-primary 15 medium) over the status line (text-muted, a
|
|
// LITERAL 12px — not a Typography token); the ManageButton right-aligned and
|
|
// vertically centred.
|
|
//
|
|
// ManageButton (:26-64) is `width: max(content + 32, 160)`, height 34, Radius.sm,
|
|
// 1px border-subtle, surfaceHover on hover else surfaceElevated, content centred
|
|
// with spacing 6: label "Manage" text-secondary 15 medium, then chevron-right
|
|
// 16x16 tint secondary. controls/SettingsButton.qml already matches every one of
|
|
// those metrics, so it is reused with a trailing-icon slot rather than forked.
|
|
// Accepted deltas (the port's settings-button voice): content padding 24 vs 32,
|
|
// icon 15 vs 16, label textPrimary vs textSecondary.
|
|
//
|
|
// The counters come from the per-user blacklist singleton
|
|
// (crate::artist_blacklist, published into the settings document by
|
|
// settings_qt/devtools.rs::blacklist_counts), so they tell the truth
|
|
// immediately after a mutation — every blacklist mutation republishes the
|
|
// settings document as well as its own, which is what makes
|
|
// Manage -> unblock -> Back show fresh numbers.
|
|
|
|
import QtQuick
|
|
import com.blitzfc.qbz
|
|
import "../controls"
|
|
import "../theme"
|
|
|
|
Column {
|
|
id: root
|
|
|
|
property var doc: ({})
|
|
readonly property var dev: doc.dev || ({})
|
|
|
|
QbzTheme { id: theme }
|
|
|
|
spacing: 4
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: 64
|
|
|
|
Row {
|
|
anchors.fill: parent
|
|
// The Slint's 24px gap between the left group and the button.
|
|
spacing: 24
|
|
|
|
Row {
|
|
id: leftGroup
|
|
width: Math.max(0, parent.width - 24 - manageBtn.width)
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 12
|
|
|
|
QbzIcon {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
name: "blind-eye"
|
|
width: 18
|
|
height: 18
|
|
tintName: "secondary"
|
|
}
|
|
Column {
|
|
// 30 = the 18px glyph + the 12px row spacing.
|
|
width: Math.max(0, parent.width - 30)
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 3
|
|
Text {
|
|
width: parent.width
|
|
text: QbzSession.tr("Blacklist", QbzSession.trRev)
|
|
color: theme.textPrimary
|
|
font.pixelSize: theme.fontBody
|
|
font.weight: theme.weightMedium
|
|
}
|
|
Text {
|
|
width: parent.width
|
|
// Two placeholders, replaced left to right:
|
|
// artists first, then albums.
|
|
text: (root.dev.blacklistEnabled === false
|
|
? QbzSession.tr("{} artists, {} albums blocked (disabled)", QbzSession.trRev)
|
|
: QbzSession.tr("{} artists, {} albums blocked", QbzSession.trRev))
|
|
.replace("{}", root.dev.blacklistArtists || 0)
|
|
.replace("{}", root.dev.blacklistAlbums || 0)
|
|
color: theme.textMuted
|
|
font.pixelSize: 12
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsButton {
|
|
id: manageBtn
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: QbzSession.tr("Manage", QbzSession.trRev)
|
|
trailingIconName: "chevron-right"
|
|
onClicked: QbzBlacklist.openManager()
|
|
}
|
|
}
|
|
}
|
|
}
|