feat(web+android): search play queues only the tapped song (radio fills the rest)
Playing a song from search results queued the whole result list. Now it plays only the clicked song; Radio mode, if on, fills the rest — closer to Spotify. Both platforms already trigger a radio top-up on the first track (web playQueue → fillRadioQueue; Android onMediaItemTransition → checkRadioRefill), so no extra wiring is needed. Album/playlist play still queues the full list. Bump SW cache to ns-v44. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
14fd5119f2
commit
1e242796fe
5 files changed
+9
-7
No files matched your search
@@ -343,7 +343,8 @@ private fun LibrarySearchContent(
|
|||||||
coverUrl = coverUrl(song.coverArt),
|
coverUrl = coverUrl(song.coverArt),
|
||||||
isPlaying = playerState.currentSong?.id == song.id,
|
isPlaying = playerState.currentSong?.id == song.id,
|
||||||
starred = song.starred != null,
|
starred = song.starred != null,
|
||||||
onPlay = { onSongPlay(songs, idx) },
|
// Play only the tapped song; Radio mode (if on) fills the rest.
|
||||||
|
onPlay = { onSongPlay(listOf(song), 0) },
|
||||||
onMore = onShowContextMenu?.let { cb -> { cb(song) } },
|
onMore = onShowContextMenu?.let { cb -> { cb(song) } },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,12 +57,12 @@ fun SearchScreen(
|
|||||||
if (r.song.isNotEmpty()) {
|
if (r.song.isNotEmpty()) {
|
||||||
item { ResultHeader("Songs") }
|
item { ResultHeader("Songs") }
|
||||||
items(r.song, key = { it.id }) { song ->
|
items(r.song, key = { it.id }) { song ->
|
||||||
val idx = r.song.indexOf(song)
|
|
||||||
SongRow(
|
SongRow(
|
||||||
song = song,
|
song = song,
|
||||||
coverUrl = coverUrl(song.coverArt),
|
coverUrl = coverUrl(song.coverArt),
|
||||||
isPlaying = playerState.currentSong?.id == song.id,
|
isPlaying = playerState.currentSong?.id == song.id,
|
||||||
onPlay = { onSongPlay(r.song, idx) },
|
// Play only the tapped song; Radio mode (if on) fills the rest.
|
||||||
|
onPlay = { onSongPlay(listOf(song), 0) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'ns-v43';
|
const CACHE = 'ns-v44';
|
||||||
const PRECACHE = ['/', 'index.html', 'manifest.json', 'icon.svg'];
|
const PRECACHE = ['/', 'index.html', 'manifest.json', 'icon.svg'];
|
||||||
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
||||||
// Auth token params stripped from cache keys so artwork survives across sessions/token refreshes.
|
// Auth token params stripped from cache keys so artwork survives across sessions/token refreshes.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function makeAlbumCard(a) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ─── SONG LIST ITEM (library) ───
|
// ─── SONG LIST ITEM (library) ───
|
||||||
export function makeSongItem(s, idx, list, { noArtistLink = false } = {}) {
|
export function makeSongItem(s, idx, list, { noArtistLink = false, singlePlay = false } = {}) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
if(s.starred && !s._starred) s._starred = true;
|
if(s.starred && !s._starred) s._starred = true;
|
||||||
el.className = 'song-item' + (S.queue[S.qIdx]?.id === s.id ? ' playing' : '');
|
el.className = 'song-item' + (S.queue[S.qIdx]?.id === s.id ? ' playing' : '');
|
||||||
@@ -69,7 +69,8 @@ export function makeSongItem(s, idx, list, { noArtistLink = false } = {}) {
|
|||||||
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(); showCtxMenu(e, s, list, idx); };
|
btns[1].onclick = (e) => { e.stopPropagation(); showCtxMenu(e, s, list, idx); };
|
||||||
el.onclick = () => playQueue(list, idx, { noShuffle: true });
|
// singlePlay (search results): play only the clicked song; Radio mode (if on) fills the rest.
|
||||||
|
el.onclick = () => singlePlay ? playQueue([s], 0, { noShuffle: true }) : playQueue(list, idx, { noShuffle: true });
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@ export async function doLocalSearch(q, el) {
|
|||||||
if(songs.length) {
|
if(songs.length) {
|
||||||
const sec = mkSection('Titel');
|
const sec = mkSection('Titel');
|
||||||
const list = document.createElement('div'); list.className = 'song-list';
|
const list = document.createElement('div'); list.className = 'song-list';
|
||||||
songs.forEach((so,i) => list.appendChild(makeSongItem(so, i, songs)));
|
songs.forEach((so,i) => list.appendChild(makeSongItem(so, i, songs, { singlePlay: true })));
|
||||||
sec.appendChild(list); el.appendChild(sec);
|
sec.appendChild(list); el.appendChild(sec);
|
||||||
}
|
}
|
||||||
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
||||||
|
|||||||
Reference in new issue
Block a user