Add kick (remove) confirmation and reason (#4507)
* Add confirmation dialog when kicking someone, ith ability to provide a reason. Also add the reason for banning people. * Fix padding issue in dialogs. * Improve TextField in dialog. * Update screenshots * Fix tests * Format and import * Add missing UI tests. * Use `needsConfirmation` as it's already used in the code base. --------- Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
parent
1eff15d6e4
commit
0545c56486
39 changed files with 367 additions and 91 deletions
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright 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.features.roomdetails.impl.members.moderation
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
data class ConfirmingWithReason(
|
||||
val reason: String,
|
||||
) : AsyncAction.Confirming
|
||||
|
|
@ -12,8 +12,8 @@ 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 class KickUser(val reason: String, val needsConfirmation: Boolean) : RoomMembersModerationEvents
|
||||
data class BanUser(val reason: String, val needsConfirmation: Boolean) : RoomMembersModerationEvents
|
||||
data class UnbanUser(val userId: UserId) : RoomMembersModerationEvents
|
||||
data object Reset : RoomMembersModerationEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,19 +96,23 @@ class RoomMembersModerationPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
is RoomMembersModerationEvents.KickUser -> {
|
||||
selectedMember?.let {
|
||||
coroutineScope.kickUser(it.userId, kickUserAsyncAction)
|
||||
}
|
||||
selectedMember = null
|
||||
}
|
||||
is RoomMembersModerationEvents.BanUser -> {
|
||||
if (banUserAsyncAction.value.isConfirming()) {
|
||||
if (event.needsConfirmation) {
|
||||
kickUserAsyncAction.value = ConfirmingWithReason(event.reason)
|
||||
} else {
|
||||
selectedMember?.let {
|
||||
coroutineScope.banUser(it.userId, banUserAsyncAction)
|
||||
coroutineScope.kickUser(it.userId, event.reason, kickUserAsyncAction)
|
||||
}
|
||||
selectedMember = null
|
||||
}
|
||||
}
|
||||
is RoomMembersModerationEvents.BanUser -> {
|
||||
if (event.needsConfirmation) {
|
||||
banUserAsyncAction.value = ConfirmingWithReason(event.reason)
|
||||
} else {
|
||||
banUserAsyncAction.value = AsyncAction.ConfirmingNoParams
|
||||
selectedMember?.let {
|
||||
coroutineScope.banUser(it.userId, event.reason, banUserAsyncAction)
|
||||
}
|
||||
selectedMember = null
|
||||
}
|
||||
}
|
||||
is RoomMembersModerationEvents.UnbanUser -> {
|
||||
|
|
@ -138,18 +142,26 @@ class RoomMembersModerationPresenter @Inject constructor(
|
|||
|
||||
private fun CoroutineScope.kickUser(
|
||||
userId: UserId,
|
||||
reason: String,
|
||||
kickUserAction: MutableState<AsyncAction<Unit>>,
|
||||
) = runActionAndWaitForMembershipChange(kickUserAction) {
|
||||
analyticsService.capture(RoomModeration(RoomModeration.Action.KickMember))
|
||||
room.kickUser(userId)
|
||||
room.kickUser(
|
||||
userId = userId,
|
||||
reason = reason.takeIf { it.isNotBlank() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.banUser(
|
||||
userId: UserId,
|
||||
reason: String,
|
||||
banUserAction: MutableState<AsyncAction<Unit>>,
|
||||
) = runActionAndWaitForMembershipChange(banUserAction) {
|
||||
analyticsService.capture(RoomModeration(RoomModeration.Action.BanMember))
|
||||
room.banUser(userId)
|
||||
room.banUser(
|
||||
userId = userId,
|
||||
reason = reason.takeIf { it.isNotBlank() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.unbanUser(
|
||||
|
|
|
|||
|
|
@ -37,10 +37,26 @@ class RoomMembersModerationStateProvider : PreviewParameterProvider<RoomMembersM
|
|||
ModerationAction.BanUser(userId = anAlice().userId),
|
||||
),
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
kickUserAsyncAction = ConfirmingWithReason(""),
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
kickUserAsyncAction = ConfirmingWithReason("A reason"),
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
kickUserAsyncAction = AsyncAction.Loading,
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
banUserAsyncAction = ConfirmingWithReason(""),
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
banUserAsyncAction = ConfirmingWithReason("A reason"),
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
banUserAsyncAction = AsyncAction.Loading,
|
||||
|
|
@ -54,10 +70,6 @@ class RoomMembersModerationStateProvider : PreviewParameterProvider<RoomMembersM
|
|||
banUserAsyncAction = AsyncAction.Failure(Exception("Failed to ban user")),
|
||||
unbanUserAsyncAction = AsyncAction.Failure(Exception("Failed to unban user")),
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
),
|
||||
aRoomMembersModerationState(
|
||||
selectedRoomMember = anAlice(),
|
||||
unbanUserAsyncAction = ConfirmingRoomMemberAction(anAlice()),
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ import io.element.android.libraries.designsystem.components.async.rememberAsyncI
|
|||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ListDialog
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.list.TextFieldListItem
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
|
|
@ -72,10 +74,10 @@ fun RoomMembersModerationView(
|
|||
onDisplayMemberProfile(action.userId)
|
||||
}
|
||||
is ModerationAction.KickUser -> {
|
||||
state.eventSink(RoomMembersModerationEvents.KickUser)
|
||||
state.eventSink(RoomMembersModerationEvents.KickUser(reason = "", needsConfirmation = true))
|
||||
}
|
||||
is ModerationAction.BanUser -> {
|
||||
state.eventSink(RoomMembersModerationEvents.BanUser)
|
||||
state.eventSink(RoomMembersModerationEvents.BanUser(reason = "", needsConfirmation = true))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -87,6 +89,47 @@ fun RoomMembersModerationView(
|
|||
AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), state = asyncIndicatorState)
|
||||
|
||||
when (val action = state.kickUserAsyncAction) {
|
||||
is AsyncAction.Confirming -> {
|
||||
if (action is ConfirmingWithReason) {
|
||||
ListDialog(
|
||||
title = stringResource(R.string.screen_room_member_list_kick_member_confirmation_title),
|
||||
submitText = stringResource(R.string.screen_room_member_list_kick_member_confirmation_action),
|
||||
onSubmit = {
|
||||
state.eventSink(
|
||||
RoomMembersModerationEvents.KickUser(
|
||||
reason = action.reason,
|
||||
needsConfirmation = false,
|
||||
)
|
||||
)
|
||||
},
|
||||
applyPaddingToContents = true,
|
||||
onDismissRequest = { state.eventSink(RoomMembersModerationEvents.Reset) },
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_room_member_list_kick_member_confirmation_description),
|
||||
style = ElementTheme.materialTypography.bodyMedium,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextFieldListItem(
|
||||
placeholder = stringResource(id = CommonStrings.common_reason),
|
||||
label = stringResource(id = CommonStrings.common_reason),
|
||||
withBorder = true,
|
||||
text = action.reason,
|
||||
onTextChange = { newText ->
|
||||
state.eventSink(
|
||||
RoomMembersModerationEvents.KickUser(
|
||||
reason = newText,
|
||||
needsConfirmation = true,
|
||||
)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is AsyncAction.Loading -> {
|
||||
LaunchedEffect(action) {
|
||||
val userDisplayName = state.selectedRoomMember?.getBestName().orEmpty()
|
||||
|
|
@ -113,13 +156,45 @@ fun RoomMembersModerationView(
|
|||
|
||||
when (val action = state.banUserAsyncAction) {
|
||||
is AsyncAction.Confirming -> {
|
||||
ConfirmationDialog(
|
||||
title = stringResource(R.string.screen_room_member_list_ban_member_confirmation_title),
|
||||
content = stringResource(R.string.screen_room_member_list_ban_member_confirmation_description),
|
||||
submitText = stringResource(R.string.screen_room_member_list_ban_member_confirmation_action),
|
||||
onSubmitClick = { state.eventSink(RoomMembersModerationEvents.BanUser) },
|
||||
onDismiss = { state.eventSink(RoomMembersModerationEvents.Reset) }
|
||||
)
|
||||
if (action is ConfirmingWithReason) {
|
||||
ListDialog(
|
||||
title = stringResource(R.string.screen_room_member_list_ban_member_confirmation_title),
|
||||
submitText = stringResource(R.string.screen_room_member_list_ban_member_confirmation_action),
|
||||
onSubmit = {
|
||||
state.eventSink(
|
||||
RoomMembersModerationEvents.BanUser(
|
||||
reason = action.reason,
|
||||
needsConfirmation = false,
|
||||
)
|
||||
)
|
||||
},
|
||||
applyPaddingToContents = true,
|
||||
onDismissRequest = { state.eventSink(RoomMembersModerationEvents.Reset) },
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_room_member_list_ban_member_confirmation_description),
|
||||
style = ElementTheme.materialTypography.bodyMedium,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TextFieldListItem(
|
||||
placeholder = stringResource(id = CommonStrings.common_reason),
|
||||
label = stringResource(id = CommonStrings.common_reason),
|
||||
withBorder = true,
|
||||
text = action.reason,
|
||||
onTextChange = { newText ->
|
||||
state.eventSink(
|
||||
RoomMembersModerationEvents.BanUser(
|
||||
reason = newText,
|
||||
needsConfirmation = true,
|
||||
)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is AsyncAction.Loading -> {
|
||||
LaunchedEffect(action) {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@
|
|||
<item quantity="one">"%1$d person"</item>
|
||||
<item quantity="other">"%1$d people"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_member_list_kick_member_confirmation_action">"Remove"</string>
|
||||
<string name="screen_room_member_list_kick_member_confirmation_description">"They will be able to join this room again if invited."</string>
|
||||
<string name="screen_room_member_list_kick_member_confirmation_title">"Are you sure you want to remove this member?"</string>
|
||||
<string name="screen_room_member_list_manage_member_ban">"Remove and ban member"</string>
|
||||
<string name="screen_room_member_list_manage_member_remove">"Remove from room"</string>
|
||||
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Remove and ban member"</string>
|
||||
|
|
|
|||
|
|
@ -17,14 +17,18 @@ import io.element.android.features.roomdetails.impl.members.aRoomMember
|
|||
import io.element.android.features.roomdetails.impl.members.aVictor
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.test.A_REASON
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -153,13 +157,14 @@ class RoomMembersModerationPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - Kick removes the user`() = runTest {
|
||||
fun `present - Kick requires confirmation and then kicks the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val kickUserResult = lambdaRecorder<UserId, String?, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
kickUserResult = { _, _ -> Result.success(Unit) },
|
||||
kickUserResult = kickUserResult,
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||
|
|
@ -168,7 +173,15 @@ class RoomMembersModerationPresenterTest {
|
|||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.KickUser)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.KickUser(reason = "", needsConfirmation = true))
|
||||
val confirmingState = awaitItem()
|
||||
assertThat(confirmingState.kickUserAsyncAction).isEqualTo(ConfirmingWithReason(""))
|
||||
// Change the reason
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.KickUser(reason = A_REASON, needsConfirmation = true))
|
||||
val confirmingWithReasonState = awaitItem()
|
||||
assertThat(confirmingWithReasonState.kickUserAsyncAction).isEqualTo(ConfirmingWithReason(A_REASON))
|
||||
// Confirm
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.KickUser(reason = A_REASON, needsConfirmation = false))
|
||||
skipItems(1)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.actions).isEmpty()
|
||||
|
|
@ -178,17 +191,22 @@ class RoomMembersModerationPresenterTest {
|
|||
assertThat(selectedRoomMember).isNull()
|
||||
}
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.KickMember))
|
||||
kickUserResult.assertions().isCalledOnce().with(
|
||||
value(selectedMember.userId),
|
||||
value(A_REASON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val banUserResult = lambdaRecorder<UserId, String?, Result<Unit>> { _, _ -> Result.success(Unit) }
|
||||
val room = aMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
banUserResult = { _, _ -> Result.success(Unit) },
|
||||
banUserResult = banUserResult,
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||
|
|
@ -197,12 +215,15 @@ class RoomMembersModerationPresenterTest {
|
|||
}.test {
|
||||
skipItems(1)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(selectedMember))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.BanUser)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.BanUser(reason = "", needsConfirmation = true))
|
||||
val confirmingState = awaitItem()
|
||||
assertThat(confirmingState.banUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
|
||||
assertThat(confirmingState.banUserAsyncAction).isEqualTo(ConfirmingWithReason(""))
|
||||
// Change the reason
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.BanUser(reason = A_REASON, needsConfirmation = true))
|
||||
val confirmingWithReasonState = awaitItem()
|
||||
assertThat(confirmingWithReasonState.banUserAsyncAction).isEqualTo(ConfirmingWithReason(A_REASON))
|
||||
// Confirm
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.BanUser)
|
||||
confirmingState.eventSink(RoomMembersModerationEvents.BanUser(reason = A_REASON, needsConfirmation = false))
|
||||
skipItems(1)
|
||||
val loadingItem = awaitItem()
|
||||
assertThat(loadingItem.actions).isEmpty()
|
||||
|
|
@ -213,6 +234,10 @@ class RoomMembersModerationPresenterTest {
|
|||
assertThat(selectedRoomMember).isNull()
|
||||
}
|
||||
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.BanMember))
|
||||
banUserResult.assertions().isCalledOnce().with(
|
||||
value(selectedMember.userId),
|
||||
value(A_REASON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +314,7 @@ class RoomMembersModerationPresenterTest {
|
|||
val initialItem = awaitItem()
|
||||
// Kick user and fail
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.KickUser)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.KickUser(reason = "", needsConfirmation = false))
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().kickUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
|
|
@ -299,8 +324,7 @@ class RoomMembersModerationPresenterTest {
|
|||
|
||||
// Ban user and fail
|
||||
initialItem.eventSink(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.BanUser)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.BanUser)
|
||||
awaitItem().eventSink(RoomMembersModerationEvents.BanUser(reason = "", needsConfirmation = false))
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().banUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@ package io.element.android.features.roomdetails.impl.members.moderation
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
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.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.test.A_REASON
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
|
|
@ -92,7 +94,57 @@ class RoomMembersModerationViewTest {
|
|||
rule.clickOn(R.string.screen_room_member_list_manage_member_remove)
|
||||
// Give time for the bottom sheet to animate
|
||||
rule.mainClock.advanceTimeBy(1_000)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.KickUser)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.KickUser(reason = "", needsConfirmation = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cancelling 'Remove member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
kickUserAsyncAction = ConfirmingWithReason(A_REASON),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(CommonStrings.action_cancel)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.Reset)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove member' reason edition emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
kickUserAsyncAction = ConfirmingWithReason(A_REASON),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
rule.onNodeWithText(A_REASON).performTextInput("z")
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.KickUser(reason = "z$A_REASON", needsConfirmation = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
kickUserAsyncAction = ConfirmingWithReason(A_REASON),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(R.string.screen_room_member_list_kick_member_confirmation_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.KickUser(reason = A_REASON, needsConfirmation = false))
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
|
|
@ -116,7 +168,7 @@ class RoomMembersModerationViewTest {
|
|||
rule.clickOn(R.string.screen_room_member_list_manage_member_remove_confirmation_ban)
|
||||
// Give time for the bottom sheet to animate
|
||||
rule.mainClock.advanceTimeBy(1_000)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.BanUser)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.BanUser(reason = "", needsConfirmation = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -125,7 +177,7 @@ class RoomMembersModerationViewTest {
|
|||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
banUserAsyncAction = ConfirmingWithReason(A_REASON),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
|
|
@ -136,13 +188,29 @@ class RoomMembersModerationViewTest {
|
|||
eventsRecorder.assertSingle(RoomMembersModerationEvents.Reset)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove and ban member' reason edition emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
banUserAsyncAction = ConfirmingWithReason(A_REASON),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
state = state,
|
||||
)
|
||||
rule.onNodeWithText(A_REASON).performTextInput("z")
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.BanUser(reason = "z$A_REASON", needsConfirmation = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `confirming 'Remove and ban member' confirmation emits the expected event`() {
|
||||
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||
val roomMember = anAlice()
|
||||
val state = aRoomMembersModerationState(
|
||||
selectedRoomMember = roomMember,
|
||||
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
|
||||
banUserAsyncAction = ConfirmingWithReason(A_REASON),
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
rule.setRoomMembersModerationView(
|
||||
|
|
@ -150,7 +218,7 @@ class RoomMembersModerationViewTest {
|
|||
)
|
||||
// Note: the string key semantics is not perfect here :/
|
||||
rule.clickOn(R.string.screen_room_member_list_ban_member_confirmation_action)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.BanUser)
|
||||
eventsRecorder.assertSingle(RoomMembersModerationEvents.BanUser(reason = A_REASON, needsConfirmation = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue