From b1f5499ad9306049afa597a7e5b0b98c3efe78fa Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 18 Dec 2025 21:36:55 +0100 Subject: [PATCH] change(member moderation): fix available moderation actions --- .../impl/RoomMemberModerationPresenter.kt | 28 ++++++++------- .../matrix/ui/model/RoomInfoExtension.kt | 21 +++++++++--- .../matrix/ui/room/MatrixRoomState.kt | 34 ------------------- 3 files changed, 32 insertions(+), 51 deletions(-) delete mode 100644 libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt index 16a11aee96..cfb9412394 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt @@ -28,6 +28,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.RoomMember @@ -35,7 +36,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.ui.room.userPowerLevelAsState +import io.element.android.libraries.matrix.ui.model.powerLevelOf import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -56,11 +57,14 @@ class RoomMemberModerationPresenter( @Composable override fun present(): RoomMemberModerationState { val coroutineScope = rememberCoroutineScope() - val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val permissions by room.permissionsAsState(RoomMemberModerationPermissions.DEFAULT) { perms -> perms.roomMemberModerationPermissions() } - val currentUserMemberPowerLevel = room.userPowerLevelAsState(syncUpdateFlow.value) + val currentUserPowerLevel by remember { + room.roomInfoFlow.mapState { info -> + info.powerLevelOf(room.sessionId) + } + }.collectAsState() val kickUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction) } @@ -83,7 +87,7 @@ class RoomMemberModerationPresenter( moderationActions.value = computeModerationActions( member = member, permissions = permissions, - currentUserMemberPowerLevel = currentUserMemberPowerLevel.value, + currentUserPowerLevel = currentUserPowerLevel, ) } is RoomMemberModerationEvents.ProcessAction -> { @@ -148,26 +152,26 @@ class RoomMemberModerationPresenter( private fun computeModerationActions( member: RoomMember?, permissions: RoomMemberModerationPermissions, - currentUserMemberPowerLevel: Long, + currentUserPowerLevel: Long, ): ImmutableList { return buildList { add(ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true)) // Assume the member is a regular user when it's unknown val targetMemberPowerLevel = member?.powerLevel ?: 0 - val canModerateThisUser = currentUserMemberPowerLevel > targetMemberPowerLevel + val canModerateThisUser = currentUserPowerLevel > targetMemberPowerLevel // Assume the member is joined when it's unknown val membership = member?.membership ?: RoomMembershipState.JOIN if (permissions.canKick) { - val isKickEnabled = canModerateThisUser && membership.isActive() - add(ModerationActionState(action = ModerationAction.KickUser, isEnabled = isKickEnabled)) - } - if (permissions.canBan) { + // Unban requires kick permission instead of a dedicated unban permission if (membership == RoomMembershipState.BAN) { add(ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = canModerateThisUser)) - } else { - add(ModerationActionState(action = ModerationAction.BanUser, isEnabled = canModerateThisUser)) + } else if (membership != RoomMembershipState.LEAVE) { + add(ModerationActionState(action = ModerationAction.KickUser, isEnabled = canModerateThisUser)) } } + if (permissions.canBan && membership != RoomMembershipState.BAN) { + add(ModerationActionState(action = ModerationAction.BanUser, isEnabled = canModerateThisUser)) + } }.toImmutableList() } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt index f9a86c9bd7..3a03d8329b 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt @@ -21,6 +21,20 @@ fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData( size = size, ) +/** + * Returns the power level of the user in the room. + * If the user is a creator and [RoomInfo.privilegedCreatorRole] is true, returns the power level of [RoomMember.Role.Owner]. + * Otherwise, checks the room's power levels for the user's power level. + * If no specific power level is set for the user, defaults to 0. + */ +fun RoomInfo.powerLevelOf(userId: UserId): Long { + return if (privilegedCreatorRole && creators.contains(userId)) { + RoomMember.Role.Owner(isCreator = true).powerLevel + } else { + roomPowerLevels?.powerLevelOf(userId = userId) ?: 0L + } +} + /** * Returns the role of the user in the room. * If the user is a creator and [RoomInfo.privilegedCreatorRole] is true, returns [RoomMember.Role.Owner]. @@ -28,9 +42,6 @@ fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData( * If no specific power level is set for the user, defaults to [RoomMember.Role.User]. */ fun RoomInfo.roleOf(userId: UserId): RoomMember.Role { - return if (privilegedCreatorRole && creators.contains(userId)) { - RoomMember.Role.Owner(isCreator = true) - } else { - roomPowerLevels?.roleOf(userId) ?: RoomMember.Role.User - } + val powerLevel = powerLevelOf(userId = userId) + return RoomMember.Role.forPowerLevel(powerLevel) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt deleted file mode 100644 index d4e6127b69..0000000000 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.ui.room - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.produceState -import io.element.android.libraries.matrix.api.room.BaseRoom -import io.element.android.libraries.matrix.api.room.RoomMember -import io.element.android.libraries.matrix.ui.model.roleOf - -@Composable -fun BaseRoom.userPowerLevelAsState(updateKey: Long): State { - return produceState(initialValue = 0, key1 = updateKey) { - value = userRole(sessionId) - .getOrDefault(RoomMember.Role.User) - .powerLevel - } -} - -@Composable -fun BaseRoom.isOwnUserAdmin(): Boolean { - val roomInfo by roomInfoFlow.collectAsState() - val role = roomInfo.roleOf(sessionId) - return role == RoomMember.Role.Admin || role is RoomMember.Role.Owner -}