This commit is contained in:
2026-02-25 12:49:07 +00:00
parent fd42bd65b0
commit f5d1eb210e
3 changed files with 48 additions and 24 deletions

View File

@@ -150,6 +150,7 @@ fun MainScreen(
isReorderMode = isReorderMode,
onToggleReorderMode = { viewModel.toggleReorderMode() },
onMoveChannel = { from, to -> viewModel.moveChannel(from, to) },
onMoveChannelById = { id, delta -> viewModel.moveChannelById(id, delta) },
onMoveChildSpace = { from, to -> viewModel.moveChildSpace(from, to) },
)
}

View File

@@ -348,8 +348,13 @@ class MainViewModel(
// Child space sections
for ((csId, csName, csRoomIds) in childSpaces) {
val sectionChannels = csRoomIds.mapNotNull { channelMap[it] }
val csChannels = csRoomIds.mapNotNull { channelMap[it] }
.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()) {
sectionList.add(ChannelSection(spaceId = csId, spaceName = csName, channels = sectionChannels))
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) {
val spaceId = _selectedSpace.value ?: return
val current = _directChildSpaces.value[spaceId]?.toMutableList() ?: return

View File

@@ -74,14 +74,15 @@ fun ChannelList(
isReorderMode: Boolean = false,
onToggleReorderMode: () -> Unit = {},
onMoveChannel: (from: Int, to: Int) -> Unit = { _, _ -> },
onMoveChannelById: (channelId: String, delta: Int) -> Unit = { _, _ -> },
onMoveChildSpace: (from: Int, to: Int) -> Unit = { _, _ -> },
) {
var showLogoutDialog by remember { mutableStateOf(false) }
val collapsedSections = remember { mutableStateMapOf<String, Boolean>() }
// Channel drag state
var draggingIndex by remember { mutableIntStateOf(-1) }
var dragOffsetY by remember { mutableFloatStateOf(0f) }
// Channel drag state — track by ID, no visual offset (let LazyColumn handle positioning)
var draggingChannelId by remember { mutableStateOf<String?>(null) }
var dragAccumulator by remember { mutableFloatStateOf(0f) }
// Section drag state
var draggingSectionIndex by remember { mutableIntStateOf(-1) }
@@ -260,9 +261,7 @@ fun ChannelList(
itemsIndexed(section.channels, key = { _, ch -> ch.id }) { index, channel ->
val isSelected = channel.id == selectedChannel
val hasUnread = channel.unreadStatus != UnreadStatus.NONE
val globalIndex = channels.indexOfFirst { it.id == channel.id }
val isDragging = draggingIndex == globalIndex
val elevation by animateDpAsState(if (isDragging) 8.dp else 0.dp, label = "elevation")
val isDragging = draggingChannelId == channel.id
Row(
modifier = Modifier
@@ -270,8 +269,7 @@ fun ChannelList(
.then(
if (isDragging) Modifier
.zIndex(1f)
.offset { IntOffset(0, dragOffsetY.roundToInt()) }
.shadow(elevation, RoundedCornerShape(8.dp))
.background(MaterialTheme.colorScheme.surfaceContainerHigh)
else Modifier
)
.clip(RoundedCornerShape(8.dp))
@@ -287,7 +285,9 @@ fun ChannelList(
verticalAlignment = Alignment.CenterVertically,
) {
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(
imageVector = Icons.Default.DragHandle,
contentDescription = "Drag to reorder",
@@ -296,28 +296,29 @@ fun ChannelList(
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(
onDragStart = {
draggingIndex = currentGlobalIndex
dragOffsetY = 0f
draggingChannelId = currentChannelId
dragAccumulator = 0f
},
onDrag = { change, dragAmount ->
change.consume()
dragOffsetY += dragAmount.y
val itemHeight = 40.dp.toPx()
val draggedPositions = (dragOffsetY / itemHeight).roundToInt()
val targetIndex = (draggingIndex + draggedPositions).coerceIn(0, channels.lastIndex)
if (targetIndex != draggingIndex) {
onMoveChannel(draggingIndex, targetIndex)
dragOffsetY -= (targetIndex - draggingIndex) * itemHeight
draggingIndex = targetIndex
dragAccumulator += dragAmount.y
val itemHeight = 56.dp.toPx()
val id = draggingChannelId ?: return@detectDragGesturesAfterLongPress
if (dragAccumulator > itemHeight * 0.6f && currentLocalIndex < currentSectionSize - 1) {
onMoveChannelById(id, 1)
dragAccumulator = 0f
} else if (dragAccumulator < -itemHeight * 0.6f && currentLocalIndex > 0) {
onMoveChannelById(id, -1)
dragAccumulator = 0f
}
},
onDragEnd = {
draggingIndex = -1
dragOffsetY = 0f
draggingChannelId = null
dragAccumulator = 0f
},
onDragCancel = {
draggingIndex = -1
dragOffsetY = 0f
draggingChannelId = null
dragAccumulator = 0f
},
)
},