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 94aa75e9..720c9273 100644 --- a/app/src/main/kotlin/com/subtracks/data/repo/QueueRepository.kt +++ b/app/src/main/kotlin/com/subtracks/data/repo/QueueRepository.kt @@ -21,6 +21,7 @@ const val QUEUE_CHUNK = 60 private const val LENGTH_CACHE_LIMIT = 512 private data class EntryLengthKey( + val version: Long, val sourceId: Long, val kind: QueueKind, val refId: String, @@ -147,7 +148,7 @@ class QueueRepository( val loaded = db.useReaderConnection { transactor -> transactor.deferredTransaction { - val entries = dao.entries().map { ResolvedQueueEntry(it, lengthOf(it)) } + val entries = dao.entries().map { ResolvedQueueEntry(it, lengthOf(it, version)) } val row = dao.cursor() val shuffled = row?.shuffleEnabled == true val order = if (shuffled) loadShuffleOrder(row.shuffleSeed, entries.sumOf { it.length }) else null @@ -386,8 +387,11 @@ class QueueRepository( } } - private suspend fun lengthOf(entry: QueueEntry): Long { - val key = entry.lengthKey() + private suspend fun lengthOf( + entry: QueueEntry, + version: Long, + ): Long { + val key = entry.lengthKey(version) lengthCache[key]?.let { return it } val length = entry.resolvedLength() if (lengthCache.size >= LENGTH_CACHE_LIMIT) lengthCache.clear() @@ -395,8 +399,9 @@ class QueueRepository( return length } - private fun QueueEntry.lengthKey() = + private fun QueueEntry.lengthKey(version: Long) = EntryLengthKey( + version = version, sourceId = sourceId, kind = kind, refId = refId, diff --git a/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt b/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt index ac776364..929d501b 100644 --- a/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt @@ -184,7 +184,7 @@ class QueueViewModel( viewModelScope.launch { playbackController.move(from, to) mutex.withLock { - val base = rows.firstOrNull()?.position ?: return@withLock + val base = rows.minOfOrNull { it.position } ?: return@withLock // The loaded order already reflects the move, so only the positions need updating. val updated = rows.mapIndexed { index, row -> diff --git a/app/src/test/kotlin/com/subtracks/ui/playback/QueueViewModelTest.kt b/app/src/test/kotlin/com/subtracks/ui/playback/QueueViewModelTest.kt index e06f774d..e6418c7e 100644 --- a/app/src/test/kotlin/com/subtracks/ui/playback/QueueViewModelTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/playback/QueueViewModelTest.kt @@ -199,8 +199,30 @@ class QueueViewModelTest { viewModel.rows[13] .song.song.id, ) - assertEquals(QUEUE_WINDOW_ROWS, viewModel.rows.size) - assertContiguous() + assertEquals((220L..399L).toList(), viewModel.rows.map { it.position }) + } + + @Test + fun movingTheTopRowDownRenumbersFromTheWindowStart() { + runBlocking { seedSongs(400) } + controller.playAlbum(1, "al1", 100) + await { controller.state.value.position == 100L } + + viewModel.open() + await { viewModel.ready } + extendToEnd() + assertEquals((220L..399L).toList(), viewModel.rows.map { it.position }) + + val from = viewModel.rows.first().position + viewModel.reorder(0, 5) + val to = dropTarget(viewModel.rows, index = 5, from = from) + viewModel.move(from, to) + await { + viewModel.rows.firstOrNull()?.position == 220L && + viewModel.rows.lastOrNull()?.position == 399L + } + + assertEquals((220L..399L).toList(), viewModel.rows.map { it.position }) } @Test