First Commit

This commit is contained in:
2025-12-18 16:28:50 +07:00
commit 8c3e4f491f
9974 changed files with 396488 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
import extension.setupDependencyInjection
import extension.testCommonDependencies
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.
*/
plugins {
id("io.element.android-compose-library")
}
android {
namespace = "io.element.android.features.roommembermoderation.impl"
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
setupDependencyInjection()
dependencies {
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixui)
api(projects.features.roommembermoderation.api)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)
implementation(projects.services.analytics.compose)
testCommonDependencies(libs, true)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.libraries.testtags)
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.user.MatrixUser
import timber.log.Timber
@ContributesBinding(RoomScope::class)
class DefaultRoomMemberModerationRenderer : RoomMemberModerationRenderer {
@Composable
override fun Render(
state: RoomMemberModerationState,
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
modifier: Modifier
) {
if (state is InternalRoomMemberModerationState) {
RoomMemberModerationView(state, onSelectAction, modifier)
} else {
SideEffect {
Timber.d("RoomMemberModerationRenderer: Render called with unsupported state: $state")
}
}
}
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
sealed interface InternalRoomMemberModerationEvents : RoomMemberModerationEvents {
data class DoKickUser(val reason: String) : InternalRoomMemberModerationEvents
data class DoBanUser(val reason: String) : InternalRoomMemberModerationEvents
data class DoUnbanUser(val reason: String) : InternalRoomMemberModerationEvents
data object Reset : InternalRoomMemberModerationEvents
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
data class InternalRoomMemberModerationState(
override val canKick: Boolean,
override val canBan: Boolean,
val selectedUser: MatrixUser?,
val actions: ImmutableList<ModerationActionState>,
val kickUserAsyncAction: AsyncAction<Unit>,
val banUserAsyncAction: AsyncAction<Unit>,
val unbanUserAsyncAction: AsyncAction<Unit>,
override val eventSink: (RoomMemberModerationEvents) -> Unit,
) : RoomMemberModerationState {
val canDisplayActions = actions.isNotEmpty()
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.toImmutableList
class InternalRoomMemberModerationStateProvider : PreviewParameterProvider<InternalRoomMemberModerationState> {
override val values: Sequence<InternalRoomMemberModerationState>
get() = sequenceOf(
aRoomMembersModerationState(
selectedUser = anAlice(),
actions = listOf(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
),
),
aRoomMembersModerationState(
selectedUser = anAlice(),
actions = listOf(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = true),
),
),
aRoomMembersModerationState(
selectedUser = anAlice(),
actions = listOf(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = false),
ModerationActionState(action = ModerationAction.BanUser, isEnabled = true),
),
),
aRoomMembersModerationState(
selectedUser = anAlice(),
actions = listOf(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = false),
ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = true),
),
),
aRoomMembersModerationState(
selectedUser = anAlice(),
kickUserAsyncAction = AsyncAction.ConfirmingNoParams,
),
aRoomMembersModerationState(
selectedUser = anAlice(),
kickUserAsyncAction = AsyncAction.Loading,
),
aRoomMembersModerationState(
selectedUser = anAlice(),
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
),
aRoomMembersModerationState(
selectedUser = anAlice(),
banUserAsyncAction = AsyncAction.Loading,
),
aRoomMembersModerationState(
selectedUser = anAlice(),
unbanUserAsyncAction = AsyncAction.ConfirmingNoParams,
),
aRoomMembersModerationState(
selectedUser = anAlice(),
unbanUserAsyncAction = AsyncAction.Loading,
),
)
}
fun anAlice() = MatrixUser(
UserId(value = "@alice:server.org"),
displayName = "Alice",
avatarUrl = null,
)
fun aRoomMembersModerationState(
canKick: Boolean = false,
canBan: Boolean = false,
selectedUser: MatrixUser? = null,
actions: List<ModerationActionState> = emptyList(),
kickUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
banUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
unbanUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (RoomMemberModerationEvents) -> Unit = {},
) = InternalRoomMemberModerationState(
canKick = canKick,
canBan = canBan,
selectedUser = selectedUser,
actions = actions.toImmutableList(),
kickUserAsyncAction = kickUserAsyncAction,
banUserAsyncAction = banUserAsyncAction,
unbanUserAsyncAction = unbanUserAsyncAction,
eventSink = eventSink,
)

View File

@@ -0,0 +1,233 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import dev.zacsweers.metro.Inject
import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
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.matrix.api.core.UserId
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.RoomMembershipState
import io.element.android.libraries.matrix.api.room.roomMembers
import io.element.android.libraries.matrix.api.user.MatrixUser
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.userPowerLevelAsState
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.milliseconds
@Inject
class RoomMemberModerationPresenter(
private val room: JoinedRoom,
private val dispatchers: CoroutineDispatchers,
private val analyticsService: AnalyticsService,
) : Presenter<RoomMemberModerationState> {
@Composable
override fun present(): RoomMemberModerationState {
val coroutineScope = rememberCoroutineScope()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val canBan = room.canBanAsState(syncUpdateFlow.value)
val canKick = room.canKickAsState(syncUpdateFlow.value)
val currentUserMemberPowerLevel = room.userPowerLevelAsState(syncUpdateFlow.value)
val kickUserAsyncAction =
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
val banUserAsyncAction =
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
val unbanUserAsyncAction =
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
var selectedUser by remember {
mutableStateOf<MatrixUser?>(null)
}
val moderationActions = remember { mutableStateOf<ImmutableList<ModerationActionState>>(persistentListOf()) }
fun handleEvent(event: RoomMemberModerationEvents) {
when (event) {
is RoomMemberModerationEvents.ShowActionsForUser -> {
selectedUser = event.user
val member = room.membersStateFlow.value.roomMembers()?.firstOrNull {
it.userId == event.user.userId
}
moderationActions.value = computeModerationActions(
member = member,
canKick = canKick.value,
canBan = canBan.value,
currentUserMemberPowerLevel = currentUserMemberPowerLevel.value,
)
}
is RoomMemberModerationEvents.ProcessAction -> {
// First, hide any list of existing actions that could be displayed
moderationActions.value = persistentListOf()
when (event.action) {
is ModerationAction.DisplayProfile -> Unit
is ModerationAction.KickUser -> {
selectedUser = event.targetUser
kickUserAsyncAction.value = AsyncAction.ConfirmingNoParams
}
is ModerationAction.BanUser -> {
selectedUser = event.targetUser
banUserAsyncAction.value = AsyncAction.ConfirmingNoParams
}
is ModerationAction.UnbanUser -> {
selectedUser = event.targetUser
unbanUserAsyncAction.value = AsyncAction.ConfirmingNoParams
}
}
}
is InternalRoomMemberModerationEvents.DoKickUser -> {
selectedUser?.let {
coroutineScope.kickUser(it.userId, event.reason, kickUserAsyncAction)
}
selectedUser = null
}
is InternalRoomMemberModerationEvents.DoBanUser -> {
selectedUser?.let {
coroutineScope.banUser(it.userId, event.reason, banUserAsyncAction)
}
selectedUser = null
}
is InternalRoomMemberModerationEvents.DoUnbanUser -> {
selectedUser?.let {
coroutineScope.unbanUser(it.userId, event.reason, unbanUserAsyncAction)
}
selectedUser = null
}
is InternalRoomMemberModerationEvents.Reset -> {
selectedUser = null
moderationActions.value = persistentListOf()
kickUserAsyncAction.value = AsyncAction.Uninitialized
banUserAsyncAction.value = AsyncAction.Uninitialized
unbanUserAsyncAction.value = AsyncAction.Uninitialized
}
}
}
return InternalRoomMemberModerationState(
canKick = canKick.value,
canBan = canBan.value,
selectedUser = selectedUser,
actions = moderationActions.value,
kickUserAsyncAction = kickUserAsyncAction.value,
banUserAsyncAction = banUserAsyncAction.value,
unbanUserAsyncAction = unbanUserAsyncAction.value,
eventSink = ::handleEvent,
)
}
private fun computeModerationActions(
member: RoomMember?,
canKick: Boolean,
canBan: Boolean,
currentUserMemberPowerLevel: Long,
): ImmutableList<ModerationActionState> {
return buildList {
add(ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true))
// Assume the member is a regular user when it's unknown
val targetMemberPowerLevel = member?.powerLevel ?: 0
val canModerateThisUser = currentUserMemberPowerLevel > targetMemberPowerLevel
// Assume the member is joined when it's unknown
val membership = member?.membership ?: RoomMembershipState.JOIN
if (canKick) {
val isKickEnabled = canModerateThisUser && membership.isActive()
add(ModerationActionState(action = ModerationAction.KickUser, isEnabled = isKickEnabled))
}
if (canBan) {
if (membership == RoomMembershipState.BAN) {
add(ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = canModerateThisUser))
} else {
add(ModerationActionState(action = ModerationAction.BanUser, isEnabled = canModerateThisUser))
}
}
}.toImmutableList()
}
private fun CoroutineScope.kickUser(
userId: UserId,
reason: String,
kickUserAction: MutableState<AsyncAction<Unit>>,
) = runActionAndWaitForMembershipChange(kickUserAction) {
analyticsService.capture(RoomModeration(RoomModeration.Action.KickMember))
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 = userId,
reason = reason.takeIf { it.isNotBlank() },
)
}
private fun CoroutineScope.unbanUser(
userId: UserId,
reason: String,
unbanUserAction: MutableState<AsyncAction<Unit>>,
) = runActionAndWaitForMembershipChange(unbanUserAction) {
analyticsService.capture(RoomModeration(RoomModeration.Action.UnbanMember))
room.unbanUser(
userId = userId,
reason = reason.takeIf { it.isNotBlank() },
)
}
private fun <T> CoroutineScope.runActionAndWaitForMembershipChange(
action: MutableState<AsyncAction<T>>,
block: suspend () -> Result<T>
) {
launch(dispatchers.io) {
action.runUpdatingState {
val result = block()
if (result.isSuccess) {
// We wait a bit to ensure the server has processed the membership change
delay(50.milliseconds)
// Update the members to ensure we have the latest state
launch { room.updateMembers() }
// Wait for the membership change to be processed and returned
// We drop the first emission as it's the current state
room.membersStateFlow.drop(1).first()
}
result
}
}
}
}

