Iterate on removing space child rooms.

This commit is contained in:
ganfra 2026-01-14 15:29:18 +01:00
parent df3fe6d6d6
commit f50165807d
3 changed files with 54 additions and 10 deletions

View file

@ -112,6 +112,23 @@ class SpacePresenter(
var isManageMode by remember { mutableStateOf(false) }
var selectedRoomIds by remember { mutableStateOf<Set<RoomId>>(emptySet()) }
var removeRoomsAction by remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
var removedRoomIds by remember { mutableStateOf<Set<RoomId>>(emptySet()) }
val filteredChildren by remember {
derivedStateOf {
children
.filterNot { it.roomId in removedRoomIds }
.let { list ->
if (isManageMode) {
// In manage mode, only show rooms (not spaces)
list.filter { !it.isSpace }
} else {
list
}
}
.toImmutableList()
}
}
LaunchedEffect(children) {
// Remove joined children from the join actions
@ -171,10 +188,19 @@ class SpacePresenter(
localCoroutineScope.launch {
removeRoomsAction = AsyncAction.Loading
val spaceId = spaceRoomList.roomId
val results = selectedRoomIds.map { roomId ->
async { spaceService.removeChildFromSpace(spaceId, roomId) }
val roomsToRemove = selectedRoomIds.toSet()
val successfullyRemoved = mutableSetOf<RoomId>()
val results = roomsToRemove.map { roomId ->
async {
spaceService.removeChildFromSpace(spaceId, roomId)
.onSuccess { successfullyRemoved.add(roomId) }
}
}
val hasError = results.awaitAll().any { it.isFailure }
results.awaitAll()
if (successfullyRemoved.isNotEmpty()) {
removedRoomIds = removedRoomIds + successfullyRemoved
}
val hasError = successfullyRemoved.size < roomsToRemove.size
if (hasError) {
removeRoomsAction = AsyncAction.Failure(Exception("Failed to remove some rooms"))
} else {
@ -191,7 +217,7 @@ class SpacePresenter(
}
return SpaceState(
currentSpace = currentSpace.getOrNull(),
children = children,
children = filteredChildren,
seenSpaceInvites = seenSpaceInvites,
hideInvitesAvatar = hideInvitesAvatar,
hasMoreToLoad = hasMoreToLoad,

View file

@ -34,11 +34,12 @@ data class SpaceState(
val eventSink: (SpaceEvents) -> Unit
) {
fun isJoining(spaceId: RoomId): Boolean = joinActions[spaceId] == AsyncAction.Loading
fun isSelected(spaceId: RoomId): Boolean = selectedRoomIds.contains(spaceId)
val hasAnyFailure: Boolean = joinActions.values.any {
it is AsyncAction.Failure
}
val showManageRoomsAction: Boolean = canManageRooms && children.isNotEmpty()
val showManageRoomsAction: Boolean = canManageRooms && children.any { spaceRoom -> !spaceRoom.isSpace }
val selectedCount: Int = selectedRoomIds.size
val isRemoveButtonEnabled: Boolean = selectedRoomIds.isNotEmpty()
}

View file

@ -8,6 +8,7 @@
package io.element.android.features.space.impl.root
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -42,10 +43,9 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.atomic.molecules.InviteButtonsRowMolecule
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.ClickableLinkText
import io.element.android.libraries.designsystem.components.SimpleModalBottomSheet
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncIndicator
import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost
import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState
@ -54,18 +54,19 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
@ -90,6 +91,15 @@ fun SpaceView(
modifier: Modifier = Modifier,
acceptDeclineInviteView: @Composable () -> Unit,
) {
BackHandler {
if (state.isManageMode) {
state.eventSink(SpaceEvents.ExitManageMode)
} else {
onBackClick()
}
}
Scaffold(
modifier = modifier,
topBar = {
@ -230,7 +240,7 @@ private fun SpaceViewContent(
) { index, spaceRoom ->
val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED
val isCurrentlyJoining = state.isJoining(spaceRoom.roomId)
val isSelected = spaceRoom.roomId in state.selectedRoomIds
val isSelected = state.isSelected(spaceRoom.roomId)
SpaceRoomItemView(
spaceRoom = spaceRoom,
showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites,
@ -530,6 +540,13 @@ private fun RemoveRoomsConfirmationDialog(
onSubmitClick = onConfirm,
onDismiss = onDismiss,
destructiveSubmit = true,
icon = {
Icon(
imageVector = CompoundIcons.Error(),
tint = ElementTheme.colors.textCriticalPrimary,
contentDescription = null
)
}
)
}
else -> {