feat(slint): labels and similar artists sections in the sidebar

Extracts the two Qobuz-driven sidebar sections from /artist/page data
the artist controller already receives and wires them into the
NetworkSidebar.

Controller (`crates/qbz-slint/src/artist.rs`):
  - Labels collected during the existing release-bucketing loop,
    matching Tauri's extractLabelsFromPageReleases: only the "album"
    release group, only releases attributed to this artist, dedupe by
    label id, sorted by name. Each label exposes {id, name}.
  - Similar artists pulled from page.similar_artists.items as
    {id, name}. No artwork on the sidebar rows (Tauri only renders a
    User icon + name there).
  - ArtistData / ArtistState gained `labels` and `similar_artists`;
    reset_artist clears them on artist change.

Slint (`ui/state.slint`, `ui/artist/ArtistPageView.slint`):
  - New LabelEntry and SimilarEntry structs alongside ArtistState.
  - New SidebarLink local component — small leading icon (12px) +
    elided name, surface-elevated hover, mirrors Tauri's
    `.sidebar-artist-link`. Used by Labels (Disc), Similar (User),
    and the upcoming Relationships rows.
  - Labels section: title + items or "No label info" placeholder.
  - Similar Artists section: title + items, with "Loading…" while
    the artist page itself is loading and "No similar artists found"
    once loaded with an empty list.

main.rs:
  - on_artist_clicked now navigates: records nav history and calls
    navigate_artist, same flow as the top-level on_open_artist. The
    target view exists in Slint already (unlike LabelReleases /
    ArtistsByLocation / MusicianPage, which stay logged-only).

cc
This commit is contained in:
vicrodh committed 2026-05-20 09:59:02 -06:00
1 parent d107d4f577
commit 6d25b25d1d
4 files changed
+244 -8

No files matched your search

@@ -211,6 +211,55 @@ component SectionTitle inherits Text {
font-weight: Typography.semibold;
}
// One row in the network sidebar — small leading icon + name. Used by
// Labels (Disc icon), Similar Artists / Members / Discovery (User
// icon), and Member-Of groups (Music icon). Mirrors Tauri's
// `.sidebar-artist-link` rendering.
component SidebarLink inherits Rectangle {
in property <string> label;
in property <image> icon;
in property <string> tooltip;
callback clicked();
height: 28px;
border-radius: 4px;
background: link-ta.has-hover ? Theme.surface-elevated : transparent;
HorizontalLayout {
padding-left: 8px;
padding-right: 8px;
spacing: 8px;
alignment: stretch;
VerticalLayout {
alignment: center;
horizontal-stretch: 0;
QbzIcon {
source: root.icon;
width: 12px;
height: 12px;
tint: link-ta.has-hover ? Theme.text-primary : Theme.text-muted;
}
}
Text {
text: root.label;
color: link-ta.has-hover ? Theme.text-primary : Theme.text-secondary;
font-size: 13px;
overflow: elide;
vertical-alignment: center;
horizontal-stretch: 1;
}
}
link-ta := TouchArea {
mouse-cursor: pointer;
clicked => {
root.clicked();
}
}
}
export component ArtistPageView inherits Rectangle {
callback open-album(string);
callback media-action(string, string, string);
@@ -768,6 +817,76 @@ export component ArtistPageView inherits Rectangle {
}
}
}
// --- Labels (Qobuz, deduped from own albums) ---
VerticalLayout {
padding-left: 14px;
padding-right: 14px;
padding-top: 12px;
padding-bottom: 6px;
spacing: 4px;
Text {
text: @tr("LABELS");
color: Theme.text-muted;
font-size: 11px;
font-weight: Typography.semibold;
letter-spacing: 0.5px;
}
if ArtistState.labels.length == 0: Text {
text: @tr("No label info");
color: Theme.text-muted;
font-size: 12px;
}
for label in ArtistState.labels: SidebarLink {
label: label.name;
icon: @image-url("../assets/icons/disc.svg");
tooltip: label.name;
clicked => {
NetworkSidebarActions.label-clicked(
label.id, label.name);
}
}
}
// --- Similar Artists (Qobuz) ---
VerticalLayout {
padding-left: 14px;
padding-right: 14px;
padding-top: 12px;
padding-bottom: 6px;
spacing: 4px;
Text {
text: @tr("SIMILAR ARTISTS");
color: Theme.text-muted;
font-size: 11px;
font-weight: Typography.semibold;
letter-spacing: 0.5px;
}
if ArtistState.similar-artists.length == 0
&& !ArtistState.loading: Text {
text: @tr("No similar artists found");
color: Theme.text-muted;
font-size: 12px;
}
if ArtistState.similar-artists.length == 0
&& ArtistState.loading: Text {
text: @tr("Loading…");
color: Theme.text-muted;
font-size: 12px;
}
for similar in ArtistState.similar-artists: SidebarLink {
label: similar.name;
icon: @image-url("../assets/icons/user.svg");
tooltip: similar.name;
clicked => {
NetworkSidebarActions.artist-clicked(similar.id);
}
}
}
}
}
}