fix(artist pin): esc() crashed on numeric Deezer id; move pin button to banner top-right

- esc() now coerces to string first (String(s ?? '')), so passing a number (e.g. the
  Deezer artist id in the pin picker) no longer throws 'replace is not a function'.
- The '⚙ Falscher Künstler?' button moved from under the fan count to the top-right
  of the artist banner (absolute, on the hero). SW cache -> ns-v75.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-13 00:16:14 +02:00
1 parent 02960cf886
commit 5f251d555f
3 files changed
+10 -11

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'ns-v74'; const CACHE = 'ns-v75';
const PRECACHE = ['/', 'index.html', 'manifest.json', 'icon.svg']; const PRECACHE = ['/', 'index.html', 'manifest.json', 'icon.svg'];
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js']; const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
// Auth token params stripped from cache keys so artwork survives across sessions/token refreshes. // Auth token params stripped from cache keys so artwork survives across sessions/token refreshes.
+3 -1
View File
@@ -36,7 +36,9 @@ export function md5(input) {
} }
export function esc(s) { export function esc(s) {
return (s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); // Coerce to string first — callers sometimes pass numbers (e.g. a Deezer artist id), which
// would throw on .replace. null/undefined → ''.
return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
} }
export function formatDur(s) { export function formatDur(s) {
+6 -9
View File
@@ -94,19 +94,16 @@ export async function loadArtistDetail(artist) {
const knownFans = _pin?.fanCount ?? artist.deezerFanCount ?? null; const knownFans = _pin?.fanCount ?? artist.deezerFanCount ?? null;
if (knownFans) _showFans(knownFans); // from the global-search card / pin — show immediately if (knownFans) _showFans(knownFans); // from the global-search card / pin — show immediately
// "Falscher Künstler?" pin button (local artists only) — fix a wrong same-name Deezer match. // "Falscher Künstler?" pin button (local artists only) — top-right of the banner.
if (hasND) { if (hasND && hero) {
const infoEl = hero.querySelector('.artist-hero-info'); hero.querySelector('.artist-pin-btn')?.remove();
infoEl?.querySelector('.artist-pin-btn')?.remove();
if (infoEl) {
const pinBtn = document.createElement('button'); const pinBtn = document.createElement('button');
pinBtn.className = 'artist-pin-btn'; pinBtn.className = 'artist-pin-btn';
pinBtn.textContent = _pin ? '⚙ Künstler gepinnt' : '⚙ Falscher Künstler?'; pinBtn.textContent = _pin ? '⚙ Gepinnt' : '⚙ Falscher Künstler?';
pinBtn.title = 'Richtigen Deezer-Künstler wählen'; pinBtn.title = 'Richtigen Deezer-Künstler wählen';
pinBtn.style.cssText = 'margin-top:8px;padding:5px 12px;border-radius:999px;border:1px solid var(--border2);background:rgba(0,0,0,.35);color:#fff;font-family:var(--font);font-size:12px;font-weight:600;cursor:pointer'; pinBtn.style.cssText = 'position:absolute;top:12px;right:12px;z-index:5;padding:5px 12px;border-radius:999px;border:1px solid rgba(255,255,255,.25);background:rgba(0,0,0,.45);color:#fff;font-family:var(--font);font-size:12px;font-weight:600;cursor:pointer;backdrop-filter:blur(4px)';
pinBtn.onclick = () => openArtistPin(artist.id, artist.name); pinBtn.onclick = () => openArtistPin(artist.id, artist.name);
infoEl.appendChild(pinBtn); hero.appendChild(pinBtn);
}
} }
// Fire all requests immediately so they run in parallel. // Fire all requests immediately so they run in parallel.