fix(web): Zurück-Knopf tat oft nichts + Besitzer geteilter Playlists (2.6)

navigate() ließ den Stack-Eintrag weg, wenn sich die Ansicht nicht
geändert hatte, rief history.pushState aber trotzdem. Zweimal auf
denselben Tab geklickt = zwei Browser-Einträge auf dieselbe Ansicht,
jeder davon ein Zurück-Druck ohne sichtbare Wirkung. Im Test: 3x "Alben"
-> 3 Browser-Einträge bei 1 Stack-Eintrag, danach bewegten 4 Zurück-
Drücke die Ansicht einmal. Jetzt pusht nur eine wirklich neue Ansicht,
sonst replaceState. Vorwärts mitgetestet, unverändert.

Playlists: Navidrome liefert owner mit, das Web nutzte es nie. Übersicht
und Detail zeigen "· von <Name>", nur bei fremden Listen. Android zeigte
ihn bisher bei jeder Liste und unbeschriftet; beide Plattformen jetzt
gleich, Bibliotheks-Übersicht ebenfalls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-09-12 14:12:07 +02:00
1 parent ea448e109f
commit 568bb5002c
8 files changed
+85 -14

No files matched your search

+19 -4
View File
@@ -8,6 +8,15 @@ import { openEditPlModal } from '../ui/ui.js';
import { navigate } from './navigation.js';
import { quickPlayPlaylist } from './detail.js';
// Besitzer einer Playlist, sofern es nicht die eigene ist. Navidrome liefert `owner` und `public`
// in getPlaylists/getPlaylist mit; interessant ist die Angabe nur bei geteilten (oeffentlichen)
// Listen — bei den eigenen waere "von mir" nur Rauschen.
function _besitzer(pl) {
const o = pl?.owner;
if (!o || o === S.cfg.user) return '';
return o;
}
// ─── PLAYLISTS ───
export async function loadPlaylists() {
const g = document.getElementById('playlists-grid');
@@ -19,7 +28,9 @@ export async function loadPlaylists() {
if(!lists.length) { g.innerHTML = '<div class="empty-state"><div class="icon">🎶</div><p>Keine Playlists vorhanden</p></div>'; return; }
lists.forEach(pl => {
const cv = coverUrl(pl.coverArt, 512);
const c = makeCard(pl.name, pl.songCount+' Titel', cv, '🎶');
const von = _besitzer(pl);
const c = makeCard(pl.name, pl.songCount+' Titel' + (von ? ' · von '+von : ''), cv, '🎶');
c.dataset.plOwner = von;
c.dataset.plId = pl.id;
c.querySelector('.card-art').onclick = (e) => { e.stopPropagation(); quickPlayPlaylist(pl); };
c.querySelector('.card-info').onclick = () => navigate('playlist-detail', pl);
@@ -29,7 +40,8 @@ export async function loadPlaylists() {
api('getPlaylist', `&id=${pl.id}`).then(r => {
const count = (r.playlist?.entry || []).length;
const card = g.querySelector(`[data-pl-id="${pl.id}"]`);
if (card) card.querySelector('.card-sub').textContent = count + ' Titel';
if (card) card.querySelector('.card-sub').textContent =
count + ' Titel' + (card.dataset.plOwner ? ' · von ' + card.dataset.plOwner : '');
}).catch(() => {});
});
} catch(e) { g.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
@@ -50,9 +62,12 @@ export async function loadPlaylistDetail(pl) {
S._currentPlaylist = {...pl, ...full, id: pl.id};
const songs = full.entry || [];
const totalMin = Math.round(songs.reduce((s,t) => s+(t.duration||0), 0) / 60);
document.getElementById('playlist-detail-stats').textContent = `${songs.length} Titel · ${totalMin} Min.`;
const von = _besitzer({ ...pl, ...full });
document.getElementById('playlist-detail-stats').textContent =
`${songs.length} Titel · ${totalMin} Min.` + (von ? ` · von ${von}` : '');
const overviewCard = document.querySelector(`#playlists-grid [data-pl-id="${pl.id}"]`);
if (overviewCard) overviewCard.querySelector('.card-sub').textContent = songs.length + ' Titel';
if (overviewCard) overviewCard.querySelector('.card-sub').textContent =
songs.length + ' Titel' + (von ? ' · von ' + von : '');
const descEl = document.getElementById('playlist-detail-desc');
if(descEl) { descEl.textContent = full.comment||''; descEl.style.display = full.comment ? '' : 'none'; }
document.getElementById('btn-play-playlist').onclick = () => playQueue(songs, 0);