Update to artist credit handling

This commit is contained in:
Connor Johnstone
2026-03-18 14:34:58 -04:00
parent a268ec4e56
commit 8b16859526
10 changed files with 181 additions and 60 deletions

View File

@@ -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::<String>);
let id = props.id.clone();
// Flag to prevent the background enrichment from overwriting a user-triggered refresh
let user_acted: Rc<Cell<bool>> = 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<Cell<bool>> = 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! { <div class="error">{ format!("Error: {err}") }</div> };
}
@@ -69,10 +108,53 @@ pub fn artist_page(props: &Props) -> Html {
return html! { <p class="loading">{ "Loading discography from MusicBrainz..." }</p> };
};
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! {
<button class="btn btn-sm btn-success"
onclick={Callback::from(move |_: MouseEvent| {
let artist_name = artist_name.clone();
let artist_mbid = artist_mbid.clone();
let message = message.clone();
let error = error.clone();
let fetch = fetch.clone();
let artist_id = artist_id.clone();
wasm_bindgen_futures::spawn_local(async move {
match api::add_artist(&artist_name, artist_mbid.as_deref()).await {
Ok(s) => {
message.set(Some(format!(
"Watching {}: added {} tracks ({} already owned)",
artist_name, s.tracks_added, s.tracks_already_owned
)));
fetch.emit(artist_id);
}
Err(e) => error.set(Some(e.0)),
}
});
})}>
{ "Watch All" }
</button>
}
} else {
html! {}
}
};
html! {
<div>
<div class="page-header">
<h2>{ &d.artist.name }</h2>
<div class="flex items-center justify-between">
<h2>{ &d.artist.name }</h2>
{ watch_all_btn }
</div>
if d.enriched {
<p class="text-sm">
<span style="color: var(--accent);">