fix(local): apply source filters to selected artist albums

Filter physical album variants in the compatibility detail view and cancel stale native queries and unfiltered retries. Load album rows when switching out of the native model.

cx
This commit is contained in:
vicrodh committed 2026-09-06 14:34:18 -06:00
1 parent 9c9424bd30
commit bf84f2e65b
3 files changed
+30 -1

No files matched your search

+3 -1
View File
@@ -1174,7 +1174,9 @@ Rectangle {
readonly property var artistAlbums: { readonly property var artistAlbums: {
if (selectedArtist === "") return [] if (selectedArtist === "") return []
if (QbzLocal.localArtistsNativeActive) return [] if (QbzLocal.localArtistsNativeActive) return []
var rows = albums // binding dependency: re-derive on every republish // The rail filters artist aggregates; the detail must also filter
// physical album copies, including when the selected artist stays.
var rows = applyFilter(albums, artistsFilter)
var ids = {} var ids = {}
try { try {
var arr = JSON.parse(QbzLocal.artistAlbumIds(selectedArtist)) var arr = JSON.parse(QbzLocal.artistAlbumIds(selectedArtist))
@@ -594,6 +594,9 @@ fn activate_artists(opened: OpenedArtists) {
crate::local_bridge_ops::publish_counts(); crate::local_bridge_ops::publish_counts();
} }
ui(move |mut bridge| { ui(move |mut bridge| {
if QUERY_GENERATION.load(Ordering::Acquire) != generation {
return;
}
cpp_artist_reset(generation, total, artist_total); cpp_artist_reset(generation, total, artist_total);
let bytes = publish_artist_page_now(generation, 0, &wire); let bytes = publish_artist_page_now(generation, 0, &wire);
bridge.as_mut().set_local_artists_native_active(true); bridge.as_mut().set_local_artists_native_active(true);
@@ -1580,6 +1583,12 @@ fn fallback_detail(generation: u64, reason: &'static str) {
} }
fn deactivate_for_legacy(reason: &'static str) { fn deactivate_for_legacy(reason: &'static str) {
// A filtered compatibility view must not be replaced by an older native
// response or an unfiltered retry when the catalog publishes an update.
next_generation(&QUERY_GENERATION);
*LAST_SEARCH
.lock()
.unwrap_or_else(|error| error.into_inner()) = None;
*SESSION.lock().unwrap_or_else(|error| error.into_inner()) = None; *SESSION.lock().unwrap_or_else(|error| error.into_inner()) = None;
let detail_generation = next_generation(&DETAIL_GENERATION); let detail_generation = next_generation(&DETAIL_GENERATION);
*DETAIL_SESSION *DETAIL_SESSION
@@ -1646,6 +1655,7 @@ fn next_generation(counter: &AtomicU64) -> u64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
static QUERY_TEST_LOCK: Mutex<()> = Mutex::new(());
fn artist_entry(index: usize) -> ArtistEntry { fn artist_entry(index: usize) -> ArtistEntry {
ArtistEntry { ArtistEntry {
@@ -1684,6 +1694,7 @@ mod tests {
#[test] #[test]
fn stale_artist_generation_is_not_current() { fn stale_artist_generation_is_not_current() {
let _guard = QUERY_TEST_LOCK.lock().unwrap();
let old = next_generation(&QUERY_GENERATION); let old = next_generation(&QUERY_GENERATION);
let new = next_generation(&QUERY_GENERATION); let new = next_generation(&QUERY_GENERATION);
assert_ne!(old, new); assert_ne!(old, new);
@@ -1691,6 +1702,21 @@ mod tests {
assert_eq!(QUERY_GENERATION.load(Ordering::Acquire), new); assert_eq!(QUERY_GENERATION.load(Ordering::Acquire), new);
} }
#[test]
fn filtered_artist_query_cancels_native_results_and_unfiltered_retry() {
let _guard = QUERY_TEST_LOCK.lock().unwrap();
*LAST_SEARCH.lock().unwrap() = Some("Led Zeppelin".into());
let generation = QUERY_GENERATION.load(Ordering::Acquire);
assert!(!reset(
"Led Zeppelin".into(),
"name-asc".into(),
r#"{"local":true}"#.into()
));
assert!(QUERY_GENERATION.load(Ordering::Acquire) > generation);
assert!(LAST_SEARCH.lock().unwrap().is_none());
assert!(!retry_last());
}
#[test] #[test]
fn artist_alpha_bucket_matches_the_existing_rail() { fn artist_alpha_bucket_matches_the_existing_rail() {
assert_eq!(artist_initial("air"), "A"); assert_eq!(artist_initial("air"), "A");
+1
View File
@@ -1051,6 +1051,7 @@ impl qbz_local::QbzLocal {
filter_json.to_string(), filter_json.to_string(),
) { ) {
crate::local_bridge_ops::load_artists_legacy(); crate::local_bridge_ops::load_artists_legacy();
crate::local_bridge_ops::load_albums_legacy();
} }
} }