avoid reloading the window on queue edits; add clear up next

- moves that straddle the player window, and deletes that are not the
  current track, rebuilt the playlist with setWindow, restarting the
  playing track (a pause). They now reuse rebuildWindow, which re-lays
  the window around the playing item without replacing it.
- deleting the currently playing queued track now removes it from the
  player in place so it advances to the next track without a reload.
- the Up next section header has a clear button that drops the whole
  queued block and resumes the context after the anchor.
This commit is contained in:
austinried
2026-10-04 00:30:00 +00:00
parent bff16e0dd1
commit 996b0f8f48
4 changed files with 175 additions and 11 deletions
@@ -851,6 +851,81 @@ class PlaybackControllerTest {
assertFalse(handle.operations.any { it.startsWith("setWindow") })
}
@Test
fun clearingTheQueueResumesTheContext() {
seedAlbum(3, sourceId = 1)
seedSong("x1", "al2")
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
controller.addToQueue(1, QueueKind.Song, "x1")
controller.next()
await {
controller.state.value.item
?.id == "x1"
}
controller.clearUpNext()
await {
controller.state.value.item
?.id == "s2"
}
assertTrue(runBlocking { queues.snapshot().upNext }.isEmpty())
}
@Test
fun clearingTheQueueWhileOnTheContextKeepsPlaying() {
seedAlbum(3, sourceId = 1)
seedSong("x1", "al2")
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
controller.addToQueue(1, QueueKind.Song, "x1")
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
controller.clearUpNext()
Thread.sleep(200)
assertEquals(
"s1",
controller.state.value.item
?.id,
)
assertTrue(runBlocking { queues.snapshot().upNext }.isEmpty())
}
@Test
fun movingAFarQueuedTrackDoesNotReloadTheWindow() {
seedAlbum(3, sourceId = 1)
seedAlbumTracks("al2", 60)
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
controller.addToQueue(1, QueueKind.Album, "al2")
await { runBlocking { controller.upcomingItem()?.id } == "al2-b1" }
handle.operations.clear()
runBlocking { controller.move(50, 2) }
Thread.sleep(300)
assertEquals(
"s1",
controller.state.value.item
?.id,
)
assertFalse(handle.operations.any { it.startsWith("setWindow") })
}
@Test
fun nextFollowsTheReorderedQueue() {
seedAlbum(3, sourceId = 1)
@@ -1846,6 +1921,32 @@ class PlaybackControllerTest {
}
}
private fun seedAlbumTracks(
albumId: String,
count: Int,
) {
runBlocking {
db.libraryDao().upsertSongs(
(1..count).map { track ->
Song(
sourceId = 1,
id = "$albumId-b$track",
albumId = albumId,
artistId = "ar1",
title = "Album $albumId track $track",
album = "Album $albumId",
artist = "Artist",
duration = 100,
track = track.toLong(),
disc = 1,
starred = null,
genre = null,
)
},
)
}
}
private fun seedSong(
id: String,
albumId: String,