change(member moderation): fix available moderation actions
This commit is contained in:
parent
2b12e22cdd
commit
a0d6fddf73
3 changed files with 32 additions and 51 deletions
|
|
@ -28,6 +28,7 @@ import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
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.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
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.powerlevels.permissionsAsState
|
||||||
import io.element.android.libraries.matrix.api.room.roomMembers
|
import io.element.android.libraries.matrix.api.room.roomMembers
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
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 io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
@ -56,11 +57,14 @@ class RoomMemberModerationPresenter(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomMemberModerationState {
|
override fun present(): RoomMemberModerationState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
|
||||||
val permissions by room.permissionsAsState(RoomMemberModerationPermissions.DEFAULT) { perms ->
|
val permissions by room.permissionsAsState(RoomMemberModerationPermissions.DEFAULT) { perms ->
|
||||||
perms.roomMemberModerationPermissions()
|
perms.roomMemberModerationPermissions()
|
||||||
}
|
}
|
||||||
val currentUserMemberPowerLevel = room.userPowerLevelAsState(syncUpdateFlow.value)
|
val currentUserPowerLevel by remember {
|
||||||
|
room.roomInfoFlow.mapState { info ->
|
||||||
|
info.powerLevelOf(room.sessionId)
|
||||||
|
}
|
||||||
|
}.collectAsState()
|
||||||
|
|
||||||
val kickUserAsyncAction =
|
val kickUserAsyncAction =
|
||||||
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
||||||
|
|
@ -83,7 +87,7 @@ class RoomMemberModerationPresenter(
|
||||||
moderationActions.value = computeModerationActions(
|
moderationActions.value = computeModerationActions(
|
||||||
member = member,
|
member = member,
|
||||||
permissions = permissions,
|
permissions = permissions,
|
||||||
currentUserMemberPowerLevel = currentUserMemberPowerLevel.value,
|
currentUserPowerLevel = currentUserPowerLevel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is RoomMemberModerationEvents.ProcessAction -> {
|
is RoomMemberModerationEvents.ProcessAction -> {
|
||||||
|
|
@ -148,26 +152,26 @@ class RoomMemberModerationPresenter(
|
||||||
private fun computeModerationActions(
|
private fun computeModerationActions(
|
||||||
member: RoomMember?,
|
member: RoomMember?,
|
||||||
permissions: RoomMemberModerationPermissions,
|
permissions: RoomMemberModerationPermissions,
|
||||||
currentUserMemberPowerLevel: Long,
|
currentUserPowerLevel: Long,
|
||||||
): ImmutableList<ModerationActionState> {
|
): ImmutableList<ModerationActionState> {
|
||||||
return buildList {
|
return buildList {
|
||||||
add(ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true))
|
add(ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true))
|
||||||
// Assume the member is a regular user when it's unknown
|
// Assume the member is a regular user when it's unknown
|
||||||
val targetMemberPowerLevel = member?.powerLevel ?: 0
|
val targetMemberPowerLevel = member?.powerLevel ?: 0
|
||||||
val canModerateThisUser = currentUserMemberPowerLevel > targetMemberPowerLevel
|
val canModerateThisUser = currentUserPowerLevel > targetMemberPowerLevel
|
||||||
// Assume the member is joined when it's unknown
|
// Assume the member is joined when it's unknown
|
||||||
val membership = member?.membership ?: RoomMembershipState.JOIN
|
val membership = member?.membership ?: RoomMembershipState.JOIN
|
||||||
if (permissions.canKick) {
|
if (permissions.canKick) {
|
||||||
val isKickEnabled = canModerateThisUser && membership.isActive()
|
// Unban requires kick permission instead of a dedicated unban permission
|
||||||
add(ModerationActionState(action = ModerationAction.KickUser, isEnabled = isKickEnabled))
|
|
||||||
}
|
|
||||||
if (permissions.canBan) {
|
|
||||||
if (membership == RoomMembershipState.BAN) {
|
if (membership == RoomMembershipState.BAN) {
|
||||||
add(ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = canModerateThisUser))
|
add(ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = canModerateThisUser))
|
||||||
} else {
|
} else if (membership != RoomMembershipState.LEAVE) {
|
||||||
add(ModerationActionState(action = ModerationAction.BanUser, isEnabled = canModerateThisUser))
|
add(ModerationActionState(action = ModerationAction.KickUser, isEnabled = canModerateThisUser))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (permissions.canBan && membership != RoomMembershipState.BAN) {
|
||||||
|
add(ModerationActionState(action = ModerationAction.BanUser, isEnabled = canModerateThisUser))
|
||||||
|
}
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,20 @@ fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData(
|
||||||
size = size,
|
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.
|
* Returns the role of the user in the room.
|
||||||
* If the user is a creator and [RoomInfo.privilegedCreatorRole] is true, returns [RoomMember.Role.Owner].
|
* 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].
|
* If no specific power level is set for the user, defaults to [RoomMember.Role.User].
|
||||||
*/
|
*/
|
||||||
fun RoomInfo.roleOf(userId: UserId): RoomMember.Role {
|
fun RoomInfo.roleOf(userId: UserId): RoomMember.Role {
|
||||||
return if (privilegedCreatorRole && creators.contains(userId)) {
|
val powerLevel = powerLevelOf(userId = userId)
|
||||||
RoomMember.Role.Owner(isCreator = true)
|
return RoomMember.Role.forPowerLevel(powerLevel)
|
||||||
} else {
|
|
||||||
roomPowerLevels?.roleOf(userId) ?: RoomMember.Role.User
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<Long> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue