feat(slint): origin section + MB metadata wiring on artist navigate

Wires the artist controller to call QbzCore's MusicBrainz orchestration
and feed the Origin section of the network sidebar, plus typed click
callbacks for every section (logged-only until the target views land).

Controller (`crates/qbz-slint/src/artist.rs`):
  - reset_network_sidebar() — clears sidebar MB state on artist change.
  - load_mb_metadata(runtime, name) — checks the client's enabled flag,
    resolves name -> mbid via musicbrainz_resolve_artist, fetches
    metadata via musicbrainz_get_artist_metadata, maps into MbOrigin
    (is_person derived from artist_type, dates formatted as "1976",
    "May 1976" or "May 14, 1976" matching Tauri's en-US output).
  - apply_mb_metadata() / apply_mb_unavailable() — push into
    NetworkSidebarState on the event loop.

main.rs:
  - navigate_artist now resets the sidebar before loading and spawns
    the MB enrichment in parallel with artwork. Origin loading flag
    flips to true before the resolve, back to false on success or
    on MB-unavailable.
  - NetworkSidebarActions click callbacks (location, label, artist,
    musician, discovery dismiss) wired to log() with full payload —
    placeholder until ArtistsByLocation/LabelReleases/MusicianPage land
    in Slint.

ArtistPageView.slint:
  - Sidebar body becomes a Flickable + VerticalLayout (so the upcoming
    Labels / Similar / Relationships / Discovery sections can overflow
    cleanly).
  - Origin section renders three optional rows — Born/Founded,
    Born in/Founded in, Died/Disbanded — with the same uppercased
    label + right-aligned value pattern Tauri uses. The location row
    is clickable when the location precision is finer than country
    or a city is present; click emits NetworkSidebarActions.location-
    clicked with the artist's mbid.
  - Section is gated by NetworkSidebarState.mb-available, so a
    disabled MB integration or an unmatched artist hides Origin
    entirely (next phases fill Labels/Similar with Qobuz data that
    does not depend on MB).

Adds qbz-integrations as a direct dep of qbz-slint so the controller
can match on ArtistType / LocationPrecision without going through a
re-export shim.

cc
This commit is contained in:
vicrodh committed 2026-05-20 09:51:22 -06:00
1 parent 11cdc0d238
commit d107d4f577
5 files changed
+382 -3

No files matched your search

+138 -2
View File
@@ -630,9 +630,145 @@ export component ArtistPageView inherits Rectangle {
background: Theme.border-subtle;
}
// Body — placeholder. Sections land in phases 5-8.
Rectangle {
// Body — sections: Origin (MB), Labels, Similar, MB
// Relationships, Discovery. Wrapped in a Flickable so a
// long sidebar (Labels + Relationships + Discovery) can
// scroll. Phase 5 adds Origin only; phases 6-8 fill the
// rest.
Flickable {
vertical-stretch: 1;
viewport-width: self.width;
viewport-height: sidebar-body.preferred-height;
sidebar-body := VerticalLayout {
spacing: 0px;
padding-top: 4px;
padding-bottom: 12px;
alignment: start;
// --- Origin (MB metadata) ----------------------
// Mirrors Tauri's .sidebar-section.sidebar-origin
// and the gate `mbAvailable && (loading || data)`.
if NetworkSidebarState.mb-available
&& (NetworkSidebarState.origin-loading
|| NetworkSidebarState.origin.begin-date != ""
|| NetworkSidebarState.origin.end-date != ""
|| NetworkSidebarState.origin.location-display != ""):
VerticalLayout {
padding-left: 14px;
padding-right: 14px;
padding-top: 12px;
padding-bottom: 12px;
spacing: 6px;
if NetworkSidebarState.origin-loading: Text {
text: @tr("Loading origin…");
color: Theme.text-muted;
font-size: 12px;
}
// Born / Founded
if !NetworkSidebarState.origin-loading
&& NetworkSidebarState.origin.begin-date != "":
HorizontalLayout {
spacing: 8px;
alignment: stretch;
Text {
text: NetworkSidebarState.origin.is-person
? @tr("BORN")
: @tr("FOUNDED");
color: Theme.text-muted;
font-size: 11px;
font-weight: Typography.semibold;
letter-spacing: 0.5px;
vertical-alignment: center;
horizontal-stretch: 0;
}
Text {
text: NetworkSidebarState.origin.begin-date;
color: Theme.text-primary;
font-size: 13px;
horizontal-alignment: right;
vertical-alignment: center;
horizontal-stretch: 1;
}
}
// Born in / Founded in — optionally clickable
if !NetworkSidebarState.origin-loading
&& NetworkSidebarState.origin.location-display != "":
HorizontalLayout {
spacing: 8px;
alignment: stretch;
Text {
text: NetworkSidebarState.origin.is-person
? @tr("BORN IN")
: @tr("FOUNDED IN");
color: Theme.text-muted;
font-size: 11px;
font-weight: Typography.semibold;
letter-spacing: 0.5px;
vertical-alignment: center;
horizontal-stretch: 0;
}
if NetworkSidebarState.origin.location-clickable: Rectangle {
horizontal-stretch: 1;
location-text := Text {
text: NetworkSidebarState.origin.location-display;
color: location-ta.has-hover
? Theme.accent
: Theme.text-primary;
font-size: 13px;
horizontal-alignment: right;
vertical-alignment: center;
width: 100%;
}
location-ta := TouchArea {
mouse-cursor: pointer;
clicked => {
NetworkSidebarActions.location-clicked(
NetworkSidebarState.mb-mbid);
}
}
}
if !NetworkSidebarState.origin.location-clickable: Text {
text: NetworkSidebarState.origin.location-display;
color: Theme.text-primary;
font-size: 13px;
horizontal-alignment: right;
vertical-alignment: center;
horizontal-stretch: 1;
}
}
// Died / Disbanded
if !NetworkSidebarState.origin-loading
&& NetworkSidebarState.origin.end-date != "":
HorizontalLayout {
spacing: 8px;
alignment: stretch;
Text {
text: NetworkSidebarState.origin.is-person
? @tr("DIED")
: @tr("DISBANDED");
color: Theme.text-muted;
font-size: 11px;
font-weight: Typography.semibold;
letter-spacing: 0.5px;
vertical-alignment: center;
horizontal-stretch: 0;
}
Text {
text: NetworkSidebarState.origin.end-date;
color: Theme.text-primary;
font-size: 13px;
horizontal-alignment: right;
vertical-alignment: center;
horizontal-stretch: 1;
}
}
}
}
}
}
}