feat(slint): favorites Playlists actions — play/queue/share/favorite + sub-tab/search

Wire the playlist card media-actions: play / play-next / queue fetch the
playlist's tracks (core get_playlist) and route to playback play/enqueue;
share copies the Qobuz web-player playlist URL to the clipboard; favorite
toggles the LOCAL QBZ favorite (Library un-favorites in place and drops the
row, Following adds to Library — per user decision). Wire the sub-tab switch
and the name|owner search to derive_playlists. Add share::qobuz_playlist_url
and favorites::remove_playlist_row.

cc
This commit is contained in:
vicrodh committed 2026-05-27 14:11:31 -06:00
1 parent 116bc13660
commit 120670147c
3 files changed
+116

No files matched your search

+18
View File
@@ -851,6 +851,24 @@ pub fn remove_album_row(window: &AppWindow, id: &str) {
}
}
/// Remove a playlist from the Library (favorites) source after a local
/// un-favorite, then re-derive the rendered model + update the tab badge.
/// Following is untouched (a followed playlist stays followed).
pub fn remove_playlist_row(window: &AppWindow, id: &str) {
let state = window.global::<FavoritesState>();
let model = state.get_playlists_favorites();
if let Some(vm) = model.as_any().downcast_ref::<VecModel<SearchPlaylistItem>>() {
for i in 0..vm.row_count() {
if vm.row_data(i).map(|p| p.id == id).unwrap_or(false) {
vm.remove(i);
break;
}
}
}
state.set_playlists_total(model.row_count() as i32);
derive_playlists(window);
}
// ---- Artwork propagation -----------------------------------------------
// The artwork pipeline writes a decoded cover into the SOURCE model
// (`albums` / `tracks`) by index, but the views render the derived
+93
View File
@@ -4314,6 +4314,99 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
});
}
{
// Switch the Playlists sub-tab (Library / Following) + re-derive.
let weak = window.as_weak();
window
.global::<FavoritesActions>()
.on_playlists_set_sub_tab(move |sub| {
if let Some(w) = weak.upgrade() {
w.global::<FavoritesState>().set_playlists_sub_tab(sub);
favorites::derive_playlists(&w);
}
});
}
{
// Local search over the loaded favorite playlists (name | owner).
let weak = window.as_weak();
window
.global::<FavoritesActions>()
.on_search_playlists(move |q| {
if let Some(w) = weak.upgrade() {
w.global::<FavoritesState>().set_playlists_search(q);
favorites::derive_playlists(&w);
}
});
}
{
// Playlist card actions: play / play-next / queue / share / favorite.
let runtime = app_runtime.clone();
let weak = window.as_weak();
let handle = tokio_rt.handle().clone();
window
.global::<FavoritesActions>()
.on_playlist_action(move |id, action| {
let Some(w) = weak.upgrade() else {
return;
};
match action.as_str() {
"share" => share::copy_to_clipboard(share::qobuz_playlist_url(&id)),
"favorite" => {
// Library sub-tab: un-favorite in place (remove the
// local favorite + drop the row). Following sub-tab:
// add to the local Library (per user decision).
let library = w
.global::<FavoritesState>()
.get_playlists_sub_tab()
.to_string()
!= "following";
if let Ok(pid) = id.parse::<u64>() {
let fav = !library;
handle.spawn_blocking(move || {
crate::library_db::with_db(|db| db.set_playlist_favorite(pid, fav));
});
}
if library {
favorites::remove_playlist_row(&w, &id);
}
}
act => {
// play / play-next / queue: fetch the playlist's tracks,
// then play or enqueue.
let Ok(pid) = id.parse::<u64>() else {
return;
};
let runtime = runtime.clone();
let weak2 = weak.clone();
let handle2 = handle.clone();
let act = act.to_string();
handle.spawn(async move {
let tracks = match runtime.core().get_playlist(pid).await {
Ok(p) => p.tracks.map(|t| t.items).unwrap_or_default(),
Err(e) => {
log::error!("[qbz-slint] playlist {pid} load failed: {e}");
return;
}
};
if tracks.is_empty() {
return;
}
match act.as_str() {
"play-next" => {
playback::enqueue_tracks(runtime, handle2, tracks, true)
}
"queue" => {
playback::enqueue_tracks(runtime, handle2, tracks, false)
}
_ => {
playback::play_tracks(runtime, weak2, handle2, tracks, 0);
}
}
});
}
}
});
}
{
let weak = window.as_weak();
window
+5
View File
@@ -6,6 +6,11 @@ pub fn qobuz_track_url(track_id: &str) -> String {
format!("https://www.qobuz.com/track/{track_id}")
}
/// Qobuz web-player playlist URL (matches Tauri's share-playlist link).
pub fn qobuz_playlist_url(playlist_id: &str) -> String {
format!("https://play.qobuz.com/playlist/{playlist_id}")
}
/// Copy `text` to the system clipboard. Runs on a blocking thread —
/// clipboard backends (X11/Wayland) can block, and arboard keeps an
/// owner thread alive so the contents persist after this returns.