mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
fix shuffled queue edits: flat remap, pinned shuffle start, stable highlight
The previous commit assumed a removed/moved track only shifts play positions by one, but a shuffled context removal re-derives the permutation, so the current track's play position is a remap of its flat index. `removeAt` now maps the current's flat through the updated order (shuffled) or shifts it (unshuffled), and re-anchors the up-next block to the track the cursor lands on. This fixes removing any track while shuffled picking a random track and the anchor removal not advancing to the block. Restore "shuffle starts at the top": pick the seed so the requested start track maps to play position 0 (`pinnedSeed`), instead of leaving the current at its derived position. Applies to shuffle-play, tapping a track while shuffled, toggling shuffle on, and restore. The queue view's playing highlight now requires both the combined position and the song id, so a transient position mismatch no longer flashes the row above as playing.
This commit is contained in:
@@ -2003,6 +2003,76 @@ class PlaybackControllerTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shufflePlayStartsAtTheTopOfTheQueue() {
|
||||
seedAlbum(20, sourceId = 1)
|
||||
|
||||
controller.shuffleAlbum(1, "al1")
|
||||
await { controller.state.value.item != null && controller.state.value.shuffle }
|
||||
|
||||
assertEquals(0L, controller.state.value.position)
|
||||
val snapshot = runBlocking { queues.snapshot() }
|
||||
assertEquals(
|
||||
controller.state.value.item
|
||||
?.id,
|
||||
runBlocking { queues.itemAt(snapshot, 0)?.song?.id },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun enablingShufflePinsThePlayingTrackToTheTop() {
|
||||
seedAlbum(20, sourceId = 1)
|
||||
|
||||
controller.playAlbum(1, "al1", 7)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s8"
|
||||
}
|
||||
|
||||
controller.toggleShuffle()
|
||||
await { controller.state.value.shuffle }
|
||||
|
||||
assertEquals(0L, controller.state.value.position)
|
||||
assertEquals(
|
||||
"s8",
|
||||
controller.state.value.item
|
||||
?.id,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removingAContextTrackWhileShuffledKeepsThePlayingTrackAndTheBlock() {
|
||||
seedAlbum(6, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 2)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s3"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
controller.toggleShuffle()
|
||||
await { controller.state.value.shuffle && runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
|
||||
val currentId =
|
||||
controller.state.value.item!!
|
||||
.id
|
||||
val before = runBlocking { queues.snapshot() }
|
||||
val currentPosition = controller.state.value.position!!
|
||||
val removePosition = (0 until before.size).first { !before.isUpNext(it) && it != currentPosition }
|
||||
|
||||
runBlocking { controller.removeAt(removePosition) }
|
||||
|
||||
await {
|
||||
val snap = runBlocking { queues.snapshot() }
|
||||
controller.state.value.item
|
||||
?.id == currentId &&
|
||||
runBlocking { queues.itemAt(snap, controller.state.value.position!!)?.song?.id } == currentId
|
||||
}
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shufflingTheSameAlbumAgainPicksADifferentTrack() {
|
||||
seedAlbum(5, sourceId = 1)
|
||||
|
||||
@@ -416,6 +416,7 @@ class AppScreenshotTest {
|
||||
Fixtures.songItems.mapIndexed { index, item -> QueueRow(index.toLong(), index.toLong(), item) },
|
||||
ready = true,
|
||||
currentPosition = 1L,
|
||||
currentSongId = Fixtures.songItems[1].song.id,
|
||||
coverArt = { id, _ -> id?.let { CoverArtRef(it, "test:$it") } },
|
||||
onBack = {},
|
||||
onPlay = {},
|
||||
|
||||
Reference in New Issue
Block a user