diff --git a/frontend/src/components/artist-details/artist-details.ts b/frontend/src/components/artist-details/artist-details.ts index 339169c..6bd989f 100644 --- a/frontend/src/components/artist-details/artist-details.ts +++ b/frontend/src/components/artist-details/artist-details.ts @@ -150,18 +150,6 @@ export class ArtistDetails extends LitElement { height: 100%; } - .loading-message, - .empty-message { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - color: var( - --yj-text-secondary, - #b3b3b3 - ); - font-size: 14px; - } `; override connectedCallback() { @@ -185,26 +173,76 @@ export class ArtistDetails extends LitElement { private async loadAlbums() { if (!this.artistId) return; - try { - this.loading = true; + // Try to populate instantly from the + // cached all-albums list if available. + const cached = + this.libraryCtrl.getAlbumsByArtistNameCached( + this.artistName, + ); + if (cached !== null && cached.length > 0) { + this.albums = cached; + this.loading = false; + } + + // Always run the authoritative backend + // query. If we got a cache hit above, + // this serves as a correction pass. + try { const albums = await this.libraryCtrl.getAlbumsByArtist( this.artistId, ); - this.albums = albums ?? []; + const result = albums ?? []; + + // Skip update if the cached result + // is identical (same IDs in same + // order) to avoid a re-render. + if (!this.albumsMatch(result)) { + this.albums = result; + } } catch (error) { console.error( 'Error loading artist albums:', error, ); - this.albums = []; + + // Only overwrite if we had no cached + // result to fall back on. + if (cached === null) { + this.albums = []; + } } finally { this.loading = false; } } + /** + * Compare two album lists by ID to avoid + * unnecessary re-renders when the backend + * result matches the cached approximation. + */ + private albumsMatch( + incoming: library.Album[], + ): boolean { + const current = this.albums; + + if (current.length !== incoming.length) { + return false; + } + + for (let i = 0; i < current.length; i++) { + if ( + current[i]!.ID !== incoming[i]!.ID + ) { + return false; + } + } + + return true; + } + /* ================================================================ * Navigation * ================================================================ */ @@ -277,20 +315,9 @@ export class ArtistDetails extends LitElement {
- ${this.loading - ? html` -
- Loading albums... -
- ` - : html` - - `} +
`; } diff --git a/frontend/src/store/controllers/library-controller.ts b/frontend/src/store/controllers/library-controller.ts index d727cd1..5fa52bf 100644 --- a/frontend/src/store/controllers/library-controller.ts +++ b/frontend/src/store/controllers/library-controller.ts @@ -61,6 +61,14 @@ export class LibraryController implements ReactiveController { return libraryStore.getAlbumsByArtist(artistID); } + getAlbumsByArtistNameCached( + artistName: string, + ): library.Album[] | null { + return libraryStore.getAlbumsByArtistNameCached( + artistName, + ); + } + get cachedTracks(): library.Track[] | null { return libraryStore.getCachedTracks(); } diff --git a/frontend/src/store/library-store.ts b/frontend/src/store/library-store.ts index eeb1440..0ee7d7a 100644 --- a/frontend/src/store/library-store.ts +++ b/frontend/src/store/library-store.ts @@ -130,6 +130,24 @@ class LibraryStore { return GetAlbumsByArtist(artistID); } + /** + * Returns albums filtered by artist name from + * the in-memory cache, or null if the cache is + * not populated. This provides an instant + * result when the all-albums list has already + * been loaded (e.g. the user visited the albums + * view first). + */ + getAlbumsByArtistNameCached( + artistName: string, + ): library.Album[] | null { + if (this.albums === null) return null; + + return this.albums.filter( + (a) => a.ArtistName === artistName, + ); + } + // =================================================================== // STATE ACCESSORS // Synchronous access for controllers that need current cached values.