fix(favorites): bulk un-favorite without the UI freeze, with feedback
- Local Library bulk un-favorite was O(selected x visible) on the UI thread and froze on big libraries (owner: 'se ciclo'). Now: one model pass (hearts hollow instantly), store mutations on a blocking thread, 'Removing…' info toast up front and a success toast at the end. - Favorites > Tracks bulk remove: 150 ms courtesy delay between Qobuz API calls (no burst) + the same progress toasts. km
This commit is contained in:
9 files changed
+79
-12
No files matched your search
@@ -4864,3 +4864,6 @@ msgstr "Als Nächstes"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "{} Titel aus Favoriten entfernt"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "{} Titel werden aus Favoriten entfernt…"
|
||||
@@ -660,3 +660,6 @@ msgstr "Next up"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "Removed {} tracks from favorites"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "Removing {} tracks from favorites…"
|
||||
@@ -4894,3 +4894,6 @@ msgstr "A continuación"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "Se quitaron {} tracks de favoritos"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "Quitando {} tracks de favoritos…"
|
||||
@@ -4864,3 +4864,6 @@ msgstr "À suivre"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "{} pistes retirées des favoris"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "Suppression de {} pistes des favoris…"
|
||||
@@ -4852,3 +4852,6 @@ msgstr "次に再生"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "{} トラックをお気に入りから削除しました"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "{} トラックをお気に入りから削除中…"
|
||||
@@ -4876,3 +4876,6 @@ msgstr "Hierna"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "{} nummers verwijderd uit favorieten"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "{} nummers worden verwijderd uit favorieten…"
|
||||
@@ -4864,3 +4864,6 @@ msgstr "A seguir"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "{} faixas removidas dos favoritos"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "Removendo {} faixas dos favoritos…"
|
||||
@@ -4878,3 +4878,6 @@ msgstr "Далее"
|
||||
|
||||
msgid "Removed {} tracks from favorites"
|
||||
msgstr "{} треков удалено из избранного"
|
||||
|
||||
msgid "Removing {} tracks from favorites…"
|
||||
msgstr "Удаление {} треков из избранного…"
|
||||
+55
-12
@@ -19165,35 +19165,64 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
local_library::clear_tracks_selection(&w);
|
||||
}
|
||||
"remove-favorites" => {
|
||||
// Bulk local un-favorite (owner 2026-07-26): drop every
|
||||
// selected row from the local-favorites store (the
|
||||
// All-feed reads it back), flip the row hearts live.
|
||||
// Bulk local un-favorite: ONE model pass (the first
|
||||
// cut looped the whole model per selected row and
|
||||
// froze the UI thread on big libraries — owner
|
||||
// 2026-07-26). Hearts hollow instantly; the store
|
||||
// mutations ride a blocking thread with progress
|
||||
// toasts so the user always knows it's working.
|
||||
let rows = local_library::selected_local_tracks(&w);
|
||||
let model = w.global::<LocalLibraryState>().get_tracks();
|
||||
let n = rows.len();
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
let ids: std::collections::HashSet<String> =
|
||||
rows.iter().map(|t| t.id.to_string()).collect();
|
||||
let mut keys = Vec::with_capacity(n);
|
||||
for t in &rows {
|
||||
let key = if t.source.as_deref() == Some("plex") {
|
||||
keys.push(if t.source.as_deref() == Some("plex") {
|
||||
format!("plex:{}", t.file_path)
|
||||
} else {
|
||||
t.file_path.clone()
|
||||
};
|
||||
let _ = crate::local_favorites::unfavorite("track", &key);
|
||||
let row_id = t.id.to_string();
|
||||
});
|
||||
}
|
||||
let model = w.global::<LocalLibraryState>().get_tracks();
|
||||
for i in 0..model.row_count() {
|
||||
if let Some(mut item) = model.row_data(i) {
|
||||
if item.id.as_str() == row_id {
|
||||
if item.is_favorite && ids.contains(item.id.as_str()) {
|
||||
item.is_favorite = false;
|
||||
model.set_row_data(i, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
local_library::clear_tracks_selection(&w);
|
||||
crate::toast::info(
|
||||
&w,
|
||||
qbz_i18n::t_args(
|
||||
"Removing {} tracks from favorites…",
|
||||
&[&n.to_string()],
|
||||
),
|
||||
);
|
||||
let weak2 = weak.clone();
|
||||
handle.spawn(async move {
|
||||
let keys2 = keys.clone();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
for k in &keys2 {
|
||||
let _ = crate::local_favorites::unfavorite("track", k);
|
||||
}
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
let _ = weak2.upgrade_in_event_loop(|w| {
|
||||
crate::toast::success(
|
||||
&w,
|
||||
qbz_i18n::t_args("Removed {} tracks from favorites", &[&n.to_string()]),
|
||||
qbz_i18n::t_args(
|
||||
"Removed {} tracks from favorites",
|
||||
&[&n.to_string()],
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
"add-to-playlist" => {
|
||||
// Source-aware refs: Plex rows ride as "plex:<key>",
|
||||
@@ -22794,10 +22823,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if ids.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Progress feedback (owner 2026-07-26): an up-front
|
||||
// toast, and a 150 ms courtesy delay between API
|
||||
// calls so a big selection isn't a burst at Qobuz.
|
||||
let runtime = runtime.clone();
|
||||
let weak = weak.clone();
|
||||
let handle = handle.clone();
|
||||
let image_cache = image_cache.clone();
|
||||
crate::toast::info(
|
||||
&w,
|
||||
qbz_i18n::t_args(
|
||||
"Removing {} tracks from favorites…",
|
||||
&[&ids.len().to_string()],
|
||||
),
|
||||
);
|
||||
handle.clone().spawn(async move {
|
||||
for id in &ids {
|
||||
if let Err(e) =
|
||||
@@ -22810,6 +22849,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Ok(tid) = id.parse::<u64>() {
|
||||
crate::fav_cache::set(tid, false);
|
||||
}
|
||||
tokio::time::sleep(
|
||||
std::time::Duration::from_millis(150),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let _ = weak.upgrade_in_event_loop(|w| {
|
||||
favorites::set_multi_select(&w, false);
|
||||
|
||||
Reference in new issue
Block a user