From c7adb599eea2b2d2e9746ed3f7b43683a27b2c6a Mon Sep 17 00:00:00 2001 From: Psychotoxical Date: Wed, 8 Apr 2026 13:10:01 +0200 Subject: [PATCH] fix(artist): decouple artist info fetch from main render path (fix #132) getArtistInfo2.view can trigger Navidrome to do slow external lookups (Last.fm / Apple Music) when no local artist image exists, blocking the entire page for 10+ seconds. Render the page immediately after getArtist() resolves (local data only), then fire getArtistInfo and getTopSongs in the background. A cancelled flag prevents stale state updates when the user navigates away before either background fetch completes. Co-Authored-By: Claude Sonnet 4.6 --- src/pages/ArtistDetail.tsx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/pages/ArtistDetail.tsx b/src/pages/ArtistDetail.tsx index 11e91495..0a5f4088 100644 --- a/src/pages/ArtistDetail.tsx +++ b/src/pages/ArtistDetail.tsx @@ -80,24 +80,32 @@ export default function ArtistDetail() { useEffect(() => { if (!id) return; + let cancelled = false; setLoading(true); + setInfo(null); + setTopSongs([]); setFeaturedAlbums([]); getArtist(id).then(artistData => { + if (cancelled) return; setArtist(artistData.artist); setAlbums(artistData.albums); setIsStarred(!!artistData.artist.starred); - return Promise.all([ - getArtistInfo(id).catch(() => null), - getTopSongs(artistData.artist.name).catch(() => []), - ]); - }).then(([artistInfo, songsData]) => { - if (artistInfo !== undefined) setInfo(artistInfo as SubsonicArtistInfo | null); - if (songsData !== undefined) setTopSongs(songsData as SubsonicSong[]); + // Render the page immediately from local data setLoading(false); + + // Fetch artist info (may trigger slow external lookup on the server) + // and top songs in the background — do not block rendering + getArtistInfo(id).then(artistInfo => { + if (!cancelled) setInfo(artistInfo ?? null); + }).catch(() => {}); + + getTopSongs(artistData.artist.name).then(songsData => { + if (!cancelled) setTopSongs(songsData ?? []); + }).catch(() => {}); }).catch(err => { - console.error(err); - setLoading(false); + if (!cancelled) { console.error(err); setLoading(false); } }); + return () => { cancelled = true; }; }, [id]); useEffect(() => {