balls
This commit is contained in:
@@ -150,6 +150,7 @@ fun MainScreen(
|
|||||||
isReorderMode = isReorderMode,
|
isReorderMode = isReorderMode,
|
||||||
onToggleReorderMode = { viewModel.toggleReorderMode() },
|
onToggleReorderMode = { viewModel.toggleReorderMode() },
|
||||||
onMoveChannel = { from, to -> viewModel.moveChannel(from, to) },
|
onMoveChannel = { from, to -> viewModel.moveChannel(from, to) },
|
||||||
|
onMoveChannelById = { id, delta -> viewModel.moveChannelById(id, delta) },
|
||||||
onMoveChildSpace = { from, to -> viewModel.moveChildSpace(from, to) },
|
onMoveChildSpace = { from, to -> viewModel.moveChildSpace(from, to) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,8 +348,13 @@ class MainViewModel(
|
|||||||
|
|
||||||
// Child space sections
|
// Child space sections
|
||||||
for ((csId, csName, csRoomIds) in childSpaces) {
|
for ((csId, csName, csRoomIds) in childSpaces) {
|
||||||
val sectionChannels = csRoomIds.mapNotNull { channelMap[it] }
|
val csChannels = csRoomIds.mapNotNull { channelMap[it] }
|
||||||
.filter { it.id !in usedIds }
|
.filter { it.id !in usedIds }
|
||||||
|
// Respect saved channel order within child space sections
|
||||||
|
val sectionChannels = if (savedOrder != null) {
|
||||||
|
val idxMap = savedOrder.withIndex().associate { (i, id) -> id to i }
|
||||||
|
csChannels.sortedBy { idxMap[it.id] ?: Int.MAX_VALUE }
|
||||||
|
} else csChannels
|
||||||
if (sectionChannels.isNotEmpty()) {
|
if (sectionChannels.isNotEmpty()) {
|
||||||
sectionList.add(ChannelSection(spaceId = csId, spaceName = csName, channels = sectionChannels))
|
sectionList.add(ChannelSection(spaceId = csId, spaceName = csName, channels = sectionChannels))
|
||||||
usedIds.addAll(sectionChannels.map { it.id })
|
usedIds.addAll(sectionChannels.map { it.id })
|
||||||
@@ -1128,6 +1133,23 @@ class MainViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun moveChannelById(channelId: String, delta: Int) {
|
||||||
|
val current = _channels.value.toMutableList()
|
||||||
|
val fromIdx = current.indexOfFirst { it.id == channelId }
|
||||||
|
if (fromIdx < 0) return
|
||||||
|
val toIdx = (fromIdx + delta).coerceIn(0, current.lastIndex)
|
||||||
|
if (fromIdx == toIdx) return
|
||||||
|
val item = current.removeAt(fromIdx)
|
||||||
|
current.add(toIdx, item)
|
||||||
|
_channels.value = current
|
||||||
|
val spaceId = _selectedSpace.value ?: return
|
||||||
|
val roomIds = current.map { it.id }
|
||||||
|
_channelOrderMap.value = _channelOrderMap.value + (spaceId to roomIds)
|
||||||
|
viewModelScope.launch {
|
||||||
|
preferencesManager.saveChannelOrder(spaceId, roomIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun moveChildSpace(from: Int, to: Int) {
|
fun moveChildSpace(from: Int, to: Int) {
|
||||||
val spaceId = _selectedSpace.value ?: return
|
val spaceId = _selectedSpace.value ?: return
|
||||||
val current = _directChildSpaces.value[spaceId]?.toMutableList() ?: return
|
val current = _directChildSpaces.value[spaceId]?.toMutableList() ?: return
|
||||||
|
|||||||
@@ -74,14 +74,15 @@ fun ChannelList(
|
|||||||
isReorderMode: Boolean = false,
|
isReorderMode: Boolean = false,
|
||||||
onToggleReorderMode: () -> Unit = {},
|
onToggleReorderMode: () -> Unit = {},
|
||||||
onMoveChannel: (from: Int, to: Int) -> Unit = { _, _ -> },
|
onMoveChannel: (from: Int, to: Int) -> Unit = { _, _ -> },
|
||||||
|
onMoveChannelById: (channelId: String, delta: Int) -> Unit = { _, _ -> },
|
||||||
onMoveChildSpace: (from: Int, to: Int) -> Unit = { _, _ -> },
|
onMoveChildSpace: (from: Int, to: Int) -> Unit = { _, _ -> },
|
||||||
) {
|
) {
|
||||||
var showLogoutDialog by remember { mutableStateOf(false) }
|
var showLogoutDialog by remember { mutableStateOf(false) }
|
||||||
val collapsedSections = remember { mutableStateMapOf<String, Boolean>() }
|
val collapsedSections = remember { mutableStateMapOf<String, Boolean>() }
|
||||||
|
|
||||||
// Channel drag state
|
// Channel drag state — track by ID, no visual offset (let LazyColumn handle positioning)
|
||||||
var draggingIndex by remember { mutableIntStateOf(-1) }
|
var draggingChannelId by remember { mutableStateOf<String?>(null) }
|
||||||
var dragOffsetY by remember { mutableFloatStateOf(0f) }
|
var dragAccumulator by remember { mutableFloatStateOf(0f) }
|
||||||
|
|
||||||
// Section drag state
|
// Section drag state
|
||||||
var draggingSectionIndex by remember { mutableIntStateOf(-1) }
|
var draggingSectionIndex by remember { mutableIntStateOf(-1) }
|
||||||
@@ -260,9 +261,7 @@ fun ChannelList(
|
|||||||
itemsIndexed(section.channels, key = { _, ch -> ch.id }) { index, channel ->
|
itemsIndexed(section.channels, key = { _, ch -> ch.id }) { index, channel ->
|
||||||
val isSelected = channel.id == selectedChannel
|
val isSelected = channel.id == selectedChannel
|
||||||
val hasUnread = channel.unreadStatus != UnreadStatus.NONE
|
val hasUnread = channel.unreadStatus != UnreadStatus.NONE
|
||||||
val globalIndex = channels.indexOfFirst { it.id == channel.id }
|
val isDragging = draggingChannelId == channel.id
|
||||||
val isDragging = draggingIndex == globalIndex
|
|
||||||
val elevation by animateDpAsState(if (isDragging) 8.dp else 0.dp, label = "elevation")
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -270,8 +269,7 @@ fun ChannelList(
|
|||||||
.then(
|
.then(
|
||||||
if (isDragging) Modifier
|
if (isDragging) Modifier
|
||||||
.zIndex(1f)
|
.zIndex(1f)
|
||||||
.offset { IntOffset(0, dragOffsetY.roundToInt()) }
|
.background(MaterialTheme.colorScheme.surfaceContainerHigh)
|
||||||
.shadow(elevation, RoundedCornerShape(8.dp))
|
|
||||||
else Modifier
|
else Modifier
|
||||||
)
|
)
|
||||||
.clip(RoundedCornerShape(8.dp))
|
.clip(RoundedCornerShape(8.dp))
|
||||||
@@ -287,7 +285,9 @@ fun ChannelList(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
if (isReorderMode) {
|
if (isReorderMode) {
|
||||||
val currentGlobalIndex by rememberUpdatedState(globalIndex)
|
val currentChannelId by rememberUpdatedState(channel.id)
|
||||||
|
val currentSectionSize by rememberUpdatedState(section.channels.size)
|
||||||
|
val currentLocalIndex by rememberUpdatedState(index)
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.DragHandle,
|
imageVector = Icons.Default.DragHandle,
|
||||||
contentDescription = "Drag to reorder",
|
contentDescription = "Drag to reorder",
|
||||||
@@ -296,28 +296,29 @@ fun ChannelList(
|
|||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectDragGesturesAfterLongPress(
|
detectDragGesturesAfterLongPress(
|
||||||
onDragStart = {
|
onDragStart = {
|
||||||
draggingIndex = currentGlobalIndex
|
draggingChannelId = currentChannelId
|
||||||
dragOffsetY = 0f
|
dragAccumulator = 0f
|
||||||
},
|
},
|
||||||
onDrag = { change, dragAmount ->
|
onDrag = { change, dragAmount ->
|
||||||
change.consume()
|
change.consume()
|
||||||
dragOffsetY += dragAmount.y
|
dragAccumulator += dragAmount.y
|
||||||
val itemHeight = 40.dp.toPx()
|
val itemHeight = 56.dp.toPx()
|
||||||
val draggedPositions = (dragOffsetY / itemHeight).roundToInt()
|
val id = draggingChannelId ?: return@detectDragGesturesAfterLongPress
|
||||||
val targetIndex = (draggingIndex + draggedPositions).coerceIn(0, channels.lastIndex)
|
if (dragAccumulator > itemHeight * 0.6f && currentLocalIndex < currentSectionSize - 1) {
|
||||||
if (targetIndex != draggingIndex) {
|
onMoveChannelById(id, 1)
|
||||||
onMoveChannel(draggingIndex, targetIndex)
|
dragAccumulator = 0f
|
||||||
dragOffsetY -= (targetIndex - draggingIndex) * itemHeight
|
} else if (dragAccumulator < -itemHeight * 0.6f && currentLocalIndex > 0) {
|
||||||
draggingIndex = targetIndex
|
onMoveChannelById(id, -1)
|
||||||
|
dragAccumulator = 0f
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDragEnd = {
|
onDragEnd = {
|
||||||
draggingIndex = -1
|
draggingChannelId = null
|
||||||
dragOffsetY = 0f
|
dragAccumulator = 0f
|
||||||
},
|
},
|
||||||
onDragCancel = {
|
onDragCancel = {
|
||||||
draggingIndex = -1
|
draggingChannelId = null
|
||||||
dragOffsetY = 0f
|
dragAccumulator = 0f
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user