mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
fix queue delete latency, reorder hitch and open scroll
This commit is contained in:
@@ -244,19 +244,40 @@ class PlaybackController(
|
||||
this.snapshot = queueRepository.snapshot()
|
||||
val target = movedCursor(current, from, to)
|
||||
windowJob?.cancel()
|
||||
val inWindow = from in windowStart..windowEnd && to in windowStart..windowEnd
|
||||
val fromInWindow = from in windowStart..windowEnd
|
||||
val toInWindow = to in windowStart..windowEnd
|
||||
lastEdit =
|
||||
if (inWindow) {
|
||||
if (fromInWindow == toInWindow) {
|
||||
MovedUndo(entries, cursor, target, from, to)
|
||||
} else {
|
||||
ReloadUndo(entries, cursor)
|
||||
}
|
||||
if (inWindow) {
|
||||
updating = true
|
||||
player.move((from - windowStart).toInt(), (to - windowStart).toInt())
|
||||
updating = false
|
||||
} else {
|
||||
loadWindow(target, autoplay = player.playWhenReady, startPositionMs = player.currentPositionMs)
|
||||
when {
|
||||
fromInWindow && toInWindow -> {
|
||||
updating = true
|
||||
player.move((from - windowStart).toInt(), (to - windowStart).toInt())
|
||||
updating = false
|
||||
}
|
||||
|
||||
!fromInWindow && !toInWindow -> {
|
||||
// The moved track never enters the window, so the playlist is unchanged; only
|
||||
// the window's positions shift, and a move that spans it shifts them by one.
|
||||
when {
|
||||
from < windowStart && to > windowEnd -> {
|
||||
windowStart--
|
||||
windowEnd--
|
||||
}
|
||||
|
||||
from > windowEnd && to < windowStart -> {
|
||||
windowStart++
|
||||
windowEnd++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
loadWindow(target, autoplay = player.playWhenReady, startPositionMs = player.currentPositionMs)
|
||||
}
|
||||
}
|
||||
shiftWindowLocked(target)
|
||||
queueRepository.setCursor(target)
|
||||
@@ -311,6 +332,21 @@ class PlaybackController(
|
||||
true
|
||||
}
|
||||
|
||||
undo is MovedUndo && undo.from !in windowStart..windowEnd && undo.to !in windowStart..windowEnd -> {
|
||||
when {
|
||||
undo.from < windowStart && undo.to > windowEnd -> {
|
||||
windowStart++
|
||||
windowEnd++
|
||||
}
|
||||
|
||||
undo.from > windowEnd && undo.to < windowStart -> {
|
||||
windowStart--
|
||||
windowEnd--
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -52,15 +52,18 @@ import com.subtracks.playback.PlaybackController
|
||||
import com.subtracks.ui.components.EmptyState
|
||||
import com.subtracks.ui.components.LoadingState
|
||||
import com.subtracks.ui.library.SongRow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import org.koin.compose.koinInject
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
|
||||
private const val LOAD_THRESHOLD = 5
|
||||
private const val POSITION_TIMEOUT_MS = 1_000L
|
||||
|
||||
data class QueueRow(
|
||||
val id: Long,
|
||||
@@ -94,7 +97,10 @@ class QueueViewModel(
|
||||
first = 0
|
||||
last = -1
|
||||
if (size > 0L) {
|
||||
val cursor = (playbackController.state.value.position ?: 0L).coerceIn(0, size - 1)
|
||||
val cursor =
|
||||
withTimeoutOrNull(POSITION_TIMEOUT_MS) {
|
||||
playbackController.state.first { it.position != null }.position
|
||||
}?.coerceIn(0, size - 1) ?: 0L
|
||||
val start = (cursor - QUEUE_CHUNK / 2).coerceAtLeast(0)
|
||||
val end = (start + QUEUE_CHUNK - 1).coerceAtMost(size - 1)
|
||||
rows.addAll(queueRepository.range(snapshot, start, end).map(::newRow))
|
||||
@@ -172,8 +178,31 @@ class QueueViewModel(
|
||||
|
||||
fun remove(position: Long) {
|
||||
viewModelScope.launch {
|
||||
val index = rows.indexOfFirst { it.position == position }
|
||||
if (index >= 0) {
|
||||
rows.removeAt(index)
|
||||
for (i in index until rows.size) {
|
||||
val row = rows[i]
|
||||
rows[i] = row.copy(position = row.position - 1)
|
||||
}
|
||||
}
|
||||
val next = (rows.lastOrNull()?.position ?: (first - 1)) + 1
|
||||
playbackController.removeAt(position)
|
||||
mutex.withLock { removeRow(position) }
|
||||
mutex.withLock {
|
||||
if (rows.isEmpty()) {
|
||||
size = 0
|
||||
first = 0
|
||||
last = -1
|
||||
return@withLock
|
||||
}
|
||||
val snapshot = queueRepository.snapshot()
|
||||
size = snapshot.size
|
||||
if (next <= size - 1) {
|
||||
queueRepository.range(snapshot, next, next).firstOrNull()?.let { rows.add(newRow(it)) }
|
||||
}
|
||||
first = rows.first().position
|
||||
last = rows.last().position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,33 +213,6 @@ class QueueViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun removeRow(position: Long) {
|
||||
val index = rows.indexOfFirst { it.position == position }
|
||||
if (index < 0) {
|
||||
reload()
|
||||
return
|
||||
}
|
||||
rows.removeAt(index)
|
||||
for (i in index until rows.size) {
|
||||
val row = rows[i]
|
||||
rows[i] = row.copy(position = row.position - 1)
|
||||
}
|
||||
val snapshot = queueRepository.snapshot()
|
||||
size = snapshot.size
|
||||
if (rows.isEmpty()) {
|
||||
first = 0
|
||||
last = -1
|
||||
return
|
||||
}
|
||||
first = rows.first().position
|
||||
last = rows.last().position
|
||||
val next = last + 1
|
||||
if (next <= size - 1) {
|
||||
queueRepository.range(snapshot, next, next).firstOrNull()?.let { rows.add(newRow(it)) }
|
||||
last = rows.last().position
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun reload() {
|
||||
val snapshot = queueRepository.snapshot()
|
||||
size = snapshot.size
|
||||
@@ -292,10 +294,10 @@ fun QueueScreen(
|
||||
if (centered || !ready || rows.isEmpty()) return@LaunchedEffect
|
||||
val position = currentPosition ?: return@LaunchedEffect
|
||||
val index = rows.indexOfFirst { it.position == position }
|
||||
if (index >= 0) {
|
||||
listState.scrollToItem(index)
|
||||
centered = true
|
||||
}
|
||||
if (index < 0) return@LaunchedEffect
|
||||
snapshotFlow { listState.layoutInfo.totalItemsCount }.first { it > 0 }
|
||||
listState.scrollToItem(index)
|
||||
centered = true
|
||||
}
|
||||
|
||||
val reorderState =
|
||||
|
||||
Reference in New Issue
Block a user