I **think** I've at least 99% fixed the top songs mismatch

This commit is contained in:
Connor Johnstone
2026-03-25 21:50:09 -04:00
parent 1a890b0c11
commit 159cdda386
3 changed files with 146 additions and 58 deletions

View File

@@ -21,6 +21,11 @@ pub fn artist_page(props: &Props) -> Html {
let message = use_state(|| None::<String>);
let active_tab = use_state(|| "discography".to_string());
let top_songs_limit = use_state(|| 25usize);
// Track top song status overrides — use Rc<RefCell> so async callbacks always see latest state
let top_song_overrides: Rc<std::cell::RefCell<std::collections::HashMap<usize, Option<String>>>> =
use_mut_ref(std::collections::HashMap::new);
// Counter to force re-renders when overrides change
let overrides_version = use_state(|| 0u32);
let id = props.id.clone();
// Flag to prevent the background enrichment from overwriting a user-triggered refresh
@@ -314,50 +319,65 @@ pub fn artist_page(props: &Props) -> Html {
<tbody>
{ for visible.iter().enumerate().map(|(i, song)| {
let rank = i + 1;
let has_status = song.status.is_some();
// Use override status if set, otherwise use the original
let effective_status = top_song_overrides
.borrow()
.get(&i)
.cloned()
.unwrap_or_else(|| song.status.clone());
let has_status = effective_status.is_some();
// Check if this song is in-flight (override set to exactly "pending")
let is_pending = top_song_overrides
.borrow()
.get(&i)
.map(|s| s.as_deref() == Some("pending"))
.unwrap_or(false);
let on_watch = {
let detail = detail.clone();
let overrides = top_song_overrides.clone();
let version = overrides_version.clone();
let name = song.name.clone();
let mbid = song.mbid.clone();
let artist = artist_name.clone();
Callback::from(move |_: MouseEvent| {
let detail = detail.clone();
// Immediately mark as pending
overrides.borrow_mut().insert(i, Some("pending".to_string()));
version.set(*version + 1);
let overrides = overrides.clone();
let version = version.clone();
let name = name.clone();
let mbid = mbid.clone();
let artist = artist.clone();
wasm_bindgen_futures::spawn_local(async move {
if let Ok(resp) = api::watch_track(Some(&artist), &name, mbid.as_deref().unwrap_or("")).await {
if let Some(ref d) = *detail {
let mut updated = d.clone();
if let Some(s) = updated.top_songs.get_mut(i) {
s.status = Some(resp.status);
}
detail.set(Some(updated));
}
}
let status = match api::watch_track(Some(&artist), &name, mbid.as_deref().unwrap_or("")).await {
Ok(resp) => resp.status,
Err(_) => "wanted".to_string(),
};
overrides.borrow_mut().insert(i, Some(status));
version.set(*version + 1);
});
})
};
let on_unwatch = {
let detail = detail.clone();
let overrides = top_song_overrides.clone();
let version = overrides_version.clone();
let mbid = song.mbid.clone();
Callback::from(move |_: MouseEvent| {
let detail = detail.clone();
// Immediately mark as pending
overrides.borrow_mut().insert(i, Some("pending".to_string()));
version.set(*version + 1);
let overrides = overrides.clone();
let version = version.clone();
let mbid = mbid.clone();
wasm_bindgen_futures::spawn_local(async move {
if let Some(ref m) = mbid {
if api::unwatch_track(m).await.is_ok() {
if let Some(ref d) = *detail {
let mut updated = d.clone();
if let Some(s) = updated.top_songs.get_mut(i) {
s.status = None;
}
detail.set(Some(updated));
}
}
let _ = api::unwatch_track(m).await;
}
overrides.borrow_mut().insert(i, None);
version.set(*version + 1);
});
})
};
@@ -376,12 +396,16 @@ pub fn artist_page(props: &Props) -> Html {
<td>{ &song.name }</td>
<td class="text-muted text-sm">{ plays }</td>
<td>
if let Some(ref status) = song.status {
<StatusBadge status={status.clone()} />
if let Some(ref status) = effective_status {
if status != "pending" {
<StatusBadge status={status.clone()} />
}
}
</td>
<td class="actions">
if !has_status {
if is_pending {
<button class="btn btn-sm" disabled={true}>{ "..." }</button>
} else if !has_status {
<button class="btn btn-sm" onclick={on_watch}>
{ "Watch" }
</button>