fix: radio mode appends songs without restarting; favorites/song row mobile fixes
- fillRadioQueue: use addToQueue instead of setQueue so toggling radio mode no longer resets position to 0 on the current song - loadStarred: pass noArtistLink/noAlbumLink to prevent accidental navigation taps on mobile (matches playlist behavior) - styles.css: add pointer:coarse alongside hover:none so song-actions are visible on Android WebView which may report hover:hover - components.js: remove inline download button from song row; keep only star and 3-dots (download stays accessible via context menu) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
1b7c05f66d
commit
e893b7cc12
4 files changed
+9
-8
No files matched your search
+2
-4
@@ -2,7 +2,7 @@ import { S } from './state.js';
|
|||||||
import { esc, formatDur } from './utils.js';
|
import { esc, formatDur } from './utils.js';
|
||||||
import { coverUrl } from './api.js';
|
import { coverUrl } from './api.js';
|
||||||
// Circular imports — safe (only used inside event handlers after module init)
|
// Circular imports — safe (only used inside event handlers after module init)
|
||||||
import { playQueue, dlSongLocal, toggleStar, playPreview } from './player.js';
|
import { playQueue, toggleStar, playPreview } from './player.js';
|
||||||
import { showCtxMenu, openDlModal } from './ui.js';
|
import { showCtxMenu, openDlModal } from './ui.js';
|
||||||
import { navigate, quickPlayAlbum, quickPlayArtist } from './views.js';
|
import { navigate, quickPlayAlbum, quickPlayArtist } from './views.js';
|
||||||
|
|
||||||
@@ -54,14 +54,12 @@ export function makeSongItem(s, idx, list, { noArtistLink = false, noAlbumLink =
|
|||||||
<span class="song-dur">${formatDur(s.duration)}</span>
|
<span class="song-dur">${formatDur(s.duration)}</span>
|
||||||
<div class="song-actions">
|
<div class="song-actions">
|
||||||
<button class="icon-btn${s._starred?' starred':''}" title="Favorit"><svg width="12" height="12" viewBox="0 0 24 24" fill="${s._starred?'currentColor':'none'}" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg></button>
|
<button class="icon-btn${s._starred?' starred':''}" title="Favorit"><svg width="12" height="12" viewBox="0 0 24 24" fill="${s._starred?'currentColor':'none'}" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg></button>
|
||||||
<button class="icon-btn" title="Download"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button>
|
|
||||||
<button class="icon-btn song-menu-btn" title="Mehr Optionen"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/></svg></button>
|
<button class="icon-btn song-menu-btn" title="Mehr Optionen"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/></svg></button>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
const btns = el.querySelectorAll('.icon-btn');
|
const btns = el.querySelectorAll('.icon-btn');
|
||||||
btns[0].onclick = (e) => { e.stopPropagation(); toggleStar(s, btns[0]); };
|
btns[0].onclick = (e) => { e.stopPropagation(); toggleStar(s, btns[0]); };
|
||||||
btns[1].onclick = (e) => { e.stopPropagation(); dlSongLocal(s); };
|
btns[1].onclick = (e) => { e.stopPropagation(); showCtxMenu(e, s, list, idx); };
|
||||||
btns[2].onclick = (e) => { e.stopPropagation(); showCtxMenu(e, s, list, idx); };
|
|
||||||
el.onclick = () => playQueue(list, idx);
|
el.onclick = () => playQueue(list, idx);
|
||||||
el.addEventListener('contextmenu', e => { e.preventDefault(); showCtxMenu(e, s, list, idx); });
|
el.addEventListener('contextmenu', e => { e.preventDefault(); showCtxMenu(e, s, list, idx); });
|
||||||
return el;
|
return el;
|
||||||
|
|||||||
+5
-2
@@ -595,9 +595,12 @@ export async function fillRadioQueue() {
|
|||||||
if(!S.radioMode) return;
|
if(!S.radioMode) return;
|
||||||
try {
|
try {
|
||||||
const r = await api('getRandomSongs', '&size=20');
|
const r = await api('getRandomSongs', '&size=20');
|
||||||
S.queue.push(...(r.randomSongs?.song || []));
|
const newSongs = r.randomSongs?.song || [];
|
||||||
|
const prevLen = S.queue.length;
|
||||||
|
S.queue.push(...newSongs);
|
||||||
renderQueue();
|
renderQueue();
|
||||||
_pushQueueIfBridge();
|
// Append-only: never call setQueue here (that resets position to 0).
|
||||||
|
S.queue.slice(prevLen).forEach(s => _nsBridge('addToQueue', JSON.stringify(_trackToNative(s)), -1));
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -669,7 +669,7 @@ export async function loadStarred(sort = _starredSort) {
|
|||||||
else if (sort === 'plays') songs.sort((a, b) => (b.playCount||0) - (a.playCount||0));
|
else if (sort === 'plays') songs.sort((a, b) => (b.playCount||0) - (a.playCount||0));
|
||||||
el.innerHTML = '';
|
el.innerHTML = '';
|
||||||
if(!songs.length) { el.innerHTML = '<div class="empty-state"><div class="icon">⭐</div><p>Noch keine Favoriten</p></div>'; return; }
|
if(!songs.length) { el.innerHTML = '<div class="empty-state"><div class="icon">⭐</div><p>Noch keine Favoriten</p></div>'; return; }
|
||||||
songs.forEach((s,i) => el.appendChild(makeSongItem(s, i, songs)));
|
songs.forEach((s,i) => el.appendChild(makeSongItem(s, i, songs, { noArtistLink: true, noAlbumLink: true })));
|
||||||
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -189,7 +189,7 @@ nav{flex:1;overflow-y:auto;padding:4px 10px}
|
|||||||
.song-dur{font-size:11px;color:var(--text3);font-family:var(--mono)}
|
.song-dur{font-size:11px;color:var(--text3);font-family:var(--mono)}
|
||||||
.song-actions{display:flex;gap:2px;opacity:0;transition:opacity .15s}
|
.song-actions{display:flex;gap:2px;opacity:0;transition:opacity .15s}
|
||||||
.song-item:hover .song-actions{opacity:1}
|
.song-item:hover .song-actions{opacity:1}
|
||||||
@media (hover:none){.song-actions{opacity:1}}
|
@media (hover:none),(pointer:coarse){.song-actions{opacity:1}}
|
||||||
/* ── PLAYLIST DRAG & DROP ── */
|
/* ── PLAYLIST DRAG & DROP ── */
|
||||||
.song-list.pl-draggable .song-item{grid-template-columns:18px 32px 44px 1fr auto}
|
.song-list.pl-draggable .song-item{grid-template-columns:18px 32px 44px 1fr auto}
|
||||||
.pl-drag-handle{display:flex;align-items:center;justify-content:center;color:var(--text3);opacity:0;cursor:grab;transition:opacity .15s;touch-action:none}
|
.pl-drag-handle{display:flex;align-items:center;justify-content:center;color:var(--text3);opacity:0;cursor:grab;transition:opacity .15s;touch-action:none}
|
||||||
|
|||||||
Reference in new issue
Block a user