change(room permissions): fix some role&permissions inconsistencies after last changes

This commit is contained in:
ganfra
2025-12-18 21:37:56 +01:00
parent a0d6fddf73
commit 4c2aa0ba33
5 changed files with 47 additions and 29 deletions

View File

@@ -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) {

View File

@@ -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<Unit>>(AsyncAction.Uninitialized) }
val resetPermissionsAction = remember { mutableStateOf<AsyncAction<Unit>>(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,

View File

@@ -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<DemoteActions>,
val changeOwnRoleAction: AsyncAction<Unit>,
val resetPermissionsAction: AsyncAction<Unit>,
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)
}

View File

@@ -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<RolesAndPermissionsState> {
override val values: Sequence<RolesAndPermissionsState>
@@ -46,7 +47,7 @@ class RolesAndPermissionsStateProvider : PreviewParameterProvider<RolesAndPermis
moderatorCount = 2,
resetPermissionsAction = AsyncAction.Failure(IllegalStateException("Failed to reset permissions")),
),
aRolesAndPermissionsState(canDemoteSelf = false),
aRolesAndPermissionsState(availableDemoteActions = emptyList()),
)
}
@@ -54,14 +55,14 @@ internal fun aRolesAndPermissionsState(
roomSupportsOwners: Boolean = true,
adminCount: Int = 0,
moderatorCount: Int = 0,
canDemoteSelf: Boolean = true,
availableDemoteActions: List<DemoteActions> = listOf(DemoteActions.ToModerator, DemoteActions.ToMember),
changeOwnRoleAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
resetPermissionsAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (RolesAndPermissionsEvents) -> Unit = {},
) = RolesAndPermissionsState(
roomSupportsOwnerRole = roomSupportsOwners,
adminCount = adminCount,
canDemoteSelf = canDemoteSelf,
availableDemoteActions = availableDemoteActions.toImmutableList(),
moderatorCount = moderatorCount,
changeOwnRoleAction = changeOwnRoleAction,
resetPermissionsAction = resetPermissionsAction,

View File

@@ -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<DemoteActions>,
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,