fix(locallibrary): track-row source indicator (drop fav/offline) + AlbumView favorite item

LocalLibrary track rows (Tracks tab + local album detail) no longer carry the
favorite or offline-cache columns — they don't apply to local content. Instead
they show a subtle per-source indicator (local -> hard-drive tinted; qobuz
offline / plex -> their mark), small + flat rather than the bold album-card
badge (mirrors Tauri's track-row local indicator). TrackItem gains a `source`
field, set by every constructor (Qobuz surfaces -> "qobuz"; map_local_track ->
local/qobuz/plex); TrackRow gains `show-source`.

Also fixes Qobuz AlbumView: the track list had a Favorite column header but the
rows never rendered the heart (show-favorite defaulted false). AlbumPageView
now sets show-favorite + show-download for Qobuz albums (matching the header)
and the source indicator for local albums, with the column header switched to
match each.

cc
This commit is contained in:
vicrodh committed 2026-05-29 08:43:26 -06:00
1 parent 6e1009afd0
commit c36b99b8e8
12 files changed
+60 -6

No files matched your search

+2
View File
@@ -259,6 +259,8 @@ pub fn apply_album(window: &AppWindow, data: AlbumData) {
removing: false,
cache_status: if crate::offline_cache::is_cached(&track.id) { 3 } else { 0 },
cache_progress: 0.0,
// Qobuz album-detail rows; local albums override via map_local_track.
source: "qobuz".into(),
unlocking: false,
})
.collect();
+1
View File
@@ -505,6 +505,7 @@ pub fn apply_artist(window: &AppWindow, data: ArtistData) {
removing: false,
cache_status: if crate::offline_cache::is_cached(&track.id) { 3 } else { 0 },
cache_progress: 0.0,
source: "qobuz".into(),
unlocking: false,
})
.collect();
+1
View File
@@ -421,6 +421,7 @@ pub fn apply_favorites(window: &AppWindow, data: FavData) {
removing: false,
cache_status: if crate::offline_cache::is_cached(&t.id) { 3 } else { 0 },
cache_progress: 0.0,
source: "qobuz".into(),
unlocking: false,
})
.collect();
+1
View File
@@ -765,6 +765,7 @@ fn top_track_to_item(t: &TopTrack) -> TrackItem {
removing: false,
cache_status: if crate::offline_cache::is_cached(&t.id) { 3 } else { 0 },
cache_progress: 0.0,
source: "qobuz".into(),
unlocking: false,
}
}
+7
View File
@@ -454,6 +454,13 @@ fn map_local_track(t: qbz_library::LocalTrack) -> TrackItem {
removing: false,
cache_status: 0,
cache_progress: 0.0,
// Source indicator: offline copies read as Qobuz, user files as local.
source: match t.source.as_deref() {
Some("qobuz_download") => "qobuz",
Some("plex") => "plex",
_ => "local",
}
.into(),
unlocking: false,
}
}
+1
View File
@@ -177,6 +177,7 @@ fn to_item(track: &Track) -> TrackItem {
removing: false,
cache_status: if crate::offline_cache::is_cached(&track.id.to_string()) { 3 } else { 0 },
cache_progress: 0.0,
source: "qobuz".into(),
unlocking: false,
}
}
+1
View File
@@ -384,6 +384,7 @@ fn to_item(track: &Track) -> TrackItem {
removing: false,
cache_status: if crate::offline_cache::is_cached(&track.id.to_string()) { 3 } else { 0 },
cache_progress: 0.0,
source: "qobuz".into(),
unlocking: false,
}
}
+1
View File
@@ -375,6 +375,7 @@ fn track_item(row: TrackRowData) -> TrackItem {
removing: false,
cache_status: if is_cached { 3 } else { 0 },
cache_progress: 0.0,
source: "qobuz".into(),
unlocking: false,
}
}
+13 -6
View File
@@ -529,8 +529,10 @@ export component AlbumPageView inherits Rectangle {
horizontal-alignment: center;
vertical-alignment: center;
}
// Favorite / offline-cache column headers.
Rectangle {
// Favorite / offline-cache column headers (Qobuz). For
// a local album the rows show a source indicator
// instead, so one unlabeled column keeps alignment.
if !AlbumState.is-local: Rectangle {
width: 28px;
QbzIcon {
source: @image-url("../assets/icons/heart.svg");
@@ -541,7 +543,7 @@ export component AlbumPageView inherits Rectangle {
tint: Theme.text-muted;
}
}
Rectangle {
if !AlbumState.is-local: Rectangle {
width: 28px;
QbzIcon {
source: @image-url("../assets/icons/cloud-download.svg");
@@ -552,6 +554,7 @@ export component AlbumPageView inherits Rectangle {
tint: Theme.text-muted;
}
}
if AlbumState.is-local: Rectangle { width: 28px; }
// Aligns with the per-row more-button slot.
Rectangle { width: 32px; }
}
@@ -559,10 +562,14 @@ export component AlbumPageView inherits Rectangle {
for track[index] in AlbumState.tracks: TrackRow {
track: track;
index: index;
// Album context: number cell (no cover), with the
// offline-cache button.
// Number cell (no cover). Qobuz albums carry the
// favorite heart (matching the column header) + the
// offline-cache button; local albums show the source
// indicator instead.
show-artwork: false;
show-download: true;
show-favorite: !AlbumState.is-local;
show-download: !AlbumState.is-local;
show-source: AlbumState.is-local;
media-action(kind, id, action) => {
root.media-action(kind, id, action);
}
@@ -513,6 +513,7 @@ export component LocalLibraryView inherits Rectangle {
show-album: true;
show-favorite: false;
show-download: false;
show-source: true;
media-action(kind, id, action) => {
if (kind == "album" && action == "open") {
LocalLibraryActions.open-album(id);
@@ -36,6 +36,9 @@ export component TrackRow inherits Rectangle {
// button. A non-Qobuz surface (future Local Library / Plex rows, which are
// already local) should set this false.
in property <bool> show-download: true;
// Local Library: a subtle per-source indicator (local / qobuz / plex)
// shown INSTEAD of the favorite + offline columns.
in property <bool> show-source: false;
in property <bool> multi-select-mode: false;
// Custom-order reorder controls (up/down chevrons) at the leading
// edge — used by the playlist custom sort.
@@ -451,6 +454,31 @@ export component TrackRow inherits Rectangle {
}
}
// Source indicator (Local Library) — a subtle per-source glyph shown
// instead of the favorite/offline columns. local -> hard-drive
// (tinted), plex / qobuz(offline) -> their mark; small + flat, not the
// bold album-card badge.
if root.show-source && root.track.source != "": VerticalLayout {
alignment: center;
Rectangle {
width: 28px;
height: 28px;
Image {
source: root.track.source == "plex"
? @image-url("../assets/icons/plex-logo.svg")
: (root.track.source == "qobuz"
? @image-url("../assets/icons/qobuz-logo-filled.svg")
: @image-url("../assets/icons/hard-drive.svg"));
colorize: root.track.source == "local" ? Theme.text-muted : transparent;
width: root.track.source == "local" ? 15px : 16px;
height: self.width;
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
image-fit: contain;
}
}
}
// Context menu.
VerticalLayout {
alignment: center;
+3
View File
@@ -251,6 +251,9 @@ export struct TrackItem {
// album view, both via the media-action bubble channel.
artist-id: string,
album-id: string,
// "qobuz" | "local" | "plex" | "" — drives the Local Library source
// indicator (shown instead of the favorite/offline columns).
source: string,
// Fading out (un-favorited in the favorites view) before removal.
removing: bool,
// Offline-cache status: 0 none, 1 queued, 2 downloading, 3 ready, 4 failed.