From 9e6e81bc2204cd129fcc4bb213b99d5c5c95911b Mon Sep 17 00:00:00 2001 From: Honey Date: Mon, 8 Jun 2026 23:07:02 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20auto-populate=20artist=20sort=20map=20on?= =?UTF-8?q?=20album=20page=20=E2=80=94=20no=20Artists=20page=20visit=20nee?= =?UTF-8?q?ded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensureArtistSortMap() fetches getArtists once per session (cached) and populates _artistSortMap automatically. Called at start of loadAlbumDetail when showRomaji is on, so artist sort names are always available. Co-Authored-By: Claude Sonnet 4.6 --- src/api.js | 18 +++++++++++++++++- src/views/detail.js | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/api.js b/src/api.js index 00022b7..cd9a51e 100644 --- a/src/api.js +++ b/src/api.js @@ -95,6 +95,7 @@ let _artistRomajiMap = null; // Map — kuromoji backend let _artistSortMap = null; // Map — Navidrome sort tag (preferred) let _albumRomajiMap = null; // Map — for album cards without song ID let _romajiIndexFetch = null; // in-flight promise dedup +let _artistSortFetch = null; // in-flight dedup for ensureArtistSortMap function _buildRomajiMap(idx) { const m = new Map(); @@ -122,7 +123,22 @@ export async function fetchRomajiIndex() { return _romajiIndexFetch; } -export function invalidateRomajiIndex() { _romajiIndex = null; _romajiMap = null; _artistRomajiMap = null; _artistSortMap = null; _albumRomajiMap = null; } +export function invalidateRomajiIndex() { _romajiIndex = null; _romajiMap = null; _artistRomajiMap = null; _artistSortMap = null; _albumRomajiMap = null; _artistSortFetch = null; } + +// Ensures _artistSortMap is populated from getArtists (cached — one network call per session). +// Safe to call from anywhere; deduplicates concurrent calls automatically. +export async function ensureArtistSortMap() { + if (_artistSortMap) return; + if (_artistSortFetch) return _artistSortFetch; + _artistSortFetch = apiCached('getArtists') + .then(r => { + const all = (r?.artists?.index || []).flatMap(i => i.artist || []); + cacheArtistSortNames(all); + _artistSortFetch = null; + }) + .catch(() => { _artistSortFetch = null; }); + return _artistSortFetch; +} // Populate _artistSortMap from Navidrome artist objects (OpenSubsonic sortName field). // Called whenever getArtists or getArtist data is loaded so dispArtistById can use it. diff --git a/src/views/detail.js b/src/views/detail.js index ed5a2fa..a2b55cd 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -1,6 +1,6 @@ import { S } from '../state.js'; import { esc, toast } from '../utils.js'; -import { api, coverUrl, itunesSearch, itunesLookupAlbum, itunesLookupArtistSongs, itunesLookupArtistAlbums, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames } from '../api.js'; +import { api, coverUrl, itunesSearch, itunesLookupAlbum, itunesLookupArtistSongs, itunesLookupArtistAlbums, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames, ensureArtistSortMap } from '../api.js'; import { makeCard, makeAlbumCard, makeGlobalTrackItem, makeSongItem } from '../ui/components.js'; import { playQueue, dlSongLocal, toggleStar, parseArtists } from '../player/player.js'; import { showCtxMenu, openDlModal } from '../ui/ui.js'; @@ -378,6 +378,7 @@ export async function loadItunesAlbumDetail(album) { // ─── ALBUM DETAIL ─── export async function loadAlbumDetail(album) { + if (S.cfg.showRomaji) await ensureArtistSortMap().catch(() => {}); document.getElementById('album-back-btn').onclick = () => album._fromArtist ? navigate('artist-detail', album._fromArtist) : navigate('albums'); document.getElementById('album-detail-name').textContent = dispAlbumName(album);