From 4c2aa0ba33c3f564fd7490c1fb8579fc459a5b90 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 18 Dec 2025 21:37:56 +0100 Subject: [PATCH] change(room permissions): fix some role&permissions inconsistencies after last changes --- .../impl/roles/ChangeRolesPresenter.kt | 8 +++-- .../impl/root/RolesAndPermissionsPresenter.kt | 14 ++++++-- .../impl/root/RolesAndPermissionsState.kt | 14 ++++++-- .../root/RolesAndPermissionsStateProvider.kt | 7 ++-- .../impl/root/RolesAndPermissionsView.kt | 33 ++++++++----------- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index 4181fb6e2e..3989a76df3 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.room.powerlevels.usersWithRole import io.element.android.libraries.matrix.api.room.toMatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.model.powerLevelOf import io.element.android.libraries.matrix.ui.model.roleOf import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator import io.element.android.services.analytics.api.AnalyticsService @@ -124,9 +125,10 @@ class ChangeRolesPresenter( val roomInfo by room.roomInfoFlow.collectAsState() fun canChangeMemberRole(userId: UserId): Boolean { - val currentUserRole = roomInfo.roleOf(room.sessionId) - val otherUserRole = roomInfo.roleOf(userId) - return currentUserRole.powerLevel > otherUserRole.powerLevel + val currentUserPowerLevel = roomInfo.powerLevelOf(room.sessionId) + val otherUserPowerLevel = roomInfo.powerLevelOf(userId) + return currentUserPowerLevel > otherUserPowerLevel && + currentUserPowerLevel >= role.powerLevel } fun handleEvent(event: ChangeRolesEvent) { diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt index bde20affd8..f80899fc0f 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsPresenter.kt @@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.room.powerlevels.userCountWithRole import io.element.android.libraries.matrix.ui.model.roleOf import io.element.android.services.analytics.api.AnalyticsService +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -49,7 +50,16 @@ class RolesAndPermissionsPresenter( room.userCountWithRole { role -> role is RoomMember.Role.Admin || role is RoomMember.Role.Owner } }.collectAsState(null) - val canDemoteSelf = remember { derivedStateOf { roomInfo.roleOf(room.sessionId) !is RoomMember.Role.Owner } } + val availableDemoteActions by remember { + derivedStateOf { + val currentRole = roomInfo.roleOf(room.sessionId) + when (currentRole) { + is RoomMember.Role.Admin -> persistentListOf(DemoteActions.ToModerator, DemoteActions.ToMember) + is RoomMember.Role.Moderator -> persistentListOf(DemoteActions.ToMember) + else -> persistentListOf() + } + } + } val changeOwnRoleAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val resetPermissionsAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } @@ -78,7 +88,7 @@ class RolesAndPermissionsPresenter( roomSupportsOwnerRole = roomInfo.privilegedCreatorRole, adminCount = adminCount, moderatorCount = moderatorCount, - canDemoteSelf = canDemoteSelf.value, + availableDemoteActions = availableDemoteActions, changeOwnRoleAction = changeOwnRoleAction.value, resetPermissionsAction = resetPermissionsAction.value, eventSink = ::handleEvent, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt index 90785d1b38..9064f559c6 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsState.kt @@ -8,14 +8,24 @@ package io.element.android.features.rolesandpermissions.impl.root +import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.room.RoomMember +import kotlinx.collections.immutable.ImmutableList data class RolesAndPermissionsState( val roomSupportsOwnerRole: Boolean, val adminCount: Int?, val moderatorCount: Int?, - val canDemoteSelf: Boolean, + val availableDemoteActions: ImmutableList, val changeOwnRoleAction: AsyncAction, val resetPermissionsAction: AsyncAction, val eventSink: (RolesAndPermissionsEvents) -> Unit, -) +) { + val canDemoteSelf = availableDemoteActions.isNotEmpty() +} + +enum class DemoteActions(val role: RoomMember.Role, val titleRes: Int) { + ToModerator(RoomMember.Role.Moderator, R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator), + ToMember(RoomMember.Role.User, R.string.screen_room_roles_and_permissions_change_role_demote_to_member) +} diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt index 23448c0351..cf0163d0ba 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsStateProvider.kt @@ -10,6 +10,7 @@ package io.element.android.features.rolesandpermissions.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction +import kotlinx.collections.immutable.toImmutableList class RolesAndPermissionsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -46,7 +47,7 @@ class RolesAndPermissionsStateProvider : PreviewParameterProvider = listOf(DemoteActions.ToModerator, DemoteActions.ToMember), changeOwnRoleAction: AsyncAction = AsyncAction.Uninitialized, resetPermissionsAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (RolesAndPermissionsEvents) -> Unit = {}, ) = RolesAndPermissionsState( roomSupportsOwnerRole = roomSupportsOwners, adminCount = adminCount, - canDemoteSelf = canDemoteSelf, + availableDemoteActions = availableDemoteActions.toImmutableList(), moderatorCount = moderatorCount, changeOwnRoleAction = changeOwnRoleAction, resetPermissionsAction = resetPermissionsAction, diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index c0cb15983c..82b49f0d81 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -39,8 +39,8 @@ import io.element.android.libraries.designsystem.theme.components.ListSectionHea import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.hide -import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList @Composable fun RolesAndPermissionsView( @@ -117,6 +117,7 @@ fun RolesAndPermissionsView( when (state.changeOwnRoleAction) { is AsyncAction.Confirming -> { ChangeOwnRoleBottomSheet( + availableDemoteActions = state.availableDemoteActions, eventSink = state.eventSink, ) } @@ -136,6 +137,7 @@ fun RolesAndPermissionsView( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun ChangeOwnRoleBottomSheet( + availableDemoteActions: ImmutableList, eventSink: (RolesAndPermissionsEvents) -> Unit, ) { val coroutineScope = rememberCoroutineScope() @@ -164,24 +166,17 @@ private fun ChangeOwnRoleBottomSheet( style = ElementTheme.typography.fontBodyLgRegular, color = ElementTheme.colors.textPrimary, ) - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator)) }, - onClick = { - sheetState.hide(coroutineScope) { - eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator)) - } - }, - style = ListItemStyle.Destructive, - ) - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_role_demote_to_member)) }, - onClick = { - sheetState.hide(coroutineScope) { - eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.User)) - } - }, - style = ListItemStyle.Destructive, - ) + for (demoteAction in availableDemoteActions) { + ListItem( + headlineContent = { Text(stringResource(demoteAction.titleRes)) }, + onClick = { + sheetState.hide(coroutineScope) { + eventSink(RolesAndPermissionsEvents.DemoteSelfTo(demoteAction.role)) + } + }, + style = ListItemStyle.Destructive, + ) + } ListItem( headlineContent = { Text(stringResource(CommonStrings.action_cancel)) }, onClick = ::dismiss,