View File

@@ -0,0 +1,345 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.components.async.AsyncIndicator
import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost
import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState
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.avatar.AvatarType
import io.element.android.libraries.designsystem.components.dialogs.TextFieldDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
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
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.ListItemStyle
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.launch
import timber.log.Timber
@Composable
fun RoomMemberModerationView(
state: InternalRoomMemberModerationState,
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
modifier: Modifier = Modifier,
) {
Box(modifier = modifier) {
val selectedUser = state.selectedUser
if (selectedUser != null && state.canDisplayActions) {
RoomMemberActionsBottomSheet(
user = selectedUser,
actions = state.actions,
onSelectAction = onSelectAction,
onDismiss = { state.eventSink(InternalRoomMemberModerationEvents.Reset) },
)
}
RoomMemberAsyncActions(state = state)
}
}
@Composable
private fun RoomMemberAsyncActions(
state: InternalRoomMemberModerationState,
modifier: Modifier = Modifier,
) {
Box(modifier = modifier) {
val selectedUser = state.selectedUser
val asyncIndicatorState = rememberAsyncIndicatorState()
AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), state = asyncIndicatorState)
when (val action = state.kickUserAsyncAction) {
is AsyncAction.Confirming -> {
TextFieldDialog(
title = stringResource(R.string.screen_bottom_sheet_manage_room_member_kick_member_confirmation_title),
submitText = stringResource(R.string.screen_bottom_sheet_manage_room_member_kick_member_confirmation_action),
destructiveSubmit = true,
minLines = 2,
onSubmit = { reason ->
state.eventSink(InternalRoomMemberModerationEvents.DoKickUser(reason = reason))
},
onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) },
placeholder = stringResource(id = CommonStrings.common_reason),
content = stringResource(R.string.screen_bottom_sheet_manage_room_member_kick_member_confirmation_description),
value = "",
)
}
is AsyncAction.Loading -> {
LaunchedEffect(action) {
val userDisplayName = selectedUser?.getBestName().orEmpty()
asyncIndicatorState.enqueue {
AsyncIndicator.Loading(text = stringResource(R.string.screen_bottom_sheet_manage_room_member_removing_user, userDisplayName))
}
}
}
is AsyncAction.Failure -> {
Timber.e(action.error, "Failed to kick user.")
LaunchedEffect(action) {
asyncIndicatorState.enqueue(AsyncIndicator.DURATION_SHORT) {
AsyncIndicator.Failure(
text = stringResource(CommonStrings.common_failed),
)
}
}
}
is AsyncAction.Success -> {
LaunchedEffect(action) { asyncIndicatorState.clear() }
}
else -> Unit
}
when (val action = state.banUserAsyncAction) {
is AsyncAction.Confirming -> {
TextFieldDialog(
title = stringResource(R.string.screen_bottom_sheet_manage_room_member_ban_member_confirmation_title),
submitText = stringResource(R.string.screen_bottom_sheet_manage_room_member_ban_member_confirmation_action),
destructiveSubmit = true,
minLines = 2,
onSubmit = { reason ->
state.eventSink(InternalRoomMemberModerationEvents.DoBanUser(reason = reason))
},
onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) },
placeholder = stringResource(id = CommonStrings.common_reason),
content = stringResource(R.string.screen_bottom_sheet_manage_room_member_ban_member_confirmation_description),
value = "",
)
}
is AsyncAction.Loading -> {
LaunchedEffect(action) {
val userDisplayName = selectedUser?.getBestName().orEmpty()
asyncIndicatorState.enqueue {
AsyncIndicator.Loading(text = stringResource(R.string.screen_bottom_sheet_manage_room_member_banning_user, userDisplayName))
}
}
}
is AsyncAction.Failure -> {
Timber.e(action.error, "Failed to ban user.")
LaunchedEffect(action) {
asyncIndicatorState.enqueue(AsyncIndicator.DURATION_SHORT) {
AsyncIndicator.Failure(
text = stringResource(CommonStrings.common_failed),
)
}
}
}
is AsyncAction.Success -> {
LaunchedEffect(action) { asyncIndicatorState.clear() }
}
else -> Unit
}
when (val action = state.unbanUserAsyncAction) {
is AsyncAction.Confirming -> {
TextFieldDialog(
title = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_title),
submitText = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_action),
destructiveSubmit = true,
minLines = 2,
onSubmit = { reason ->
val userDisplayName = selectedUser?.getBestName().orEmpty()
asyncIndicatorState.enqueue {
AsyncIndicator.Loading(text = stringResource(R.string.screen_bottom_sheet_manage_room_member_unbanning_user, userDisplayName))
}
state.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser(reason = reason))
},
onDismissRequest = { state.eventSink(InternalRoomMemberModerationEvents.Reset) },
placeholder = stringResource(id = CommonStrings.common_reason),
content = stringResource(R.string.screen_bottom_sheet_manage_room_member_unban_member_confirmation_description),
value = "",
)
}
is AsyncAction.Failure -> {
Timber.e(action.error, "Failed to unban user.")
LaunchedEffect(action) {
asyncIndicatorState.enqueue(AsyncIndicator.DURATION_SHORT) {
AsyncIndicator.Failure(
text = stringResource(CommonStrings.common_failed),
)
}
}
}
is AsyncAction.Success -> {
LaunchedEffect(action) { asyncIndicatorState.clear() }
}
is AsyncAction.Loading,
AsyncAction.Uninitialized -> Unit
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun RoomMemberActionsBottomSheet(
user: MatrixUser,
actions: ImmutableList<ModerationActionState>,
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
onDismiss: () -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(
modifier = Modifier.systemBarsPadding(),
sheetState = bottomSheetState,
onDismissRequest = {
coroutineScope.launch {
bottomSheetState.hide()
onDismiss()
}
},
) {
Column(
modifier = Modifier.padding(vertical = 16.dp)
) {
Avatar(
avatarData = user.getAvatarData(size = AvatarSize.RoomListManageUser),
avatarType = AvatarType.User,
modifier = Modifier
.padding(bottom = 24.dp)
.align(Alignment.CenterHorizontally)
)
val bestName = user.getBestName()
Text(
text = bestName,
style = ElementTheme.typography.fontHeadingLgBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
.fillMaxWidth()
)
// Show user ID only if it's different from the display name
if (bestName != user.userId.value) {
Text(
text = user.userId.value,
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
)
}
Spacer(modifier = Modifier.height(32.dp))
for (actionState in actions) {
when (val action = actionState.action) {
is ModerationAction.DisplayProfile -> {
ListItem(
style = ListItemStyle.Primary,
headlineContent = { Text(stringResource(R.string.screen_bottom_sheet_manage_room_member_member_user_info)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserProfile())),
onClick = {
coroutineScope.launch {
onSelectAction(action, user)
bottomSheetState.hide()
}
},
enabled = actionState.isEnabled
)
}
is ModerationAction.KickUser -> {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_bottom_sheet_manage_room_member_remove)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Close())),
style = ListItemStyle.Destructive,
onClick = {
coroutineScope.launch {
bottomSheetState.hide()
onSelectAction(action, user)
}
},
enabled = actionState.isEnabled
)
}
is ModerationAction.BanUser -> {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_bottom_sheet_manage_room_member_ban)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
style = ListItemStyle.Destructive,
onClick = {
coroutineScope.launch {
bottomSheetState.hide()
onSelectAction(action, user)
}
},
enabled = actionState.isEnabled
)
}
is ModerationAction.UnbanUser -> {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_bottom_sheet_manage_room_member_unban)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Restart())),
style = ListItemStyle.Destructive,
onClick = {
coroutineScope.launch {
bottomSheetState.hide()
onSelectAction(action, user)
}
},
enabled = actionState.isEnabled
)
}
}
}
}
}
}
@PreviewsDayNight
@Composable
internal fun RoomMemberModerationViewPreview(@PreviewParameter(InternalRoomMemberModerationStateProvider::class) state: InternalRoomMemberModerationState) {
ElementPreview {
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 64.dp)
) {
RoomMemberModerationView(
state = state,
onSelectAction = { _, _ ->
},
)
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl.di
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.Binds
import dev.zacsweers.metro.ContributesTo
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.features.roommembermoderation.impl.RoomMemberModerationPresenter
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
@ContributesTo(RoomScope::class)
@BindingContainer
interface RoomMemberModerationModule {
@Binds
fun bindRoomMemberModerationPresenter(presenter: RoomMemberModerationPresenter): Presenter<RoomMemberModerationState>
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Выдаліць і заблакіраваць удзельніка"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Заблакіраваць"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Яны не змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Вы ўпэўнены, што хочаце заблакіраваць гэтага карыстальніка?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Блакіроўка %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Яны змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Прагляд профілю"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Выдаліць удзельніка з пакоя"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Выдаліць удзельніка і забараніць далучацца ў будучыні?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Выдаленне %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Разблакіраваць"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Разблакіроўка %1$s"</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Преглед на профила"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Odebrat a vykázat člena"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Vykázat"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Nebudou se moci znovu připojit k této místnosti, pokud budou pozváni."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Jste si jisti, že chcete vykázat tohoto člena?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Pokud budou pozváni, nebudou moci vstoupit do tohoto prostoru, ale stále si zachovají členství ve všech místnostech nebo podprostorech."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Vykazování %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Odebrat"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Pokud budou pozváni, budou se moci do této místnosti znovu připojit."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Opravdu chcete tohoto člena odebrat?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Pokud budou pozváni, budou moci vstoupit do tohoto prostoru a zachovají si členství ve všech místnostech nebo podprostorech."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Zobrazit profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Odebrat uživatele"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Odebrat člena a zakázat mu připojení v budoucnu?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Odstraňování %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Zrušit vykázání z místnosti"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Zrušit vykázání"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Pokud by byli pozváni, mohli by se znovu připojit do místnosti"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Opravdu chcete zrušit vykázání tohoto člena?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Rušení vykázání %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Gwahardd o ystafell"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Atal"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Fyddan nhw ddim yn gallu ymuno â\'r ystafell hon eto os cân nhw wahoddiad."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Ydych chi\'n siŵr eich bod am wahardd yr aelod hwn?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Yn gwahardd %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Tynnu"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Fyddan nhw yn gallu ymuno â\'r ystafell hon eto os cân nhw wahoddiad."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Ydych chi\'n siŵr eich bod am ddileu\'r aelod hwn?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Gweld proffil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Tynnu o\'r ystafell"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Dileu aelod a\'u gwahardd rhag ymuno yn y dyfodol?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Wrthi\'n dileu %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Dad-wahardd o\'r ystafell"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Adfer"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Bydden nhw\'n gallu ymuno â\'r ystafell eto os fydd rhywun yn eu gwahodd"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Ydych chi\'n siŵr eich bod chi eisiau dadwahardd yr aelod hwn?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Dad-wahardd %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Spær fra rum"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Spær"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"De vil ikke være i stand til at deltage i dette rum igen, selv om de inviteres."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Er du stikker på, at du ønsker at spærre dette medlem?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"De vil ikke kunne deltage i gruppen igen, selv hvis de bliver inviteret. Men vil beholde deres medlemskaber i rum og undergrupper."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Spærrer %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Fjern"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"De vil være i stand til at deltage i dette rum igen, hvis de inviteres."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Er du sikker på, at du vil fjerne dette medlem?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"De vil kunne deltage i dette rum igen, hvis de bliver inviteret, og de vil stadig beholde deres medlemskaber af eventuelle rum eller sub-grupper."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Se profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Fjern bruger"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Fjern medlem og udeluk dem fra at deltage i fremtiden?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Fjerner %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Fjern brugerens spærring fra rummet"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Fjern spærring af"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"De ville være i stand til at deltage i rummet igen, hvis de blev inviteret"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Er du sikker på, at du vil fjerne spærringen af dette medlem?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Ophæver spærring af %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Mitglied entfernen und sperren"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Sperren"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Sie können diesem Chat auch auf Einladung nicht erneut beitreten."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Möchtest du diesen Nutzer wirklich sperren?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s wird gesperrt."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Entfernen"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Die Nutzer können dem Chat wieder beitreten, wenn sie eingeladen werden."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Möchtest du dieses Mitglied wirklich entfernen?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Nutzerprofil anzeigen"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Mitglied entfernen"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Mitglied entfernen und für die Zukunft sperren?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s wird entfernt."</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Sperre für diesen Chat aufheben"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Sperre aufheben"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Sie können dann diesem Chat auf Einladung wieder beitreten."</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Möchtest du die Sperre dieses Mitglieds wirklich aufheben?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s wird entsperrt."</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Αφαίρεση και αποκλεισμός μέλους"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Αποκλεισμός"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Δεν θα μπορούν να ενταχθούν ξανά σε αυτή την αίθουσα, αν προσκληθούν."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Θες σίγουρα να αποκλείσεις αυτό το μέλος;"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Αποκλεισμός %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Αφαίρεση"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Θα μπορούν να συμμετάσχουν ξανά σε αυτή την αίθουσα, εάν προσκληθούν."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Είστε βέβαιοι ότι θέλετε να αφαιρέσετε αυτό το μέλος;"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Προβολή προφίλ"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Αφαίρεση από την αίθουσα"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Αφαίρεση μέλους και απαγόρευση συμμετοχής στο μέλλον;"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Αφαίρεση %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Άρση αποκλεισμού από την αίθουσα"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Αναίρεση αποκλεισμού"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Θα μπορούν να συμμετάσχουν και πάλι στην αίθουσα αν προσκληθούν"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Σίγουρα θες να καταργήσεις τον αποκλεισμό αυτού του μέλους;"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Άρση αποκλεισμού %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Sacar y vetar a un miembro"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Vetar"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"No podrán volver a unirse a esta sala si son invitados."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"¿Estás seguro de que quieres vetar a este miembro?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Vetando a %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Echar"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Podrá volver a unirse a esta sala si se le invita."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"¿Seguro que quieres echar a este miembro?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Ver perfil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Sacar de la sala"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"¿Sacar al miembro y prohibirle unirse en el futuro?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Eliminando %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Eliminar veto en la sala"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Quitar veto"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Podría volver a unirse a la sala si se le invita"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"¿Seguro que quieres levantarle el veto a este miembro?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Levantando veto a %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Eemalda ja sea suhtluskeeld"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Sea suhtluskeeld"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Ta ei saa selle jututoaga liituda isegi kutse olemasolul."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Kas sa oled kindel, et soovid sellele kasutajale seada suhtluskeelu?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Ta ei saa ka kutsumise puhul selle kogukonnaga uuesti liituda, kuid liikmelisus jututubades või alamkogukondades säilib."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Seame kasutajale %1$s suhtluskeelu"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Eemalda"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Kutse olemasolul saab ta nüüd jututoaga uuesti liituda"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Kas sa oled kindel, et soovid selle osaleja eemaldada?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Ta saab kutsumise puhul selle kogukonnaga uuesti liituda ning liikmelisus jututubades või alamkogukondades säilib."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Vaata profiili"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Eemalda kasutaja jututoast"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Kas eemaldama kasutaja ja seame talle tulevikuks suhtluskeelu?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Eemaldame kasutajat %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Eemalda suhtluskeeld jututoas"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Eemalda suhtluskeeld"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Ta võib kutse saamisel liituda jututoaga uuesti"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Kas oled kindel, et soovid selle liikme suhtluskeelu eemaldada?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Eemaldame suhtluskeelu kasutajalt %1$s"</string>
</resources>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Kendu kidea eta ezarri debekua"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Ezarri debekua"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Ziur kide honi debekua ezarri nahi diozula?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s(r)i debekua ezartzen"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Kendu"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Ikusi profila"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Kendu gelatik"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Kidea kendu eta etorkizunean sartzea debekatu?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s kentzen…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Kendu debekua"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s(r)i debekua kentzen"</string>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"برداشت و تحریم عضو"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"تحریم"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"در صورت دعوت نمی‌تواند دوباره به اتاق بپیوندد."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"مطمئنید می‌خواهید این عضو را تحریم کنید؟"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"تحریم کردن %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"برداشتن"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"در صورت دعوت می‌تواند دوباره به اتاق بپیوندد."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"مطمئنید می‌خواهید این عضو را بردارید؟"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"دیدن نمایه"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"برداشتن از اتاق"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"برداشتن عضو و تحریم پیوستن در آینده؟"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"برداشتن %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"تحریم نکردن از اتاق"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"رفع انسداد"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"رفع تحریم %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Poista jäsen huoneesta ja anna porttikielto"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Anna porttikielto"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"He eivät voi enää liittyä tähän huoneeseen, jos heidät kutsutaan."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Haluatko varmasti antaa tälle jäsenelle porttikiellon?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"He eivät voi liittyä tähän tilaan uudelleen, vaikka heidät kutsuttaisiin, mutta he säilyttävät jäsenyytensä muissa huoneissa tai alitiloissa."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Annetaan porttikieltoa käyttäjälle %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Poista"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"He voivat liittyä tähän huoneeseen uudelleen, jos heidät kutsutaan."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Haluatko varmasti poistaa tämän jäsenen?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"He voivat liittyä tähän tilaan uudelleen, jos heidät kutsutaan, ja he säilyttävät jäsenyytensä kaikissa huoneissa tai alitiloissa."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Näytä profiili"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Poista käyttäjä"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Poistetaanko jäsen huoneesta ja kielletäänkö heitä liittymästä tulevaisuudessa?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Poistetaan käyttäjää %1$s huoneesta…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Poista porttikielto huoneesta"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Poista porttikielto"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"He voivat liittyä huoneeseen uudelleen, jos heidät kutsutaan"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Haluatko varmasti poistaa tämän jäsenen porttikiellon?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Poistetaan käyttäjän %1$s porttikieltoa"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Bannir du salon"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Bannir"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Ce compte ne pourra pas rejoindre le salon à nouveau, même si il est invité."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Êtes-vous certain de vouloir bannir ce membre ?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Lutilisateur ne pourra plus accéder à cet espace même sur invitation, mais il restera membre de tous les salons et sous-espaces dont il est déjà membre."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Bannissement de %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Retirer"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Il pourra rejoindre le salon à nouveau si il est invité."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Voulez-vous vraiment supprimer ce membre ?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Lutilisateur pourra rejoindre cet espace à nouveau sil y est invité, et il restera membre de tous les salons et sous-espaces."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Voir le profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Exclure ce membre"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Retirer le membre et interdire ladhésion à lavenir ?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Enlever %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Débannir du salon"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Débannir"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Ce compte pourra à nouveau rejoindre le salon sil est invité."</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Êtes-vous sûr de vouloir débannir ce compte?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Débannissement de %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Eltávolítás és a tag kitiltása"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Kitiltás"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Többé nem csatlakozhat ehhez a szobához, akkor sem, ha meghívják."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Biztos, hogy kitiltja ezt a tagot?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Ha meghívják őket akkor sem tudnak újra csatlakozni ehhez a térhez, de továbbra is megtartják tagságukat a szobáikban vagy altereikben."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s kitiltása"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Eltávolítás"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Ehhez a szobához is csatlakozhat, ha meghívják."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Biztos, hogy eltávolítja ezt a tagot?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Ha meghívják őket újra csatlakozhatnak ehhez a térhez, és továbbra is megtartják a tagságukat a szobáikban vagy altereikben."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Profil megtekintése"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Felhasználó eltávolítása"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Eltávolítja a tagot, és megtiltja a jövőbeni csatlakozást?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s eltávolítása…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Visszaengedés a szobába"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Tiltás feloldása"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Újra beléphetnek a szobába, ha meghívják őket."</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Biztos, hogy feloldja a felhasználó kitiltását?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s tiltásának feloldása"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Keluarkan dan cekal anggota"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Cekal"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Mereka tidak akan dapat bergabung ke ruangan ini lagi jika diundang."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Apakah Anda yakin ingin mencekal anggota ini?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Mencekal %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Hapus"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Pengguna dapat bergabung ke ruangan ini lagi jika diundang."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Apakah Anda yakin ingin menghapus anggota ini?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Tampilkan profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Keluarkan dari ruangan"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Keluarkan pengguna dan cekal pengguna bergabung lagi di masa mendatang?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Mengeluarkan %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Batalkan cekalan dari ruangan"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Batalkan pencekalan"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Mereka akan dapat bergabung dengan ruangan lagi jika diundang"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Apakah Anda yakin ingin membatalkan pencekalan anggota ini?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Membatalkan cekalan %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Rimuovi ed escludi"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Escludi"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Non potrà entrare nuovamente in questa stanza se invitato."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Vuoi davvero escludere questo membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Se invitati, non potranno più unirsi a questo spazio, ma manterranno comunque la loro iscrizione a tutte le stanze o sottospazi."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Esclusione di %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Rimuovi"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Potrà entrare nuovamente in questa stanza se invitato."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Sei sicuro di voler rimuovere questo membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Potranno unirsi nuovamente a questo spazio se invitati e manterranno comunque la loro iscrizione a tutte le stanze o sottospazi."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Visualizza profilo"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Rimuovi utente"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Rimuovere e vietare l\'accesso in futuro?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Rimozione di %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Riammetti nella stanza"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Riammetti"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Potranno unirsi di nuovo alla stanza se invitati"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Sei sicuro di voler sbloccare questo membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Riammissione di %1$s"</string>
</resources>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"წევრის წაშლა და დაბლოკვა"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"დაბლოკვა"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"მოწვევის შემთხვევაში ამ ოთახში კვლავ გაწევრიანებას ვერ შეძლებენ."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"დარწმუნებული ხართ, რომ ამ წევრის დაბლოკვა გსურთ?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s-ს დაბლოკვა"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"მოწვევის შემთხვევაში განბლოკილი მომხმარებელი ისევ შეძლებს ოთახს შეუერთდეს."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"პროფილის ნახვა"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"ოთახიდან გაგდება"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"გსურთ წევრის გაგდება და მომავალში გაწევრიანების აკრძალვა?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s-ს გაგდება…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"განბლოკვა"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s-ს განბლოკვა"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"방에서 차단"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"차단"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"초대하더라도 그들은 이 방에 다시 참여할 수 없습니다."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"정말로 이 회원을 차단하시겠습니까?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"차단 %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"제거"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"초대받으면 이 방에 다시 들어올 수 있습니다."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"이 회원을 정말로 제거하시겠습니까?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"프로필 보기"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"방에서 제거"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"회원을 삭제하고 앞으로 가입을 금지하시겠습니까?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s 제거 중…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"방에서 차단 해제"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"금지 해제"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"초대되면 다시 방에 참여할 수 있습니다."</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"이 회원을 정말로 차단해제 하시겠습니까?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"차단 해제 %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Fjern og utesteng medlem"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Utesteng"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"De vil ikke kunne bli med i dette rommet igjen hvis de blir invitert."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Er du sikker på at du vil utestenge dette medlemmet?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Utestenger %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Fjern"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"De vil kunne bli med i dette rommet igjen hvis de blir invitert."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Er du sikker på at du vil fjerne dette medlemmet?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Vis profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Fjern fra rommet"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Fjerne medlem og utestenge fra å bli med i fremtiden?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Fjerner %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Fjern utestengelsen fra rommet"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Opphev utestengelse"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"De vil kunne bli med i rommet igjen hvis de blir invitert"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Er du sikker på at du vil oppheve utestengelsen av dette medlemmet?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Oppheve utestengelsen av %1$s"</string>
</resources>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Lid verwijderen en verbannen"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Verbannen"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Ze kunnen niet meer toetreden tot deze kamer als ze worden uitgenodigd."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Weet je zeker dat je dit lid wilt verbannen?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s verbannen"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Ze kunnen opnieuw tot de kamer toetreden als ze worden uitgenodigd."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Profiel bekijken"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Verwijderen uit kamer"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Lid verwijderen en toekomstige deelname verbieden?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s wordt verwijderd…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Ontbannen"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s ontbannen"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Usuń i zbanuj członka"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Zbanuj"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Nie będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Czy na pewno chcesz zbanować tego członka?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Banowanie %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Usuń"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Czy na pewno chcesz usunąć tego członka?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Wyświetl profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Usuń z pokoju"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Usunąć członka i zablokować możliwość dołączenia w przyszłości?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Usuwanie %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Odbanuj z pokoju"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Odbanuj"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Mogą ponownie dołączyć do pokoju, po otrzymaniu zaproszenia"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Czy na pewno chcesz odbanować tego członka?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Odbanowanie %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Banir da sala"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Banir"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Essa pessoa não poderá entrar nesta sala novamente se for convidada."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Tem certeza de que quer banir este membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Eles não poderão mais entrar no espaço novamente se forem convidados, mas manterão sua participação em quaisquer salas e sub-espaços."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Banindo %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Remover"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Esta pessoa poderá entrar nesta sala novamente se for convidada."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Tem certeza de que deseja remover este membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Eles poderão entrar no espaço novamente se convidados, e manterão sua participação em quaisquer salas e sub-espaços."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Ver perfil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Remover usuário"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Remover membro e banir de entrar novamente no futuro?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Removendo %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Desbanir da sala"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Desbanir"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Essa pessoa poderia entrar na sala novamente se for convidada"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Tem certeza que quer desbanir esse membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Desbanindo %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Remover e banir participante"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Banir"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Não poderão voltar a entrar nesta sala, mesmo se forem convidados."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Tens a certeza que queres banir este participante?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"A banir %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Remover"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Poderão juntar-se novamente a esta sala se forem convidados."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Tens certeza que queres remover este membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Ver perfil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Remover utilizador"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Remover participante e proibir que entre no futuro?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"A remover %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Desbanir da sala"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Anular banimento"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Eles poderão entrar novamente na sala se forem convidados"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Tens certeza que queres desbanir este membro?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"A anular banimento de %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Îndepărtați și interziceți membrul"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Interzicere"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Nu se vor putea alătura din nou acestei camere dacă sunt invitați."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Sunteți sigur că doriți să interziceți acest membru?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Se interzice %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Îndepărtați"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Se vor putea alătura din nou acestei săli dacă sunt invitați."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Sunteți sigur că doriți să îndepărtați acest membru?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Vizualizare profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Înlăturați membrul"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Înlăturați membrul și interziceți-i să se alăture în viitor?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Se îndepărtează %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Revocati excluderea din camera"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Anulare excludere"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Aceștia se vor putea alătura din nou camerei dacă sunt invitați."</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Sunteți sigur că doriți să dezactivați excluderea impusă acestui membru?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Se anulează interzicerea lui %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Удалить и заблокировать участника"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Заблокировать"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Они не смогут снова присоединиться к этой комнате, если их пригласят."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Вы уверены, что хотите заблокировать этого участника?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Они не смогут снова присоединиться к этому пространству, если их пригласят, но они по-прежнему сохранят свое членство в любых комнатах или подпространствах."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Блокировка %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Удалить"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Они снова смогут присоединиться в эту комнату если их пригласят."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Вы действительно хотите удалить этого участника?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Они смогут снова присоединиться к этому пространству, если их пригласят и сохранят свое членство во всех комнатах или подпространствах."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Посмотреть профиль"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Удалить участника из комнаты"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Удалить участника и запретить присоединяться в будущем?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Удаление %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Разблокировать в комнате"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Разблокировать"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Они смогут снова войти в комнату, если их пригласят."</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Вы действительно хотите разблокировать этого участника?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Разблокировка %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Odstrániť a zakázať člena"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Zakázať"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Nebudú sa môcť pripojiť k tejto miestnosti znova ani ak budú pozvaní."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Ste si istý, že chcete zakázať tohto člena?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"Ak dostanú pozvánku, nebudú sa môcť k tomuto priestoru znova pripojiť, ale stále si ponechajú členstvo vo všetkých miestnostiach alebo podpriestoroch."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Zakazuje sa %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Odstrániť"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"V prípade pozvania sa budú môcť znova pripojiť k tejto miestnosti."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Ste si istý, že chcete odstrániť tohto člena?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"Ak dostanú pozvánku, budú sa môcť k tomuto priestoru znova pripojiť a stále si ponechajú členstvo vo všetkých miestnostiach alebo podpriestoroch."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Zobraziť profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Odstrániť používateľa"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Odstrániť člena a zakázať vstup v budúcnosti?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Odstraňuje sa %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Zrušiť zákaz prístupu do miestnosti"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Zrušiť zákaz"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"V prípade pozvania by sa mohli opäť pripojiť k miestnosti"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Naozaj chcete zrušiť zablokovanie tohto člena?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Zrušenie zákazu %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Ta bort och banna medlem"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Banna"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Denne kommer inte att kunna gå med i det här rummet igen om denne bjuds in."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Är du säker på att du vill banna den här medlemmen?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Bannar %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Ta bort"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Denne kommer kunna gå med i rummet igen om denne bjuds in"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Är du säker på att du vill ta bort den här medlemmen?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Visa profil"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Ta bort från rummet"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Ta bort medlem och banna från att gå med i framtiden?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Tar bort %1$s …"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Avbanna från rummet"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Avbanna"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"De skulle kunna gå med i rummet igen om de blev inbjudna"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Är du säker på att du vill avbanna den här medlemmen?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Avbannar %1$s"</string>
</resources>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Üyeyi çıkar ve yasakla"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Yasakla"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Davet edilseler bile bu odaya tekrar katılamazlar."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Bu üyeyi yasaklamak istediğinize emin misiniz?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Yasaklanıyor %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Davet edildikleri takdirde bu odaya tekrar katılabileceklerdir."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Profili görüntüle"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Odadan çıkar"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Üyeyi çıkarın ve gelecekte katılmasını yasaklayın?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Kaldırılıyor %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Yasağı Kaldır"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Yasak kaldırılıyor %1$s"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Вилучити й заблокувати учасника"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Заблокувати"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Він не зможе приєднатися до цієї кімнати знову, якщо його запросять."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Ви точно хочете заблокувати цього користувача?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Блокування %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Вилучити"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Вони зможуть знову приєднатися до цієї кімнати, якщо їх запросять."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Ви дійсно хочете вилучити цього учасника?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Переглянути профіль"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Вилучити з кімнати"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Вилучити учасника та заборонити приєднання в майбутньому?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Вилучення %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Розблокувати в кімнаті"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Розблокувати"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Вони зможуть знову приєднатися до кімнати, якщо їх запросять"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Ви впевнені, що хочете розблокувати цього учасника?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Розблокування %1$s"</string>
</resources>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"کمرے سے محظور کریں"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"محظور کریں"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"اگر وہ مدعو کیا گیا تو وہ دوبارہ اس کمرے میں شامل نہیں ہوسکیں گے۔"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"کیا آپ کو یقین ہے کہ آپ اس رکن کو محظور کرنا چاہتے ہیں؟"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s کو محظور کر رہا ہے"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"اگر وہ مدعو کیا جائیں تو وہ دوبارہ اس کمرے میں شامل ہوسکیں گے۔"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"نمایہ ملاحظہ کریں"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"کمرے سے ہٹائیں"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"رکن کو ہٹائیں اور مستقبل میں شمولیت پر پابندی لگائیں؟"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s کو ہٹا رہا ہے…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"غیر محظور کریں"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s کو غیر محظور کر رہا ہے"</string>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Xonadan chetlashtirish"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Taqiqlash"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Taklif qilingan taqdirda ham, ular bu xonaga boshqa qoshila olmaydilar."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Haqiqatan ham bu aʼzoni taqiqlamoqchimisiz?"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Taqiqlash %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Oʻchirish"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Agar taklif qilinsa, ular bu xonaga qayta qoshilishlari mumkin."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Haqiqatan ham bu azoni olib tashlaysizmi?"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Profilni koʻrish"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Xonadan olib tashlash"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Aʻzo oʻchirilsinmi va kelgusida qoʻshilish taqiqlansinmi?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Oʻchirish %1$s …"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Xonadan taqiqni olib tashlash"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Taqiqni bekor qilish"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Agar taklif qilinsa, ular xonaga yana qoshilishlari mumkin"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Haqiqatan ham bu azoni blokdan chiqarmoqchimisiz?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Taqiqni bekor qilish %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"踢出並加入黑名單"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"加入黑名單"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"即使收到邀請,他們仍然無法加入聊天室。"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"您確定要將此成員加入黑名單?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"即使被邀請,他們也無法再次加入此空間,但他們仍將保留其在任何聊天室或子空間的成員資格。"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"正在將 %1$s 加入黑名單"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"移除"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"如果收到邀請,他們能再次加入聊天室。"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"您真的想要移除此成員嗎?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"若受邀,他們將可以再次加入此空間,並保留所有聊天室與子空間的成員資格。"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"查看個人檔案"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"移除使用者"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"移除成員並禁止未來再度加入?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"正在踢出 %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"從聊天室解除封鎖"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"解除黑名單"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"若受到邀請,他們仍可再次加入聊天室"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"您確定您想要取消封鎖此成員嗎?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"正在解除黑名單 %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"移除并封禁成员"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"封禁"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"即使受到邀请,他们也无法再次加入聊天室。"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"您确定要封禁该成员吗?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"即使再次受邀,他们也无法加入这个空间,但他们仍将保留其在任何房间或子空间的成员资格。"</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"封禁 %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"移除"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"如果受到邀请,他们可以重新加入聊天室。"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"您确定要移除此成员吗?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"如果受到邀请,他们将能够再次加入这个空间,并且他们仍将保留其在任何房间或子空间的成员资格。"</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"查看个人资料"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"移除用户"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"删除成员并禁止重新加入?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"正在移除 %1$s……"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"从房间取消解封"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"取消封禁"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"如果再次收到邀请,他们可以重新加入该聊天室"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"确定要解除该成员的封禁吗?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"解除封禁 %1$s"</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_bottom_sheet_manage_room_member_ban">"Ban user"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Ban"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"They wont be able to join again if invited."</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Are you sure you want to ban this member?"</string>
<string name="screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description">"They wont be able to join this space again if invited, but theyll still keep their memberships of any rooms or subspaces."</string>
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Banning %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Remove"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"They will be able to join this room again if invited."</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Are you sure you want to remove this member?"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description">"They will be able to join this space again if invited, and theyll still keep their memberships of any rooms or subspaces."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"View profile"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Remove user"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Remove member and ban from joining in the future?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Removing %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban">"Unban user"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Unban"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"They would be able to join again if invited"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Are you sure you want to unban this member?"</string>
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Unbanning %1$s"</string>
</resources>

View File

@@ -0,0 +1,384 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.roommembermoderation.impl
import app.cash.turbine.TurbineTestContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
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.RoomMembersState
import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.room.aRoomMember
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.test
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
class RoomMemberModerationPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
private val targetUser = MatrixUser(userId = A_USER_ID)
@Test
fun `present - initial state`() = runTest {
val room = aJoinedRoom()
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
assertThat(initialState.canKick).isFalse()
assertThat(initialState.canBan).isFalse()
assertThat(initialState.selectedUser).isNull()
assertThat(initialState.banUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(initialState.kickUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(initialState.unbanUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(initialState.actions).isEmpty()
}
}
@Test
fun `present - show actions when canBan=false, canKick=false`() = runTest {
val room = aJoinedRoom(
canBan = false,
canKick = false,
myUserRole = RoomMember.Role.User,
targetRoomMember = aRoomMember(userId = A_USER_ID, powerLevel = RoomMember.Role.User.powerLevel)
)
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser))
skipItems(1)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.actions).containsExactly(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
)
}
}
@Test
fun `present - show actions when canBan=true, canKick=true, userRole=Admin and target member is unknown`() = runTest {
val room = aJoinedRoom(
canBan = true,
canKick = true,
myUserRole = RoomMember.Role.Admin,
targetRoomMember = null
)
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser))
skipItems(2)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.actions).containsExactly(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = true),
ModerationActionState(action = ModerationAction.BanUser, isEnabled = true),
)
}
}
@Test
fun `show actions when canBan=true, canKick=true, userRole=Admin and target is User`() = runTest {
val room = aJoinedRoom(
canBan = true,
canKick = true,
myUserRole = RoomMember.Role.Admin,
targetRoomMember = aRoomMember(userId = A_USER_ID, powerLevel = RoomMember.Role.User.powerLevel)
)
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser))
skipItems(2)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.actions).containsExactly(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = true),
ModerationActionState(action = ModerationAction.BanUser, isEnabled = true),
)
}
}
@Test
fun `show actions when canBan=true, canKick=true, userRole=Moderator and target is Admin`() = runTest {
val room = aJoinedRoom(
canBan = true,
canKick = true,
myUserRole = RoomMember.Role.Moderator,
targetRoomMember = aRoomMember(userId = A_USER_ID, powerLevel = RoomMember.Role.Admin.powerLevel)
)
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser))
skipItems(2)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.actions).containsExactly(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = false),
ModerationActionState(action = ModerationAction.BanUser, isEnabled = false),
)
}
}
@Test
fun `show actions when canBan=true, canKick=true, userRole=Moderator and target is Banned`() = runTest {
val room = aJoinedRoom(
canBan = true,
canKick = true,
myUserRole = RoomMember.Role.Moderator,
targetRoomMember = aRoomMember(userId = A_USER_ID, membership = RoomMembershipState.BAN)
)
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(targetUser))
skipItems(2)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.actions).containsExactly(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
ModerationActionState(action = ModerationAction.KickUser, isEnabled = false),
ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = true),
)
}
}
@Test
fun `present - process kick action sets confirming state`() = runTest {
createRoomMemberModerationPresenter(room = aJoinedRoom()).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.KickUser
)
)
skipItems(1)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.kickUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams)
}
}
@Test
fun `present - process ban action sets confirming state`() = runTest {
createRoomMemberModerationPresenter(room = aJoinedRoom()).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.BanUser
)
)
skipItems(1)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.banUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams)
}
}
@Test
fun `present - process unban action sets confirming state`() = runTest {
createRoomMemberModerationPresenter(room = aJoinedRoom()).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.UnbanUser
)
)
skipItems(1)
val updatedState = awaitState()
assertThat(updatedState.selectedUser).isEqualTo(targetUser)
assertThat(updatedState.unbanUserAsyncAction).isEqualTo(AsyncAction.ConfirmingNoParams)
}
}
@Test
fun `present - do kick user with success`() = runTest {
val room = aJoinedRoom()
room.baseRoom.givenUpdateMembersResult {
// Simulate the member list being updated
room.givenRoomMembersState(RoomMembersState.Ready(
persistentListOf(aRoomMember())
))
}
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.KickUser
)
)
skipItems(2)
initialState.eventSink(InternalRoomMemberModerationEvents.DoKickUser("Reason"))
skipItems(1)
val loadingState = awaitState()
assertThat(loadingState.kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
val successState = awaitState()
assertThat(successState.kickUserAsyncAction).isInstanceOf(AsyncAction.Success::class.java)
assertThat(successState.selectedUser).isNull()
}
}
@Test
fun `present - do ban user with success`() = runTest {
val room = aJoinedRoom()
room.baseRoom.givenUpdateMembersResult {
// Simulate the member list being updated
room.givenRoomMembersState(RoomMembersState.Ready(
persistentListOf(aRoomMember())
))
}
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.BanUser
)
)
skipItems(2)
initialState.eventSink(InternalRoomMemberModerationEvents.DoBanUser("Reason"))
skipItems(1)
val loadingState = awaitState()
assertThat(loadingState.banUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
val successState = awaitState()
assertThat(successState.banUserAsyncAction).isInstanceOf(AsyncAction.Success::class.java)
assertThat(successState.selectedUser).isNull()
}
}
@Test
fun `present - do unban user with success`() = runTest {
val room = aJoinedRoom()
room.baseRoom.givenUpdateMembersResult {
// Simulate the member list being updated
room.givenRoomMembersState(RoomMembersState.Ready(
persistentListOf(aRoomMember())
))
}
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.UnbanUser
)
)
skipItems(2)
initialState.eventSink(InternalRoomMemberModerationEvents.DoUnbanUser("Reason"))
skipItems(1)
val loadingState = awaitState()
assertThat(loadingState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
val successState = awaitState()
assertThat(successState.unbanUserAsyncAction).isInstanceOf(AsyncAction.Success::class.java)
assertThat(successState.selectedUser).isNull()
}
}
@Test
fun `present - do kick user with failure`() = runTest {
val error = RuntimeException("Test error")
val room = aJoinedRoom(
kickUserResult = Result.failure(error),
)
createRoomMemberModerationPresenter(room = room).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(
targetUser = targetUser,
action = ModerationAction.KickUser
)
)
skipItems(2)
initialState.eventSink(InternalRoomMemberModerationEvents.DoKickUser("Reason"))
skipItems(1)
val loadingState = awaitState()
assertThat(loadingState.kickUserAsyncAction).isInstanceOf(AsyncAction.Loading::class.java)
val failureState = awaitState()
assertThat(failureState.kickUserAsyncAction).isInstanceOf(AsyncAction.Failure::class.java)
}
}
@Test
fun `present - reset clears all async actions and selected user`() = runTest {
createRoomMemberModerationPresenter(room = aJoinedRoom()).test {
val initialState = awaitState()
initialState.eventSink(
RoomMemberModerationEvents.ProcessAction(targetUser = targetUser, action = ModerationAction.BanUser)
)
skipItems(2)
initialState.eventSink(InternalRoomMemberModerationEvents.Reset)
skipItems(1)
val resetState = awaitState()
assertThat(resetState.selectedUser).isNull()
assertThat(resetState.banUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
}
}
private fun aJoinedRoom(
canKick: Boolean = false,
canBan: Boolean = false,
myUserRole: RoomMember.Role = RoomMember.Role.User,
kickUserResult: Result<Unit> = Result.success(Unit),
banUserResult: Result<Unit> = Result.success(Unit),
unBanUserResult: Result<Unit> = Result.success(Unit),
targetRoomMember: RoomMember? = null,
): FakeJoinedRoom {
return FakeJoinedRoom(
kickUserResult = { _, _ -> kickUserResult },
banUserResult = { _, _ -> banUserResult },
unBanUserResult = { _, _ -> unBanUserResult },
baseRoom = FakeBaseRoom(
canBanResult = { _ -> Result.success(canBan) },
canKickResult = { _ -> Result.success(canKick) },
userRoleResult = { Result.success(myUserRole) },
updateMembersResult = { Result.success(Unit) }
),
).apply {
val roomMembers = listOfNotNull(targetRoomMember).toImmutableList()
givenRoomMembersState(state = RoomMembersState.Ready(roomMembers))
}
}
private fun TestScope.createRoomMemberModerationPresenter(
room: JoinedRoom,
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
analyticsService: AnalyticsService = FakeAnalyticsService(),
): RoomMemberModerationPresenter {
return RoomMemberModerationPresenter(
room = room,
dispatchers = dispatchers,
analyticsService = analyticsService,
)
}
private suspend fun TurbineTestContext<RoomMemberModerationState>.awaitState(): InternalRoomMemberModerationState {
return awaitItem() as InternalRoomMemberModerationState
}
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 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.roommembermoderation.impl
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.features.roommembermoderation.api.ModerationAction
import io.element.android.features.roommembermoderation.api.ModerationActionState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.testtags.TestTags
import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnceWithTwoParams
import io.element.android.tests.testutils.pressTag
import io.element.android.tests.testutils.setSafeContent
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RoomMemberModerationViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on display profile action calls onSelectAction`() {
val user = anAlice()
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>(expectEvents = false)
ensureCalledOnceWithTwoParams<ModerationAction, MatrixUser>(ModerationAction.DisplayProfile, user) { callback ->
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = user,
actions = listOf(
ModerationActionState(action = ModerationAction.DisplayProfile, isEnabled = true),
),
eventSink = eventsRecorder
),
onSelectAction = callback
)
rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_member_user_info)
}
}
@Test
fun `clicking on kick user action calls onSelectAction`() {
val user = anAlice()
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>(expectEvents = false)
ensureCalledOnceWithTwoParams<ModerationAction, MatrixUser>(ModerationAction.KickUser, user) { callback ->
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = user,
actions = listOf(
ModerationActionState(action = ModerationAction.KickUser, isEnabled = true),
),
eventSink = eventsRecorder
),
onSelectAction = callback
)
rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_remove)
// Gives time for bottomsheet to hide
rule.mainClock.advanceTimeBy(1_000)
}
}
@Test
fun `clicking on ban user action calls onSelectAction`() {
val user = anAlice()
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>(expectEvents = false)
ensureCalledOnceWithTwoParams<ModerationAction, MatrixUser>(ModerationAction.BanUser, user) { callback ->
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = user,
actions = listOf(
ModerationActionState(action = ModerationAction.BanUser, isEnabled = true),
),
eventSink = eventsRecorder
),
onSelectAction = callback
)
rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_ban)
// Gives time for bottomsheet to hide
rule.mainClock.advanceTimeBy(1_000)
}
}
@Test
fun `clicking on unban user action calls onSelectAction`() {
val user = anAlice()
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>(expectEvents = false)
ensureCalledOnceWithTwoParams<ModerationAction, MatrixUser>(ModerationAction.UnbanUser, user) { callback ->
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = user,
actions = listOf(
ModerationActionState(action = ModerationAction.UnbanUser, isEnabled = true),
),
eventSink = eventsRecorder
),
onSelectAction = callback
)
rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_unban)
// Gives time for bottomsheet to hide
rule.mainClock.advanceTimeBy(1_000)
}
}
@Test
fun `clicking submit on kick confirmation dialog sends DoKickUser event`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>()
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
kickUserAsyncAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.pressTag(TestTags.dialogPositive.value)
eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoKickUser(reason = ""))
}
@Test
fun `clicking dismiss on kick confirmation dialog sends Reset event`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>()
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
kickUserAsyncAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.pressTag(TestTags.dialogNegative.value)
eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset)
}
@Test
fun `clicking submit on ban confirmation dialog sends DoBanUser event`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>()
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.pressTag(TestTags.dialogPositive.value)
eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoBanUser(reason = ""))
}
@Test
fun `clicking dismiss on ban confirmation dialog sends Reset event`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>()
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
banUserAsyncAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.pressTag(TestTags.dialogNegative.value)
eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset)
}
@Test
fun `clicking confirm on unban confirmation dialog sends DoUnbanUser event`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>()
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
unbanUserAsyncAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.pressTag(TestTags.dialogPositive.value)
eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoUnbanUser(""))
}
@Test
fun `clicking dismiss on unban confirmation dialog sends Reset event`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>()
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
unbanUserAsyncAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.pressTag(TestTags.dialogNegative.value)
eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset)
}
@Test
fun `disabled actions are not clickable`() {
val eventsRecorder = EventsRecorder<RoomMemberModerationEvents>(expectEvents = false)
rule.setRoomMemberModerationView(
aRoomMembersModerationState(
selectedUser = anAlice(),
actions = listOf(
ModerationActionState(action = ModerationAction.KickUser, isEnabled = false),
),
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_remove)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomMemberModerationView(
state: InternalRoomMemberModerationState,
onSelectAction: (ModerationAction, MatrixUser) -> Unit = EnsureNeverCalledWithTwoParams(),
) {
setSafeContent {
RoomMemberModerationView(
state = state,
onSelectAction = onSelectAction,
)
}
}