fix: stop a queue drag from moving the wrong rows

A drop captured the dragged row's queue position once, at drag start, and
committed on a fire-and-forget coroutine. If the queue renumbered while the
drag was in flight (up-next consumed, a reconcile) or before a prior move had
reconciled, the drop resolved its target against stale coordinates and moved
the wrong entry - the "two middle rows swap on release" symptom.

Resolve the move from the current rows at drop time, and have move() report
whether it was accepted so a refused reorder is not announced as done; a
further move is refused while one is still committing.
This commit is contained in:
austinried
2026-10-09 19:01:22 +00:00
parent 7bbc29a1d6
commit 8e7431cc2e
3 changed files with 68 additions and 25 deletions
@@ -506,7 +506,7 @@ class AppScreenshotTest {
onPlay = {},
onRemove = {},
onReorder = { _, _ -> },
onMove = { _, _ -> },
onMove = { _, _ -> true },
onLoadOlder = {},
onLoadNewer = {},
onUndo = {},
@@ -325,6 +325,42 @@ class QueueViewModelTest {
assertEquals((220L..399L).toList(), rowsSnapshot().map { it.position })
}
@Test
fun ignoringASecondMoveWhileTheFirstIsStillCommitting() {
runBlocking { seedSongs(10) }
controller.playAlbum(1, "al1", 0)
await { controller.state.value.position == 0L }
viewModel.open()
await { viewModel.ready }
val before = rowsSnapshot().map { it.song.song.id }
val from = rowsSnapshot().first().position
runOnMain {
assertTrue(viewModel.move(from, from + 3))
assertFalse(viewModel.move(from, from + 1))
}
val expected = before.toMutableList().also { it.add(3, it.removeAt(0)) }
await { rowsSnapshot().map { it.song.song.id } == expected }
assertEquals(expected, rowsSnapshot().map { it.song.song.id })
}
@Test
fun dropMoveTakesTheDraggedRowsCurrentPosition() {
val rows =
listOf(
row(10).copy(id = 2L),
row(11).copy(id = 3L),
row(9).copy(id = 1L),
)
val move = rows.dropMoveFor(1L)!!
assertEquals(9L, move.from)
assertEquals(11L, move.to)
}
@Test
fun removingATrackUnderTheBlockReloadsRows() {
runBlocking { seedSongs(10) }