First Commit
This commit is contained in:
41
features/roommembermoderation/impl/build.gradle.kts
Normal file
41
features/roommembermoderation/impl/build.gradle.kts
Normal 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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = { _, _ ->
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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">"L’utilisateur 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">"L’utilisateur pourra rejoindre cet espace à nouveau s’il 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 l’adhésion à l’avenir ?"</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 s’il 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 qo‘shila 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 qo‘shilishlari mumkin."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Haqiqatan ham bu a’zoni 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 qo‘shilishlari mumkin"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Haqiqatan ham bu a’zoni blokdan chiqarmoqchimisiz?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Taqiqni bekor qilish %1$s"</string>
|
||||
</resources>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 won’t 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 won’t be able to join this space again if invited, but they’ll 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 they’ll 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>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user