diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingRoomMemberAction.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingRoomMemberAction.kt new file mode 100644 index 0000000000..29e4e6b451 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/ConfirmingRoomMemberAction.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.roomdetails.impl.members.moderation + +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.room.RoomMember + +data class ConfirmingRoomMemberAction( + val roomMember: RoomMember, +) : AsyncAction.Confirming diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt index 02700264f2..15304207f2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationEvents.kt @@ -7,12 +7,13 @@ package io.element.android.features.roomdetails.impl.members.moderation +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember sealed interface RoomMembersModerationEvents { data class SelectRoomMember(val roomMember: RoomMember) : RoomMembersModerationEvents data object KickUser : RoomMembersModerationEvents data object BanUser : RoomMembersModerationEvents - data object UnbanUser : RoomMembersModerationEvents + data class UnbanUser(val userId: UserId) : RoomMembersModerationEvents data object Reset : RoomMembersModerationEvents } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt index 97e518fb47..700344b139 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt @@ -10,9 +10,9 @@ package io.element.android.features.roomdetails.impl.members.moderation import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -21,16 +21,15 @@ 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.extensions.finally import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState -import io.element.android.libraries.matrix.api.room.isDm -import io.element.android.libraries.matrix.api.room.powerlevels.canBan -import io.element.android.libraries.matrix.api.room.powerlevels.canKick +import io.element.android.libraries.matrix.ui.room.canBanAsState +import io.element.android.libraries.matrix.ui.room.canKickAsState +import io.element.android.libraries.matrix.ui.room.isDmAsState +import io.element.android.libraries.matrix.ui.room.userPowerLevelAsState import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.drop @@ -45,21 +44,39 @@ class RoomMembersModerationPresenter @Inject constructor( ) : Presenter { private var selectedMember by mutableStateOf(null) - private suspend fun canBan() = room.canBan().getOrDefault(false) - private suspend fun canKick() = room.canKick().getOrDefault(false) - @Composable override fun present(): RoomMembersModerationState { val coroutineScope = rememberCoroutineScope() - var moderationActions by remember { mutableStateOf(persistentListOf()) } - val syncUpdateFlow = room.syncUpdateFlow.collectAsState() - val canDisplayModerationActions by produceState( - initialValue = false, - key1 = syncUpdateFlow.value - ) { - value = !room.isDm && (canBan() || canKick()) + val canBan by room.canBanAsState(syncUpdateFlow.value) + val canKick by room.canKickAsState(syncUpdateFlow.value) + val isDm by room.isDmAsState(syncUpdateFlow.value) + val currentUserMemberPowerLevel by room.userPowerLevelAsState(syncUpdateFlow.value) + + val canDisplayModerationActions by remember { + derivedStateOf { !isDm && (canBan || canKick) } } + val canDisplayBannedUsers by remember { + derivedStateOf { !isDm && canBan } + } + val moderationActions by remember { + derivedStateOf { + buildList { + selectedMember?.let { roomMember -> + add(ModerationAction.DisplayProfile(roomMember.userId)) + if (currentUserMemberPowerLevel > roomMember.powerLevel) { + if (canKick) { + add(ModerationAction.KickUser(roomMember.userId)) + } + if (canBan) { + add(ModerationAction.BanUser(roomMember.userId)) + } + } + } + }.toPersistentList() + } + } + val kickUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction) } val banUserAsyncAction = @@ -67,64 +84,39 @@ class RoomMembersModerationPresenter @Inject constructor( val unbanUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction) } - val canDisplayBannedUsers by produceState(initialValue = false) { - value = !room.isDm && canBan() - } - fun handleEvent(event: RoomMembersModerationEvents) { when (event) { is RoomMembersModerationEvents.SelectRoomMember -> { - coroutineScope.launch { + if (event.roomMember.membership == RoomMembershipState.BAN && canBan) { + // In this case the view will render a dialog to confirm the unbanning of the user + unbanUserAsyncAction.value = ConfirmingRoomMemberAction(event.roomMember) + } else { + // In this case the view will render a bottom sheet. selectedMember = event.roomMember - if (event.roomMember.membership == RoomMembershipState.BAN && canBan()) { - unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams - } else { - moderationActions = buildList { - add(ModerationAction.DisplayProfile(event.roomMember.userId)) - val currentUserMemberPowerLevel = room.userRole(room.sessionId) - .getOrDefault(RoomMember.Role.USER) - .powerLevel - if (currentUserMemberPowerLevel > event.roomMember.powerLevel) { - if (canKick()) { - add(ModerationAction.KickUser(event.roomMember.userId)) - } - if (canBan()) { - add(ModerationAction.BanUser(event.roomMember.userId)) - } - } - }.toPersistentList() - } } } is RoomMembersModerationEvents.KickUser -> { - moderationActions = persistentListOf() selectedMember?.let { coroutineScope.kickUser(it.userId, kickUserAsyncAction) } + selectedMember = null } is RoomMembersModerationEvents.BanUser -> { if (banUserAsyncAction.value.isConfirming()) { - moderationActions = persistentListOf() selectedMember?.let { coroutineScope.banUser(it.userId, banUserAsyncAction) } + selectedMember = null } else { banUserAsyncAction.value = AsyncAction.ConfirmingNoParams } } is RoomMembersModerationEvents.UnbanUser -> { - if (unbanUserAsyncAction.value.isConfirming()) { - moderationActions = persistentListOf() - selectedMember?.let { - coroutineScope.unbanUser(it.userId, unbanUserAsyncAction) - } - } else { - unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams - } + // We are already confirming when we are reaching this point + coroutineScope.unbanUser(event.userId, unbanUserAsyncAction) } is RoomMembersModerationEvents.Reset -> { selectedMember = null - moderationActions = persistentListOf() kickUserAsyncAction.value = AsyncAction.Uninitialized banUserAsyncAction.value = AsyncAction.Uninitialized unbanUserAsyncAction.value = AsyncAction.Uninitialized @@ -149,7 +141,7 @@ class RoomMembersModerationPresenter @Inject constructor( kickUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(kickUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.KickMember)) - room.kickUser(userId).finally { selectedMember = null } + room.kickUser(userId) } private fun CoroutineScope.banUser( @@ -157,7 +149,7 @@ class RoomMembersModerationPresenter @Inject constructor( banUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(banUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.BanMember)) - room.banUser(userId).finally { selectedMember = null } + room.banUser(userId) } private fun CoroutineScope.unbanUser( @@ -165,7 +157,7 @@ class RoomMembersModerationPresenter @Inject constructor( unbanUserAction: MutableState>, ) = runActionAndWaitForMembershipChange(unbanUserAction) { analyticsService.capture(RoomModeration(RoomModeration.Action.UnbanMember)) - room.unbanUser(userId).finally { selectedMember = null } + room.unbanUser(userId) } private fun CoroutineScope.runActionAndWaitForMembershipChange( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt index 221bce15ff..8f05d36da6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationStateProvider.kt @@ -60,7 +60,7 @@ class RoomMembersModerationStateProvider : PreviewParameterProvider { - state.selectedRoomMember?.let { + if (action is ConfirmingRoomMemberAction) { ConfirmationDialog( title = stringResource(R.string.screen_room_member_list_manage_member_unban_title), content = stringResource(R.string.screen_room_member_list_manage_member_unban_message), submitText = stringResource(R.string.screen_room_member_list_manage_member_unban_action), - onSubmitClick = { state.eventSink(RoomMembersModerationEvents.UnbanUser) }, + onSubmitClick = { + val userDisplayName = action.roomMember.getBestName() + asyncIndicatorState.enqueue { + AsyncIndicator.Loading(text = stringResource(R.string.screen_room_member_list_unbanning_user, userDisplayName)) + } + state.eventSink(RoomMembersModerationEvents.UnbanUser(action.roomMember.userId)) + }, onDismiss = { state.eventSink(RoomMembersModerationEvents.Reset) }, ) } } - is AsyncAction.Loading -> { - LaunchedEffect(action) { - val userDisplayName = state.selectedRoomMember?.getBestName().orEmpty() - asyncIndicatorState.enqueue { - AsyncIndicator.Loading(text = stringResource(R.string.screen_room_member_list_unbanning_user, userDisplayName)) - } - } - } is AsyncAction.Failure -> { Timber.e(action.error, "Failed to unban user.") LaunchedEffect(action) { @@ -178,7 +176,8 @@ fun RoomMembersModerationView( is AsyncAction.Success -> { LaunchedEffect(action) { asyncIndicatorState.clear() } } - else -> Unit + is AsyncAction.Loading, + AsyncAction.Uninitialized -> Unit } } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt index a4308e23aa..423b93fc63 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationPresenterTest.kt @@ -14,6 +14,7 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.RoomModeration import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.aVictor +import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingRoomMemberAction import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter @@ -37,7 +38,14 @@ import org.junit.Test class RoomMembersModerationPresenterTest { @Test fun `canDisplayModerationActions - when room is DM is false`() = runTest { - val room = FakeMatrixRoom(isDirect = true, isPublic = true, activeMemberCount = 2).apply { + val room = FakeMatrixRoom( + isDirect = true, + isPublic = true, + activeMemberCount = 2, + canKickResult = { Result.success(true) }, + canBanResult = { Result.success(true) }, + userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, + ).apply { givenRoomInfo(aRoomInfo(isDirect = true, isPublic = false, activeMembersCount = 2)) } val presenter = createRoomMembersModerationPresenter(matrixRoom = room) @@ -53,6 +61,7 @@ class RoomMembersModerationPresenterTest { activeMemberCount = 10, canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, + userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, ) val presenter = createRoomMembersModerationPresenter(matrixRoom = room) presenter.test { @@ -66,7 +75,9 @@ class RoomMembersModerationPresenterTest { val room = FakeMatrixRoom( isDirect = false, activeMemberCount = 10, + canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, + userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, ) val presenter = createRoomMembersModerationPresenter(matrixRoom = room) presenter.test { @@ -141,8 +152,8 @@ class RoomMembersModerationPresenterTest { skipItems(1) awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) with(awaitItem()) { - assertThat(selectedRoomMember).isNotNull() - assertThat(unbanUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams) + assertThat(selectedRoomMember).isNull() + assertThat(unbanUserAsyncAction).isEqualTo(ConfirmingRoomMemberAction(selectedMember)) } } } @@ -165,8 +176,9 @@ class RoomMembersModerationPresenterTest { awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) awaitItem().eventSink(RoomMembersModerationEvents.KickUser) skipItems(1) - assertThat(awaitItem().actions).isEmpty() - assertThat(awaitItem().kickUserAsyncAction).isEqualTo(AsyncAction.Loading) + val loadingState = awaitItem() + assertThat(loadingState.actions).isEmpty() + assertThat(loadingState.kickUserAsyncAction).isEqualTo(AsyncAction.Loading) with(awaitItem()) { assertThat(kickUserAsyncAction).isEqualTo(AsyncAction.Success(Unit)) assertThat(selectedRoomMember).isNull() @@ -198,8 +210,10 @@ class RoomMembersModerationPresenterTest { // Confirm confirmingState.eventSink(RoomMembersModerationEvents.BanUser) skipItems(1) - assertThat(awaitItem().actions).isEmpty() - assertThat(awaitItem().banUserAsyncAction).isEqualTo(AsyncAction.Loading) + val loadingItem = awaitItem() + assertThat(loadingItem.actions).isEmpty() + assertThat(loadingItem.selectedRoomMember).isNull() + assertThat(loadingItem.banUserAsyncAction).isEqualTo(AsyncAction.Loading) with(awaitItem()) { assertThat(banUserAsyncAction).isEqualTo(AsyncAction.Success(Unit)) assertThat(selectedRoomMember).isNull() @@ -225,11 +239,14 @@ class RoomMembersModerationPresenterTest { presenter.present() }.test { skipItems(1) - // Displays confirmation dialog + // Displays unban confirmation dialog awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember)) + val confirmingState = awaitItem() + assertThat(confirmingState.selectedRoomMember).isNull() + assertThat(confirmingState.actions).isEmpty() + assertThat(confirmingState.unbanUserAsyncAction).isEqualTo(ConfirmingRoomMemberAction(selectedMember)) // Confirms unban - awaitItem().eventSink(RoomMembersModerationEvents.UnbanUser) - assertThat(awaitItem().actions).isEmpty() + confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(selectedMember.userId)) assertThat(awaitItem().unbanUserAsyncAction).isEqualTo(AsyncAction.Loading) with(awaitItem()) { assertThat(unbanUserAsyncAction).isEqualTo(AsyncAction.Success(Unit)) @@ -251,12 +268,13 @@ class RoomMembersModerationPresenterTest { presenter.present() }.test { skipItems(1) - // Displays confirmation dialog + // Select a user awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor())) // Reset state awaitItem().eventSink(RoomMembersModerationEvents.Reset) - assertThat(awaitItem().selectedRoomMember).isNull() - assertThat(awaitItem().actions).isEmpty() + val finalItem = awaitItem() + assertThat(finalItem.selectedRoomMember).isNull() + assertThat(finalItem.actions).isEmpty() } } @@ -278,7 +296,7 @@ class RoomMembersModerationPresenterTest { // Kick user and fail awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor())) awaitItem().eventSink(RoomMembersModerationEvents.KickUser) - skipItems(2) + skipItems(1) assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java) // Reset it @@ -289,7 +307,7 @@ class RoomMembersModerationPresenterTest { initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor())) awaitItem().eventSink(RoomMembersModerationEvents.BanUser) awaitItem().eventSink(RoomMembersModerationEvents.BanUser) - skipItems(2) + skipItems(1) assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java) // Reset it @@ -300,8 +318,7 @@ class RoomMembersModerationPresenterTest { initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor().copy(membership = RoomMembershipState.BAN))) val confirmingState = awaitItem() assertThat(confirmingState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Confirming::class.java) - confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser) - skipItems(1) + confirmingState.eventSink(RoomMembersModerationEvents.UnbanUser(aVictor().userId)) assertThat(awaitItem().unbanUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java) assertThat(awaitItem().unbanUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java) // Reset it diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt index 723f94d20a..02fdff8036 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/RoomMembersModerationViewTest.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomdetails.impl.R import io.element.android.features.roomdetails.impl.members.anAlice +import io.element.android.features.roomdetails.impl.members.moderation.ConfirmingRoomMemberAction import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState @@ -164,7 +165,7 @@ class RoomMembersModerationViewTest { val roomMember = anAlice() val state = aRoomMembersModerationState( selectedRoomMember = roomMember, - unbanUserAsyncAction = AsyncAction.ConfirmingNoParams, + unbanUserAsyncAction = ConfirmingRoomMemberAction(roomMember), eventSink = eventsRecorder ) rule.setRoomMembersModerationView( @@ -181,7 +182,7 @@ class RoomMembersModerationViewTest { val roomMember = anAlice() val state = aRoomMembersModerationState( selectedRoomMember = roomMember, - unbanUserAsyncAction = AsyncAction.ConfirmingNoParams, + unbanUserAsyncAction = ConfirmingRoomMemberAction(roomMember), eventSink = eventsRecorder ) rule.setRoomMembersModerationView( @@ -189,7 +190,7 @@ class RoomMembersModerationViewTest { ) // Note: the string key semantics is not perfect here :/ rule.clickOn(R.string.screen_room_member_list_manage_member_unban_action) - eventsRecorder.assertSingle(RoomMembersModerationEvents.UnbanUser) + eventsRecorder.assertSingle(RoomMembersModerationEvents.UnbanUser(roomMember.userId)) } } 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 index 3dd81344fc..81ae3e6b89 100644 --- 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 @@ -15,7 +15,10 @@ import androidx.compose.runtime.produceState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.isDm +import io.element.android.libraries.matrix.api.room.powerlevels.canBan import io.element.android.libraries.matrix.api.room.powerlevels.canInvite +import io.element.android.libraries.matrix.api.room.powerlevels.canKick import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage @@ -62,6 +65,36 @@ fun MatrixRoom.canPinUnpin(updateKey: Long): State { } } +@Composable +fun MatrixRoom.isDmAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = isDm + } +} + +@Composable +fun MatrixRoom.canKickAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canKick().getOrElse { false } + } +} + +@Composable +fun MatrixRoom.canBanAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canBan().getOrElse { false } + } +} + +@Composable +fun MatrixRoom.userPowerLevelAsState(updateKey: Long): State { + return produceState(initialValue = 0, key1 = updateKey) { + value = userRole(sessionId) + .getOrDefault(RoomMember.Role.USER) + .powerLevel + } +} + @Composable fun MatrixRoom.isOwnUserAdmin(): Boolean { val roomInfo by roomInfoFlow.collectAsState(initial = null) diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png index dfd31916cb..1b6fb4bab8 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e0b84ff9cdbc6cc203304ff350789437533f9f7a1d95e8a196cce3585c454ec -size 9143 +oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650 +size 3642 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png index 24e6d2e293..d6fd8eeb70 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b4fd072108b60d09d5c37c56e0e97272a1664ca5d21a27152e111539ac1a640 -size 7861 +oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd +size 3659