diff --git a/app/src/main/kotlin/com/subtracks/data/repo/QueueRepository.kt b/app/src/main/kotlin/com/subtracks/data/repo/QueueRepository.kt index 8fe28ab8..e9e11ff8 100644 --- a/app/src/main/kotlin/com/subtracks/data/repo/QueueRepository.kt +++ b/app/src/main/kotlin/com/subtracks/data/repo/QueueRepository.kt @@ -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 }) diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt index 897172f5..fe9ce3e8 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt @@ -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 diff --git a/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt b/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt index 6edd5bc8..2d34bb4b 100644 --- a/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt +++ b/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt @@ -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)