feat: add navigation button to notifications for direct song/album/playlist access
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d9b9151f50
commit
1bb8494fdb
3 files changed
+46
-8
No files matched your search
@@ -273,7 +273,7 @@ export async function executeDownload() {
|
||||
collectionId: S.downloadTarget.collectionId || null,
|
||||
explicit: S.downloadTarget.trackExplicitness === 'explicit',
|
||||
};
|
||||
addJob(jobId, payload.title, payload.artist);
|
||||
addJob(jobId, payload.title, payload.artist, payload.type);
|
||||
try {
|
||||
const res = await fetch(`${S.cfg.backendUrl}/api/download`, {
|
||||
method: 'POST', headers: {'Content-Type':'application/json'},
|
||||
@@ -291,7 +291,7 @@ export async function executeDownload() {
|
||||
export async function executeSpotifyDownload({ type, title, artist, artworkUrl, tracks, createPlaylist, playlistName }) {
|
||||
if (!S.cfg.backendUrl) { toast('⚠ Kein Backend konfiguriert'); return; }
|
||||
const jobId = 'job_' + Date.now();
|
||||
addJob(jobId, title, artist);
|
||||
addJob(jobId, title, artist, type);
|
||||
try {
|
||||
const res = await fetch(`${S.cfg.backendUrl}/api/download`, {
|
||||
method: 'POST',
|
||||
@@ -344,10 +344,11 @@ async function _createNdPlaylist(tracks, name) {
|
||||
return;
|
||||
}
|
||||
const songParams = songIds.map(id => `&songId=${encodeURIComponent(id)}`).join('');
|
||||
await api('createPlaylist', `&name=${encodeURIComponent(name)}${songParams}`);
|
||||
const plRes = await api('createPlaylist', `&name=${encodeURIComponent(name)}${songParams}`);
|
||||
const plId = plRes.playlist?.id || null;
|
||||
const msg = `${songIds.length}/${tracks.length} Tracks hinzugefügt${missing.length ? `, ${missing.length} nicht gefunden` : ''}`;
|
||||
toast(`✅ Playlist "${name}" mit ${songIds.length}/${tracks.length} Tracks erstellt`);
|
||||
_addNotif({ id: 'pl_' + Date.now(), title: `Playlist: ${name}`, artist: '', status: 'done', message: msg, missing, timestamp: Date.now(), read: false });
|
||||
_addNotif({ id: 'pl_' + Date.now(), title: `Playlist: ${name}`, artist: '', status: 'done', message: msg, missing, timestamp: Date.now(), read: false, navType: 'playlist', navId: plId, navName: name });
|
||||
} catch(e) {
|
||||
toast('Playlist-Erstellung fehlgeschlagen: ' + e.message);
|
||||
}
|
||||
@@ -425,6 +426,10 @@ function _renderNotifList() {
|
||||
const missingHtml = n.missing?.length
|
||||
? `<details class="notif-failed"><summary>${n.missing.length} Track${n.missing.length > 1 ? 's' : ''} nicht in Navidrome gefunden</summary><ul>${n.missing.map(f => `<li>${esc(f)}</li>`).join('')}</ul></details>`
|
||||
: '';
|
||||
const hasNav = n.status === 'done' && ((n.navType === 'playlist' && n.navId) || n.navArtist);
|
||||
const navBtn = hasNav
|
||||
? `<button class="notif-nav-btn" onclick="_notifNavigate('${esc(n.id)}')">Öffnen →</button>`
|
||||
: '';
|
||||
return `<div class="notif-card${n.read ? '' : ' unread'}">
|
||||
<div class="notif-card-top">
|
||||
<span class="notif-icon">${icon}</span>
|
||||
@@ -437,18 +442,46 @@ function _renderNotifList() {
|
||||
${n.message ? `<div class="notif-msg">${esc(n.message)}</div>` : ''}
|
||||
${failedHtml}
|
||||
${missingHtml}
|
||||
${navBtn}
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
export async function _notifNavigate(notifId) {
|
||||
const notifs = _loadNotifs();
|
||||
const n = notifs.find(x => x.id === notifId);
|
||||
if (!n) return;
|
||||
// close panel first
|
||||
const panel = document.getElementById('notif-panel');
|
||||
const overlay = document.getElementById('notif-overlay');
|
||||
panel?.classList.remove('open');
|
||||
overlay?.classList.remove('active');
|
||||
|
||||
if (n.navType === 'playlist' && n.navId) {
|
||||
navigate('playlist-detail', { id: n.navId, name: n.navName || n.title.replace(/^Playlist:\s*/, '') });
|
||||
return;
|
||||
}
|
||||
|
||||
if (n.navArtist) {
|
||||
try {
|
||||
const norm = s => s?.toLowerCase().trim() || '';
|
||||
const r = await api('search3', `&query=${encodeURIComponent(n.navArtist)}&artistCount=5&albumCount=0&songCount=0`);
|
||||
const artists = r.searchResult3?.artist || [];
|
||||
const match = artists.find(a => norm(a.name) === norm(n.navArtist)) || artists[0];
|
||||
if (match) { navigate('artist-detail', { id: match.id, name: match.name }); return; }
|
||||
} catch {}
|
||||
toast('Künstler nicht gefunden — Library-Scan nötig?');
|
||||
}
|
||||
}
|
||||
|
||||
function _parseFailedTracks(log) {
|
||||
const re = /Fehler bei "([^"]+)"/;
|
||||
return (log || []).map(line => re.exec(line)?.[1]).filter(Boolean);
|
||||
}
|
||||
|
||||
// ─── JOBS ───
|
||||
function addJob(id, title, artist) {
|
||||
S.jobs[id] = {title, artist, progress:0, status:'pending'};
|
||||
function addJob(id, title, artist, type) {
|
||||
S.jobs[id] = {title, artist, type: type || '', progress:0, status:'pending'};
|
||||
renderJobs();
|
||||
}
|
||||
function updateJob(id, status, msg, progress) {
|
||||
@@ -494,6 +527,9 @@ async function pollJob(jobId) {
|
||||
failed: _parseFailedTracks(d.log),
|
||||
timestamp: Date.now(),
|
||||
read: false,
|
||||
navType: job?.type || '',
|
||||
navArtist: job?.artist || '',
|
||||
navTitle: title,
|
||||
});
|
||||
if (d.status === 'done') {
|
||||
toast(d.skipped ? `⏭ "${title}" bereits auf dem Server` : `✅ ${title} gespeichert!`);
|
||||
|
||||
Reference in new issue
Block a user