From 8b16859526e5c9454f30a8b951d0f1279bd99897 Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Wed, 18 Mar 2026 14:34:58 -0400 Subject: [PATCH] Update to artist credit handling --- frontend/src/pages/artist.rs | 90 +++++++++++++++++++++++++++++++-- frontend/src/pages/dashboard.rs | 2 +- frontend/src/pages/library.rs | 26 ++++++++-- frontend/src/pages/search.rs | 47 +++++------------ frontend/style.css | 7 +++ src/routes/albums.rs | 30 +++++++++-- src/routes/artists.rs | 9 +++- src/routes/downloads.rs | 26 +++++----- src/routes/search.rs | 2 +- src/routes/system.rs | 2 + 10 files changed, 181 insertions(+), 60 deletions(-) diff --git a/frontend/src/pages/artist.rs b/frontend/src/pages/artist.rs index 22313dd..b7b3545 100644 --- a/frontend/src/pages/artist.rs +++ b/frontend/src/pages/artist.rs @@ -1,8 +1,10 @@ +use std::rc::Rc; +use std::cell::Cell; +use gloo_timers::callback::Interval; use yew::prelude::*; use yew_router::prelude::*; use crate::api; -use crate::components::watch_indicator::WatchIndicator; use crate::pages::Route; use crate::types::FullArtistDetail; @@ -18,11 +20,16 @@ pub fn artist_page(props: &Props) -> Html { let message = use_state(|| None::); let id = props.id.clone(); + // Flag to prevent the background enrichment from overwriting a user-triggered refresh + let user_acted: Rc> = use_memo((), |_| Cell::new(false)); + // Full fetch (with track counts) — used for refresh after actions let fetch = { let detail = detail.clone(); let error = error.clone(); + let user_acted = user_acted.clone(); Callback::from(move |id: String| { + user_acted.set(true); let detail = detail.clone(); let error = error.clone(); wasm_bindgen_futures::spawn_local(async move { @@ -39,7 +46,9 @@ pub fn artist_page(props: &Props) -> Html { let detail = detail.clone(); let error = error.clone(); let id = id.clone(); + let user_acted = user_acted.clone(); use_effect_with(id.clone(), move |_| { + user_acted.set(false); wasm_bindgen_futures::spawn_local(async move { // Phase 1: quick load (instant for browsing artists) match api::get_artist_full_quick(&id).await { @@ -48,9 +57,14 @@ pub fn artist_page(props: &Props) -> Html { detail.set(Some(d)); // Phase 2: if not enriched, fetch full data in background - if needs_enrich { + if needs_enrich && !user_acted.get() { match api::get_artist_full(&id).await { - Ok(full) => detail.set(Some(full)), + Ok(full) => { + // Only apply if user hasn't triggered a refresh + if !user_acted.get() { + detail.set(Some(full)); + } + } Err(_) => {} // quick data is still showing, don't overwrite with error } } @@ -61,6 +75,31 @@ pub fn artist_page(props: &Props) -> Html { }); } + // Auto-refresh when background tasks complete (downloads, organize, etc.) + { + let fetch = fetch.clone(); + let id = id.clone(); + let had_tasks: Rc> = use_memo((), |_| Cell::new(false)); + use_effect_with((), move |_| { + let interval = Interval::new(3_000, move || { + let fetch = fetch.clone(); + let id = id.clone(); + let had_tasks = had_tasks.clone(); + wasm_bindgen_futures::spawn_local(async move { + if let Ok(status) = api::get_status().await { + let has_running = status.tasks.iter().any(|t| t.status == "Running"); + if had_tasks.get() && !has_running { + // Tasks just finished — refresh artist data + fetch.emit(id); + } + had_tasks.set(has_running); + } + }); + }); + move || drop(interval) + }); + } + if let Some(ref err) = *error { return html! {
{ format!("Error: {err}") }
}; } @@ -69,10 +108,53 @@ pub fn artist_page(props: &Props) -> Html { return html! {

{ "Loading discography from MusicBrainz..." }

}; }; + let watch_all_btn = { + let artist_status = d.artist_status.clone(); + let show = artist_status != "owned"; + if show { + let artist_name = d.artist.name.clone(); + let artist_mbid = d.artist.musicbrainz_id.clone(); + let message = message.clone(); + let error = error.clone(); + let fetch = fetch.clone(); + let artist_id = id.clone(); + html! { + + } + } else { + html! {} + } + }; + html! {