fix: playlist panel UX issues

- Phantom playlists: stop pushing locally on create; let PLAYLIST_CREATED
  STOMP event add the entry to avoid the REST/STOMP race that doubled items
- Row click: attach toggle-expand listener on the li instead of the name
  span only; add cursor:pointer to .playlist-list-item
- Input styling: apply surface-3 bg, border, radius, and color to
  .playlist-create-row input and .playlist-rename-form input with focus ring
- Play/pause error toast: apiFetch now skips json() when response has no
  application/json Content-Type (empty 200 body from play/pause endpoints)
This commit is contained in:
2026-05-01 11:02:46 +02:00
parent cc478f99dd
commit 648dbd9ff7
4 changed files with 23 additions and 5 deletions
+17
View File
@@ -1069,6 +1069,14 @@ button:disabled:hover {
font-size: 12px; font-size: 12px;
padding: 5px 8px; padding: 5px 8px;
min-width: 0; min-width: 0;
background: var(--color-surface-3);
border: 1px solid var(--color-border);
border-radius: 6px;
color: var(--color-text);
outline: none;
}
.playlist-create-row input:focus {
border-color: var(--color-accent-border);
} }
.playlist-list { .playlist-list {
@@ -1095,6 +1103,7 @@ button:disabled:hover {
border-radius: 8px; border-radius: 8px;
border: 1px solid transparent; border: 1px solid transparent;
transition: background 0.12s; transition: background 0.12s;
cursor: pointer;
} }
.playlist-list-item:hover { .playlist-list-item:hover {
@@ -1171,6 +1180,14 @@ button:disabled:hover {
font-size: 12px; font-size: 12px;
padding: 4px 7px; padding: 4px 7px;
min-width: 0; min-width: 0;
background: var(--color-surface-3);
border: 1px solid var(--color-border);
border-radius: 6px;
color: var(--color-text);
outline: none;
}
.playlist-rename-form input:focus {
border-color: var(--color-accent-border);
} }
/* Playback controls */ /* Playback controls */
+6 -5
View File
@@ -62,7 +62,8 @@
if (csrfToken && csrfHeader) headers[csrfHeader] = csrfToken; if (csrfToken && csrfHeader) headers[csrfHeader] = csrfToken;
const response = await fetch(`${apiBase()}${path}`, { ...options, headers }); const response = await fetch(`${apiBase()}${path}`, { ...options, headers });
if (!response.ok) throw new Error(`Request failed: ${response.status}`); if (!response.ok) throw new Error(`Request failed: ${response.status}`);
if (response.status === 204) return null; const ct = response.headers.get("content-type") || "";
if (response.status === 204 || !ct.includes("application/json")) return null;
return response.json(); return response.json();
} }
@@ -160,10 +161,10 @@
if (!name) { showToast("Enter a playlist name.", "info"); return; } if (!name) { showToast("Enter a playlist name.", "info"); return; }
try { try {
const view = await apiFetch("", { method: "POST", body: JSON.stringify({ name }) }); const view = await apiFetch("", { method: "POST", body: JSON.stringify({ name }) });
playlists.push(view);
input.value = ""; input.value = "";
renderPlaylistList(); // Don't push locally — the PLAYLIST_CREATED STOMP event will add it.
expandPlaylist(view.id); // Just pre-set the expanded id so it opens as soon as the event renders it.
if (view?.id) expandedPlaylistId = view.id;
} catch { } catch {
showToast("Could not create playlist.", "error"); showToast("Could not create playlist.", "error");
} }
@@ -186,11 +187,11 @@
const li = document.createElement("li"); const li = document.createElement("li");
li.className = "playlist-list-item" + (p.id === expandedPlaylistId ? " expanded" : "") + (p.id === activePlaylistId ? " active" : ""); li.className = "playlist-list-item" + (p.id === expandedPlaylistId ? " expanded" : "") + (p.id === activePlaylistId ? " active" : "");
li.dataset.id = p.id; li.dataset.id = p.id;
li.addEventListener("click", () => toggleExpand(p.id));
const nameSpan = document.createElement("span"); const nameSpan = document.createElement("span");
nameSpan.className = "playlist-list-name"; nameSpan.className = "playlist-list-name";
nameSpan.textContent = p.name; nameSpan.textContent = p.name;
nameSpan.addEventListener("click", () => toggleExpand(p.id));
const actions = document.createElement("div"); const actions = document.createElement("div");
actions.className = "playlist-list-actions"; actions.className = "playlist-list-actions";