misc(power level) : expose RoomMemberModerationPermissions after PR review

This commit is contained in:
ganfra 2025-12-12 18:09:06 +01:00
parent 5c8fd831e3
commit 0476cbd4d9
9 changed files with 52 additions and 30 deletions

View file

@ -41,6 +41,7 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr
import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.features.roomcall.api.aStandByCallState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -164,11 +165,9 @@ fun aMessagesState(
) )
fun aRoomMemberModerationState( fun aRoomMemberModerationState(
canKick: Boolean = false, permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions.DEFAULT,
canBan: Boolean = false,
) = object : RoomMemberModerationState { ) = object : RoomMemberModerationState {
override val canKick: Boolean = canKick override val permissions: RoomMemberModerationPermissions = permissions
override val canBan: Boolean = canBan
override val eventSink: (RoomMemberModerationEvents) -> Unit = {} override val eventSink: (RoomMemberModerationEvents) -> Unit = {}
} }

View file

@ -26,7 +26,7 @@ data class RoomMemberListState(
val moderationState: RoomMemberModerationState, val moderationState: RoomMemberModerationState,
val eventSink: (RoomMemberListEvents) -> Unit, val eventSink: (RoomMemberListEvents) -> Unit,
) { ) {
val showBannedSection: Boolean = moderationState.canBan && roomMembers.dataOrNull()?.banned?.isNotEmpty() == true val showBannedSection: Boolean = moderationState.permissions.canBan && roomMembers.dataOrNull()?.banned?.isNotEmpty() == true
} }
enum class SelectedSection { enum class SelectedSection {

View file

@ -10,6 +10,7 @@ package io.element.android.features.roomdetails.impl.members
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.map import io.element.android.libraries.architecture.map
@ -99,8 +100,10 @@ fun aRoomMemberModerationState(
canKick: Boolean = false, canKick: Boolean = false,
): RoomMemberModerationState { ): RoomMemberModerationState {
return object : RoomMemberModerationState { return object : RoomMemberModerationState {
override val canKick: Boolean = canKick override val permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions(
override val canBan: Boolean = canBan canBan = canBan,
canKick = canKick,
)
override val eventSink: (RoomMemberModerationEvents) -> Unit = {} override val eventSink: (RoomMemberModerationEvents) -> Unit = {}
} }
} }

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2025 Element Creations 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.features.roommembermoderation.api
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions
data class RoomMemberModerationPermissions(
val canKick: Boolean,
val canBan: Boolean,
) {
companion object {
val DEFAULT = RoomMemberModerationPermissions(
canKick = false,
canBan = false,
)
}
}
fun RoomPermissions.roomMemberModerationPermissions(): RoomMemberModerationPermissions {
return RoomMemberModerationPermissions(
canKick = canOwnUserKick(),
canBan = canOwnUserBan(),
)
}

View file

@ -12,8 +12,7 @@ import androidx.compose.runtime.Immutable
@Immutable @Immutable
interface RoomMemberModerationState { interface RoomMemberModerationState {
val canKick: Boolean val permissions: RoomMemberModerationPermissions
val canBan: Boolean
val eventSink: (RoomMemberModerationEvents) -> Unit val eventSink: (RoomMemberModerationEvents) -> Unit
} }

View file

@ -10,14 +10,14 @@ package io.element.android.features.roommembermoderation.impl
import io.element.android.features.roommembermoderation.api.ModerationActionState import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
data class InternalRoomMemberModerationState( data class InternalRoomMemberModerationState(
override val canKick: Boolean, override val permissions: RoomMemberModerationPermissions,
override val canBan: Boolean,
val selectedUser: MatrixUser?, val selectedUser: MatrixUser?,
val actions: ImmutableList<ModerationActionState>, val actions: ImmutableList<ModerationActionState>,
val kickUserAsyncAction: AsyncAction<Unit>, val kickUserAsyncAction: AsyncAction<Unit>,

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
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.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
@ -83,8 +84,7 @@ fun anAlice() = MatrixUser(
) )
fun aRoomMembersModerationState( fun aRoomMembersModerationState(
canKick: Boolean = false, permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions.DEFAULT,
canBan: Boolean = false,
selectedUser: MatrixUser? = null, selectedUser: MatrixUser? = null,
actions: List<ModerationActionState> = emptyList(), actions: List<ModerationActionState> = emptyList(),
kickUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized, kickUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
@ -92,8 +92,7 @@ fun aRoomMembersModerationState(
unbanUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized, unbanUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (RoomMemberModerationEvents) -> Unit = {}, eventSink: (RoomMemberModerationEvents) -> Unit = {},
) = InternalRoomMemberModerationState( ) = InternalRoomMemberModerationState(
canKick = canKick, permissions = permissions,
canBan = canBan,
selectedUser = selectedUser, selectedUser = selectedUser,
actions = actions.toImmutableList(), actions = actions.toImmutableList(),
kickUserAsyncAction = kickUserAsyncAction, kickUserAsyncAction = kickUserAsyncAction,

View file

@ -21,7 +21,9 @@ import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.features.roommembermoderation.api.roomMemberModerationPermissions
import io.element.android.libraries.architecture.AsyncAction 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
@ -55,11 +57,8 @@ class RoomMemberModerationPresenter(
override fun present(): RoomMemberModerationState { override fun present(): RoomMemberModerationState {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val permissions by room.permissionsAsState(Permissions()) { perms -> val permissions by room.permissionsAsState(RoomMemberModerationPermissions.DEFAULT) { perms ->
Permissions( perms.roomMemberModerationPermissions()
canKick = perms.canOwnUserKick(),
canBan = perms.canOwnUserBan(),
)
} }
val currentUserMemberPowerLevel = room.userPowerLevelAsState(syncUpdateFlow.value) val currentUserMemberPowerLevel = room.userPowerLevelAsState(syncUpdateFlow.value)
@ -136,8 +135,7 @@ class RoomMemberModerationPresenter(
} }
return InternalRoomMemberModerationState( return InternalRoomMemberModerationState(
canKick = permissions.canKick, permissions = permissions,
canBan = permissions.canBan,
selectedUser = selectedUser, selectedUser = selectedUser,
actions = moderationActions.value, actions = moderationActions.value,
kickUserAsyncAction = kickUserAsyncAction.value, kickUserAsyncAction = kickUserAsyncAction.value,
@ -149,7 +147,7 @@ class RoomMemberModerationPresenter(
private fun computeModerationActions( private fun computeModerationActions(
member: RoomMember?, member: RoomMember?,
permissions: Permissions, permissions: RoomMemberModerationPermissions,
currentUserMemberPowerLevel: Long, currentUserMemberPowerLevel: Long,
): ImmutableList<ModerationActionState> { ): ImmutableList<ModerationActionState> {
return buildList { return buildList {
@ -209,11 +207,6 @@ class RoomMemberModerationPresenter(
) )
} }
private data class Permissions(
val canKick: Boolean = false,
val canBan: Boolean = false,
)
private fun <T> CoroutineScope.runActionAndWaitForMembershipChange( private fun <T> CoroutineScope.runActionAndWaitForMembershipChange(
action: MutableState<AsyncAction<T>>, action: MutableState<AsyncAction<T>>,
block: suspend () -> Result<T> block: suspend () -> Result<T>

View file

@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
@ -49,8 +50,7 @@ class RoomMemberModerationPresenterTest {
val room = aJoinedRoom() val room = aJoinedRoom()
createRoomMemberModerationPresenter(room = room).test { createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState() val initialState = awaitState()
assertThat(initialState.canKick).isFalse() assertThat(initialState.permissions).isEqualTo(RoomMemberModerationPermissions.DEFAULT)
assertThat(initialState.canBan).isFalse()
assertThat(initialState.selectedUser).isNull() assertThat(initialState.selectedUser).isNull()
assertThat(initialState.banUserAsyncAction).isEqualTo(AsyncAction.Uninitialized) assertThat(initialState.banUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(initialState.kickUserAsyncAction).isEqualTo(AsyncAction.Uninitialized) assertThat(initialState.kickUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)