mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
keep the block in place when the anchor is removed while a queued track plays
`adjustAnchorOnRemove` moved the anchor to the canonical predecessor of the removed track, which in a shuffled queue is an unrelated play position, so removing the context anchor while playing a queued track teleported the up-next block somewhere random (and out of view). When the removed context track is the anchor, move the anchor to the track before it in play order (or -1), so the block stays where it sits. This also covers removing the playing context anchor, so the controller's separate re-anchor is no longer needed and is removed.
This commit is contained in:
@@ -465,9 +465,17 @@ class QueueRepository(
|
||||
// tracks that remain keep their positions.
|
||||
val order = snapshot.shuffle ?: return
|
||||
val flat = snapshot.flatContext(play) ?: return
|
||||
val predecessor = if (play <= 0L) -1L else snapshot.flatContext(play - 1L) ?: -1L
|
||||
shuffleRemoved = order.withRemoved(order.domainFor(flat))
|
||||
write(compact(removeEntry(snapshot.entries, flat)).map { it.entry })
|
||||
adjustAnchorOnRemove(flat)
|
||||
if (cursorRow().upNextAnchor == flat) {
|
||||
// The removed track carried the block; move the anchor to the track before it in
|
||||
// play order so the block stays in place instead of jumping to the canonical
|
||||
// predecessor, which in a shuffled queue is an unrelated position.
|
||||
setUpNextAnchor(if (predecessor > flat) predecessor - 1 else predecessor)
|
||||
} else {
|
||||
adjustAnchorOnRemove(flat)
|
||||
}
|
||||
} else {
|
||||
val removed = compact(removeEntry(snapshot.entries, play))
|
||||
write(removed.map { it.entry })
|
||||
|
||||
@@ -344,18 +344,6 @@ class PlaybackController(
|
||||
lastEdit = ReloadUndo(entries, cursor, snapshot.shuffleSeed, upNext, snapshot.upNextAnchor)
|
||||
return@withLock
|
||||
}
|
||||
// The block floats behind the currently playing context track, so removing that track
|
||||
// removes its anchor. Re-anchor the block to the track that now precedes the removed
|
||||
// slot in play order, so it takes the slot and plays next, rather than the anchor
|
||||
// landing wherever `adjustAnchorOnRemove` left it.
|
||||
if (snapshot.shuffled && position == current && !wasUpNext && updated.upNextSize > 0L) {
|
||||
val play = snapshot.contextPlay(current)
|
||||
val predecessor = play - 1L
|
||||
val anchor = if (predecessor < 0L) -1L else updated.flatContext(predecessor) ?: -1L
|
||||
queueRepository.setUpNextAnchor(anchor)
|
||||
updated = readSnapshot()
|
||||
this.snapshot = updated
|
||||
}
|
||||
// Derive the current track's new position from the edit. Resolving its song id after
|
||||
// the edit can match an identical song elsewhere (a duplicate in the context or the
|
||||
// block), so use positions throughout. Removing the playing track resumes at the same
|
||||
|
||||
@@ -1670,6 +1670,32 @@ class PlaybackControllerTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removingTheAnchorWhilePlayingABlockTrackKeepsTheBlockInPlace() {
|
||||
seedShuffledQueue(seed = 5L)
|
||||
seedSong("x1", "al2")
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
|
||||
controller.playAt(1)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x1"
|
||||
}
|
||||
val before = runBlocking { queues.snapshot() }
|
||||
|
||||
runBlocking { controller.removeAt(before.anchorPlay) }
|
||||
|
||||
val after = runBlocking { queues.snapshot() }
|
||||
assertEquals(-1L, after.anchorPlay)
|
||||
assertEquals("x1", runBlocking { queues.itemAt(after, 0)?.song?.id })
|
||||
assertEquals(
|
||||
"x1",
|
||||
controller.state.value.item
|
||||
?.id,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removingThePlayingTrackWhileShuffledPlaysTheNextShuffledTrack() {
|
||||
seedShuffledQueue(seed = 12345L)
|
||||
|
||||
Reference in New Issue
Block a user