diff --git a/images/unknown.svg b/images/unknown.svg index c13340a..6d7401c 100644 --- a/images/unknown.svg +++ b/images/unknown.svg @@ -1,5 +1,4 @@ - diff --git a/index.css b/index.css index 30f9902..5a06bfa 100644 --- a/index.css +++ b/index.css @@ -99,7 +99,8 @@ body.loading #requestOverlay { #requestList { margin: 0; - padding-left: 2.2rem; + padding: 0; + list-style: none; font-size: 1.5rem; line-height: 1.25; } @@ -122,7 +123,20 @@ body.loading #requestOverlay { } .request-item { - display: list-item; + display: flex; + align-items: center; + gap: 0.5rem; + flex-wrap: wrap; + min-width: 0; +} + +.request-cover { + width: 2em; + height: 2em; + flex-shrink: 0; + object-fit: cover; + border-radius: 0.15em; + vertical-align: middle; } .request-title { diff --git a/index.html b/index.html index ddee349..ab02353 100644 --- a/index.html +++ b/index.html @@ -46,7 +46,7 @@
Song requests
-
    +
    No pending requests
    diff --git a/index.js b/index.js index 555c477..0c2734a 100644 --- a/index.js +++ b/index.js @@ -680,7 +680,7 @@ var debugBsrExampleIndex = 0; var requestListEl = must("requestList"); var requestOverlayEl = must("requestOverlay"); var requestEmptyEl = must("requestEmpty"); -var requestTitleCache = /* @__PURE__ */ new Map(); +var requestBeatSaverCache = /* @__PURE__ */ new Map(); var requestTitleMisses = /* @__PURE__ */ new Set(); function loadChatRequestJson() { const base = new URL("ChatRequest.json", location.href); @@ -700,10 +700,12 @@ function requesterLine(item) { ].filter(Boolean); return parts.length ? parts.join(" ") : item.rqn || ""; } -async function enrichRequestTitle(key, titleEl) { +async function enrichRequestFromBeatSaver(key, titleEl, coverEl) { if (requestTitleMisses.has(key)) return; - if (requestTitleCache.has(key)) { - titleEl.textContent = requestTitleCache.get(key) ?? ""; + const cached = requestBeatSaverCache.get(key); + if (cached) { + titleEl.textContent = cached.title; + coverEl.src = cached.coverUrl || "images/unknown.svg"; return; } try { @@ -713,12 +715,19 @@ async function enrichRequestTitle(key, titleEl) { return; } const name = map.metadata?.songName ?? map.name; - if (name && typeof name === "string") { - requestTitleCache.set(key, name); - titleEl.textContent = name; + const title2 = name && typeof name === "string" ? name : ""; + if (!title2) { + requestTitleMisses.add(key); return; } - requestTitleMisses.add(key); + const rawCover = map.versions?.[0]?.coverURL?.trim(); + const coverUrl = rawCover && /^https?:\/\//i.test(rawCover) ? rawCover : ""; + requestBeatSaverCache.set(key, { + title: title2, + coverUrl + }); + titleEl.textContent = title2; + if (coverUrl) coverEl.src = coverUrl; } catch { requestTitleMisses.add(key); } @@ -729,6 +738,12 @@ function renderRequestList(items) { for (const item of items) { const li = document.createElement("li"); li.className = "request-item"; + const coverEl = document.createElement("img"); + coverEl.className = "request-cover"; + coverEl.src = "images/unknown.svg"; + coverEl.alt = ""; + coverEl.decoding = "async"; + li.appendChild(coverEl); const titleEl = document.createElement("span"); titleEl.className = "request-title"; titleEl.textContent = `!bsr ${item.key}`; @@ -741,7 +756,7 @@ function renderRequestList(items) { li.appendChild(meta); } requestListEl.appendChild(li); - void enrichRequestTitle(item.key, titleEl); + void enrichRequestFromBeatSaver(item.key, titleEl, coverEl); } } async function loadRequestQueue() { diff --git a/src/client/index.ts b/src/client/index.ts index 8b6f8fb..2a17775 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -567,10 +567,10 @@ const DEBUG_BSR_EXAMPLE_IDS = [ ] as const; let debugBsrExampleIndex = 0; -const requestListEl = must("requestList"); +const requestListEl = must("requestList"); const requestOverlayEl = must("requestOverlay"); const requestEmptyEl = must("requestEmpty"); -const requestTitleCache = new Map(); +const requestBeatSaverCache = new Map(); const requestTitleMisses = new Set(); function loadChatRequestJson() { @@ -588,10 +588,12 @@ function requesterLine(item: ChatRequestEntry) { return parts.length ? parts.join(" ") : item.rqn || ""; } -async function enrichRequestTitle(key: string, titleEl: HTMLElement) { +async function enrichRequestFromBeatSaver(key: string, titleEl: HTMLElement, coverEl: HTMLImageElement) { if (requestTitleMisses.has(key)) return; - if (requestTitleCache.has(key)) { - titleEl.textContent = requestTitleCache.get(key) ?? ""; + const cached = requestBeatSaverCache.get(key); + if (cached) { + titleEl.textContent = cached.title; + coverEl.src = cached.coverUrl || "images/unknown.svg"; return; } try { @@ -601,12 +603,16 @@ async function enrichRequestTitle(key: string, titleEl: HTMLElement) { return; } const name = map.metadata?.songName ?? map.name; - if (name && typeof name === "string") { - requestTitleCache.set(key, name); - titleEl.textContent = name; + const title = name && typeof name === "string" ? name : ""; + if (!title) { + requestTitleMisses.add(key); return; } - requestTitleMisses.add(key); + const rawCover = map.versions?.[0]?.coverURL?.trim(); + const coverUrl = rawCover && /^https?:\/\//i.test(rawCover) ? rawCover : ""; + requestBeatSaverCache.set(key, { title, coverUrl }); + titleEl.textContent = title; + if (coverUrl) coverEl.src = coverUrl; } catch { requestTitleMisses.add(key); } @@ -618,6 +624,12 @@ function renderRequestList(items: ChatRequestEntry[]) { for (const item of items) { const li = document.createElement("li"); li.className = "request-item"; + const coverEl = document.createElement("img"); + coverEl.className = "request-cover"; + coverEl.src = "images/unknown.svg"; + coverEl.alt = ""; + coverEl.decoding = "async"; + li.appendChild(coverEl); const titleEl = document.createElement("span"); titleEl.className = "request-title"; titleEl.textContent = `!bsr ${item.key}`; @@ -630,7 +642,7 @@ function renderRequestList(items: ChatRequestEntry[]) { li.appendChild(meta); } requestListEl.appendChild(li); - void enrichRequestTitle(item.key, titleEl); + void enrichRequestFromBeatSaver(item.key, titleEl, coverEl); } }