feat(slint): playlist detail track menus 1:1 (remove, source routing, bulk parity)
Per-row Remove from playlist lands (danger styling, owner-gated by design - Tauri renders it un-gated and lets the API reject; we port the intent, commented) working across qobuz/local/plex rows through the namespace split. Per-row Play next / Add to queue route by source: local rows enqueue their snapshot QueueTrack instead of mis-parsing row ids as catalog ids, and plex rows enqueue too (deliberate deviation from Tauri's silent no-op - the machinery exists). Hero Play next/later of mixed playlists merge the sidecar before enqueueing (Tauri T2 fix-forward: their hero arms drop source and crash plex auto-advance). Bulk bar reaches parity: select mode on followed playlists, source-aware bulk Play next / Add to queue / Add to playlist (mixed selections send Qobuz rows to the catalog picker, all-sidecar selections to the local picker), Remove stays owner-only. The offline-cache action is now cache-state aware app-wide (Make available / Refresh / Remove with a sequenced recache arm), and Track info hides on this surface until the feature exists. cc
This commit is contained in:
9 files changed
+564
-103
No files matched your search
@@ -17,21 +17,37 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
// §3.1/§3.2); they default to the block so Qobuz surfaces are unchanged.
|
||||
// `playlist-action` = Add to playlist — on for Qobuz + offline + local +
|
||||
// Plex (local playlists store each source's own ref), off for ephemeral.
|
||||
// `remove-from-playlist-action` = the playlist-detail per-row removal
|
||||
// (every row source — the arm splits by id namespace); the surface
|
||||
// gates it on ownership (spec §3.1).
|
||||
// `track-info-action` lets a surface HIDE the Track Info entry while
|
||||
// its target feature is missing there (spec §3.3) — defaults on so
|
||||
// other surfaces are untouched.
|
||||
// The transient actions (play / next / queue) always show.
|
||||
in property <bool> qobuz-actions: true;
|
||||
in property <bool> favorite-action: root.qobuz-actions;
|
||||
in property <bool> mixtape-action: root.qobuz-actions;
|
||||
in property <bool> playlist-action: true;
|
||||
in property <bool> remove-from-playlist-action: false;
|
||||
in property <bool> track-info-action: true;
|
||||
// The row's offline-cache status (TrackItem.cache-status): when cached
|
||||
// (3) the single "Make available offline" entry swaps for the
|
||||
// Refresh / Remove pair — Tauri's ready-state submenu, rendered flat
|
||||
// (cosmetic-flat per spec §3.5).
|
||||
in property <int> cache-status: 0;
|
||||
callback media-action(string, string, string);
|
||||
|
||||
width: 224px;
|
||||
// 3 transient + optional favorite/mixtape/add-to-playlist + the
|
||||
// 7-item Qobuz block.
|
||||
// 3 transient + optional favorite/mixtape/add-to-playlist/remove + the
|
||||
// Qobuz block (5 fixed + 1-or-2 offline entries + optional track info).
|
||||
height: (3
|
||||
+ (root.favorite-action ? 1 : 0)
|
||||
+ (root.mixtape-action ? 1 : 0)
|
||||
+ (root.playlist-action ? 1 : 0)
|
||||
+ (root.qobuz-actions ? 7 : 0)) * 30px;
|
||||
+ (root.remove-from-playlist-action ? 1 : 0)
|
||||
+ (root.qobuz-actions
|
||||
? 5 + (root.cache-status == 3 ? 2 : 1) + (root.track-info-action ? 1 : 0)
|
||||
: 0)) * 30px;
|
||||
|
||||
ContextMenu {
|
||||
ContextMenuItem {
|
||||
@@ -83,6 +99,16 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
root.media-action("track", root.track-id, "add-to-playlist");
|
||||
}
|
||||
}
|
||||
// Remove from playlist (danger) — playlist detail only, after Add
|
||||
// to playlist (Tauri group order, spec §1.3 #8).
|
||||
if root.remove-from-playlist-action: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/trash-2.svg");
|
||||
label: @tr("Remove from playlist");
|
||||
danger: true;
|
||||
clicked => {
|
||||
root.media-action("track", root.track-id, "remove-from-playlist");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/link.svg");
|
||||
label: @tr("Share Qobuz link");
|
||||
@@ -97,13 +123,32 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
root.media-action("track", root.track-id, "share-songlink");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions: ContextMenuItem {
|
||||
// Offline-cache block — cached-state-aware (Tauri's ready-state
|
||||
// submenu, flat per spec §3.5): not cached -> Make available
|
||||
// offline; cached -> Refresh + Remove (icons 1:1 with Tauri's
|
||||
// RefreshCw / Trash2).
|
||||
if root.qobuz-actions && root.cache-status != 3: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/cloud-download.svg");
|
||||
label: @tr("Make available offline");
|
||||
clicked => {
|
||||
root.media-action("track", root.track-id, "download");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions && root.cache-status == 3: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/refresh-cw.svg");
|
||||
label: @tr("Refresh offline copy");
|
||||
clicked => {
|
||||
root.media-action("track", root.track-id, "recache");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions && root.cache-status == 3: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/trash-2.svg");
|
||||
label: @tr("Remove offline copy");
|
||||
danger: true;
|
||||
clicked => {
|
||||
root.media-action("track", root.track-id, "uncache");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/disc-3.svg");
|
||||
label: @tr("Go to album");
|
||||
@@ -118,7 +163,7 @@ export component TrackContextMenu inherits PopupWindow {
|
||||
root.media-action("track", root.track-id, "go-to-artist");
|
||||
}
|
||||
}
|
||||
if root.qobuz-actions: ContextMenuItem {
|
||||
if root.qobuz-actions && root.track-info-action: ContextMenuItem {
|
||||
icon: @image-url("../assets/icons/info.svg");
|
||||
label: @tr("Track info");
|
||||
clicked => {
|
||||
|
||||
Reference in new issue
Block a user