First Commit

This commit is contained in:
2025-12-18 16:28:50 +07:00
commit 8c3e4f491f
9974 changed files with 396488 additions and 0 deletions
@@ -0,0 +1,45 @@
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")
id("kotlin-parcelize")
}
android {
namespace = "io.element.android.features.rolesandpermissions.impl"
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
setupDependencyInjection()
dependencies {
api(projects.features.rolesandpermissions.api)
implementation(projects.appnav)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.matrix.api)
// For test fixtures used in previews
implementation(projects.libraries.previewutils)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.uiStrings)
implementation(projects.services.analytics.api)
testCommonDependencies(libs, true)
testImplementation(projects.services.analytics.test)
testImplementation(projects.libraries.matrix.test)
}
@@ -0,0 +1,23 @@
/*
* 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.rolesandpermissions.impl
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.RoomScope
@ContributesBinding(RoomScope::class)
class DefaultRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint {
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
return parentNode.createNode<RolesAndPermissionsFlowNode>(buildContext)
}
}
@@ -0,0 +1,137 @@
/*
* 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.rolesandpermissions.impl
import android.os.Parcelable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.coroutineScope
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.features.rolesandpermissions.impl.permissions.ChangeRoomPermissionsNode
import io.element.android.features.rolesandpermissions.impl.roles.ChangeRolesNode
import io.element.android.features.rolesandpermissions.impl.root.RolesAndPermissionsNode
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode
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.AsyncIndicatorState
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
@ContributesNode(RoomScope::class)
@AssistedInject
class RolesAndPermissionsFlowNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
) : BaseFlowNode<RolesAndPermissionsFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Root,
savedStateMap = buildContext.savedStateMap,
),
buildContext = buildContext,
plugins = plugins,
) {
sealed interface NavTarget : Parcelable {
@Parcelize
data object Root : NavTarget
@Parcelize
data object ChangeAdmins : NavTarget
@Parcelize
data object ChangeModerators : NavTarget
@Parcelize
data object ChangeRoomPermissions : NavTarget
}
private val asyncIndicatorState = AsyncIndicatorState()
override fun onBuilt() {
super.onBuilt()
whenChildAttached { lifecycle, node: ChangeRolesNode ->
lifecycle.coroutineScope.launch {
val changesSaved = node.waitForCompletion()
onChangeComplete(changesSaved)
}
}
}
private fun onChangeComplete(changesSaved: Boolean) {
backstack.pop()
if (changesSaved) {
asyncIndicatorState.enqueue(durationMs = AsyncIndicator.DURATION_SHORT) {
AsyncIndicator.Custom(text = stringResource(CommonStrings.common_saved_changes))
}
}
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
is NavTarget.Root -> {
val callback = object : RolesAndPermissionsNode.Callback {
override fun openAdminList() {
backstack.push(NavTarget.ChangeAdmins)
}
override fun openModeratorList() {
backstack.push(NavTarget.ChangeModerators)
}
override fun openEditPermissions() {
backstack.push(NavTarget.ChangeRoomPermissions)
}
}
createNode<RolesAndPermissionsNode>(
buildContext = buildContext,
plugins = listOf(callback),
)
}
is NavTarget.ChangeAdmins -> {
val inputs = ChangeRolesNode.Inputs(ChangeRoomMemberRolesListType.Admins)
createNode<ChangeRolesNode>(buildContext = buildContext, plugins = listOf(inputs))
}
is NavTarget.ChangeModerators -> {
val inputs = ChangeRolesNode.Inputs(ChangeRoomMemberRolesListType.Moderators)
createNode<ChangeRolesNode>(buildContext = buildContext, plugins = listOf(inputs))
}
is NavTarget.ChangeRoomPermissions -> {
val callback = object : ChangeRoomPermissionsNode.Callback {
override fun onComplete(changesSaved: Boolean) {
onChangeComplete(changesSaved)
}
}
createNode<ChangeRoomPermissionsNode>(buildContext = buildContext, plugins = listOf(callback))
}
}
}
@Composable
override fun View(modifier: Modifier) {
Box(modifier = modifier) {
BackstackView()
AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), asyncIndicatorState)
}
}
}
@@ -0,0 +1,39 @@
/*
* 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.rolesandpermissions.impl
import dev.zacsweers.metro.Inject
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.roomMembers
import kotlinx.coroutines.withContext
@Inject
class RoomMemberListDataSource(
private val room: BaseRoom,
private val coroutineDispatchers: CoroutineDispatchers,
) {
suspend fun search(query: String): List<RoomMember> = withContext(coroutineDispatchers.io) {
val roomMembersState = room.membersStateFlow.value
val activeRoomMembers = roomMembersState.roomMembers()
?.filter { it.membership.isActive() }
.orEmpty()
val filteredMembers = if (query.isBlank()) {
activeRoomMembers
} else {
activeRoomMembers.filter { member ->
member.userId.value.contains(query, ignoreCase = true) ||
member.displayName?.contains(query, ignoreCase = true).orFalse()
}
}
filteredMembers
}
}
@@ -0,0 +1,52 @@
/*
* 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.rolesandpermissions.impl.analytics
import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
import io.element.android.services.analytics.api.AnalyticsService
internal fun RoomMember.Role.toAnalyticsMemberRole(): RoomModeration.Role = when (this) {
is RoomMember.Role.Owner -> RoomModeration.Role.Administrator // TODO - distinguish creator from admin
RoomMember.Role.Admin -> RoomModeration.Role.Administrator
RoomMember.Role.Moderator -> RoomModeration.Role.Moderator
RoomMember.Role.User -> RoomModeration.Role.User
}
internal fun analyticsMemberRoleForPowerLevel(powerLevel: Long): RoomModeration.Role {
return RoomMember.Role.forPowerLevel(powerLevel).toAnalyticsMemberRole()
}
internal fun AnalyticsService.trackPermissionChangeAnalytics(initial: RoomPowerLevelsValues?, updated: RoomPowerLevelsValues) {
if (updated.ban != initial?.ban) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsBanMembers, analyticsMemberRoleForPowerLevel(updated.ban)))
}
if (updated.invite != initial?.invite) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsInviteUsers, analyticsMemberRoleForPowerLevel(updated.invite)))
}
if (updated.kick != initial?.kick) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsKickMembers, analyticsMemberRoleForPowerLevel(updated.kick)))
}
if (updated.sendEvents != initial?.sendEvents) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsSendMessages, analyticsMemberRoleForPowerLevel(updated.sendEvents)))
}
if (updated.redactEvents != initial?.redactEvents) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsRedactMessages, analyticsMemberRoleForPowerLevel(updated.redactEvents)))
}
if (updated.roomName != initial?.roomName) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsRoomName, analyticsMemberRoleForPowerLevel(updated.roomName)))
}
if (updated.roomAvatar != initial?.roomAvatar) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsRoomAvatar, analyticsMemberRoleForPowerLevel(updated.roomAvatar)))
}
if (updated.roomTopic != initial?.roomTopic) {
capture(RoomModeration(RoomModeration.Action.ChangePermissionsRoomTopic, analyticsMemberRoleForPowerLevel(updated.roomTopic)))
}
}
@@ -0,0 +1,16 @@
/*
* 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.rolesandpermissions.impl.permissions
interface ChangeRoomPermissionsEvent {
data class ChangeMinimumRoleForAction(val action: RoomPermissionType, val role: SelectableRole) : ChangeRoomPermissionsEvent
data object Save : ChangeRoomPermissionsEvent
data object Exit : ChangeRoomPermissionsEvent
data object ResetPendingActions : ChangeRoomPermissionsEvent
}
@@ -0,0 +1,44 @@
/*
* 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.rolesandpermissions.impl.permissions
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.RoomScope
@ContributesNode(RoomScope::class)
@AssistedInject
class ChangeRoomPermissionsNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val presenter: ChangeRoomPermissionsPresenter,
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onComplete(changesSaved: Boolean)
}
private val callback: Callback = callback()
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
ChangeRoomPermissionsView(
modifier = modifier,
state = state,
onComplete = callback::onComplete,
)
}
}
@@ -0,0 +1,156 @@
/*
* 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.rolesandpermissions.impl.permissions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
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 io.element.android.features.rolesandpermissions.impl.analytics.trackPermissionChangeAnalytics
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
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.powerlevels.RoomPowerLevelsValues
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Inject
class ChangeRoomPermissionsPresenter(
private val room: JoinedRoom,
private val analyticsService: AnalyticsService,
) : Presenter<ChangeRoomPermissionsState> {
companion object {
private fun itemsForSection(section: RoomPermissionsSection) = when (section) {
RoomPermissionsSection.SpaceDetails,
RoomPermissionsSection.RoomDetails -> persistentListOf(
RoomPermissionType.ROOM_NAME,
RoomPermissionType.ROOM_AVATAR,
RoomPermissionType.ROOM_TOPIC,
)
RoomPermissionsSection.MessagesAndContent -> persistentListOf(
RoomPermissionType.SEND_EVENTS,
RoomPermissionType.REDACT_EVENTS,
)
RoomPermissionsSection.MembershipModeration -> persistentListOf(
RoomPermissionType.INVITE,
RoomPermissionType.KICK,
RoomPermissionType.BAN,
)
}
private fun RoomPermissionsSection.shouldShow(isSpace: Boolean): Boolean {
return when (this) {
RoomPermissionsSection.RoomDetails -> !isSpace
RoomPermissionsSection.MembershipModeration -> true
RoomPermissionsSection.MessagesAndContent -> !isSpace
RoomPermissionsSection.SpaceDetails -> isSpace
}
}
internal fun buildItems(isSpace: Boolean) =
RoomPermissionsSection.entries
.filter { section -> section.shouldShow(isSpace) }
.associateWith { itemsForSection(it) }
.toImmutableMap()
}
private val itemsBySection = buildItems(isSpace = room.info().isSpace)
private var initialPermissions by mutableStateOf<RoomPowerLevelsValues?>(null)
private var currentPermissions by mutableStateOf<RoomPowerLevelsValues?>(null)
private var saveAction by mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized)
private var confirmExitAction by mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized)
@Composable
override fun present(): ChangeRoomPermissionsState {
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(Unit) {
updatePermissions()
}
val hasChanges by remember {
derivedStateOf { initialPermissions != currentPermissions }
}
fun handleEvent(event: ChangeRoomPermissionsEvent) {
when (event) {
is ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction -> {
val powerLevel = when (event.role) {
SelectableRole.Admin -> RoomMember.Role.Admin.powerLevel
SelectableRole.Moderator -> RoomMember.Role.Moderator.powerLevel
SelectableRole.Everyone -> RoomMember.Role.User.powerLevel
}
currentPermissions = when (event.action) {
RoomPermissionType.BAN -> currentPermissions?.copy(ban = powerLevel)
RoomPermissionType.INVITE -> currentPermissions?.copy(invite = powerLevel)
RoomPermissionType.KICK -> currentPermissions?.copy(kick = powerLevel)
RoomPermissionType.SEND_EVENTS -> currentPermissions?.copy(sendEvents = powerLevel)
RoomPermissionType.REDACT_EVENTS -> currentPermissions?.copy(redactEvents = powerLevel)
RoomPermissionType.ROOM_NAME -> currentPermissions?.copy(roomName = powerLevel)
RoomPermissionType.ROOM_AVATAR -> currentPermissions?.copy(roomAvatar = powerLevel)
RoomPermissionType.ROOM_TOPIC -> currentPermissions?.copy(roomTopic = powerLevel)
}
}
is ChangeRoomPermissionsEvent.Save -> coroutineScope.save()
is ChangeRoomPermissionsEvent.Exit -> {
confirmExitAction = if (!hasChanges || confirmExitAction.isConfirming()) {
AsyncAction.Success(Unit)
} else {
AsyncAction.ConfirmingNoParams
}
}
is ChangeRoomPermissionsEvent.ResetPendingActions -> {
saveAction = AsyncAction.Uninitialized
confirmExitAction = AsyncAction.Uninitialized
}
}
}
return ChangeRoomPermissionsState(
currentPermissions = currentPermissions,
itemsBySection = itemsBySection,
hasChanges = hasChanges,
saveAction = saveAction,
confirmExitAction = confirmExitAction,
eventSink = ::handleEvent,
)
}
private suspend fun updatePermissions() {
val powerLevels = room.powerLevels().getOrNull() ?: return
initialPermissions = powerLevels
currentPermissions = initialPermissions
}
private fun CoroutineScope.save() = launch {
saveAction = AsyncAction.Loading
val updatedRoomPowerLevels = currentPermissions ?: run {
saveAction = AsyncAction.Failure(IllegalStateException("Failed to set room power levels"))
return@launch
}
room.updatePowerLevels(updatedRoomPowerLevels)
.onSuccess {
analyticsService.trackPermissionChangeAnalytics(initialPermissions, updatedRoomPowerLevels)
initialPermissions = currentPermissions
saveAction = AsyncAction.Success(Unit)
}
.onFailure {
saveAction = AsyncAction.Failure(it)
}
}
}
@@ -0,0 +1,85 @@
/*
* 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.rolesandpermissions.impl.permissions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.res.stringResource
import io.element.android.features.rolesandpermissions.impl.R
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.components.preferences.DropdownOption
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
data class ChangeRoomPermissionsState(
val currentPermissions: RoomPowerLevelsValues?,
val itemsBySection: ImmutableMap<RoomPermissionsSection, ImmutableList<RoomPermissionType>>,
val hasChanges: Boolean,
val saveAction: AsyncAction<Unit>,
val confirmExitAction: AsyncAction<Unit>,
val eventSink: (ChangeRoomPermissionsEvent) -> Unit,
) {
fun selectedRoleForType(type: RoomPermissionType): SelectableRole? {
if (currentPermissions == null) return null
val role = when (type) {
RoomPermissionType.BAN -> RoomMember.Role.forPowerLevel(currentPermissions.ban)
RoomPermissionType.INVITE -> RoomMember.Role.forPowerLevel(currentPermissions.invite)
RoomPermissionType.KICK -> RoomMember.Role.forPowerLevel(currentPermissions.kick)
RoomPermissionType.SEND_EVENTS -> RoomMember.Role.forPowerLevel(currentPermissions.sendEvents)
RoomPermissionType.REDACT_EVENTS -> RoomMember.Role.forPowerLevel(currentPermissions.redactEvents)
RoomPermissionType.ROOM_NAME -> RoomMember.Role.forPowerLevel(currentPermissions.roomName)
RoomPermissionType.ROOM_AVATAR -> RoomMember.Role.forPowerLevel(currentPermissions.roomAvatar)
RoomPermissionType.ROOM_TOPIC -> RoomMember.Role.forPowerLevel(currentPermissions.roomTopic)
}
return when (role) {
is RoomMember.Role.Owner,
RoomMember.Role.Admin -> SelectableRole.Admin
RoomMember.Role.Moderator -> SelectableRole.Moderator
RoomMember.Role.User -> SelectableRole.Everyone
}
}
}
enum class RoomPermissionsSection {
SpaceDetails,
RoomDetails,
MessagesAndContent,
MembershipModeration,
}
enum class SelectableRole : DropdownOption {
Admin {
@Composable
@ReadOnlyComposable
override fun getText(): String = stringResource(R.string.screen_room_member_list_role_administrator)
},
Moderator {
@Composable
@ReadOnlyComposable
override fun getText(): String = stringResource(R.string.screen_room_member_list_role_moderator)
},
Everyone {
@Composable
@ReadOnlyComposable
override fun getText(): String = stringResource(R.string.screen_room_change_permissions_everyone)
}
}
enum class RoomPermissionType {
BAN,
INVITE,
KICK,
SEND_EVENTS,
REDACT_EVENTS,
ROOM_NAME,
ROOM_AVATAR,
ROOM_TOPIC
}
@@ -0,0 +1,64 @@
/*
* 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.rolesandpermissions.impl.permissions
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableMap
class ChangeRoomPermissionsStateProvider : PreviewParameterProvider<ChangeRoomPermissionsState> {
override val values: Sequence<ChangeRoomPermissionsState>
get() = sequenceOf(
aChangeRoomPermissionsState(),
aChangeRoomPermissionsState(hasChanges = true),
aChangeRoomPermissionsState(hasChanges = true, saveAction = AsyncAction.Loading),
aChangeRoomPermissionsState(
hasChanges = true,
saveAction = AsyncAction.Failure(IllegalStateException("Failed to save changes"))
),
aChangeRoomPermissionsState(hasChanges = true, confirmExitAction = AsyncAction.ConfirmingNoParams),
)
}
internal fun aChangeRoomPermissionsState(
currentPermissions: RoomPowerLevelsValues = previewPermissions(),
itemsBySection: Map<RoomPermissionsSection, ImmutableList<RoomPermissionType>> = ChangeRoomPermissionsPresenter.buildItems(false),
hasChanges: Boolean = false,
saveAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
confirmExitAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (ChangeRoomPermissionsEvent) -> Unit = {},
) = ChangeRoomPermissionsState(
currentPermissions = currentPermissions,
itemsBySection = itemsBySection.toImmutableMap(),
hasChanges = hasChanges,
saveAction = saveAction,
confirmExitAction = confirmExitAction,
eventSink = eventSink,
)
private fun previewPermissions(): RoomPowerLevelsValues {
return RoomPowerLevelsValues(
// MembershipModeration section
invite = RoomMember.Role.Admin.powerLevel,
kick = RoomMember.Role.Moderator.powerLevel,
ban = RoomMember.Role.User.powerLevel,
// MessagesAndContent section
redactEvents = RoomMember.Role.Moderator.powerLevel,
sendEvents = RoomMember.Role.Admin.powerLevel,
// RoomDetails section
roomName = RoomMember.Role.Admin.powerLevel,
roomAvatar = RoomMember.Role.Moderator.powerLevel,
roomTopic = RoomMember.Role.User.powerLevel,
// SpaceManagement section
spaceChild = RoomMember.Role.Moderator.powerLevel,
)
}
@@ -0,0 +1,144 @@
/*
* 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.rolesandpermissions.impl.permissions
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.rolesandpermissions.impl.R
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceDropdown
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.toImmutableList
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChangeRoomPermissionsView(
state: ChangeRoomPermissionsState,
onComplete: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
BackHandler {
state.eventSink(ChangeRoomPermissionsEvent.Exit)
}
Scaffold(
modifier = modifier,
topBar = {
TopAppBar(
titleStr = stringResource(R.string.screen_room_roles_and_permissions_permissions_header),
navigationIcon = {
BackButton(onClick = { state.eventSink(ChangeRoomPermissionsEvent.Exit) })
},
actions = {
TextButton(
text = stringResource(CommonStrings.action_save),
onClick = { state.eventSink(ChangeRoomPermissionsEvent.Save) },
enabled = state.hasChanges,
)
}
)
}
) { padding ->
LazyColumn(
modifier = Modifier
.padding(padding)
.fillMaxSize()
) {
state.itemsBySection.onEachIndexed { index, (section, items) ->
item {
ListSectionHeader(titleForSection(section), hasDivider = index > 0)
}
for (permissionType in items) {
item {
PreferenceDropdown(
title = titleForType(permissionType),
selectedOption = state.selectedRoleForType(permissionType),
options = SelectableRole.entries.toImmutableList(),
onSelectOption = { role ->
state.eventSink(
ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(
action = permissionType,
role = role
)
)
}
)
}
}
}
}
}
AsyncActionView(
async = state.saveAction,
onSuccess = { onComplete(true) },
onErrorDismiss = { state.eventSink(ChangeRoomPermissionsEvent.ResetPendingActions) }
)
AsyncActionView(
async = state.confirmExitAction,
onSuccess = { onComplete(false) },
confirmationDialog = {
ConfirmationDialog(
title = stringResource(R.string.screen_room_change_role_unsaved_changes_title),
content = stringResource(R.string.screen_room_change_role_unsaved_changes_description),
submitText = stringResource(CommonStrings.action_save),
cancelText = stringResource(CommonStrings.action_discard),
onSubmitClick = { state.eventSink(ChangeRoomPermissionsEvent.Save) },
onDismiss = { state.eventSink(ChangeRoomPermissionsEvent.Exit) }
)
},
onErrorDismiss = {},
)
}
@Composable
private fun titleForSection(section: RoomPermissionsSection): String = when (section) {
RoomPermissionsSection.SpaceDetails -> stringResource(R.string.screen_room_roles_and_permissions_space_details)
RoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_roles_and_permissions_room_details)
RoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_roles_and_permissions_messages_and_content)
RoomPermissionsSection.MembershipModeration -> stringResource(R.string.screen_room_roles_and_permissions_member_moderation)
}
@Composable
private fun titleForType(type: RoomPermissionType): String = when (type) {
RoomPermissionType.INVITE -> stringResource(R.string.screen_room_change_permissions_invite_people)
RoomPermissionType.KICK -> stringResource(R.string.screen_room_change_permissions_remove_people)
RoomPermissionType.BAN -> stringResource(R.string.screen_room_change_permissions_ban_people)
RoomPermissionType.SEND_EVENTS -> stringResource(R.string.screen_room_change_permissions_send_messages)
RoomPermissionType.REDACT_EVENTS -> stringResource(R.string.screen_room_change_permissions_delete_messages)
RoomPermissionType.ROOM_NAME -> stringResource(R.string.screen_room_change_permissions_room_name)
RoomPermissionType.ROOM_AVATAR -> stringResource(R.string.screen_room_change_permissions_room_avatar)
RoomPermissionType.ROOM_TOPIC -> stringResource(R.string.screen_room_change_permissions_room_topic)
}
@PreviewsDayNight
@Composable
internal fun ChangeRoomPermissionsViewPreview(@PreviewParameter(ChangeRoomPermissionsStateProvider::class) state: ChangeRoomPermissionsState) {
ElementPreview {
ChangeRoomPermissionsView(
state = state,
onComplete = {},
)
}
}
@@ -0,0 +1,20 @@
/*
* 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.rolesandpermissions.impl.roles
import io.element.android.libraries.matrix.api.user.MatrixUser
sealed interface ChangeRolesEvent {
data object ToggleSearchActive : ChangeRolesEvent
data class QueryChanged(val query: String?) : ChangeRolesEvent
data class UserSelectionToggled(val matrixUser: MatrixUser) : ChangeRolesEvent
data object Save : ChangeRolesEvent
data object Exit : ChangeRolesEvent
data object CloseDialog : ChangeRolesEvent
}
@@ -0,0 +1,64 @@
/*
* 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.rolesandpermissions.impl.roles
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.appyx.launchMolecule
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.room.RoomMember
import kotlinx.coroutines.flow.first
@ContributesNode(RoomScope::class)
@AssistedInject
class ChangeRolesNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: ChangeRolesPresenter.Factory,
) : Node(buildContext, plugins = plugins) {
data class Inputs(
val listType: ChangeRoomMemberRolesListType,
) : NodeInputs
private val inputs: Inputs = inputs()
private val presenter = presenterFactory.create(inputs.listType.toRoomMemberRole())
private val stateFlow = launchMolecule { presenter.present() }
suspend fun waitForCompletion(): Boolean {
val successState = stateFlow.first { it.savingState.isSuccess() }
return successState.savingState.dataOrNull().orFalse()
}
@Composable
override fun View(modifier: Modifier) {
val state by stateFlow.collectAsState()
ChangeRolesView(
state = state,
modifier = modifier,
)
}
}
internal fun ChangeRoomMemberRolesListType.toRoomMemberRole() = when (this) {
ChangeRoomMemberRolesListType.Admins -> RoomMember.Role.Admin
ChangeRoomMemberRolesListType.Moderators -> RoomMember.Role.Moderator
ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving -> RoomMember.Role.Owner(isCreator = false)
}
@@ -0,0 +1,237 @@
/*
* 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.rolesandpermissions.impl.roles
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.features.rolesandpermissions.impl.RoomMemberListDataSource
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.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.di.annotations.RoomCoroutineScope
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.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.api.room.powerlevels.usersWithRole
import io.element.android.libraries.matrix.api.room.toMatrixUser
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.roleOf
import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator
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.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@AssistedInject
class ChangeRolesPresenter(
@Assisted private val role: RoomMember.Role,
private val room: JoinedRoom,
private val dataSource: RoomMemberListDataSource,
private val analyticsService: AnalyticsService,
@RoomCoroutineScope private val roomCoroutineScope: CoroutineScope,
) : Presenter<ChangeRolesState> {
@AssistedFactory
fun interface Factory {
fun create(role: RoomMember.Role): ChangeRolesPresenter
}
private val powerLevelRoomMemberComparator = PowerLevelRoomMemberComparator()
@Composable
override fun present(): ChangeRolesState {
var query by rememberSaveable { mutableStateOf<String?>(null) }
var searchActive by rememberSaveable { mutableStateOf(false) }
var searchResults by remember {
mutableStateOf<SearchBarResultState<MembersByRole>>(SearchBarResultState.Initial())
}
val selectedUsers = remember {
mutableStateOf<ImmutableList<MatrixUser>>(persistentListOf())
}
val saveState: MutableState<AsyncAction<Boolean>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
val usersWithRole = produceState<ImmutableList<MatrixUser>>(initialValue = persistentListOf()) {
// If the role is admin, we need to include the owners as well since they implicitly have admin role
val owners = if (role == RoomMember.Role.Admin) {
combine(
room.usersWithRole(RoomMember.Role.Owner(isCreator = true)),
room.usersWithRole(RoomMember.Role.Owner(isCreator = false)),
) { creators, superAdmins ->
creators + superAdmins
}
} else {
emptyFlow()
}
combine(
owners,
room.usersWithRole(role),
) { owners, users ->
owners + users
}.map { members -> members.map { it.toMatrixUser() } }
.onEach { users ->
val previous = value
value = users.toImmutableList()
// Users who were selected but didn't have the role, so their role change was pending
val toAdd = selectedUsers.value.filter { user -> users.none { it.userId == user.userId } && previous.none { it.userId == user.userId } }
// Users who no longer have the role
val toRemove = previous.filter { user -> users.none { it.userId == user.userId } }.toSet()
selectedUsers.value = (users + toAdd - toRemove).toImmutableList()
}
.launchIn(this)
}
val roomMemberState by room.membersStateFlow.collectAsState()
// Update search results for every query change
LaunchedEffect(query, roomMemberState) {
val results = dataSource
.search(query.orEmpty())
.groupedByRole()
searchResults = if (results.isEmpty()) {
SearchBarResultState.NoResultsFound()
} else {
SearchBarResultState.Results(results)
}
}
val hasPendingChanges by remember {
derivedStateOf {
usersWithRole.value.toSet() != selectedUsers.value.toSet()
}
}
val roomInfo by room.roomInfoFlow.collectAsState()
fun canChangeMemberRole(userId: UserId): Boolean {
val currentUserRole = roomInfo.roleOf(room.sessionId)
val otherUserRole = roomInfo.roleOf(userId)
return currentUserRole.powerLevel > otherUserRole.powerLevel
}
fun handleEvent(event: ChangeRolesEvent) {
when (event) {
is ChangeRolesEvent.ToggleSearchActive -> {
searchActive = !searchActive
}
is ChangeRolesEvent.QueryChanged -> {
query = event.query
}
is ChangeRolesEvent.UserSelectionToggled -> {
val newList = selectedUsers.value.toMutableList()
val index = newList.indexOfFirst { it.userId == event.matrixUser.userId }
if (index >= 0) {
newList.removeAt(index)
} else {
newList.add(event.matrixUser)
}
selectedUsers.value = newList.toImmutableList()
}
is ChangeRolesEvent.Save -> {
val currentUserIsAdmin = roomInfo.roleOf(room.sessionId) == RoomMember.Role.Admin
val isModifyingAdmins = role == RoomMember.Role.Admin
val isConfirming = saveState.value.isConfirming()
val modifyingOwners = role is RoomMember.Role.Owner
val confirmationValue = if (hasPendingChanges && !isConfirming) {
when {
modifyingOwners -> ConfirmingModifyingOwners
currentUserIsAdmin && isModifyingAdmins -> ConfirmingModifyingAdmins
else -> null
}
} else {
null
}
when {
confirmationValue != null -> {
saveState.value = confirmationValue
}
!saveState.value.isLoading() -> {
roomCoroutineScope.save(usersWithRole.value, selectedUsers, saveState)
}
}
}
is ChangeRolesEvent.Exit -> {
saveState.value = if (saveState.value.isUninitialized() && hasPendingChanges) {
// Has pending changes, confirm exit
AsyncAction.ConfirmingCancellation
} else {
// No pending changes, exit immediately
AsyncAction.Success(false)
}
}
is ChangeRolesEvent.CloseDialog -> {
saveState.value = AsyncAction.Uninitialized
}
}
}
return ChangeRolesState(
role = role,
query = query,
isSearchActive = searchActive,
searchResults = searchResults,
selectedUsers = selectedUsers.value,
hasPendingChanges = hasPendingChanges,
savingState = saveState.value,
canChangeMemberRole = ::canChangeMemberRole,
eventSink = ::handleEvent,
)
}
private fun List<RoomMember>.groupedByRole(): MembersByRole {
return MembersByRole(this, powerLevelRoomMemberComparator)
}
private fun CoroutineScope.save(
usersWithRole: ImmutableList<MatrixUser>,
selectedUsers: MutableState<ImmutableList<MatrixUser>>,
saveState: MutableState<AsyncAction<Boolean>>,
) = launch {
runUpdatingState(saveState) {
val toAdd = selectedUsers.value - usersWithRole
val toRemove = usersWithRole - selectedUsers.value
val changes: List<UserRoleChange> = buildList {
for (selectedUser in toAdd) {
analyticsService.capture(RoomModeration(RoomModeration.Action.ChangeMemberRole, role.toAnalyticsMemberRole()))
add(UserRoleChange(selectedUser.userId, role))
}
for (selectedUser in toRemove) {
analyticsService.capture(RoomModeration(RoomModeration.Action.ChangeMemberRole, RoomModeration.Role.User))
add(UserRoleChange(selectedUser.userId, RoomMember.Role.User))
}
}
room.updateUsersRoles(changes).map { true }
}
}
internal fun RoomMember.Role.toAnalyticsMemberRole(): RoomModeration.Role = when (this) {
is RoomMember.Role.Owner -> RoomModeration.Role.Administrator // TODO - distinguish creator from admin
RoomMember.Role.Admin -> RoomModeration.Role.Administrator
RoomMember.Role.Moderator -> RoomModeration.Role.Moderator
RoomMember.Role.User -> RoomModeration.Role.User
}
}
@@ -0,0 +1,53 @@
/*
* 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.rolesandpermissions.impl.roles
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
data class ChangeRolesState(
val role: RoomMember.Role,
val query: String?,
val isSearchActive: Boolean,
val searchResults: SearchBarResultState<MembersByRole>,
val selectedUsers: ImmutableList<MatrixUser>,
val hasPendingChanges: Boolean,
val savingState: AsyncAction<Boolean>,
val canChangeMemberRole: (UserId) -> Boolean,
val eventSink: (ChangeRolesEvent) -> Unit,
)
data class MembersByRole(
val owners: ImmutableList<RoomMember> = persistentListOf(),
val admins: ImmutableList<RoomMember> = persistentListOf(),
val moderators: ImmutableList<RoomMember> = persistentListOf(),
val members: ImmutableList<RoomMember> = persistentListOf(),
) {
constructor(members: List<RoomMember>, comparator: Comparator<RoomMember>) : this(
owners = members.filterAndSort(comparator) { it.role is RoomMember.Role.Owner },
admins = members.filterAndSort(comparator) { it.role == RoomMember.Role.Admin },
moderators = members.filterAndSort(comparator) { it.role == RoomMember.Role.Moderator },
members = members.filterAndSort(comparator) { it.role == RoomMember.Role.User },
)
fun isEmpty() = owners.isEmpty() && admins.isEmpty() && moderators.isEmpty() && members.isEmpty()
}
private fun Iterable<RoomMember>.filterAndSort(
comparator: Comparator<RoomMember>,
predicate: (RoomMember) -> Boolean,
): ImmutableList<RoomMember> {
return filter(predicate).sortedWith(comparator).toImmutableList()
}
@@ -0,0 +1,146 @@
/*
* 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.rolesandpermissions.impl.roles
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.UserId
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.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator
import io.element.android.libraries.previewutils.room.aRoomMember
import io.element.android.libraries.previewutils.room.aRoomMemberList
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
class ChangeRolesStateProvider : PreviewParameterProvider<ChangeRolesState> {
override val values: Sequence<ChangeRolesState>
get() = sequenceOf(
aChangeRolesState(),
aChangeRolesStateWithSelectedUsers().copy(role = RoomMember.Role.Moderator),
aChangeRolesStateWithSelectedUsers().copy(hasPendingChanges = false),
aChangeRolesStateWithSelectedUsers(),
aChangeRolesStateWithSelectedUsers().copy(
selectedUsers = aMatrixUserList().take(2).toImmutableList(),
),
aChangeRolesStateWithSelectedUsers().copy(
query = "Alice",
isSearchActive = true,
searchResults = SearchBarResultState.Results(
MembersByRole(
members = aRoomMemberList().take(1),
comparator = PowerLevelRoomMemberComparator(),
)
),
selectedUsers = aMatrixUserList().take(1).toImmutableList(),
),
aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.ConfirmingCancellation),
aChangeRolesStateWithSelectedUsers().copy(savingState = ConfirmingModifyingAdmins),
aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Loading),
aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Success(true)),
aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.Failure(Exception("boom"))),
aChangeRolesStateWithOwners(role = RoomMember.Role.Admin),
aChangeRolesStateWithOwners(role = RoomMember.Role.Owner(isCreator = false)),
aChangeRolesStateWithOwners(role = RoomMember.Role.Owner(isCreator = false))
.copy(savingState = ConfirmingModifyingOwners),
)
}
internal fun aChangeRolesState(
role: RoomMember.Role = RoomMember.Role.Admin,
query: String? = null,
isSearchActive: Boolean = false,
searchResults: SearchBarResultState<MembersByRole> = SearchBarResultState.NoResultsFound(),
selectedUsers: ImmutableList<MatrixUser> = persistentListOf(),
hasPendingChanges: Boolean = false,
savingState: AsyncAction<Boolean> = AsyncAction.Uninitialized,
canRemoveMember: (UserId) -> Boolean = { true },
eventSink: (ChangeRolesEvent) -> Unit = {},
) = ChangeRolesState(
role = role,
query = query,
isSearchActive = isSearchActive,
searchResults = searchResults,
selectedUsers = selectedUsers,
hasPendingChanges = hasPendingChanges,
savingState = savingState,
canChangeMemberRole = canRemoveMember,
eventSink = eventSink,
)
internal fun aChangeRolesStateWithSelectedUsers() = aChangeRolesState(
selectedUsers = aMatrixUserList().toImmutableList(),
searchResults = SearchBarResultState.Results(
MembersByRole(
members = aRoomMemberList().mapIndexed { index, roomMember ->
if (index % 2 == 0) {
roomMember.copy(membership = RoomMembershipState.INVITE)
} else {
roomMember
}
},
comparator = PowerLevelRoomMemberComparator(),
)
),
hasPendingChanges = true,
canRemoveMember = { it != UserId("@alice:server.org") },
)
internal fun aChangeRolesStateWithOwners(
role: RoomMember.Role = RoomMember.Role.Admin,
selectedUsers: List<MatrixUser> = listOf(
aMatrixUser(id = "@alice:server.org", displayName = "Alice"),
aMatrixUser(id = "@bob:server.org", displayName = "Bob"),
aMatrixUser(id = "@carol:server.org", displayName = "Carol"),
),
) = aChangeRolesState(
role = role,
searchResults = SearchBarResultState.Results(
MembersByRole(
members = persistentListOf(
aRoomMember(
userId = UserId("@alice:server.org"),
displayName = "Alice",
role = RoomMember.Role.Owner(isCreator = true),
),
aRoomMember(
userId = UserId("@bob:server.org"),
displayName = "Bob",
role = RoomMember.Role.Owner(isCreator = false),
),
aRoomMember(
userId = UserId("@carol:server.org"),
displayName = "Carol",
role = RoomMember.Role.Admin,
),
aRoomMember(
userId = UserId("@david:server.org"),
displayName = "David",
role = RoomMember.Role.User,
),
),
comparator = PowerLevelRoomMemberComparator(),
),
),
canRemoveMember = { userId ->
when (userId) {
UserId("@alice:server.org") -> false // Owner - creator
UserId("@bob:server.org") -> false // Owner - super admin
UserId("@carol:server.org") -> true // Admin
UserId("@david:server.org") -> true // User
else -> false
}
},
selectedUsers = selectedUsers.toImmutableList(),
)
@@ -0,0 +1,424 @@
/*
* 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.rolesandpermissions.impl.roles
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
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.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
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.features.rolesandpermissions.impl.R
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.components.async.AsyncActionView
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.AvatarData
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.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.SaveChangesDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.SearchBar
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.UserId
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.getBestName
import io.element.android.libraries.matrix.api.room.toMatrixUser
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChangeRolesView(
state: ChangeRolesState,
modifier: Modifier = Modifier,
) {
BackHandler(enabled = !state.isSearchActive) {
state.eventSink(ChangeRolesEvent.Exit)
}
Box(modifier = modifier) {
Scaffold(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding(),
topBar = {
AnimatedVisibility(visible = !state.isSearchActive) {
TopAppBar(
titleStr = when (state.role) {
is RoomMember.Role.Owner -> stringResource(R.string.screen_room_change_role_owners_title)
RoomMember.Role.Admin -> stringResource(R.string.screen_room_change_role_administrators_title)
RoomMember.Role.Moderator -> stringResource(R.string.screen_room_change_role_moderators_title)
RoomMember.Role.User -> error("This should never be reached")
},
navigationIcon = {
BackButton(onClick = { state.eventSink(ChangeRolesEvent.Exit) })
},
actions = {
TextButton(
text = stringResource(CommonStrings.action_save),
enabled = state.hasPendingChanges,
onClick = { state.eventSink(ChangeRolesEvent.Save) }
)
}
)
}
}
) { paddingValues ->
Column(
modifier = Modifier.padding(paddingValues),
) {
val lazyListState = rememberLazyListState()
SearchBar(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
placeHolderTitle = stringResource(CommonStrings.common_search_for_someone),
query = state.query.orEmpty(),
onQueryChange = { state.eventSink(ChangeRolesEvent.QueryChanged(it)) },
active = state.isSearchActive,
onActiveChange = { state.eventSink(ChangeRolesEvent.ToggleSearchActive) },
resultState = state.searchResults,
) { members ->
SearchResultsList(
currentRole = state.role,
lazyListState = lazyListState,
searchResults = members,
selectedUsers = state.selectedUsers,
canRemoveMember = state.canChangeMemberRole,
onToggleSelection = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it.toMatrixUser())) },
selectedUsersList = {},
)
}
AnimatedVisibility(
visible = !state.isSearchActive,
enter = fadeIn(),
exit = fadeOut()
) {
Column {
SearchResultsList(
currentRole = state.role,
lazyListState = lazyListState,
searchResults = (state.searchResults as? SearchBarResultState.Results)?.results ?: MembersByRole(),
selectedUsers = state.selectedUsers,
canRemoveMember = state.canChangeMemberRole,
onToggleSelection = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it.toMatrixUser())) },
selectedUsersList = { users ->
SelectedUsersRowList(
contentPadding = PaddingValues(start = 16.dp, end = 16.dp, bottom = 16.dp),
selectedUsers = users,
onUserRemove = {
state.eventSink(ChangeRolesEvent.UserSelectionToggled(it))
},
canDeselect = { state.canChangeMemberRole(it.userId) },
)
}
)
}
}
}
}
val asyncIndicatorState = rememberAsyncIndicatorState()
AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), asyncIndicatorState)
AsyncActionView(
async = state.savingState,
onSuccess = {},
confirmationDialog = { confirming ->
when (confirming) {
is AsyncAction.ConfirmingCancellation -> {
SaveChangesDialog(
onSubmitClick = { state.eventSink(ChangeRolesEvent.Exit) },
onDismiss = { state.eventSink(ChangeRolesEvent.CloseDialog) }
)
}
is ConfirmingModifyingOwners -> {
ConfirmationDialog(
title = stringResource(R.string.screen_room_change_role_confirm_change_owners_title),
content = stringResource(R.string.screen_room_change_role_confirm_change_owners_description),
submitText = stringResource(CommonStrings.action_continue),
onSubmitClick = { state.eventSink(ChangeRolesEvent.Save) },
onDismiss = { state.eventSink(ChangeRolesEvent.CloseDialog) },
destructiveSubmit = true,
)
}
is ConfirmingModifyingAdmins -> {
ConfirmationDialog(
title = stringResource(R.string.screen_room_change_role_confirm_add_admin_title),
content = stringResource(R.string.screen_room_change_role_confirm_add_admin_description),
onSubmitClick = { state.eventSink(ChangeRolesEvent.Save) },
onDismiss = { state.eventSink(ChangeRolesEvent.CloseDialog) }
)
}
}
},
errorMessage = {
stringResource(CommonStrings.error_unknown)
},
onErrorDismiss = {
state.eventSink(ChangeRolesEvent.CloseDialog)
},
)
}
}
@Composable
private fun SearchResultsList(
currentRole: RoomMember.Role,
searchResults: MembersByRole,
selectedUsers: ImmutableList<MatrixUser>,
canRemoveMember: (UserId) -> Boolean,
onToggleSelection: (RoomMember) -> Unit,
lazyListState: LazyListState,
selectedUsersList: @Composable (ImmutableList<MatrixUser>) -> Unit,
) {
LazyColumn(
state = lazyListState,
) {
item {
selectedUsersList(selectedUsers)
}
if (searchResults.owners.isNotEmpty()) {
stickyHeader { ListSectionHeader(text = stringResource(R.string.screen_room_roles_and_permissions_owners)) }
item {
Text(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
text = stringResource(R.string.screen_room_change_role_moderators_owner_section_footer),
color = ElementTheme.colors.textSecondary,
style = ElementTheme.typography.fontBodySmRegular,
)
}
items(searchResults.owners, key = { it.userId }) { roomMember ->
ListMemberItem(
roomMember = roomMember,
canRemoveMember = canRemoveMember,
onToggleSelection = onToggleSelection,
selectedUsers = selectedUsers
)
}
}
if (searchResults.admins.isNotEmpty()) {
stickyHeader { ListSectionHeader(text = stringResource(R.string.screen_room_roles_and_permissions_admins)) }
// Add a footer for the admin section in change role to moderator screen
if (currentRole == RoomMember.Role.Moderator) {
item {
Text(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
text = stringResource(R.string.screen_room_change_role_moderators_admin_section_footer),
color = ElementTheme.colors.textSecondary,
style = ElementTheme.typography.fontBodySmRegular,
)
}
}
items(searchResults.admins, key = { it.userId }) { roomMember ->
ListMemberItem(
roomMember = roomMember,
canRemoveMember = canRemoveMember,
onToggleSelection = onToggleSelection,
selectedUsers = selectedUsers
)
}
}
if (searchResults.moderators.isNotEmpty()) {
stickyHeader { ListSectionHeader(text = stringResource(R.string.screen_room_roles_and_permissions_moderators)) }
items(searchResults.moderators, key = { it.userId }) { roomMember ->
ListMemberItem(
roomMember = roomMember,
canRemoveMember = canRemoveMember,
onToggleSelection = onToggleSelection,
selectedUsers = selectedUsers
)
}
}
if (searchResults.members.isNotEmpty()) {
stickyHeader { ListSectionHeader(text = stringResource(R.string.screen_room_member_list_mode_members)) }
items(searchResults.members, key = { it.userId }) { roomMember ->
ListMemberItem(
roomMember = roomMember,
canRemoveMember = canRemoveMember,
onToggleSelection = onToggleSelection,
selectedUsers = selectedUsers
)
}
}
}
}
@Composable
private fun ListSectionHeader(text: String) {
Text(
modifier = Modifier
.background(ElementTheme.colors.bgCanvasDefault)
.padding(horizontal = 16.dp, vertical = 8.dp)
.fillMaxWidth(),
text = text,
style = ElementTheme.typography.fontBodyLgMedium,
)
}
@Composable
private fun ListMemberItem(
roomMember: RoomMember,
canRemoveMember: (UserId) -> Boolean,
onToggleSelection: (RoomMember) -> Unit,
selectedUsers: ImmutableList<MatrixUser>,
) {
val canToggle = canRemoveMember(roomMember.userId)
val trailingContent: @Composable (() -> Unit) = {
if (canToggle) {
Checkbox(
checked = selectedUsers.any { it.userId == roomMember.userId },
onCheckedChange = { onToggleSelection(roomMember) },
)
}
}
Column {
MemberRow(
modifier = Modifier.clickable(enabled = canToggle, onClick = { onToggleSelection(roomMember) }),
avatarData = roomMember.getAvatarData(size = AvatarSize.UserListItem),
name = roomMember.getBestName(),
userId = roomMember.userId.value.takeIf { roomMember.displayName?.isNotBlank() == true },
isPending = roomMember.membership == RoomMembershipState.INVITE,
trailingContent = trailingContent,
)
HorizontalDivider()
}
}
@Composable
private fun MemberRow(
avatarData: AvatarData,
name: String,
userId: String?,
isPending: Boolean,
modifier: Modifier = Modifier,
trailingContent: @Composable (() -> Unit)? = null,
) {
Row(
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp)
.padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Column(
modifier = Modifier
.padding(start = 12.dp)
.weight(1f),
) {
Row(verticalAlignment = Alignment.CenterVertically) {
// Name
Text(
modifier = Modifier.weight(1f, fill = false),
text = name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = ElementTheme.colors.textPrimary,
style = ElementTheme.typography.fontBodyLgRegular,
)
// Invitation pending marker
if (isPending) {
Text(
modifier = Modifier.padding(start = 8.dp),
text = stringResource(id = R.string.screen_room_member_list_pending_status),
style = ElementTheme.typography.fontBodySmRegular.copy(fontStyle = FontStyle.Italic),
color = ElementTheme.colors.textSecondary
)
}
}
// Id
userId?.let {
Text(
text = userId,
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = ElementTheme.typography.fontBodySmRegular,
)
}
}
trailingContent?.invoke()
}
}
@PreviewsDayNight
@Composable
internal fun ChangeRolesViewPreview(@PreviewParameter(ChangeRolesStateProvider::class) state: ChangeRolesState) {
ElementPreview {
ChangeRolesView(
state = state
)
}
}
@PreviewsDayNight
@Composable
internal fun PendingMemberRowWithLongNamePreview() {
ElementPreview(
drawableFallbackForImages = CommonDrawables.sample_avatar,
) {
MemberRow(
avatarData = AvatarData("userId", "A very long name that should be truncated", "https://example.com/avatar.png", AvatarSize.UserListItem),
name = "A very long name that should be truncated",
userId = "@alice:matrix.org",
isPending = true,
trailingContent = {
Checkbox(
checked = true,
onCheckedChange = {},
enabled = true,
)
}
)
}
}
@@ -0,0 +1,77 @@
/*
* 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.rolesandpermissions.impl.roles
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.di.RoomGraphFactory
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DependencyInjectionGraphOwner
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import kotlinx.parcelize.Parcelize
@ContributesNode(SessionScope::class)
@AssistedInject
class ChangeRoomMemberRolesRootNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
roomGraphFactory: RoomGraphFactory,
) : ParentNode<ChangeRoomMemberRolesRootNode.NavTarget>(
navModel = PermanentNavModel(
navTargets = setOf(NavTarget),
savedStateMap = buildContext.savedStateMap,
),
buildContext = buildContext,
plugins = plugins,
), DependencyInjectionGraphOwner, ChangeRoomMemberRolesEntryPoint.NodeProxy {
@Parcelize object NavTarget : Parcelable
data class Inputs(
val joinedRoom: JoinedRoom,
val listType: ChangeRoomMemberRolesListType,
) : NodeInputs
private val inputs = inputs<Inputs>()
override val graph = roomGraphFactory.create(inputs.joinedRoom)
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return createNode<ChangeRolesNode>(
buildContext = buildContext,
plugins = listOf(ChangeRolesNode.Inputs(listType = inputs.listType)),
)
}
@Composable
override fun View(modifier: Modifier) {
Children(modifier = modifier, navModel = navModel)
}
override val roomId: RoomId = inputs.joinedRoom.roomId
override suspend fun waitForCompletion(): Boolean {
return waitForChildAttached<ChangeRolesNode>().waitForCompletion()
}
}
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rolesandpermissions.impl.roles
import io.element.android.libraries.architecture.AsyncAction
data object ConfirmingModifyingAdmins : AsyncAction.Confirming
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rolesandpermissions.impl.roles
import io.element.android.libraries.architecture.AsyncAction
data object ConfirmingModifyingOwners : AsyncAction.Confirming
@@ -0,0 +1,35 @@
/*
* 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.rolesandpermissions.impl.roles
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.room.JoinedRoom
@ContributesBinding(SessionScope::class)
class DefaultChangeRoomMemberRolesEntyPoint : ChangeRoomMemberRolesEntryPoint {
override fun createNode(
parentNode: Node,
buildContext: BuildContext,
room: JoinedRoom,
listType: ChangeRoomMemberRolesListType,
): Node {
return parentNode.createNode<ChangeRoomMemberRolesRootNode>(
buildContext = buildContext,
plugins = listOf(
ChangeRoomMemberRolesRootNode.Inputs(joinedRoom = room, listType = listType),
)
)
}
}
@@ -0,0 +1,18 @@
/*
* 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.rolesandpermissions.impl.root
import io.element.android.libraries.matrix.api.room.RoomMember
sealed interface RolesAndPermissionsEvents {
data object ChangeOwnRole : RolesAndPermissionsEvents
data class DemoteSelfTo(val role: RoomMember.Role) : RolesAndPermissionsEvents
data object ResetPermissions : RolesAndPermissionsEvents
data object CancelPendingAction : RolesAndPermissionsEvents
}
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-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.rolesandpermissions.impl.root
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.ui.model.roleOf
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
@ContributesNode(RoomScope::class)
@AssistedInject
class RolesAndPermissionsNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val presenter: RolesAndPermissionsPresenter,
private val room: BaseRoom,
) : Node(buildContext, plugins = plugins), RolesAndPermissionsNavigator {
interface Callback : Plugin, RolesAndPermissionsNavigator {
override fun openAdminList()
override fun openModeratorList()
override fun openEditPermissions()
override fun onBackClick() {}
}
private val callback: Callback = callback()
@Stable
private val navigator = object : RolesAndPermissionsNavigator by callback {
override fun onBackClick() {
navigateUp()
}
}
override fun onBuilt() {
super.onBuilt()
// If the user is not an admin anymore, exit this section since they won't have permissions to use it
lifecycleScope.launch {
room.roomInfoFlow
.filter { info ->
val role = info.roleOf(room.sessionId)
role != RoomMember.Role.Admin && role !is RoomMember.Role.Owner
}
.take(1)
.onEach { navigateUp() }
.collect()
}
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
RolesAndPermissionsView(
state = state,
rolesAndPermissionsNavigator = navigator,
modifier = modifier,
)
}
}
interface RolesAndPermissionsNavigator {
fun onBackClick() {}
fun openAdminList() {}
fun openModeratorList() {}
fun openEditPermissions() {}
}
@@ -0,0 +1,129 @@
/*
* 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.rolesandpermissions.impl.root
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import dev.zacsweers.metro.Inject
import im.vector.app.features.analytics.plan.RoomModeration
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.RoomInfo
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.activeRoomMembers
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.ui.model.roleOf
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Inject
class RolesAndPermissionsPresenter(
private val room: JoinedRoom,
private val dispatchers: CoroutineDispatchers,
private val analyticsService: AnalyticsService,
) : Presenter<RolesAndPermissionsState> {
@Composable
override fun present(): RolesAndPermissionsState {
val coroutineScope = rememberCoroutineScope()
val roomInfo by room.roomInfoFlow.collectAsState()
val roomMembers by room.membersStateFlow.collectAsState()
// Get the list of active room members (joined or invited), in order to filter members present in the power
// level state Event.
val activeRoomMemberIds by remember {
derivedStateOf {
roomMembers.activeRoomMembers().map { it.userId }
}
}
val moderatorCount by remember {
derivedStateOf {
roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Moderator)
}
}
val adminCount by remember {
derivedStateOf {
val admins = roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Admin)
val ownersCount = if (roomInfo.privilegedCreatorRole) {
val superAdmins = roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Owner(isCreator = false))
val creators = roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Owner(isCreator = true))
superAdmins + creators
} else {
0
}
admins + ownersCount
}
}
val canDemoteSelf = remember { derivedStateOf { roomInfo.roleOf(room.sessionId) !is RoomMember.Role.Owner } }
val changeOwnRoleAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
val resetPermissionsAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
fun handleEvent(event: RolesAndPermissionsEvents) {
when (event) {
is RolesAndPermissionsEvents.ChangeOwnRole -> {
changeOwnRoleAction.value = AsyncAction.ConfirmingNoParams
}
is RolesAndPermissionsEvents.CancelPendingAction -> {
changeOwnRoleAction.value = AsyncAction.Uninitialized
resetPermissionsAction.value = AsyncAction.Uninitialized
}
is RolesAndPermissionsEvents.DemoteSelfTo -> coroutineScope.demoteSelfTo(
role = event.role,
changeOwnRoleAction = changeOwnRoleAction,
)
is RolesAndPermissionsEvents.ResetPermissions -> if (resetPermissionsAction.value.isConfirming()) {
coroutineScope.resetPermissions(resetPermissionsAction)
} else {
resetPermissionsAction.value = AsyncAction.ConfirmingNoParams
}
}
}
return RolesAndPermissionsState(
roomSupportsOwnerRole = roomInfo.privilegedCreatorRole,
adminCount = adminCount,
moderatorCount = moderatorCount,
canDemoteSelf = canDemoteSelf.value,
changeOwnRoleAction = changeOwnRoleAction.value,
resetPermissionsAction = resetPermissionsAction.value,
eventSink = ::handleEvent,
)
}
private fun CoroutineScope.demoteSelfTo(
role: RoomMember.Role,
changeOwnRoleAction: MutableState<AsyncAction<Unit>>,
) = launch(dispatchers.io) {
runUpdatingState(changeOwnRoleAction) {
room.updateUsersRoles(listOf(UserRoleChange(room.sessionId, role)))
}
}
private fun CoroutineScope.resetPermissions(
resetPermissionsAction: MutableState<AsyncAction<Unit>>,
) = launch(dispatchers.io) {
runUpdatingState(resetPermissionsAction) {
analyticsService.capture(RoomModeration(RoomModeration.Action.ResetPermissions))
room.resetPowerLevels()
}
}
private fun RoomInfo.userCountWithRole(userIds: List<UserId>, role: RoomMember.Role): Int {
return usersWithRole(role).filter { it in userIds }.size
}
}
@@ -0,0 +1,21 @@
/*
* 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.rolesandpermissions.impl.root
import io.element.android.libraries.architecture.AsyncAction
data class RolesAndPermissionsState(
val roomSupportsOwnerRole: Boolean,
val adminCount: Int,
val moderatorCount: Int,
val canDemoteSelf: Boolean,
val changeOwnRoleAction: AsyncAction<Unit>,
val resetPermissionsAction: AsyncAction<Unit>,
val eventSink: (RolesAndPermissionsEvents) -> Unit,
)
@@ -0,0 +1,69 @@
/*
* 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.rolesandpermissions.impl.root
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
class RolesAndPermissionsStateProvider : PreviewParameterProvider<RolesAndPermissionsState> {
override val values: Sequence<RolesAndPermissionsState>
get() = sequenceOf(
aRolesAndPermissionsState(roomSupportsOwners = false),
aRolesAndPermissionsState(adminCount = 1, moderatorCount = 2),
aRolesAndPermissionsState(
adminCount = 1,
moderatorCount = 2,
changeOwnRoleAction = AsyncAction.ConfirmingNoParams,
),
aRolesAndPermissionsState(
adminCount = 1,
moderatorCount = 2,
changeOwnRoleAction = AsyncAction.Loading,
),
aRolesAndPermissionsState(
adminCount = 1,
moderatorCount = 2,
changeOwnRoleAction = AsyncAction.Failure(IllegalStateException("Failed to change role")),
),
aRolesAndPermissionsState(
adminCount = 1,
moderatorCount = 2,
resetPermissionsAction = AsyncAction.ConfirmingNoParams,
),
aRolesAndPermissionsState(
adminCount = 1,
moderatorCount = 2,
resetPermissionsAction = AsyncAction.Loading,
),
aRolesAndPermissionsState(
adminCount = 1,
moderatorCount = 2,
resetPermissionsAction = AsyncAction.Failure(IllegalStateException("Failed to reset permissions")),
),
aRolesAndPermissionsState(canDemoteSelf = false),
)
}
internal fun aRolesAndPermissionsState(
roomSupportsOwners: Boolean = true,
adminCount: Int = 0,
moderatorCount: Int = 0,
canDemoteSelf: Boolean = true,
changeOwnRoleAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
resetPermissionsAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (RolesAndPermissionsEvents) -> Unit = {},
) = RolesAndPermissionsState(
roomSupportsOwnerRole = roomSupportsOwners,
adminCount = adminCount,
canDemoteSelf = canDemoteSelf,
moderatorCount = moderatorCount,
changeOwnRoleAction = changeOwnRoleAction,
resetPermissionsAction = resetPermissionsAction,
eventSink = eventSink,
)
@@ -0,0 +1,198 @@
/*
* 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.rolesandpermissions.impl.root
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
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.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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.rolesandpermissions.impl.R
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
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.ListSectionHeader
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.hide
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun RolesAndPermissionsView(
state: RolesAndPermissionsState,
rolesAndPermissionsNavigator: RolesAndPermissionsNavigator,
modifier: Modifier = Modifier,
) {
PreferencePage(
modifier = modifier,
title = stringResource(R.string.screen_room_roles_and_permissions_title),
onBackClick = rolesAndPermissionsNavigator::onBackClick,
) {
ListSectionHeader(title = stringResource(R.string.screen_room_roles_and_permissions_roles_header), hasDivider = false)
val adminsTitle = if (state.roomSupportsOwnerRole) {
stringResource(R.string.screen_room_roles_and_permissions_admins_and_owners)
} else {
stringResource(R.string.screen_room_roles_and_permissions_admins)
}
ListItem(
headlineContent = { Text(adminsTitle) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())),
trailingContent = ListItemContent.Text("${state.adminCount}"),
onClick = { rolesAndPermissionsNavigator.openAdminList() },
)
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_moderators)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ChatProblem())),
trailingContent = ListItemContent.Text("${state.moderatorCount}"),
onClick = { rolesAndPermissionsNavigator.openModeratorList() },
)
if (state.canDemoteSelf) {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_my_role)) },
onClick = { state.eventSink(RolesAndPermissionsEvents.ChangeOwnRole) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Edit()))
)
}
HorizontalDivider()
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_permissions_header)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Settings())),
onClick = { rolesAndPermissionsNavigator.openEditPermissions() },
)
HorizontalDivider()
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_reset)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Delete())),
onClick = { state.eventSink(RolesAndPermissionsEvents.ResetPermissions) },
style = ListItemStyle.Destructive,
)
}
AsyncActionView(
async = state.resetPermissionsAction,
confirmationDialog = {
ConfirmationDialog(
title = stringResource(R.string.screen_room_roles_and_permissions_reset_confirm_title),
content = stringResource(R.string.screen_room_roles_and_permissions_reset_confirm_description),
submitText = stringResource(CommonStrings.action_reset),
destructiveSubmit = true,
onSubmitClick = { state.eventSink(RolesAndPermissionsEvents.ResetPermissions) },
onDismiss = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) },
)
},
onSuccess = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) },
onErrorDismiss = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) }
)
when (state.changeOwnRoleAction) {
is AsyncAction.Confirming -> {
ChangeOwnRoleBottomSheet(
eventSink = state.eventSink,
)
}
is AsyncAction.Loading -> {
ProgressDialog()
}
is AsyncAction.Failure -> {
ErrorDialog(
content = stringResource(CommonStrings.error_unknown),
onSubmit = { state.eventSink(RolesAndPermissionsEvents.CancelPendingAction) }
)
}
else -> Unit
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ChangeOwnRoleBottomSheet(
eventSink: (RolesAndPermissionsEvents) -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
fun dismiss() {
sheetState.hide(coroutineScope) {
eventSink(RolesAndPermissionsEvents.CancelPendingAction)
}
}
ModalBottomSheet(
modifier = Modifier
.systemBarsPadding()
.navigationBarsPadding(),
sheetState = sheetState,
onDismissRequest = ::dismiss,
) {
Text(
modifier = Modifier.padding(14.dp),
text = stringResource(R.string.screen_room_roles_and_permissions_change_my_role),
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textPrimary,
)
Text(
modifier = Modifier.padding(start = 14.dp, end = 14.dp, bottom = 16.dp),
text = stringResource(R.string.screen_room_change_role_confirm_demote_self_description),
style = ElementTheme.typography.fontBodyLgRegular,
color = ElementTheme.colors.textPrimary,
)
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator)) },
onClick = {
sheetState.hide(coroutineScope) {
eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator))
}
},
style = ListItemStyle.Destructive,
)
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_role_demote_to_member)) },
onClick = {
sheetState.hide(coroutineScope) {
eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.User))
}
},
style = ListItemStyle.Destructive,
)
ListItem(
headlineContent = { Text(stringResource(CommonStrings.action_cancel)) },
onClick = ::dismiss,
style = ListItemStyle.Primary,
)
}
}
@PreviewsDayNight
@Composable
internal fun RolesAndPermissionsViewPreview(@PreviewParameter(RolesAndPermissionsStateProvider::class) state: RolesAndPermissionsState) {
ElementPreview {
RolesAndPermissionsView(
state = state,
rolesAndPermissionsNavigator = object : RolesAndPermissionsNavigator {},
)
}
}
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Толькі адміністратары"</string>
<string name="screen_room_change_permissions_ban_people">"Заблакіраваць людзей"</string>
<string name="screen_room_change_permissions_delete_messages">"Выдаліць паведамленні"</string>
<string name="screen_room_change_permissions_invite_people">"Запрашайце людзей і прымайце запыты на далучэнне"</string>
<string name="screen_room_change_permissions_messages_and_content">"Паведамленні і змест"</string>
<string name="screen_room_change_permissions_moderators">"Адміністратары і мадэратары"</string>
<string name="screen_room_change_permissions_remove_people">"Выдаляйце людзей і адхіляйце запыты на далучэнне"</string>
<string name="screen_room_change_permissions_room_avatar">"Змяніць аватар пакоя"</string>
<string name="screen_room_change_permissions_room_details">"Рэдагаваць пакой"</string>
<string name="screen_room_change_permissions_room_name">"Змяніць назву пакоя"</string>
<string name="screen_room_change_permissions_room_topic">"Змяніць тэму пакоя"</string>
<string name="screen_room_change_permissions_send_messages">"Адправіць паведамленні"</string>
<string name="screen_room_change_role_administrators_title">"Рэдагаваць адміністратараў"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не зможаце адмяніць гэта дзеянне. Вы прасоўваеце карыстальніка да таго ж узроўню магутнасці, што і вы."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Дадаць адміністратара?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Паніжэнне ўзроўню"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Вы не зможаце адмяніць гэтае змяненне, бо паніжаеце сябе. Калі вы апошні адміністратар у пакоі, вярнуць права будзе немагчыма."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Панізіць сябе?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (У чаканні)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(У чаканні)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Адміністратары аўтаматычна маюць права мадэратара"</string>
<string name="screen_room_change_role_moderators_title">"Рэдагаваць мадэратараў"</string>
<string name="screen_room_change_role_section_administrators">"Адміністратары"</string>
<string name="screen_room_change_role_section_moderators">"Мадэратары"</string>
<string name="screen_room_change_role_section_users">"Удзельнікі"</string>
<string name="screen_room_change_role_unsaved_changes_description">"У вас ёсць незахаваныя змены."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Захаваць змены?"</string>
<string name="screen_room_member_list_banned_empty">"У гэтым пакоі няма заблакіраваных удзельнікаў."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d удзельнік"</item>
<item quantity="few">"%1$d удзельнікі"</item>
<item quantity="many">"%1$d удзельнікаў"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Выдаліць і заблакіраваць удзельніка"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Толькі выдаліць удзельніка"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Разблакіраваць"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Яны змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."</string>
<string name="screen_room_member_list_mode_banned">"Заблакіраваныя"</string>
<string name="screen_room_member_list_mode_members">"Удзельнікі"</string>
<string name="screen_room_member_list_role_administrator">"Толькі адміністратары"</string>
<string name="screen_room_member_list_role_moderator">"Адміністратары і мадэратары"</string>
<string name="screen_room_member_list_room_members_header_title">"Удзельнікі пакоя"</string>
<string name="screen_room_member_list_unbanning_user">"Разблакіроўка %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Адміністратары"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Змяніць маю роль"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Панізіць да ўдзельніка"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Панізіць да мадэратара"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Мадэрацыя ўдзельнікаў"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Паведамленні і змест"</string>
<string name="screen_room_roles_and_permissions_moderators">"Мадэратары"</string>
<string name="screen_room_roles_and_permissions_reset">"Скінуць дазволы"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Пасля скіду дазволаў вы страціце бягучыя налады."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Скінуць дазволы?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Ролі"</string>
<string name="screen_room_roles_and_permissions_room_details">"Дэталі пакоя"</string>
<string name="screen_room_roles_and_permissions_title">"Ролі і дазволы"</string>
</resources>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Само администратори"</string>
<string name="screen_room_change_permissions_delete_messages">"Премахване на съобщения"</string>
<string name="screen_room_change_permissions_invite_people">"Поканване на хора и приемане на заявки за присъединяване"</string>
<string name="screen_room_change_permissions_messages_and_content">"Съобщения и съдържание"</string>
<string name="screen_room_change_permissions_moderators">"Администратори и модератори"</string>
<string name="screen_room_change_permissions_remove_people">"Премахване на хора и отхвърляне на заявки за присъединяване"</string>
<string name="screen_room_change_permissions_room_details">"Редактиране на стаята"</string>
<string name="screen_room_change_permissions_room_name">"Промяна на името на стаята"</string>
<string name="screen_room_change_permissions_room_topic">"Промяна на темата на стаята"</string>
<string name="screen_room_change_permissions_send_messages">"Изпращане на съобщения"</string>
<string name="screen_room_change_role_administrators_title">"Редактиране на администраторите"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Добавяне на администратор?"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Администраторите автоматично имат модераторски права"</string>
<string name="screen_room_change_role_moderators_title">"Редактиране на модераторите"</string>
<string name="screen_room_change_role_section_administrators">"Администратори"</string>
<string name="screen_room_change_role_section_moderators">"Модератори"</string>
<string name="screen_room_change_role_section_users">"Членове"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d човек"</item>
<item quantity="other">"%1$d души"</item>
</plurals>
<string name="screen_room_member_list_mode_members">"Членове"</string>
<string name="screen_room_member_list_role_administrator">"Само администратори"</string>
<string name="screen_room_member_list_role_moderator">"Администратори и модератори"</string>
<string name="screen_room_member_list_room_members_header_title">"Членове на стаята"</string>
<string name="screen_room_roles_and_permissions_admins">"Администратори"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Промяна на моята роля"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Модериране на членове"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Съобщения и съдържание"</string>
<string name="screen_room_roles_and_permissions_moderators">"Модератори"</string>
<string name="screen_room_roles_and_permissions_reset">"Нулиране на разрешенията"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Роли"</string>
<string name="screen_room_roles_and_permissions_room_details">"Подробности за стаята"</string>
<string name="screen_room_roles_and_permissions_space_details">"Подробности за пространството"</string>
<string name="screen_room_roles_and_permissions_title">"Роли и разрешения"</string>
</resources>
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Správce"</string>
<string name="screen_room_change_permissions_ban_people">"Vykázat lidi"</string>
<string name="screen_room_change_permissions_delete_messages">"Odstranit zprávy"</string>
<string name="screen_room_change_permissions_everyone">"Člen"</string>
<string name="screen_room_change_permissions_invite_people">"Pozvat přátele"</string>
<string name="screen_room_change_permissions_member_moderation">"Spravovat členy"</string>
<string name="screen_room_change_permissions_messages_and_content">"Zprávy a obsah"</string>
<string name="screen_room_change_permissions_moderators">"Moderátor"</string>
<string name="screen_room_change_permissions_remove_people">"Odebrat osoby"</string>
<string name="screen_room_change_permissions_room_avatar">"Změnit avatar místnosti"</string>
<string name="screen_room_change_permissions_room_details">"Upravit podrobnosti"</string>
<string name="screen_room_change_permissions_room_name">"Změnit název místnosti"</string>
<string name="screen_room_change_permissions_room_topic">"Změnit téma místnosti"</string>
<string name="screen_room_change_permissions_send_messages">"Odeslat zprávy"</string>
<string name="screen_room_change_role_administrators_title">"Upravit správce"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Tuto akci nebudete moci vrátit zpět. Upravujete oprávnění uživatele, tak aby měl stejnou úroveň jako vy."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Přidat správce?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Tuto akci nebudete moci vrátit zpět. Převádíte vlastnictví na vybrané uživatele. Jakmile tuto akci opustíte, bude tato změna trvalá."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Převést vlastnictví?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Degradovat"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Tuto změnu nebudete moci vrátit zpět, protože sami degradujete, pokud jste posledním privilegovaným uživatelem v místnosti, nebude možné znovu získat oprávnění."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Degradovat se?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (čekající)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Čeká na vyřízení)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Správci mají automaticky oprávnění moderátora"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Vlastníci mají automaticky administrátorská oprávnění."</string>
<string name="screen_room_change_role_moderators_title">"Upravit moderátory"</string>
<string name="screen_room_change_role_owners_title">"Vyberte vlastníky"</string>
<string name="screen_room_change_role_section_administrators">"Správci"</string>
<string name="screen_room_change_role_section_moderators">"Moderátoři"</string>
<string name="screen_room_change_role_section_users">"Členové"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Máte neuložené změny."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Uložit změny?"</string>
<string name="screen_room_member_list_banned_empty">"V této místnosti nejsou žádní vykázaní uživatelé."</string>
<plurals name="screen_room_member_list_banned_header_title">
<item quantity="one">"%1$d vykázán(a)"</item>
<item quantity="few">"%1$d vykázáni"</item>
<item quantity="other">"%1$d vykázáných"</item>
</plurals>
<string name="screen_room_member_list_empty_search_subtitle">"Zkontrolujte pravopis nebo zkuste nové vyhledávání"</string>
<string name="screen_room_member_list_empty_search_title">"Žádné výsledky pro “%1$s”"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d osoba"</item>
<item quantity="few">"%1$d osoby"</item>
<item quantity="other">"%1$d osob"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Odebrat a vykázat člena"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Pouze odebrat člena"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Zrušit vykázání"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Pokud budou pozváni, budou se moci do této místnosti znovu připojit."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Zrušit vykázání z místnosti"</string>
<string name="screen_room_member_list_mode_banned">"Vykázaní"</string>
<string name="screen_room_member_list_mode_members">"Členové"</string>
<plurals name="screen_room_member_list_pending_header_title">
<item quantity="one">"%1$d pozván(a)"</item>
<item quantity="few">"%1$d pozváni"</item>
<item quantity="other">"%1$d pozvaných"</item>
</plurals>
<string name="screen_room_member_list_pending_status">"Čekající"</string>
<string name="screen_room_member_list_role_administrator">"Správce"</string>
<string name="screen_room_member_list_role_moderator">"Moderátor"</string>
<string name="screen_room_member_list_role_owner">"Vlastník"</string>
<string name="screen_room_member_list_room_members_header_title">"Členové místnosti"</string>
<string name="screen_room_member_list_unbanning_user">"Rušení vykázání %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Správci"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Správci a vlastníci"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Změnit moji roli"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Degradovat na člena"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Degradovat na moderátora"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderování členů"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Zprávy a obsah"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderátoři"</string>
<string name="screen_room_roles_and_permissions_owners">"Vlastníci"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Oprávnění"</string>
<string name="screen_room_roles_and_permissions_reset">"Obnovit oprávnění"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Po obnovení oprávnění ztratíte aktuální nastavení."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Obnovit oprávnění?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Role"</string>
<string name="screen_room_roles_and_permissions_room_details">"Podrobnosti místnosti"</string>
<string name="screen_room_roles_and_permissions_space_details">"Detaily prostoru"</string>
<string name="screen_room_roles_and_permissions_title">"Role a oprávnění"</string>
</resources>
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Gweinyddwyr yn unig"</string>
<string name="screen_room_change_permissions_ban_people">"Gwahardd pobl"</string>
<string name="screen_room_change_permissions_delete_messages">"Tynnu negeseuon"</string>
<string name="screen_room_change_permissions_everyone">"Pawb"</string>
<string name="screen_room_change_permissions_invite_people">"Gwahodd pobl a derbyn ceisiadau i ymuno"</string>
<string name="screen_room_change_permissions_member_moderation">"Cymedroli aelodau"</string>
<string name="screen_room_change_permissions_messages_and_content">"Negeseuon a chynnwys"</string>
<string name="screen_room_change_permissions_moderators">"Gweinyddwyr a chymedrolwyr"</string>
<string name="screen_room_change_permissions_remove_people">"Dileu pobl a gwrthod ceisiadau i ymuno"</string>
<string name="screen_room_change_permissions_room_avatar">"Newid afatar ystafell"</string>
<string name="screen_room_change_permissions_room_details">"Ystafell Golygu"</string>
<string name="screen_room_change_permissions_room_name">"Newid enw\'r ystafell"</string>
<string name="screen_room_change_permissions_room_topic">"Newid pwnc yr ystafell"</string>
<string name="screen_room_change_permissions_send_messages">"Anfon negeseuon"</string>
<string name="screen_room_change_role_administrators_title">"Golygu Gweinyddwyr"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Fyddwch chi ddim yn gallu dadwneud y weithred hon. Rydych chi\'n hyrwyddo\'r defnyddiwr i gael yr un lefel pŵer â chi."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Ychwanegu Gweinyddwr?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Fyddwch chi ddim yn gallu dadwneud y weithred hon. Rydych yn trosglwyddo\'r berchnogaeth i\'r defnyddwyr a ddewiswyd. Unwaith y byddwch yn gadael bydd hyn yn barhaol."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Trosglwyddo perchnogaeth?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Gostwng"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Fyddwch chi ddim yn gallu dadwneud y newid hwn gan eich bod yn israddio eich hun, os mai chi yw\'r defnyddiwr breintiedig olaf yn yr ystafell bydd yn amhosibl adennill breintiau."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Israddio eich hun?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Yn aros)"</string>
<string name="screen_room_change_role_invited_member_name_android">"Yn aros"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Mae gan weinyddwyr freintiau cymedrolwr yn awtomatig"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Mae gan berchnogion freintiau gweinyddwr yn awtomatig."</string>
<string name="screen_room_change_role_moderators_title">"Golygu Cymedrolwyr"</string>
<string name="screen_room_change_role_owners_title">"Dewiswch Berchnogion"</string>
<string name="screen_room_change_role_section_administrators">"Gweinyddwyr"</string>
<string name="screen_room_change_role_section_moderators">"Cymedrolwyr"</string>
<string name="screen_room_change_role_section_users">"Aelodau"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Mae gennych newidiadau heb eu cadw."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Cadw\'r newidiadau?"</string>
<string name="screen_room_member_list_banned_empty">"Nid oes unrhyw ddefnyddwyr gwaharddedig yn yr ystafell hon."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="zero">"%1$d personau"</item>
<item quantity="one">"%1$d person"</item>
<item quantity="two">"%1$d berson"</item>
<item quantity="few">"%1$d person"</item>
<item quantity="many">"%1$d pherson"</item>
<item quantity="other">"%1$d person"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Gwahardd o ystafell"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Dileu aelod yn unig"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Adfer"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Fyddan nhw yn gallu ymuno â\'r ystafell hon eto os cân nhw wahoddiad."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Dad-wahardd o\'r ystafell"</string>
<string name="screen_room_member_list_mode_banned">"Wedi\'i wahardd"</string>
<string name="screen_room_member_list_mode_members">"Aelodau"</string>
<string name="screen_room_member_list_role_administrator">"Gweinyddwyr yn unig"</string>
<string name="screen_room_member_list_role_moderator">"Gweinyddwyr a chymedrolwyr"</string>
<string name="screen_room_member_list_role_owner">"Perchennog"</string>
<string name="screen_room_member_list_room_members_header_title">"Aelodau\'r ystafell"</string>
<string name="screen_room_member_list_unbanning_user">"Dad-wahardd %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Gweinyddwyr"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Gweinyddwyr a pherchnogion"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Newid fy rôl"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Israddio aelod"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Israddio cymedrolwr"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Cymedroli aelodau"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Negeseuon a chynnwys"</string>
<string name="screen_room_roles_and_permissions_moderators">"Cymedrolwyr"</string>
<string name="screen_room_roles_and_permissions_owners">"Perchnogion"</string>
<string name="screen_room_roles_and_permissions_reset">"Ailosod caniatâd"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Ar ôl i chi ailosod caniatâd, byddwch yn colli\'r gosodiadau cyfredol."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Ailosod caniatâd?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rolau"</string>
<string name="screen_room_roles_and_permissions_room_details">"Manylion yr ystafell"</string>
<string name="screen_room_roles_and_permissions_title">"Rolau a chaniatâd"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Kun admins"</string>
<string name="screen_room_change_permissions_ban_people">"Spær brugere"</string>
<string name="screen_room_change_permissions_delete_messages">"Fjern beskeder"</string>
<string name="screen_room_change_permissions_invite_people">"Invitér personer og acceptér anmodninger om at deltage"</string>
<string name="screen_room_change_permissions_messages_and_content">"Beskeder og indhold"</string>
<string name="screen_room_change_permissions_moderators">"Admins og moderatorer"</string>
<string name="screen_room_change_permissions_remove_people">"Fjern personer og afvis anmodninger om at deltage"</string>
<string name="screen_room_change_permissions_room_avatar">"Skift rummets avatar"</string>
<string name="screen_room_change_permissions_room_details">"Rediger rum"</string>
<string name="screen_room_change_permissions_room_name">"Skift rummets navn"</string>
<string name="screen_room_change_permissions_room_topic">"Skift emne for rummet"</string>
<string name="screen_room_change_permissions_send_messages">"Send beskeder"</string>
<string name="screen_room_change_role_administrators_title">"Redigér admins"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Du kan ikke fortryde denne handling. Du forfremmer brugeren til at have samme magtniveau som dig."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Tilføj Admin?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Du kan ikke fortryde denne handling. Du overfører ejerskabet til de valgte brugere. Når du forlader siden, vil dette være permanent."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Overdrag ejerskab?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Nedgradering"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Du vil ikke være i stand til at fortryde denne ændring, da du degraderer dig selv. Hvis du er den sidste privilegerede bruger i rummet, vil det være umuligt at genvinde privilegier."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Nedgrader dig selv?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Afventer)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Afventer)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratorer har automatisk moderatorrettigheder"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Ejere har automatisk administratorrettigheder."</string>
<string name="screen_room_change_role_moderators_title">"Redigér moderatorer"</string>
<string name="screen_room_change_role_owners_title">"Vælg ejere"</string>
<string name="screen_room_change_role_section_administrators">"Administratorer"</string>
<string name="screen_room_change_role_section_moderators">"Moderatorer"</string>
<string name="screen_room_change_role_section_users">"Medlemmer"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Du har ændringer, der ikke er gemt."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Gem ændringer?"</string>
<string name="screen_room_member_list_banned_empty">"Der er ingen spærrede brugere i dette rum."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d person"</item>
<item quantity="other">"%1$d personer"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Spær fra rum"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Fjern kun medlem"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Fjern spærring af"</string>
<string name="screen_room_member_list_manage_member_unban_message">"De vil være i stand til at deltage i dette rum igen, hvis de inviteres."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Fjern brugerens spærring fra rummet"</string>
<string name="screen_room_member_list_mode_banned">"Spærret"</string>
<string name="screen_room_member_list_mode_members">"Medlemmer"</string>
<string name="screen_room_member_list_role_administrator">"Kun admins"</string>
<string name="screen_room_member_list_role_moderator">"Admins og moderatorer"</string>
<string name="screen_room_member_list_role_owner">"Ejeren"</string>
<string name="screen_room_member_list_room_members_header_title">"Medlemmer af rummet"</string>
<string name="screen_room_member_list_unbanning_user">"Ophæver spærring af %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratorer"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administratorer og ejere"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Skift min rolle"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Nedgrader til medlem"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Nedgradering til moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderation af medlemmer"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Beskeder og indhold"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatorer"</string>
<string name="screen_room_roles_and_permissions_owners">"Ejere"</string>
<string name="screen_room_roles_and_permissions_reset">"Nulstil tilladelser"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Når du nulstiller tilladelserne, mister du de nuværende indstillinger."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Nulstil tilladelser?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roller"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detaljer om rummet"</string>
<string name="screen_room_roles_and_permissions_title">"Roller og tilladelser"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Nur Admins"</string>
<string name="screen_room_change_permissions_ban_people">"Mitglieder sperren"</string>
<string name="screen_room_change_permissions_delete_messages">"Nachrichten entfernen"</string>
<string name="screen_room_change_permissions_invite_people">"Personen einladen und Beitrittsanfragen annehmen"</string>
<string name="screen_room_change_permissions_messages_and_content">"Nachrichten senden &amp; löschen"</string>
<string name="screen_room_change_permissions_moderators">"Admins und Moderatoren"</string>
<string name="screen_room_change_permissions_remove_people">"Personen entfernen und Beitrittsanfragen ablehnen"</string>
<string name="screen_room_change_permissions_room_avatar">"Avatar ändern"</string>
<string name="screen_room_change_permissions_room_details">"Chat bearbeiten"</string>
<string name="screen_room_change_permissions_room_name">"Chat-Namen ändern"</string>
<string name="screen_room_change_permissions_room_topic">"Chat Thema ändern"</string>
<string name="screen_room_change_permissions_send_messages">"Nachrichten senden"</string>
<string name="screen_room_change_role_administrators_title">"Admins bearbeiten"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Du kannst diese Aktion nicht mehr rückgängig machen. Du vergibst dieselbe Rolle, die du auch hast."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Als Admin hinzufügen?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Du kannst diese Aktion nicht rückgängig machen. Du überträgst die Eigentumsrechte an die ausgewählten Nutzer. Sobald du diesen Vorgang abschließt, ist er endgültig."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Eigentumsrechte übertragen?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Zurückstufen"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Du stufst dich selbst herab. Diese Änderung kann nicht rückgängig gemacht werden. Wenn du der letzte Nutzer mit dieser Rolle bist, ist es nicht möglich, diese Rolle wiederzuerlangen."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Möchtest du dich selbst herabstufen?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Ausstehend)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Ausstehend)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Admins haben automatisch Moderatorenrechte"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Eigentümer haben automatisch Adminrechte."</string>
<string name="screen_room_change_role_moderators_title">"Moderatoren bearbeiten"</string>
<string name="screen_room_change_role_owners_title">"Wähle Eigentümer"</string>
<string name="screen_room_change_role_section_administrators">"Admins"</string>
<string name="screen_room_change_role_section_moderators">"Moderatoren"</string>
<string name="screen_room_change_role_section_users">"Mitglieder"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Du hast nicht gespeicherte Änderungen."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Änderungen speichern?"</string>
<string name="screen_room_member_list_banned_empty">"Es gibt keine gesperrten Nutzer."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d Person"</item>
<item quantity="other">"%1$d Personen"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Mitglied entfernen und sperren"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Mitglied nur entfernen"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Sperre aufheben"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Die Nutzer können dem Chat wieder beitreten, wenn sie eingeladen werden."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Sperre für diesen Chat aufheben"</string>
<string name="screen_room_member_list_mode_banned">"Gesperrt"</string>
<string name="screen_room_member_list_mode_members">"Mitglieder"</string>
<string name="screen_room_member_list_role_administrator">"Nur Admins"</string>
<string name="screen_room_member_list_role_moderator">"Admins und Moderatoren"</string>
<string name="screen_room_member_list_role_owner">"Eigentümer"</string>
<string name="screen_room_member_list_room_members_header_title">"Mitglieder"</string>
<string name="screen_room_member_list_unbanning_user">"%1$s wird entsperrt."</string>
<string name="screen_room_roles_and_permissions_admins">"Admins"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Admins und Eigentümer"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Ändere meine Rolle"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Zum Mitglied herabstufen"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Zum Moderator herabstufen"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderation der Mitglieder"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Nachrichten senden &amp; löschen"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatoren"</string>
<string name="screen_room_roles_and_permissions_owners">"Eigentümer"</string>
<string name="screen_room_roles_and_permissions_reset">"Rollen und Berechtigungen zurücksetzen"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Sobald du die Berechtigungen zurücksetzt, verlierst du die aktuellen Einstellungen."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Berechtigungen zurücksetzen?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rollen"</string>
<string name="screen_room_roles_and_permissions_room_details">"Chat-Details anpassen"</string>
<string name="screen_room_roles_and_permissions_title">"Rollen und Berechtigungen"</string>
</resources>
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Μόνο διαχειριστές"</string>
<string name="screen_room_change_permissions_ban_people">"Αποκλεισμός ατόμων"</string>
<string name="screen_room_change_permissions_delete_messages">"Αφαίρεση μηνυμάτων"</string>
<string name="screen_room_change_permissions_invite_people">"Προσκάλεσε άτομα και αποδέξου αιτήματα συμμετοχής"</string>
<string name="screen_room_change_permissions_messages_and_content">"Μηνύματα και περιεχόμενο"</string>
<string name="screen_room_change_permissions_moderators">"Διαχειριστές και συντονιστές"</string>
<string name="screen_room_change_permissions_remove_people">"Αφαίρεση ατόμων και απόρριψη αιτημάτων συμμετοχής"</string>
<string name="screen_room_change_permissions_room_avatar">"Αλλαγή εικόνας προφίλ αίθουσας"</string>
<string name="screen_room_change_permissions_room_details">"Επεξεργασία Αίθουσας"</string>
<string name="screen_room_change_permissions_room_name">"Αλλαγή ονόματος αίθουσας"</string>
<string name="screen_room_change_permissions_room_topic">"Αλλαγή θέματος αίθουσας"</string>
<string name="screen_room_change_permissions_send_messages">"Αποστολή μηνυμάτων"</string>
<string name="screen_room_change_role_administrators_title">"Επεξεργασία Διαχειριστών"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Δεν θα μπορείς να αναιρέσεις αυτήν την ενέργεια. Προβιβάζεις τον χρήστη να έχει το ίδιο επίπεδο ισχύος με σένα."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Προσθήκη Διαχειριστή;"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Υποβιβασμός"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Δεν θα μπορέσετε να αναιρέσετε αυτή την αλλαγή καθώς υποβιβάζετε τον εαυτό σας, αν είστε ο τελευταίος χρήστης με δικαιώματα στην αίθουσα θα είναι αδύνατο να ανακτήσετε δικαιώματα."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Υποβιβασμός του εαυτού σου;"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Σε αναμονή)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Σε αναμονή)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Οι διαχειριστές έχουν αυτόματα δικαιώματα συντονιστή"</string>
<string name="screen_room_change_role_moderators_title">"Επεξεργασία Συντονιστών"</string>
<string name="screen_room_change_role_section_administrators">"Διαχειριστές"</string>
<string name="screen_room_change_role_section_moderators">"Συντονιστές"</string>
<string name="screen_room_change_role_section_users">"Μέλη"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Έχεις μη αποθηκευμένες αλλαγές."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Αποθήκευση αλλαγών;"</string>
<string name="screen_room_member_list_banned_empty">"Δεν υπάρχουν αποκλεισμένοι χρήστες σε αυτή την αίθουσα."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d άτομο"</item>
<item quantity="other">"%1$d άτομα"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Αφαίρεση και αποκλεισμός μέλους"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Μόνο αφαίρεση μέλους"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Αναίρεση αποκλεισμού"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Θα μπορούν να συμμετάσχουν ξανά σε αυτή την αίθουσα, εάν προσκληθούν."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Άρση αποκλεισμού από την αίθουσα"</string>
<string name="screen_room_member_list_mode_banned">"Αποκλεισμένοι"</string>
<string name="screen_room_member_list_mode_members">"Μέλη"</string>
<string name="screen_room_member_list_role_administrator">"Μόνο διαχειριστές"</string>
<string name="screen_room_member_list_role_moderator">"Διαχειριστές και συντονιστές"</string>
<string name="screen_room_member_list_room_members_header_title">"Μέλη της αίθουσας"</string>
<string name="screen_room_member_list_unbanning_user">"Άρση αποκλεισμού %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Διαχειριστές"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Άλλαξε τον ρόλο μου"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Υποβιβασμός σε μέλος"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Υποβιβασμός σε συντονιστή"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Συντονισμός μελών"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Μηνύματα και περιεχόμενο"</string>
<string name="screen_room_roles_and_permissions_moderators">"Συντονιστές"</string>
<string name="screen_room_roles_and_permissions_reset">"Επαναφορά δικαιωμάτων"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Μόλις επαναφέρεις τα δικαιώματα, θα χάσεις τις τρέχουσες ρυθμίσεις."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Επαναφορά δικαιωμάτων;"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Ρόλοι"</string>
<string name="screen_room_roles_and_permissions_room_details">"Λεπτομέρειες αίθουσας"</string>
<string name="screen_room_roles_and_permissions_title">"Ρόλοι και δικαιώματα"</string>
</resources>
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Solo administradores"</string>
<string name="screen_room_change_permissions_ban_people">"Vetar personas"</string>
<string name="screen_room_change_permissions_delete_messages">"Eliminar mensajes"</string>
<string name="screen_room_change_permissions_invite_people">"Invitar personas y aceptar solicitudes de unión"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mensajes y contenido"</string>
<string name="screen_room_change_permissions_moderators">"Administradores y moderadores"</string>
<string name="screen_room_change_permissions_remove_people">"Eliminar personas y rechazar solicitudes de unión"</string>
<string name="screen_room_change_permissions_room_avatar">"Cambiar el avatar de la sala"</string>
<string name="screen_room_change_permissions_room_details">"Editar sala"</string>
<string name="screen_room_change_permissions_room_name">"Cambiar el nombre de la sala"</string>
<string name="screen_room_change_permissions_room_topic">"Cambiar el tema de la sala"</string>
<string name="screen_room_change_permissions_send_messages">"Enviar mensajes"</string>
<string name="screen_room_change_role_administrators_title">"Editar administradores"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"No podrás deshacer esta acción. Estás promocionando al usuario para que tenga el mismo nivel de poder que tú."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"¿Agregar Admin?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Degradar"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"No podrás deshacer este cambio ya que te estás degradando. Si eres el último usuario privilegiado en la sala será imposible recuperar los privilegios."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"¿Degradarte?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Pendiente)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Pendiente)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Los administradores tienen privilegios de moderador de forma automática"</string>
<string name="screen_room_change_role_moderators_title">"Editar moderadores"</string>
<string name="screen_room_change_role_section_administrators">"Administradores"</string>
<string name="screen_room_change_role_section_moderators">"Moderadores"</string>
<string name="screen_room_change_role_section_users">"Miembros"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Tienes cambios sin guardar."</string>
<string name="screen_room_change_role_unsaved_changes_title">"¿Guardar cambios?"</string>
<string name="screen_room_member_list_banned_empty">"No hay usuarios vetados en esta sala."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"Una persona"</item>
<item quantity="other">"%1$d personas"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Sacar y vetar a un miembro"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Solo eliminar miembro"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Quitar veto"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Podrá volver a unirse a esta sala si se le invita."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Eliminar veto en la sala"</string>
<string name="screen_room_member_list_mode_banned">"Vetados"</string>
<string name="screen_room_member_list_mode_members">"Miembros"</string>
<string name="screen_room_member_list_role_administrator">"Solo administradores"</string>
<string name="screen_room_member_list_role_moderator">"Administradores y moderadores"</string>
<string name="screen_room_member_list_room_members_header_title">"Miembros de la sala"</string>
<string name="screen_room_member_list_unbanning_user">"Levantando veto a %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administradores"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Cambiar mi rol"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Degradar a miembro"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Degradar a moderador"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderación de miembros"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mensajes y contenido"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderadores"</string>
<string name="screen_room_roles_and_permissions_reset">"Restablecer permisos"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Una vez que restablezca los permisos, perderá la configuración actual."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"¿Restablecer los permisos?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roles"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detalles de la sala"</string>
<string name="screen_room_roles_and_permissions_title">"Roles y permisos"</string>
</resources>
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Peakasutajad"</string>
<string name="screen_room_change_permissions_ban_people">"Suhtluskeelu seadmine"</string>
<string name="screen_room_change_permissions_delete_messages">"Eemalda sõnumid"</string>
<string name="screen_room_change_permissions_everyone">"Liikmed"</string>
<string name="screen_room_change_permissions_invite_people">"Osalejate kutsumine"</string>
<string name="screen_room_change_permissions_member_moderation">"Liikmete haldus"</string>
<string name="screen_room_change_permissions_messages_and_content">"Sõnumid ja sisu"</string>
<string name="screen_room_change_permissions_moderators">"Moderaatorid"</string>
<string name="screen_room_change_permissions_remove_people">"Osalejate eemaldamine"</string>
<string name="screen_room_change_permissions_room_avatar">"Jututoa tunnuspildi muutmine"</string>
<string name="screen_room_change_permissions_room_details">"Muuda üksikasju"</string>
<string name="screen_room_change_permissions_room_name">"Jututoa nime muutmine"</string>
<string name="screen_room_change_permissions_room_topic">"Jututoa teema muutmine"</string>
<string name="screen_room_change_permissions_send_messages">"Sõnumite saatmine"</string>
<string name="screen_room_change_role_administrators_title">"Muuda peakasutajaid"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Kuna sa annad teisele kasutajale sinu õigustega võrreldes samad õigused, siis sa ei saa seda muudatust hiljem tagasi pöörata."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisame peakasutaja?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Seda tegevust ei saa tagasi pöörata. Järgnevaga annad jututoa omandi üle valitud kasutajatele. Kui lahkus, siis muutub see muudatus püsivaks."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Kas soovid omandi üle anda?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Vähenda õigusi"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Kui sa võtad endalt kõik õigused ära ja oled viimane peakasutaja selles jututoas, siis sa ei saa seda muudatust hiljem tagasi pöörata."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Kas vähendad enda õigusi?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (ootel)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(ootel)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Peakasutajatel on automaatselt ka moderaatori õigused"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Omanikel on automaatselt ka peakasutaja õigused."</string>
<string name="screen_room_change_role_moderators_title">"Muuda moderaatoreid"</string>
<string name="screen_room_change_role_owners_title">"Vali omanikud"</string>
<string name="screen_room_change_role_section_administrators">"Peakasutajad"</string>
<string name="screen_room_change_role_section_moderators">"Moderaatorid"</string>
<string name="screen_room_change_role_section_users">"Liikmed"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Sul on salvestamata muudatusi"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Kas salvestame muudatused?"</string>
<string name="screen_room_member_list_banned_empty">"Suhtluskeeluga kasutajaid pole"</string>
<string name="screen_room_member_list_empty_search_subtitle">"Palun kontrolli otsingusõna korrektsust ja proovi siis uuesti"</string>
<string name="screen_room_member_list_empty_search_title">"Otsingul „%1$s“ pole tulemusi"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d osaleja"</item>
<item quantity="other">"%1$d osalejat"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Eemalda ja sea suhtluskeeld"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Ainult eemalda kasutaja"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Eemalda suhtluskeeld"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Kutse olemasolul saab ta nüüd jututoaga uuesti liituda"</string>
<string name="screen_room_member_list_manage_member_unban_title">"Eemalda suhtluskeeld jututoas"</string>
<string name="screen_room_member_list_mode_banned">"Suhtluskeeluga kasutajad"</string>
<string name="screen_room_member_list_mode_members">"Liikmed"</string>
<string name="screen_room_member_list_pending_status">"Ootel"</string>
<string name="screen_room_member_list_role_administrator">"Peakasutajad"</string>
<string name="screen_room_member_list_role_moderator">"Moderaatorid"</string>
<string name="screen_room_member_list_role_owner">"Omanik"</string>
<string name="screen_room_member_list_room_members_header_title">"Jututoas osalejad"</string>
<string name="screen_room_member_list_unbanning_user">"Eemaldame suhtluskeelu kasutajalt %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Peakasutajad"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Peakasutajad ja omanikud"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Muuda minu rolli"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Muuda tavaliikmeks"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Muuda moderaatoriks"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Jututoas osalejate modereerimine"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Sõnumid ja sisu"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderaatorid"</string>
<string name="screen_room_roles_and_permissions_owners">"Omanikud"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Õigused"</string>
<string name="screen_room_roles_and_permissions_reset">"Lähtesta õigused"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Kui lähtestad õigused, siis praegune õiguste kombinatsioon läheb kaotsi."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Kas lähtestame õigused?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rollid"</string>
<string name="screen_room_roles_and_permissions_room_details">"Jututoa üksikasjad"</string>
<string name="screen_room_roles_and_permissions_space_details">"Kogukonna üksikasjad"</string>
<string name="screen_room_roles_and_permissions_title">"Rollid ja õigused"</string>
</resources>
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Administratzaileak soilik"</string>
<string name="screen_room_change_permissions_ban_people">"Jarri debekua jendeari"</string>
<string name="screen_room_change_permissions_delete_messages">"Kendu mezuak"</string>
<string name="screen_room_change_permissions_invite_people">"Gonbidatu jendea"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mezuak eta edukiak"</string>
<string name="screen_room_change_permissions_moderators">"Administratzaileak eta moderatzaileak"</string>
<string name="screen_room_change_permissions_remove_people">"Kendu jendea"</string>
<string name="screen_room_change_permissions_room_avatar">"Aldatu gelaren abatarra"</string>
<string name="screen_room_change_permissions_room_details">"Editatu gela"</string>
<string name="screen_room_change_permissions_room_name">"Aldatu gelaren izena"</string>
<string name="screen_room_change_permissions_room_topic">"Aldatu gelako mintzagaia"</string>
<string name="screen_room_change_permissions_send_messages">"Bidali mezuak"</string>
<string name="screen_room_change_role_administrators_title">"Editatu administratzaileak"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Administratzailea gehitu?"</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Jabetza eskualdatu?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Jaitsi mailaz"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Ezin izango duzu hau aldatu zure burua mailaz jaisten ari zarelako, zu bazara gelan baimenak dituen azken erabiltzailea ezin izango dira baimenak berreskuratu."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Zure burua mailaz jaitsi?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (zain)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Egiteke)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratzaileek automatikoki dute moderatzaile-pribilegioak"</string>
<string name="screen_room_change_role_moderators_title">"Editatu moderatzaileak"</string>
<string name="screen_room_change_role_owners_title">"Aukeratu jabeak"</string>
<string name="screen_room_change_role_section_administrators">"Administratzaileak"</string>
<string name="screen_room_change_role_section_moderators">"Moderatzaileak"</string>
<string name="screen_room_change_role_section_users">"Kideak"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Gorde gabeko aldaketak dituzu."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Aldaketak gorde?"</string>
<string name="screen_room_member_list_banned_empty">"Gela honetan ez dago debekua ezarri zaion erabiltzailerik."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"Pertsona %1$d"</item>
<item quantity="other">"%1$d pertsona"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Kendu kidea eta ezarri debekua"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Kendu kidea soilik"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Kendu debekua"</string>
<string name="screen_room_member_list_mode_banned">"Debekatuta"</string>
<string name="screen_room_member_list_mode_members">"Kideak"</string>
<string name="screen_room_member_list_role_administrator">"Administratzaileak soilik"</string>
<string name="screen_room_member_list_role_moderator">"Administratzaileak eta moderatzaileak"</string>
<string name="screen_room_member_list_role_owner">"Jabea"</string>
<string name="screen_room_member_list_room_members_header_title">"Gelako kideak"</string>
<string name="screen_room_member_list_unbanning_user">"%1$s(r)i debekua kentzen"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratzaileak"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administratzaileak eta jabeak"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Aldatu nire rola"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Jaitsi maila, kidera"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Jaitsi maila, moderatzailera"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Kideen moderazioa"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mezuak eta edukiak"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatzaileak"</string>
<string name="screen_room_roles_and_permissions_owners">"Jabeak"</string>
<string name="screen_room_roles_and_permissions_reset">"Berrezarri baimenak"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Baimenak berrezarritakoan, uneko ezarpenak galduko dituzu."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Baimenak berrezarri?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rolak"</string>
<string name="screen_room_roles_and_permissions_room_details">"Gelaren xehetasunak"</string>
<string name="screen_room_roles_and_permissions_title">"Rolak eta baimenak"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"فقط مدیران"</string>
<string name="screen_room_change_permissions_ban_people">"تحریم افراد"</string>
<string name="screen_room_change_permissions_delete_messages">"برداشتن پیام‌ها"</string>
<string name="screen_room_change_permissions_everyone">"هرکسی"</string>
<string name="screen_room_change_permissions_invite_people">"دعوت افراد و پذیرش درخواست‌های پیوستن"</string>
<string name="screen_room_change_permissions_member_moderation">"نظارت اعضا"</string>
<string name="screen_room_change_permissions_messages_and_content">"پیام‌ها و محتوا"</string>
<string name="screen_room_change_permissions_moderators">"مدیرن و ناظران"</string>
<string name="screen_room_change_permissions_remove_people">"برداشتن افراد و رد درخواست‌های پیوستن"</string>
<string name="screen_room_change_permissions_room_avatar">"تغییر چهرک اتاق"</string>
<string name="screen_room_change_permissions_room_details">"ویرایش اتاق"</string>
<string name="screen_room_change_permissions_room_name">"تغییر نام اتاق"</string>
<string name="screen_room_change_permissions_room_topic">"دگرگونی موضوع اتاق"</string>
<string name="screen_room_change_permissions_send_messages">"فرستادن پیام‌ها"</string>
<string name="screen_room_change_role_administrators_title">"ویرایش مدیران"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"قادر نخواهید بود این کنش را بازکردانید. داردید کاربر را به سطح قدرت خودتان ارتقا می‌دهید."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"افزودن مدیر؟"</string>
<string name="screen_room_change_role_confirm_change_owners_title">"انتقال مالکیت؟"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"تنزل بده"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"شما نمی‌توانید این تغییر را بازگردانید زیرا در حال تنزل نقش خود در اتاق هستید، اگر آخرین کاربر ممتاز در اتاق باشید، امکان دستیابی مجدد به دسترسی‌های سطح بالای اتاق غیرممکن است."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"تنزل نقش شما در اتاق؟"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (منتظر)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(منتظر)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"مدیران به صورت خودکار اجازه‌های نظارتی را دارند"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"ماکان به صورت خودکار اجازه‌های مدیریتی را دارند."</string>
<string name="screen_room_change_role_moderators_title">"ویرایش ناظران"</string>
<string name="screen_room_change_role_owners_title">"گزینش مالکان"</string>
<string name="screen_room_change_role_section_administrators">"مدیران"</string>
<string name="screen_room_change_role_section_moderators">"ناظم‌ها"</string>
<string name="screen_room_change_role_section_users">"اعضا"</string>
<string name="screen_room_change_role_unsaved_changes_description">"تغییراتی ذخیره نشده دارید."</string>
<string name="screen_room_change_role_unsaved_changes_title">"ذخیرهٔ تغییرات؟"</string>
<string name="screen_room_member_list_banned_empty">"هیچ کاربر محرومی در این اتاق نیست."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d نفر"</item>
<item quantity="other">"%1$d نفر"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"برداشت و تحریم عضو"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"تنها برداشتن عضو"</string>
<string name="screen_room_member_list_manage_member_unban_action">"رفع انسداد"</string>
<string name="screen_room_member_list_manage_member_unban_message">"در صورت دعوت می‌تواند دوباره به اتاق بپیوندد."</string>
<string name="screen_room_member_list_manage_member_unban_title">"تحریم نکردن از اتاق"</string>
<string name="screen_room_member_list_mode_banned">"محروم"</string>
<string name="screen_room_member_list_mode_members">"اعضا"</string>
<string name="screen_room_member_list_role_administrator">"فقط مدیران"</string>
<string name="screen_room_member_list_role_moderator">"مدیرن و ناظران"</string>
<string name="screen_room_member_list_role_owner">"مالک"</string>
<string name="screen_room_member_list_room_members_header_title">"اعضای اتاق"</string>
<string name="screen_room_member_list_unbanning_user">"رفع تحریم %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"مدیران"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"مدیران و مالکان"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"تغییر نقشم"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"تنزّل به عضو"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"تنزّل به ناظم"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"نظارت اعضا"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"پیام‌ها و محتوا"</string>
<string name="screen_room_roles_and_permissions_moderators">"ناظم‌ها"</string>
<string name="screen_room_roles_and_permissions_owners">"مالکان"</string>
<string name="screen_room_roles_and_permissions_reset">"بازنشانی اجازه‌ها"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"بازنشانی اجازه‌ها؟"</string>
<string name="screen_room_roles_and_permissions_roles_header">"نقش‌ها"</string>
<string name="screen_room_roles_and_permissions_room_details">"جزییات اتاق"</string>
<string name="screen_room_roles_and_permissions_title">"نقش‌ها و اجازه‌ها"</string>
</resources>
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Ylläpitäjä"</string>
<string name="screen_room_change_permissions_ban_people">"Porttikieltojen antaminen"</string>
<string name="screen_room_change_permissions_delete_messages">"Viestien poistaminen"</string>
<string name="screen_room_change_permissions_everyone">"Jäsen"</string>
<string name="screen_room_change_permissions_invite_people">"Ihmisten kutsuminen ja liittymispyyntöjen hyväksyminen"</string>
<string name="screen_room_change_permissions_member_moderation">"Jäsenien hallinta"</string>
<string name="screen_room_change_permissions_messages_and_content">"Viestit ja sisältö"</string>
<string name="screen_room_change_permissions_moderators">"Valvoja"</string>
<string name="screen_room_change_permissions_remove_people">"Henkilöiden poistaminen ja liittymispyyntöjen hylkääminen"</string>
<string name="screen_room_change_permissions_room_avatar">"Huoneen avatarin vaihtaminen"</string>
<string name="screen_room_change_permissions_room_details">"Muokkaa tietoja"</string>
<string name="screen_room_change_permissions_room_name">"Huoneen nimen vaihtaminen"</string>
<string name="screen_room_change_permissions_room_topic">"Huoneen aiheen vaihtaminen"</string>
<string name="screen_room_change_permissions_send_messages">"Viestien lähettäminen"</string>
<string name="screen_room_change_role_administrators_title">"Muokkaa ylläpitäjiä"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Et voi peruuttaa tätä toimenpidettä. Ylennät käyttäjän samalle oikeustasolle kuin sinä."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisätäänkö ylläpitäjä?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Et voi kumota tätä toimintoa. Olet siirtämässä omistajuuden valituille käyttäjille. Kun poistut, muutos on pysyvä."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Siirretäänkö omistajuus?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Alenna"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Et voi perua tätä muutosta, koska olet alentamassa itseäsi. Jos olet viimeinen oikeutettu henkilö tässä huoneessa, oikeuksia ei voi enää saada takaisin."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Haluatko alentaa itsesi?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Kutsuttu)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Kutsuttu)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Ylläpitäjillä on automaattisesti valvojan oikeudet"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Omistajilla on automaattisesti ylläpitäjän oikeudet."</string>
<string name="screen_room_change_role_moderators_title">"Muokkaa valvojia"</string>
<string name="screen_room_change_role_owners_title">"Valitse Omistajat"</string>
<string name="screen_room_change_role_section_administrators">"Ylläpitäjät"</string>
<string name="screen_room_change_role_section_moderators">"Valvojat"</string>
<string name="screen_room_change_role_section_users">"Jäsenet"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Sinulla on tallentamattomia muutoksia"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Tallennetaanko muutokset?"</string>
<string name="screen_room_member_list_banned_empty">"Porttikiellettyjä käyttäjiä ei ole."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d henkilö"</item>
<item quantity="other">"%1$d henkilöä"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Poista jäsen huoneesta ja anna porttikielto"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Poista vain jäsen huoneesta"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Poista porttikielto"</string>
<string name="screen_room_member_list_manage_member_unban_message">"He voivat liittyä tähän huoneeseen uudelleen, jos heidät kutsutaan."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Poista porttikielto huoneesta"</string>
<string name="screen_room_member_list_mode_banned">"Porttikiellot"</string>
<string name="screen_room_member_list_mode_members">"Jäsenet"</string>
<string name="screen_room_member_list_role_administrator">"Ylläpitäjä"</string>
<string name="screen_room_member_list_role_moderator">"Valvoja"</string>
<string name="screen_room_member_list_role_owner">"Omistaja"</string>
<string name="screen_room_member_list_room_members_header_title">"Huoneen jäsenet"</string>
<string name="screen_room_member_list_unbanning_user">"Poistetaan käyttäjän %1$s porttikieltoa"</string>
<string name="screen_room_roles_and_permissions_admins">"Ylläpitäjät"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Ylläpitäjät ja omistajat"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Vaihda rooliani"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Alenna jäseneksi"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Alenna valvojaksi"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Jäsenten valvonta"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Viestit ja sisältö"</string>
<string name="screen_room_roles_and_permissions_moderators">"Valvojat"</string>
<string name="screen_room_roles_and_permissions_owners">"Omistajat"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Oikeudet"</string>
<string name="screen_room_roles_and_permissions_reset">"Nollaa oikeudet"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Kun nollaat käyttöoikeudet, menetät nykyiset asetukset."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Nollataanko oikeudet?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roolit"</string>
<string name="screen_room_roles_and_permissions_room_details">"Huoneen tiedot"</string>
<string name="screen_room_roles_and_permissions_space_details">"Tilan tiedot"</string>
<string name="screen_room_roles_and_permissions_title">"Roolit ja oikeudet"</string>
</resources>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Administrateurs"</string>
<string name="screen_room_change_permissions_ban_people">"Bannir des participants"</string>
<string name="screen_room_change_permissions_delete_messages">"Supprimer des messages"</string>
<string name="screen_room_change_permissions_everyone">"Membre"</string>
<string name="screen_room_change_permissions_invite_people">"Inviter des personnes"</string>
<string name="screen_room_change_permissions_member_moderation">"Gérer les membres"</string>
<string name="screen_room_change_permissions_messages_and_content">"Messages et contenus"</string>
<string name="screen_room_change_permissions_moderators">"Modérateurs"</string>
<string name="screen_room_change_permissions_remove_people">"Retirer des personnes"</string>
<string name="screen_room_change_permissions_room_avatar">"Changer lavatar du salon"</string>
<string name="screen_room_change_permissions_room_details">"Modifier les détails"</string>
<string name="screen_room_change_permissions_room_name">"Changer le nom du salon"</string>
<string name="screen_room_change_permissions_room_topic">"Changer le sujet du salon"</string>
<string name="screen_room_change_permissions_send_messages">"Envoyer des messages"</string>
<string name="screen_room_change_role_administrators_title">"Modifier les administrateurs"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Vous ne pourrez pas annuler cette action. Vous êtes en train de promouvoir lutilisateur pour quil ait le même niveau que vous."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Ajouter un administrateur ?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Vous ne pourrez pas annuler cette action. Vous transférez la propriété aux utilisateurs sélectionnés. Une fois que vous serez parti, laction sera définitive."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Transférer la propriété?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Rétrograder"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Vous ne pourrez pas annuler ce changement car vous vous rétrogradez, si vous êtes le dernier utilisateur privilégié du salon il sera impossible de retrouver les privilèges."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Vous rétrograder ?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (En attente)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(En attente)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Les administrateurs ont automatiquement les privilèges des modérateurs"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Les propriétaires disposent automatiquement des privilèges des administrateurs."</string>
<string name="screen_room_change_role_moderators_title">"Modifier les modérateurs"</string>
<string name="screen_room_change_role_owners_title">"Choisissez les propriétaires"</string>
<string name="screen_room_change_role_section_administrators">"Administrateurs"</string>
<string name="screen_room_change_role_section_moderators">"Modérateurs"</string>
<string name="screen_room_change_role_section_users">"Membres"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Vous avez des modifications non-enregistrées."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Enregistrer les changements ?"</string>
<string name="screen_room_member_list_banned_empty">"Il ny a pas dutilisateur banni."</string>
<plurals name="screen_room_member_list_banned_header_title">
<item quantity="one">"%1$d Banni(e)"</item>
<item quantity="other">"%1$d Banni(e)s"</item>
</plurals>
<string name="screen_room_member_list_empty_search_subtitle">"Vérifiez la saisie ou effectuez une nouvelle recherche"</string>
<string name="screen_room_member_list_empty_search_title">"Aucun résultat pour «%1$s»"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d Personne"</item>
<item quantity="other">"%1$d Personnes"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Bannir du salon"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Retirer le membre uniquement"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Débannir"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Il pourra rejoindre le salon à nouveau si il est invité."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Débannir du salon"</string>
<string name="screen_room_member_list_mode_banned">"Bannis"</string>
<string name="screen_room_member_list_mode_members">"Membres"</string>
<plurals name="screen_room_member_list_pending_header_title">
<item quantity="one">"%1$d Invité(e)"</item>
<item quantity="other">"%1$d Invité(e)s"</item>
</plurals>
<string name="screen_room_member_list_pending_status">"En attente"</string>
<string name="screen_room_member_list_role_administrator">"Administrateurs"</string>
<string name="screen_room_member_list_role_moderator">"Modérateurs"</string>
<string name="screen_room_member_list_role_owner">"Propriétaire"</string>
<string name="screen_room_member_list_room_members_header_title">"Membres du salon"</string>
<string name="screen_room_member_list_unbanning_user">"Débannissement de %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administrateurs"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administrateurs et propriétaires"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Changer mon rôle"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Devenir simple membre"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Devenir simple modérateur"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Administration des membres"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Messages et contenus"</string>
<string name="screen_room_roles_and_permissions_moderators">"Modérateurs"</string>
<string name="screen_room_roles_and_permissions_owners">"Propriétaires"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Autorisations"</string>
<string name="screen_room_roles_and_permissions_reset">"Réinitialisation des autorisations"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"La réinitialisation des autorisations entraîne la perte des réglages actuels."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Réinitialisation des autorisations ?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rôles"</string>
<string name="screen_room_roles_and_permissions_room_details">"Détails du salon"</string>
<string name="screen_room_roles_and_permissions_space_details">"Détails de lespace"</string>
<string name="screen_room_roles_and_permissions_title">"Rôles &amp; autorisations"</string>
</resources>
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Adminisztrátor"</string>
<string name="screen_room_change_permissions_ban_people">"Emberek kitiltása"</string>
<string name="screen_room_change_permissions_delete_messages">"Üzenetek eltávolítása"</string>
<string name="screen_room_change_permissions_everyone">"Tag"</string>
<string name="screen_room_change_permissions_invite_people">"Emberek meghívása"</string>
<string name="screen_room_change_permissions_member_moderation">"Tagok kezelése"</string>
<string name="screen_room_change_permissions_messages_and_content">"Üzenetek és tartalom"</string>
<string name="screen_room_change_permissions_moderators">"Moderátor"</string>
<string name="screen_room_change_permissions_remove_people">"Emberek eltávolítása"</string>
<string name="screen_room_change_permissions_room_avatar">"Szoba profilképének módosítása"</string>
<string name="screen_room_change_permissions_room_details">"Részletek szerkesztése"</string>
<string name="screen_room_change_permissions_room_name">"Szoba nevének módosítása"</string>
<string name="screen_room_change_permissions_room_topic">"Szoba témájának módosítása"</string>
<string name="screen_room_change_permissions_send_messages">"Üzenetek küldése"</string>
<string name="screen_room_change_role_administrators_title">"Adminisztrátorok szerkesztése"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Ezt a műveletet nem fogja tudja visszavonni. Ugyanarra a szintre lépteti elő a felhasználót, mint amellyel Ön is rendelkezik."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adminisztrátor hozzáadása?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Ezt a műveletet nem lehet visszavonni. A tulajdonjogot a kiválasztott felhasználókra ruházza át. Távozás után a művelet véglegessé válik."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Átruházza a tulajdonjogot?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Lefokozás"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Ezt a változtatást nem fogja tudni visszavonni, mivel lefokozza magát, ha Ön az utolsó jogosultságokkal rendelkező felhasználó a szobában, akkor lehetetlen lesz visszaszerezni a jogosultságokat."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Lefokozza magát?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (függőben)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Függőben)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Az adminisztrátorok automatikusan moderátori jogosultságokkal rendelkeznek"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"A tulajdonosok automatikusan adminisztrátori jogosultsággal rendelkeznek."</string>
<string name="screen_room_change_role_moderators_title">"Moderátorok szerkesztése"</string>
<string name="screen_room_change_role_owners_title">"Tulajdonosok kiválasztása"</string>
<string name="screen_room_change_role_section_administrators">"Adminisztrátorok"</string>
<string name="screen_room_change_role_section_moderators">"Moderátorok"</string>
<string name="screen_room_change_role_section_users">"Tagok"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Mentetlen módosításai vannak."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Menti a módosításokat?"</string>
<string name="screen_room_member_list_banned_empty">"Ebben a szobában nincsenek kitiltott felhasználók."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d személy"</item>
<item quantity="other">"%1$d személy"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Eltávolítás és a tag kitiltása"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Csak a tag eltávolítása"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Tiltás feloldása"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Ehhez a szobához is csatlakozhat, ha meghívják."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Visszaengedés a szobába"</string>
<string name="screen_room_member_list_mode_banned">"Kitiltva"</string>
<string name="screen_room_member_list_mode_members">"Tagok"</string>
<string name="screen_room_member_list_pending_status">"Függőben"</string>
<string name="screen_room_member_list_role_administrator">"Adminisztrátor"</string>
<string name="screen_room_member_list_role_moderator">"Moderátor"</string>
<string name="screen_room_member_list_role_owner">"Tulajdonos"</string>
<string name="screen_room_member_list_room_members_header_title">"Szoba tagjai"</string>
<string name="screen_room_member_list_unbanning_user">"%1$s tiltásának feloldása"</string>
<string name="screen_room_roles_and_permissions_admins">"Adminisztrátorok"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Adminisztrátorok és tulajdonosok"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Saját szerepkör módosítása"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Lefokozás taggá"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Lefokozás moderátorrá"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Tagok moderálása"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Üzenetek és tartalom"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderátorok"</string>
<string name="screen_room_roles_and_permissions_owners">"Tulajdonosok"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Jogosultságok"</string>
<string name="screen_room_roles_and_permissions_reset">"Jogosultságok visszaállítása"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"A jogosultságok visszaállítása után a jelenlegi beállítások elvesznek."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Jogosultságok visszaállítása?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Szerepkörök"</string>
<string name="screen_room_roles_and_permissions_room_details">"Szoba részletei"</string>
<string name="screen_room_roles_and_permissions_space_details">"Tér részletei"</string>
<string name="screen_room_roles_and_permissions_title">"Szerepkörök és jogosultságok"</string>
</resources>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Hanya admin"</string>
<string name="screen_room_change_permissions_ban_people">"Cekal orang-orang"</string>
<string name="screen_room_change_permissions_delete_messages">"Hilangkan pesan"</string>
<string name="screen_room_change_permissions_invite_people">"Undang orang-orang dan terima permintaan untuk bergabung"</string>
<string name="screen_room_change_permissions_messages_and_content">"Pesan dan konten"</string>
<string name="screen_room_change_permissions_moderators">"Admin dan moderator"</string>
<string name="screen_room_change_permissions_remove_people">"Keluarkan orang-orang dan tolak permintaan untuk bergabung"</string>
<string name="screen_room_change_permissions_room_avatar">"Ubah avatar ruangan"</string>
<string name="screen_room_change_permissions_room_details">"Sunting Ruangan"</string>
<string name="screen_room_change_permissions_room_name">"Ubah nama ruangan"</string>
<string name="screen_room_change_permissions_room_topic">"Ubah topik ruangan"</string>
<string name="screen_room_change_permissions_send_messages">"Kirim pesan"</string>
<string name="screen_room_change_role_administrators_title">"Sunting Admin"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Anda tidak akan dapat mengurungkan tindakan ini. Anda mempromosikan pengguna untuk memiliki tingkat daya yang sama seperti Anda."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Tambahkan Admin?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Turunkan"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Anda tidak akan dapat mengurungkan perubahan ini karena Anda sedang menurunkan Anda sendiri, jika Anda merupakan pengguna dengan hak khusus dalam ruangan maka tidak akan memungkinkan untuk mendapatkan hak tersebut lagi."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Turunkan Anda sendiri?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Tertunda)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Tertunda)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Admin secara otomatis memiliki hak moderator"</string>
<string name="screen_room_change_role_moderators_title">"Sunting Moderator"</string>
<string name="screen_room_change_role_section_administrators">"Admin"</string>
<string name="screen_room_change_role_section_moderators">"Moderator"</string>
<string name="screen_room_change_role_section_users">"Anggota"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Anda memiliki perubahan yang belum disimpan."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Simpan perubahan?"</string>
<string name="screen_room_member_list_banned_empty">"Tidak ada pengguna yang dicekal dalam ruangan ini."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="other">"%1$d orang"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Keluarkan dan cekal anggota"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Hanya keluarkan anggota"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Batalkan pencekalan"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Pengguna dapat bergabung ke ruangan ini lagi jika diundang."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Batalkan cekalan dari ruangan"</string>
<string name="screen_room_member_list_mode_banned">"Tercekal"</string>
<string name="screen_room_member_list_mode_members">"Anggota"</string>
<string name="screen_room_member_list_role_administrator">"Hanya admin"</string>
<string name="screen_room_member_list_role_moderator">"Admin dan moderator"</string>
<string name="screen_room_member_list_room_members_header_title">"Anggota ruangan"</string>
<string name="screen_room_member_list_unbanning_user">"Membatalkan cekalan %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Admin"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Ubah peran saya"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Turunkan ke anggota"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Turunkan ke moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderasi anggota"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Pesan dan konten"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderator"</string>
<string name="screen_room_roles_and_permissions_reset">"Atur ulang perizinan"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Setelah Anda mengatur ulang perizinan, Anda akan kehilangan pengaturan Anda saat ini."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Atur ulang perizinan?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Peran"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detail ruangan"</string>
<string name="screen_room_roles_and_permissions_title">"Peran dan perizinan"</string>
</resources>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Amministratore"</string>
<string name="screen_room_change_permissions_ban_people">"Escludi membri"</string>
<string name="screen_room_change_permissions_delete_messages">"Rimuovi messaggi"</string>
<string name="screen_room_change_permissions_everyone">"Membro"</string>
<string name="screen_room_change_permissions_invite_people">"Invita persone"</string>
<string name="screen_room_change_permissions_member_moderation">"Gestisci membri"</string>
<string name="screen_room_change_permissions_messages_and_content">"Messaggi e contenuti"</string>
<string name="screen_room_change_permissions_moderators">"Moderatore"</string>
<string name="screen_room_change_permissions_remove_people">"Rimuovi membri"</string>
<string name="screen_room_change_permissions_room_avatar">"Cambia avatar della stanza"</string>
<string name="screen_room_change_permissions_room_details">"Modifica dettagli"</string>
<string name="screen_room_change_permissions_room_name">"Cambia il nome della stanza"</string>
<string name="screen_room_change_permissions_room_topic">"Cambiare l\'argomento della stanza"</string>
<string name="screen_room_change_permissions_send_messages">"Inviare messaggi"</string>
<string name="screen_room_change_role_administrators_title">"Modifica amministratori"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Non potrai annullare questa azione. Stai promuovendo l\'utente al tuo stesso livello di potere."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Aggiungi amministratore?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Non potrai annullare questa azione. Stai trasferendo la proprietà agli utenti selezionati. Una volta abbandonato, questa azione sarà definitiva."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Trasferire proprietà?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Declassa"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Non potrai annullare questa modifica perché ti stai declassando, se sei l\'ultimo utente privilegiato nella stanza, sarà impossibile riottenere i privilegi."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Declassare te stesso?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (In attesa)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(In attesa)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Gli amministratori hanno automaticamente i privilegi di moderatore"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"I proprietari hanno automaticamente privilegi di amministratore."</string>
<string name="screen_room_change_role_moderators_title">"Modifica moderatori"</string>
<string name="screen_room_change_role_owners_title">"Scegli i proprietari"</string>
<string name="screen_room_change_role_section_administrators">"Amministratori"</string>
<string name="screen_room_change_role_section_moderators">"Moderatori"</string>
<string name="screen_room_change_role_section_users">"Membri"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Hai delle modifiche non salvate."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Salvare le modifiche?"</string>
<string name="screen_room_member_list_banned_empty">"Non ci sono utenti bannati."</string>
<plurals name="screen_room_member_list_banned_header_title">
<item quantity="one">"%1$d Bannato"</item>
<item quantity="other">"%1$d Bannati"</item>
</plurals>
<string name="screen_room_member_list_empty_search_subtitle">"Controlla l\'ortografia o prova una nuova ricerca"</string>
<string name="screen_room_member_list_empty_search_title">"Nessun risultato per “%1$s ”"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d Persona"</item>
<item quantity="other">"%1$d Persone"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Rimuovi ed escludi"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Rimuovi soltanto"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Riammetti"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Potrà entrare nuovamente in questa stanza se invitato."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Riammetti nella stanza"</string>
<string name="screen_room_member_list_mode_banned">"Esclusi"</string>
<string name="screen_room_member_list_mode_members">"Membri"</string>
<plurals name="screen_room_member_list_pending_header_title">
<item quantity="one">"%1$d Invitato"</item>
<item quantity="other">"%1$d Invitati"</item>
</plurals>
<string name="screen_room_member_list_pending_status">"In attesa"</string>
<string name="screen_room_member_list_role_administrator">"Amministratore"</string>
<string name="screen_room_member_list_role_moderator">"Moderatore"</string>
<string name="screen_room_member_list_role_owner">"Proprietario"</string>
<string name="screen_room_member_list_room_members_header_title">"Membri della stanza"</string>
<string name="screen_room_member_list_unbanning_user">"Riammissione di %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Amministratori"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Amministratori e proprietari"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Cambia il mio ruolo"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Declassa a membro"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Declassa a moderatore"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderazione dei membri"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Messaggi e contenuti"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatori"</string>
<string name="screen_room_roles_and_permissions_owners">"Proprietari"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Autorizzazioni"</string>
<string name="screen_room_roles_and_permissions_reset">"Reimpostare le autorizzazioni"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Una volta reimpostate le autorizzazioni, perderai le impostazioni correnti."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Reimpostare autorizzazioni?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Ruoli"</string>
<string name="screen_room_roles_and_permissions_room_details">"Dettagli della stanza"</string>
<string name="screen_room_roles_and_permissions_space_details">"Dettagli dello spazio"</string>
<string name="screen_room_roles_and_permissions_title">"Ruoli e autorizzazioni"</string>
</resources>
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"მხოლოდ ადმინისტრატორები"</string>
<string name="screen_room_change_permissions_ban_people">"მომხმარებლების დაბლოკვა"</string>
<string name="screen_room_change_permissions_delete_messages">"შეტყობინებების წაშლა"</string>
<string name="screen_room_change_permissions_invite_people">"მომხმარებლების მოწვევა და გაწევრიანების მოთხოვნების დადასტურება"</string>
<string name="screen_room_change_permissions_messages_and_content">"შეტყობინებები და შინაარსი"</string>
<string name="screen_room_change_permissions_moderators">"ადმინისტრატორები და მოდერატორები"</string>
<string name="screen_room_change_permissions_remove_people">"მომხმარებლების გაგდება და გაწევრიანების მოთხოვნების უარყოფა"</string>
<string name="screen_room_change_permissions_room_avatar">"ოთახის სურათის შეცვლა"</string>
<string name="screen_room_change_permissions_room_details">"ოთახის რედაქტირება"</string>
<string name="screen_room_change_permissions_room_name">"ოთახის სახელის შეცვლა"</string>
<string name="screen_room_change_permissions_room_topic">"ოთახის თემის შეცვლა"</string>
<string name="screen_room_change_permissions_send_messages">"შეტყობინებების გაგზავნა"</string>
<string name="screen_room_change_role_administrators_title">"ადმინისტრატორების რედაქტირება"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"ამ მოქმედების გაუქმებას ვერ შეძლებთ. თქვენ ნიშნავთ ამ მომხმარებელს იმავე ძალაუფლების დონეზე, რომელიც გაქვთ თქვენ."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"ადმინისტრატორის დამატება?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"დაქვეითება"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"იმის გამო, რომ აქვეითებთ თქვენ თავს, ამ მოქმედებას ვერ გააუქმებთ. პრივილეგიების აღდგენა შეუძლებელია თუ თქვენ ბოლო პრივილეგირებული მომხმარებელი ხართ ამ ოთახში."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"გსურთ საკუთარი თავის დაქვეითება?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (მოლოდინი)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(მოლოდინში)"</string>
<string name="screen_room_change_role_moderators_title">"მოდერატორების რედაქტირება"</string>
<string name="screen_room_change_role_section_administrators">"ადმინისტრატორები"</string>
<string name="screen_room_change_role_section_moderators">"მოდერატორები"</string>
<string name="screen_room_change_role_section_users">"წევრები"</string>
<string name="screen_room_change_role_unsaved_changes_description">"თქვენ გაქვთ შეუნახავი ცვლილებები"</string>
<string name="screen_room_change_role_unsaved_changes_title">"შენახვა?"</string>
<string name="screen_room_member_list_banned_empty">"ამ ოთახში არაა დაბლოკილი მომხმარებლები."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d ადამიანი"</item>
<item quantity="other">"%1$d ადამიანი"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"წევრის წაშლა და დაბლოკვა"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"მხოლოდ წევრის წაშლა"</string>
<string name="screen_room_member_list_manage_member_unban_action">"განბლოკვა"</string>
<string name="screen_room_member_list_manage_member_unban_message">"მოწვევის შემთხვევაში განბლოკილი მომხმარებელი ისევ შეძლებს ოთახს შეუერთდეს."</string>
<string name="screen_room_member_list_mode_banned">"დაბლოკილები"</string>
<string name="screen_room_member_list_mode_members">"წევრები"</string>
<string name="screen_room_member_list_role_administrator">"მხოლოდ ადმინისტრატორები"</string>
<string name="screen_room_member_list_role_moderator">"ადმინისტრატორები და მოდერატორები"</string>
<string name="screen_room_member_list_room_members_header_title">"ოთახის წევრები"</string>
<string name="screen_room_member_list_unbanning_user">"%1$s-ს განბლოკვა"</string>
<string name="screen_room_roles_and_permissions_admins">"ადმინისტრატორები"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"ჩემი როლის შეცვლა"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"დაქვეითება წევრამდე"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"დაქვეითება მოდერატორამდე"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"წევრების მოდერირება"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"შეტყობინებები და შინაარსი"</string>
<string name="screen_room_roles_and_permissions_moderators">"მოდერატორები"</string>
<string name="screen_room_roles_and_permissions_reset">"ნებართვების გადაყენება"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"ნებართვების გადაყენების შემთხვევაში მიმდინარე პარამეტრებს დაკარგავთ."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"გადავაყენოთ ცვლილებები?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"როლები"</string>
<string name="screen_room_roles_and_permissions_room_details">"ოთახის დეტალები"</string>
<string name="screen_room_roles_and_permissions_title">"როლები და ნებართვები"</string>
</resources>
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"관리자 전용"</string>
<string name="screen_room_change_permissions_ban_people">"사용자 차단"</string>
<string name="screen_room_change_permissions_delete_messages">"메시지 삭제"</string>
<string name="screen_room_change_permissions_invite_people">"사람들을 초대하고 가입 요청을 수락합니다"</string>
<string name="screen_room_change_permissions_messages_and_content">"메시지 및 콘텐츠"</string>
<string name="screen_room_change_permissions_moderators">"관리자 및 중재자"</string>
<string name="screen_room_change_permissions_remove_people">"사람들을 제거하고 가입 요청을 거부합니다"</string>
<string name="screen_room_change_permissions_room_avatar">"방 아바타 변경"</string>
<string name="screen_room_change_permissions_room_details">"방 편집"</string>
<string name="screen_room_change_permissions_room_name">"방 이름 변경"</string>
<string name="screen_room_change_permissions_room_topic">"방 화제 변경"</string>
<string name="screen_room_change_permissions_send_messages">"메시지 보내기"</string>
<string name="screen_room_change_role_administrators_title">"관리자 편집"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"이 작업은 실행 취소할 수 없습니다. 해당 사용자에게 당신과 동일한 권한 레벨을 부여하는 것입니다."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"관리자를 추가하시겠습니까?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"이 작업을 취소할 수 없습니다. 선택한 사용자에게 소유권을 이전합니다. 이 작업을 완료하면 변경 사항은 영구적으로 적용됩니다."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"소유권을 이전하시겠습니까?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"강등하다"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"이 변경 사항은 자신을 강등하는 것이므로 실행 취소할 수 없습니다. 해당 방에서 권한을 가진 마지막 사용자인 경우 권한을 다시 얻는 것은 불가능합니다."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"자신을 강등하시겠습니까?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (보류 중)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(보류 중)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"관리자는 자동으로 중재자 권한을 갖습니다."</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"소유자는 자동으로 관리자 권한을 갖습니다."</string>
<string name="screen_room_change_role_moderators_title">"편집 중재자"</string>
<string name="screen_room_change_role_owners_title">"소유자 선택"</string>
<string name="screen_room_change_role_section_administrators">"관리자"</string>
<string name="screen_room_change_role_section_moderators">"중재자"</string>
<string name="screen_room_change_role_section_users">"회원들"</string>
<string name="screen_room_change_role_unsaved_changes_description">"저장되지 않은 변경 사항이 있습니다."</string>
<string name="screen_room_change_role_unsaved_changes_title">"변경 사항을 저장하시겠습니까?"</string>
<string name="screen_room_member_list_banned_empty">"이 방에는 차단된 사용자가 없습니다."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="other">"%1$d 사람"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"방에서 차단"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"회원만 삭제할 수 있습니다."</string>
<string name="screen_room_member_list_manage_member_unban_action">"금지 해제"</string>
<string name="screen_room_member_list_manage_member_unban_message">"초대받으면 이 방에 다시 들어올 수 있습니다."</string>
<string name="screen_room_member_list_manage_member_unban_title">"방에서 차단 해제"</string>
<string name="screen_room_member_list_mode_banned">"차단됨"</string>
<string name="screen_room_member_list_mode_members">"회원들"</string>
<string name="screen_room_member_list_role_administrator">"관리자 전용"</string>
<string name="screen_room_member_list_role_moderator">"관리자 및 중재자"</string>
<string name="screen_room_member_list_role_owner">"소유자"</string>
<string name="screen_room_member_list_room_members_header_title">"방 회원들"</string>
<string name="screen_room_member_list_unbanning_user">"차단 해제 %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"관리자"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"관리자 및 소유자"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"내 역할 변경"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"회원으로 강등"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"중재자로 강등시키다"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"회원 조정"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"메시지 및 콘텐츠"</string>
<string name="screen_room_roles_and_permissions_moderators">"중재자"</string>
<string name="screen_room_roles_and_permissions_owners">"소유자"</string>
<string name="screen_room_roles_and_permissions_reset">"권한 재설정"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"권한을 재설정하면 현재 설정이 모두 삭제됩니다."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"권한을 재설정하시겠습니까?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"역할"</string>
<string name="screen_room_roles_and_permissions_room_details">"방 세부 정보"</string>
<string name="screen_room_roles_and_permissions_title">"역할 및 권한"</string>
</resources>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_room_details">"Redaguoti kambarį"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d asmuo"</item>
<item quantity="few">"%1$d asmenys"</item>
<item quantity="other">"%1$d asmenų"</item>
</plurals>
<string name="screen_room_member_list_room_members_header_title">"Kambario nariai"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Kun for administratorer"</string>
<string name="screen_room_change_permissions_ban_people">"Forby folk"</string>
<string name="screen_room_change_permissions_delete_messages">"Fjern meldinger"</string>
<string name="screen_room_change_permissions_invite_people">"Inviter folk og godta forespørsler om å bli med"</string>
<string name="screen_room_change_permissions_messages_and_content">"Meldinger og innhold"</string>
<string name="screen_room_change_permissions_moderators">"Administratorer og moderatorer"</string>
<string name="screen_room_change_permissions_remove_people">"Fjern folk og avslå forespørsler om å bli med"</string>
<string name="screen_room_change_permissions_room_avatar">"Endre romavatar"</string>
<string name="screen_room_change_permissions_room_details">"Rediger rom"</string>
<string name="screen_room_change_permissions_room_name">"Endre romnavn"</string>
<string name="screen_room_change_permissions_room_topic">"Endre temaet til rommet"</string>
<string name="screen_room_change_permissions_send_messages">"Send meldinger"</string>
<string name="screen_room_change_role_administrators_title">"Rediger administratorer"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Du vil ikke kunne angre denne handlingen. Du forfremmer brukeren til å ha samme rettighetsnivå som deg."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Legg til administrator?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Du kan ikke angre denne handlingen. Du overfører eierskapet til de valgte brukerne. Når du forlater siden, vil dette være permanent."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Overføre eierskapet?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Degradere"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Du vil ikke kunne angre denne endringen ettersom du degraderer deg selv, og hvis du er den siste privilegerte brukeren i rommet, vil det være umulig å få tilbake privilegiene."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Degradere deg selv?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Venter)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Venter)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratorer har automatisk moderatorrettigheter"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Eiere har automatisk administratorrettigheter."</string>
<string name="screen_room_change_role_moderators_title">"Rediger moderatorer"</string>
<string name="screen_room_change_role_owners_title">"Velg eiere"</string>
<string name="screen_room_change_role_section_administrators">"Administratorer"</string>
<string name="screen_room_change_role_section_moderators">"Moderatorer"</string>
<string name="screen_room_change_role_section_users">"Medlemmer"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Du har endringer som ikke er lagret."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Lagre endringer?"</string>
<string name="screen_room_member_list_banned_empty">"Det er ingen utestengte brukere i dette rommet."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d person"</item>
<item quantity="other">"%1$d personer"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Fjern og utesteng medlem"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Bare fjern medlem"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Opphev utestengelse"</string>
<string name="screen_room_member_list_manage_member_unban_message">"De vil kunne bli med i dette rommet igjen hvis de blir invitert."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Fjern utestengelsen fra rommet"</string>
<string name="screen_room_member_list_mode_banned">"Utestengt"</string>
<string name="screen_room_member_list_mode_members">"Medlemmer"</string>
<string name="screen_room_member_list_role_administrator">"Kun for administratorer"</string>
<string name="screen_room_member_list_role_moderator">"Administratorer og moderatorer"</string>
<string name="screen_room_member_list_role_owner">"Eier"</string>
<string name="screen_room_member_list_room_members_header_title">"Medlemmer av rommet"</string>
<string name="screen_room_member_list_unbanning_user">"Oppheve utestengelsen av %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratorer"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administratorer og eiere"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Endre rollen min"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Nedgradere til medlem"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Nedgradere til moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderering av medlemmer"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Meldinger og innhold"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatorer"</string>
<string name="screen_room_roles_and_permissions_owners">"Eiere"</string>
<string name="screen_room_roles_and_permissions_reset">"Tilbakestill tillatelser"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Når du har tilbakestilt tillatelsene, mister du gjeldende innstillinger."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Vil du tilbakestille tillatelsene?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roller"</string>
<string name="screen_room_roles_and_permissions_room_details">"Romdetaljer"</string>
<string name="screen_room_roles_and_permissions_title">"Roller og tillatelser"</string>
</resources>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Alleen beheerders"</string>
<string name="screen_room_change_permissions_ban_people">"Personen verbannen"</string>
<string name="screen_room_change_permissions_delete_messages">"Berichten verwijderen"</string>
<string name="screen_room_change_permissions_invite_people">"Nodig personen uit en accepteer verzoeken om deel te nemen"</string>
<string name="screen_room_change_permissions_messages_and_content">"Berichten en inhoud"</string>
<string name="screen_room_change_permissions_moderators">"Beheerders en moderators"</string>
<string name="screen_room_change_permissions_remove_people">"Verwijder personen en weiger verzoeken om deel te nemen"</string>
<string name="screen_room_change_permissions_room_avatar">"Kamerafbeelding wijzigen"</string>
<string name="screen_room_change_permissions_room_details">"Kamer bewerken"</string>
<string name="screen_room_change_permissions_room_name">"Kamernaam wijzigen"</string>
<string name="screen_room_change_permissions_room_topic">"Kameronderwerp wijzigen"</string>
<string name="screen_room_change_permissions_send_messages">"Berichten verzenden"</string>
<string name="screen_room_change_role_administrators_title">"Beheerders bewerken"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Je kunt deze actie niet ongedaan maken. Je bevordert deze gebruiker tot hetzelfde machtsniveau als jij."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Beheerder toevoegen?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Degraderen"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Je kunt deze wijziging niet ongedaan maken omdat je jezelf degradeert. Als je de laatste gebruiker met bevoegdheden in de kamer bent, is het onmogelijk om deze bevoegdheden terug te krijgen."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Jezelf degraderen?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (In behandeling)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(In afwachting)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Beheerders hebben automatisch moderatorrechten"</string>
<string name="screen_room_change_role_moderators_title">"Moderators bewerken"</string>
<string name="screen_room_change_role_section_administrators">"Beheerders"</string>
<string name="screen_room_change_role_section_moderators">"Moderators"</string>
<string name="screen_room_change_role_section_users">"Leden"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Je hebt niet-opgeslagen wijzigingen"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Wijzigingen opslaan?"</string>
<string name="screen_room_member_list_banned_empty">"Er zijn geen verbannen gebruikers in deze kamer."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d persoon"</item>
<item quantity="other">"%1$d personen"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Lid verwijderen en verbannen"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Alleen lid verwijderen"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Ontbannen"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Ze kunnen opnieuw tot de kamer toetreden als ze worden uitgenodigd."</string>
<string name="screen_room_member_list_mode_banned">"Verbannen"</string>
<string name="screen_room_member_list_mode_members">"Leden"</string>
<string name="screen_room_member_list_role_administrator">"Alleen beheerders"</string>
<string name="screen_room_member_list_role_moderator">"Beheerders en moderators"</string>
<string name="screen_room_member_list_room_members_header_title">"Kamerleden"</string>
<string name="screen_room_member_list_unbanning_user">"%1$s ontbannen"</string>
<string name="screen_room_roles_and_permissions_admins">"Beheerders"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Mijn rol wijzigen"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Degraderen tot lid"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Degraderen tot moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderatie van leden"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Berichten en inhoud"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderators"</string>
<string name="screen_room_roles_and_permissions_reset">"Rechten opnieuw instellen"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Als je de rechten opnieuw instelt, raak je de huidige instellingen kwijt."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Rechten opnieuw instellen?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rollen"</string>
<string name="screen_room_roles_and_permissions_room_details">"Kamergegevens"</string>
<string name="screen_room_roles_and_permissions_title">"Rollen en rechten"</string>
</resources>
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Tylko administratorzy"</string>
<string name="screen_room_change_permissions_ban_people">"Banowanie osób"</string>
<string name="screen_room_change_permissions_delete_messages">"Usuń wiadomości"</string>
<string name="screen_room_change_permissions_invite_people">"Zapraszanie osób i akceptowanie próśb o dołączenie"</string>
<string name="screen_room_change_permissions_messages_and_content">"Wiadomości i zawartość"</string>
<string name="screen_room_change_permissions_moderators">"Administratorzy i moderatorzy"</string>
<string name="screen_room_change_permissions_remove_people">"Usuwanie osób i odrzucanie próśb o dołączenie"</string>
<string name="screen_room_change_permissions_room_avatar">"Zmień awatar pokoju"</string>
<string name="screen_room_change_permissions_room_details">"Edytuj pokój"</string>
<string name="screen_room_change_permissions_room_name">"Zmień nazwę pokoju"</string>
<string name="screen_room_change_permissions_room_topic">"Zmień temat pokoju"</string>
<string name="screen_room_change_permissions_send_messages">"Wysyłanie wiadomości"</string>
<string name="screen_room_change_role_administrators_title">"Edytuj administratorów"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Tej akcji nie będzie można cofnąć. Promujesz użytkownika, który będzie posiadał takie same uprawnienia jak Ty."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Dodać administratora?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Tej akcji nie będzie można cofnąć. Przenosisz prawa własności na wybranych użytkowników. Po opuszczeniu tej strony zmiana będzie nieodwracalna."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Przenieść własność?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Zdegraduj"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Nie będzie można cofnąć tej zmiany, jeśli się zdegradujesz. Jeśli jesteś ostatnim uprzywilejowanym użytkownikiem w pokoju, nie będziesz w stanie odzyskać uprawnień."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Zdegradować siebie?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Oczekujące)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Oczekujący)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratorzy automatycznie mają uprawnienia moderatora"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Właściciele automatycznie mają uprawnienia administratora."</string>
<string name="screen_room_change_role_moderators_title">"Edytuj moderatorów"</string>
<string name="screen_room_change_role_owners_title">"Wybierz właścicieli"</string>
<string name="screen_room_change_role_section_administrators">"Administratorzy"</string>
<string name="screen_room_change_role_section_moderators">"Moderatorzy"</string>
<string name="screen_room_change_role_section_users">"Członków"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Masz niezapisane zmiany."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Zapisać zmiany?"</string>
<string name="screen_room_member_list_banned_empty">"W tym pokoju nie ma zbanowanych użytkowników."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d osoba"</item>
<item quantity="few">"%1$d osoby"</item>
<item quantity="many">"%1$d osób"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Usuń i zbanuj członka"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Tylko usuń członka"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Odbanuj"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Odbanuj z pokoju"</string>
<string name="screen_room_member_list_mode_banned">"Zbanowanych"</string>
<string name="screen_room_member_list_mode_members">"Członków"</string>
<string name="screen_room_member_list_role_administrator">"Tylko administratorzy"</string>
<string name="screen_room_member_list_role_moderator">"Administratorzy i moderatorzy"</string>
<string name="screen_room_member_list_role_owner">"Właściciel"</string>
<string name="screen_room_member_list_room_members_header_title">"Członkowie pokoju"</string>
<string name="screen_room_member_list_unbanning_user">"Odbanowanie %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratorzy"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administratorzy i właściciele"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Zmień moją rolę"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Zdegraduj do członka"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Zdegraduj do moderatora"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderacja członków"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Wiadomości i zawartość"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatorzy"</string>
<string name="screen_room_roles_and_permissions_owners">"Właściciele"</string>
<string name="screen_room_roles_and_permissions_reset">"Resetuj uprawnienia"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Po zresetowaniu uprawnień utracisz bieżące ustawienia."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Zresetować uprawnienia?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Role"</string>
<string name="screen_room_roles_and_permissions_room_details">"Szczegóły pokoju"</string>
<string name="screen_room_roles_and_permissions_title">"Role i uprawnienia"</string>
</resources>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Administradores"</string>
<string name="screen_room_change_permissions_ban_people">"Banir pessoas"</string>
<string name="screen_room_change_permissions_delete_messages">"Remover mensagens"</string>
<string name="screen_room_change_permissions_everyone">"Membro"</string>
<string name="screen_room_change_permissions_invite_people">"Convidar pessoas"</string>
<string name="screen_room_change_permissions_member_moderation">"Gerenciar membros"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_change_permissions_moderators">"Moderador"</string>
<string name="screen_room_change_permissions_remove_people">"Remover pessoas"</string>
<string name="screen_room_change_permissions_room_avatar">"Alterar avatar da sala"</string>
<string name="screen_room_change_permissions_room_details">"Editar detalhes"</string>
<string name="screen_room_change_permissions_room_name">"Alterar nome da sala"</string>
<string name="screen_room_change_permissions_room_topic">"Alterar tópico da sala"</string>
<string name="screen_room_change_permissions_send_messages">"Enviar mensagens"</string>
<string name="screen_room_change_role_administrators_title">"Editar administradores"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Você não poderá desfazer essa ação. Você está promovendo o usuário a ter o mesmo nível de poder que você."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adicionar administrador?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Você não poderá desfazer isto. Você está transferindo a posse desta sala para os usuários selecionados. Ao sair, isto será permanente."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Transferir posse?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Rebaixar"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Você não poderá desfazer essa alteração, pois estará removendo seus próprios privilégios. Se você for o último usuário privilegiado na sala, será impossível recuperar os privilégios."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Rebaixar seu próprio privilégio?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (pendente)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(pendente)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Os administradores têm privilégios de moderador automaticamente"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Proprietários automaticamente têm privilégios de administradores."</string>
<string name="screen_room_change_role_moderators_title">"Editar moderadores"</string>
<string name="screen_room_change_role_owners_title">"Escolher Proprietários"</string>
<string name="screen_room_change_role_section_administrators">"Administradores"</string>
<string name="screen_room_change_role_section_moderators">"Moderadores"</string>
<string name="screen_room_change_role_section_users">"Membros"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Você tem alterações não salvas."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Salvar alterações?"</string>
<string name="screen_room_member_list_banned_empty">"Não há usuários banidos."</string>
<plurals name="screen_room_member_list_banned_header_title">
<item quantity="one">"%1$d banido"</item>
<item quantity="other">"%1$d banidos"</item>
</plurals>
<string name="screen_room_member_list_empty_search_subtitle">"Confira a ortografia ou tente uma nova busca"</string>
<string name="screen_room_member_list_empty_search_title">"Nenhum resultado para “%1$s”"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d pessoa"</item>
<item quantity="other">"%1$d pessoas"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Banir da sala"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Somente remover o membro"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Desbanir"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Esta pessoa poderá entrar nesta sala novamente se for convidada."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Desbanir da sala"</string>
<string name="screen_room_member_list_mode_banned">"Banidos"</string>
<string name="screen_room_member_list_mode_members">"Membros"</string>
<plurals name="screen_room_member_list_pending_header_title">
<item quantity="one">"%1$d convidado"</item>
<item quantity="other">"%1$d convidados"</item>
</plurals>
<string name="screen_room_member_list_pending_status">"Pendente"</string>
<string name="screen_room_member_list_role_administrator">"Administradores"</string>
<string name="screen_room_member_list_role_moderator">"Moderador"</string>
<string name="screen_room_member_list_role_owner">"Proprietário"</string>
<string name="screen_room_member_list_room_members_header_title">"Membros da sala"</string>
<string name="screen_room_member_list_unbanning_user">"Desbanindo %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administradores"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administradores e proprietários"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Alterar meu cargo"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Rebaixar para membro"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Rebaixar para moderador"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderação de membros"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderadores"</string>
<string name="screen_room_roles_and_permissions_owners">"Proprietários"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Permissões"</string>
<string name="screen_room_roles_and_permissions_reset">"Redefinir permissões"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Depois de redefinir as permissões, você perderá as configurações atuais."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Redefinir permissões?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Cargos"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detalhes da sala"</string>
<string name="screen_room_roles_and_permissions_space_details">"Detalhes do espaço"</string>
<string name="screen_room_roles_and_permissions_title">"Cargos e permissões"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Apenas administradores"</string>
<string name="screen_room_change_permissions_ban_people">"Banir pessoas"</string>
<string name="screen_room_change_permissions_delete_messages">"Remover mensagens"</string>
<string name="screen_room_change_permissions_invite_people">"Convidar pessoas e aceitar pedidos de entrada"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_change_permissions_moderators">"Administradores e moderadores"</string>
<string name="screen_room_change_permissions_remove_people">"Remover pessoas e rejeitar pedidos de entrada"</string>
<string name="screen_room_change_permissions_room_avatar">"Alterar o ícone da sala"</string>
<string name="screen_room_change_permissions_room_details">"Editar sala"</string>
<string name="screen_room_change_permissions_room_name">"Altera o nome da sala"</string>
<string name="screen_room_change_permissions_room_topic">"Alterar a descrição da sala"</string>
<string name="screen_room_change_permissions_send_messages">"Enviar mensagens"</string>
<string name="screen_room_change_role_administrators_title">"Editar Administradores"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Não poderás desfazer esta ação. Estás a promover o utilizador para ter o mesmo nível de poder que tu."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adicionar administrador?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Não será possível reverter esta ação. Estás a transferir a posse para os utilizadores selecionados. Será permanente depois de saíres."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Transferir posse?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Despromover"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Não poderás desfazer esta alteração, uma vez que te estás a despromover. Se fores o último utilizador privilegiado na sala, será impossível recuperar os privilégios."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Despromover-te?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (pendente)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(pendente)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Os administradores têm automaticamente privilégios de moderador"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Os donos têm permissões de administrador automaticamente"</string>
<string name="screen_room_change_role_moderators_title">"Editar Moderadores"</string>
<string name="screen_room_change_role_owners_title">"Escolher donos"</string>
<string name="screen_room_change_role_section_administrators">"Administradores"</string>
<string name="screen_room_change_role_section_moderators">"Moderadores"</string>
<string name="screen_room_change_role_section_users">"Participantes"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Tens alterações por guardar."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Guardar alterações?"</string>
<string name="screen_room_member_list_banned_empty">"Não há nenhum utilizador banido desta sala."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d pessoa"</item>
<item quantity="other">"%1$d pessoas"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Remover e banir participante"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Remover apenas"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Anular banimento"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Poderão juntar-se novamente a esta sala se forem convidados."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Desbanir da sala"</string>
<string name="screen_room_member_list_mode_banned">"Banidos"</string>
<string name="screen_room_member_list_mode_members">"Participantes"</string>
<string name="screen_room_member_list_role_administrator">"Apenas administradores"</string>
<string name="screen_room_member_list_role_moderator">"Administradores e moderadores"</string>
<string name="screen_room_member_list_role_owner">"Dono / Dona"</string>
<string name="screen_room_member_list_room_members_header_title">"Participantes"</string>
<string name="screen_room_member_list_unbanning_user">"A anular banimento de %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administradores"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administradores e donos"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Alterar o meu cargo"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Despromover para participante"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Despromover para moderador"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderação de participantes"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderadores"</string>
<string name="screen_room_roles_and_permissions_owners">"Donos"</string>
<string name="screen_room_roles_and_permissions_reset">"Repor permissões"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Ao repores as permissões, perderás as configurações atuais."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Repor as permissões?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Cargos"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detalhes da sala"</string>
<string name="screen_room_roles_and_permissions_title">"Cargos e permissões"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Doar administratori"</string>
<string name="screen_room_change_permissions_ban_people">"Interziceți persoane"</string>
<string name="screen_room_change_permissions_delete_messages">"Ștergeți mesajele"</string>
<string name="screen_room_change_permissions_invite_people">"Invitați persoane și acceptați cereri de alaturare"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mesaje și conținut"</string>
<string name="screen_room_change_permissions_moderators">"Administratori și moderatori"</string>
<string name="screen_room_change_permissions_remove_people">"Îndepărtați persoane și refuzați cereri de alăturare"</string>
<string name="screen_room_change_permissions_room_avatar">"Schimbați avatarul camerei"</string>
<string name="screen_room_change_permissions_room_details">"Editați camera"</string>
<string name="screen_room_change_permissions_room_name">"Schimbă numele camerei"</string>
<string name="screen_room_change_permissions_room_topic">"Schimbați subiectul camerei"</string>
<string name="screen_room_change_permissions_send_messages">"Trimiteți mesaje"</string>
<string name="screen_room_change_role_administrators_title">"Editați administratorii"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Promovați utilizatorul să aibă același nivel de putere ca dumneavoastră. Nu veți putea anula această acțiune."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adăugați administrator?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Nu veți putea anula această acțiune. Transferați dreptul de proprietate către utilizatorii selectați. Odată ce părăsiți această pagină, acțiunea va fi definitivă."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Transferați proprietatea?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Retrogradare"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Nu veți putea anula această modificare, deoarece vă retrogradați. Dacă sunteți ultimul utilizator privilegiat din cameră, va fi imposibil să recâștigați privilegiile."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Vreți să vă retrogradați?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (În așteptare)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(În așteptare)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratorii au automat privilegii de moderator"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Proprietarii au automat privilegii de administrator."</string>
<string name="screen_room_change_role_moderators_title">"Editați moderatorii"</string>
<string name="screen_room_change_role_owners_title">"Alegeți proprietari"</string>
<string name="screen_room_change_role_section_administrators">"Administratori"</string>
<string name="screen_room_change_role_section_moderators">"Moderatori"</string>
<string name="screen_room_change_role_section_users">"Membri"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Aveți modificări nesalvate."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Salvați modificările?"</string>
<string name="screen_room_member_list_banned_empty">"Nu există utilizatori interziși în această cameră."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"o persoană"</item>
<item quantity="other">"%1$d persoane"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Îndepărtați și interziceți membrul"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Doar înlăturare"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Anulare excludere"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Se vor putea alătura din nou acestei săli dacă sunt invitați."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Revocati excluderea din camera"</string>
<string name="screen_room_member_list_mode_banned">"Excluși"</string>
<string name="screen_room_member_list_mode_members">"Membri"</string>
<string name="screen_room_member_list_role_administrator">"Doar administratori"</string>
<string name="screen_room_member_list_role_moderator">"Administratori și moderatori"</string>
<string name="screen_room_member_list_role_owner">"Proprietar"</string>
<string name="screen_room_member_list_room_members_header_title">"Membrii camerei"</string>
<string name="screen_room_member_list_unbanning_user">"Se anulează interzicerea lui %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratori"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administratori și proprietari"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Schimbare rol"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Degradare la membru"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Degradare la moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderarea membrilor"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mesaje și conținut"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatori"</string>
<string name="screen_room_roles_and_permissions_owners">"Proprietari"</string>
<string name="screen_room_roles_and_permissions_reset">"Resetați permisiunile"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"După ce resetați permisiunile, veți pierde setările curente."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Resetați permisiunile?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roluri"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detaliile camerei"</string>
<string name="screen_room_roles_and_permissions_title">"Roluri și permisiuni"</string>
</resources>
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Только администраторы"</string>
<string name="screen_room_change_permissions_ban_people">"Блокировать людей могут"</string>
<string name="screen_room_change_permissions_delete_messages">"Удалить сообщения"</string>
<string name="screen_room_change_permissions_everyone">"Участник"</string>
<string name="screen_room_change_permissions_invite_people">"Пригласить людей"</string>
<string name="screen_room_change_permissions_member_moderation">"Список участников"</string>
<string name="screen_room_change_permissions_messages_and_content">"Сообщения и содержание"</string>
<string name="screen_room_change_permissions_moderators">"Модератор"</string>
<string name="screen_room_change_permissions_remove_people">"Удалять участников"</string>
<string name="screen_room_change_permissions_room_avatar">"Менять изображение комнаты могут"</string>
<string name="screen_room_change_permissions_room_details">"Редактировать комнату"</string>
<string name="screen_room_change_permissions_room_name">"Менять название комнаты могут"</string>
<string name="screen_room_change_permissions_room_topic">"Менять тему комнаты могут"</string>
<string name="screen_room_change_permissions_send_messages">"Отправлять сообщения могут"</string>
<string name="screen_room_change_role_administrators_title">"Редактировать роль администраторов"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не сможете отменить это действие. Вы устанавливаете уровень пользователю соответствующий вашему."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Добавить администратора?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Отменить данное действие будет невозможно. Владение передастся выбранным пользователям. После вашего выхода действие станет необратимым."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Передать владение?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Понизить уровень"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Вы не сможете отменить это изменение, так как понижаете себя статус. Если вы являетесь последним привилегированным пользователем в комнате, восстановить привилегии будет невозможно."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Понизить свой уровень?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Ожидание)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(В ожидании)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Администраторы автоматически получают права модератора"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Владельцы автоматически получают права администратора."</string>
<string name="screen_room_change_role_moderators_title">"Редактировать роль модераторов"</string>
<string name="screen_room_change_role_owners_title">"Назначить владельцев"</string>
<string name="screen_room_change_role_section_administrators">"Администраторы"</string>
<string name="screen_room_change_role_section_moderators">"Модераторы"</string>
<string name="screen_room_change_role_section_users">"Участники"</string>
<string name="screen_room_change_role_unsaved_changes_description">"У вас есть несохраненные изменения."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Сохранить изменения?"</string>
<string name="screen_room_member_list_banned_empty">"В этой комнате нет заблокированных пользователей."</string>
<string name="screen_room_member_list_empty_search_subtitle">"Проверьте правописание или попробуйте новый поиск"</string>
<string name="screen_room_member_list_empty_search_title">"Отсутствует результат по запросу “%1$s”"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d пользователь"</item>
<item quantity="few">"%1$d пользователя"</item>
<item quantity="many">"%1$d пользователей"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Удалить и заблокировать участника"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Только удалить участника"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Разблокировать"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Они снова смогут присоединиться в эту комнату если их пригласят."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Разблокировать в комнате"</string>
<string name="screen_room_member_list_mode_banned">"Заблокированные"</string>
<string name="screen_room_member_list_mode_members">"Участники"</string>
<string name="screen_room_member_list_pending_status">"В ожидании"</string>
<string name="screen_room_member_list_role_administrator">"Только администраторы"</string>
<string name="screen_room_member_list_role_moderator">"Модератор"</string>
<string name="screen_room_member_list_role_owner">"Владелец"</string>
<string name="screen_room_member_list_room_members_header_title">"Участники комнаты"</string>
<string name="screen_room_member_list_unbanning_user">"Разблокировка %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Администраторы"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Администраторы и владельцы"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Изменить мою роль"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Понизить до участника"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Понизить до модератора"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Модерация участников"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Сообщения и содержание"</string>
<string name="screen_room_roles_and_permissions_moderators">"Модераторы"</string>
<string name="screen_room_roles_and_permissions_owners">"Владельцы"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Разрешения"</string>
<string name="screen_room_roles_and_permissions_reset">"Сбросить разрешения"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Как только вы сбросите разрешения, все текущие настройки будут утеряны."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Сбросить разрешения?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Роли"</string>
<string name="screen_room_roles_and_permissions_room_details">"Информация о комнате"</string>
<string name="screen_room_roles_and_permissions_space_details">"Подробности о пространстве"</string>
<string name="screen_room_roles_and_permissions_title">"Роли и разрешения"</string>
</resources>
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Iba správcovia"</string>
<string name="screen_room_change_permissions_ban_people">"Zakázať ľudí"</string>
<string name="screen_room_change_permissions_delete_messages">"Odstrániť správy"</string>
<string name="screen_room_change_permissions_invite_people">"Pozvite ľudí a prijmite žiadosti o pripojenie"</string>
<string name="screen_room_change_permissions_messages_and_content">"Správy a obsah"</string>
<string name="screen_room_change_permissions_moderators">"Správcovia a moderátori"</string>
<string name="screen_room_change_permissions_remove_people">"Odstrániť ľudí a odmietnuť žiadosti o pripojenie"</string>
<string name="screen_room_change_permissions_room_avatar">"Zmeniť obrázok miestnosti"</string>
<string name="screen_room_change_permissions_room_details">"Upraviť miestnosť"</string>
<string name="screen_room_change_permissions_room_name">"Zmeniť názov miestnosti"</string>
<string name="screen_room_change_permissions_room_topic">"Zmeniť tému miestnosti"</string>
<string name="screen_room_change_permissions_send_messages">"Odoslať správy"</string>
<string name="screen_room_change_role_administrators_title">"Upraviť správcov"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Túto akciu nebudete môcť vrátiť späť. Zvyšujete úroveň používateľa na rovnakú úroveň výkonu ako máte vy."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Pridať správcu?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Túto akciu nebude možné vrátiť späť. Prenášate vlastníctvo na vybraných používateľov. Po opustení bude táto akcia trvalá."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Previesť vlastníctvo?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Znížiť"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Túto zmenu nebudete môcť vrátiť späť, pretože znižujete svoju úroveň. Ak ste posledným privilegovaným používateľom v miestnosti, nebude možné získať znova oprávnenia."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Znížiť svoju úroveň?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Čaká sa)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Čaká sa)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Správcovia majú automaticky oprávnenia moderátora"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Vlastníci majú automaticky správcovské oprávnenia."</string>
<string name="screen_room_change_role_moderators_title">"Upraviť moderátorov"</string>
<string name="screen_room_change_role_owners_title">"Vybrať vlastníkov"</string>
<string name="screen_room_change_role_section_administrators">"Správcovia"</string>
<string name="screen_room_change_role_section_moderators">"Moderátori"</string>
<string name="screen_room_change_role_section_users">"Členovia"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Máte neuložené zmeny."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Uložiť zmeny?"</string>
<string name="screen_room_member_list_banned_empty">"Neexistujú žiadni zablokovaní používatelia."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d osoba"</item>
<item quantity="few">"%1$d osoby"</item>
<item quantity="other">"%1$d osôb"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Odstrániť a zakázať člena"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Iba odstrániť člena"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Zrušiť zákaz"</string>
<string name="screen_room_member_list_manage_member_unban_message">"V prípade pozvania sa budú môcť znova pripojiť k tejto miestnosti."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Zrušiť zákaz prístupu do miestnosti"</string>
<string name="screen_room_member_list_mode_banned">"Zakázaní"</string>
<string name="screen_room_member_list_mode_members">"Členovia"</string>
<string name="screen_room_member_list_role_administrator">"Iba správcovia"</string>
<string name="screen_room_member_list_role_moderator">"Správcovia a moderátori"</string>
<string name="screen_room_member_list_role_owner">"Vlastník"</string>
<string name="screen_room_member_list_room_members_header_title">"Členovia miestnosti"</string>
<string name="screen_room_member_list_unbanning_user">"Zrušenie zákazu %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Správcovia"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Správcovia a vlastníci"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Zmeniť moje oprávnenia"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Znížiť úroveň na člena"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Znížiť úroveň na moderátora"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Moderovanie členov"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Správy a obsah"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderátori"</string>
<string name="screen_room_roles_and_permissions_owners">"Vlastníci"</string>
<string name="screen_room_roles_and_permissions_reset">"Obnoviť povolenia"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Po obnovení oprávnení prídete o aktuálne nastavenia."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Obnoviť oprávnenia?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roly"</string>
<string name="screen_room_roles_and_permissions_room_details">"Podrobnosti o miestnosti"</string>
<string name="screen_room_roles_and_permissions_space_details">"Podrobnosti o priestore"</string>
<string name="screen_room_roles_and_permissions_title">"Roly a povolenia"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Endast administratörer"</string>
<string name="screen_room_change_permissions_ban_people">"Banna personer"</string>
<string name="screen_room_change_permissions_delete_messages">"Ta bort meddelanden"</string>
<string name="screen_room_change_permissions_invite_people">"Bjuda in personer och acceptera förfrågningar om att gå med"</string>
<string name="screen_room_change_permissions_messages_and_content">"Meddelanden och innehåll"</string>
<string name="screen_room_change_permissions_moderators">"Administratörer och moderatorer"</string>
<string name="screen_room_change_permissions_remove_people">"Ta bort personer och avslå förfrågningar om att gå med"</string>
<string name="screen_room_change_permissions_room_avatar">"Byt rumsavatar"</string>
<string name="screen_room_change_permissions_room_details">"Redigera rummet"</string>
<string name="screen_room_change_permissions_room_name">"Byt rumsnamn"</string>
<string name="screen_room_change_permissions_room_topic">"Byt rumsämne"</string>
<string name="screen_room_change_permissions_send_messages">"Skicka meddelanden"</string>
<string name="screen_room_change_role_administrators_title">"Redigera administratörer"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Du kommer inte att kunna ångra den här åtgärden. Du befordrar användaren till att ha samma behörighetsnivå som du."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lägg till Admin?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Du kommer inte att kunna ångra den här åtgärden. Du överför ägarskapet till de valda användarna. När du lämnar kommer detta att vara permanent."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Överför ägarskap?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Degradera"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Du kommer inte att kunna ångra denna ändring eftersom du degraderar dig själv, om du är den sista privilegierade användaren i rummet kommer det att vara omöjligt att återfå privilegier."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Degradera dig själv?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Väntar)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Väntar)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratörer har automatiskt moderatorbehörighet"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Ägare har automatiskt administratörsbehörighet."</string>
<string name="screen_room_change_role_moderators_title">"Redigera moderatorer"</string>
<string name="screen_room_change_role_owners_title">"Välj ägare"</string>
<string name="screen_room_change_role_section_administrators">"Administratörer"</string>
<string name="screen_room_change_role_section_moderators">"Moderatorer"</string>
<string name="screen_room_change_role_section_users">"Medlemmar"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Du har osparade ändringar."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Spara ändringar?"</string>
<string name="screen_room_member_list_banned_empty">"Det finns inga bannade användare i det här rummet."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d person"</item>
<item quantity="other">"%1$d personer"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Ta bort och banna medlem"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Ta bara bort medlem"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Avbanna"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Denne kommer kunna gå med i rummet igen om denne bjuds in"</string>
<string name="screen_room_member_list_manage_member_unban_title">"Avbanna från rummet"</string>
<string name="screen_room_member_list_mode_banned">"Bannade"</string>
<string name="screen_room_member_list_mode_members">"Medlemmar"</string>
<string name="screen_room_member_list_role_administrator">"Endast administratörer"</string>
<string name="screen_room_member_list_role_moderator">"Administratörer och moderatorer"</string>
<string name="screen_room_member_list_role_owner">"Ägare"</string>
<string name="screen_room_member_list_room_members_header_title">"Rumsmedlemmar"</string>
<string name="screen_room_member_list_unbanning_user">"Avbannar %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Administratörer"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Administratörer och ägare"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Ändra min roll"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Degradera till medlem"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Degradera till moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Medlemsmoderering"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Meddelanden och innehåll"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatorer"</string>
<string name="screen_room_roles_and_permissions_owners">"Ägare"</string>
<string name="screen_room_roles_and_permissions_reset">"Återställ behörigheter"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"När du har återställt behörigheterna kommer du att förlora de aktuella inställningarna."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Återställ behörigheter?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roller"</string>
<string name="screen_room_roles_and_permissions_room_details">"Rumsdetaljer"</string>
<string name="screen_room_roles_and_permissions_title">"Roller och behörigheter"</string>
</resources>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Yalnızca yöneticiler"</string>
<string name="screen_room_change_permissions_ban_people">"İnsanları yasakla"</string>
<string name="screen_room_change_permissions_delete_messages">"Mesajları kaldır"</string>
<string name="screen_room_change_permissions_invite_people">"Kişileri davet etme ve katılma isteklerini kabul etme"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mesajlar ve içerik"</string>
<string name="screen_room_change_permissions_moderators">"Yöneticiler ve moderatörler"</string>
<string name="screen_room_change_permissions_remove_people">"Kişileri kaldırma ve katılma isteklerini reddetme"</string>
<string name="screen_room_change_permissions_room_avatar">"Oda resmini değiştir"</string>
<string name="screen_room_change_permissions_room_details">"Odayı Düzenle"</string>
<string name="screen_room_change_permissions_room_name">"Oda adını değiştir"</string>
<string name="screen_room_change_permissions_room_topic">"Oda konusunu değiştir"</string>
<string name="screen_room_change_permissions_send_messages">"Mesaj gönder"</string>
<string name="screen_room_change_role_administrators_title">"Yöneticileri Düzenle"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Bu eylemi geri alamazsınız. Kullanıcıyı sizinle aynı güç seviyesine sahip olacak şekilde terfi ettiriyorsunuz."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Yönetici Ekle?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Rütbe Düşür"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Rütbenizi düşürdüğünüz için bu değişikliği geri alamazsınız, eğer odadaki son ayrıcalıklı kullanıcı sizseniz ayrıcalıkları yeniden kazanmanız mümkün olmayacaktır."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Rütbeni düşür?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Beklemede)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Beklemede)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Yöneticiler otomatik olarak moderatör ayrıcalıklarına sahiptir"</string>
<string name="screen_room_change_role_moderators_title">"Moderatörleri Düzenle"</string>
<string name="screen_room_change_role_section_administrators">"Yöneticiler"</string>
<string name="screen_room_change_role_section_moderators">"Moderatörler"</string>
<string name="screen_room_change_role_section_users">"Üyeler"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Kaydedilmemiş değişiklikleriniz var."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Değişiklikleri Kaydet?"</string>
<string name="screen_room_member_list_banned_empty">"Bu odada yasaklı kullanıcı yok."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d kişi"</item>
<item quantity="other">"%1$d kişi"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Üyeyi çıkar ve yasakla"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Yalnızca üyeyi kaldır"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Yasağı Kaldır"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Davet edildikleri takdirde bu odaya tekrar katılabileceklerdir."</string>
<string name="screen_room_member_list_mode_banned">"Yasaklandı"</string>
<string name="screen_room_member_list_mode_members">"Üyeler"</string>
<string name="screen_room_member_list_role_administrator">"Yalnızca yöneticiler"</string>
<string name="screen_room_member_list_role_moderator">"Yöneticiler ve moderatörler"</string>
<string name="screen_room_member_list_room_members_header_title">"Oda üyeleri"</string>
<string name="screen_room_member_list_unbanning_user">"Yasak kaldırılıyor %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Yöneticiler"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Rolümü değiştir"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Üyeliğe düşür"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Moderatörlüğe düşür"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Üye moderasyonu"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Mesajlar ve içerik"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatörler"</string>
<string name="screen_room_roles_and_permissions_reset">"İzinleri sıfırla"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"İzinleri sıfırladığınızda, mevcut ayarları kaybedersiniz."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"İzinleri sıfırla?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roller"</string>
<string name="screen_room_roles_and_permissions_room_details">"Oda bilgileri"</string>
<string name="screen_room_roles_and_permissions_title">"Roller ve izinler"</string>
</resources>
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Тільки для адміністраторів"</string>
<string name="screen_room_change_permissions_ban_people">"Заблоковувати людей"</string>
<string name="screen_room_change_permissions_delete_messages">"Вилучати повідомлення"</string>
<string name="screen_room_change_permissions_invite_people">"Запрошувати людей і приймати запити на приєднання"</string>
<string name="screen_room_change_permissions_messages_and_content">"Повідомлення та зміст"</string>
<string name="screen_room_change_permissions_moderators">"Адміністратори та модератори"</string>
<string name="screen_room_change_permissions_remove_people">"Вилучати людей і відхиляти запити на приєднання"</string>
<string name="screen_room_change_permissions_room_avatar">"Змінювати аватар кімнати"</string>
<string name="screen_room_change_permissions_room_details">"Редагувати кімнату"</string>
<string name="screen_room_change_permissions_room_name">"Змінювати назву кімнати"</string>
<string name="screen_room_change_permissions_room_topic">"Змінювати тему кімнати"</string>
<string name="screen_room_change_permissions_send_messages">"Надсилати повідомлення"</string>
<string name="screen_room_change_role_administrators_title">"Керувати адмінами"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Ви не зможете скасувати цю дію. Ви просуваєте користувача, щоб він мав такий же рівень прав, як і ви."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Додати адміністратора?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Ви не зможете скасувати цю дію. Ви передаєте право власності вибраним користувачам. Після вашого виходу це буде остаточно."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Передати право власності?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Понизити"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Ви не зможете скасувати цю зміну, оскільки ви понижуєте себе, якщо ви останній привілейований користувач у кімнаті, відновити повноваження буде неможливо."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Понизити себе?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Очікується)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Очікується)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Адміністратори автоматично мають повноваження модератора"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Власники автоматично отримують права адміністратора."</string>
<string name="screen_room_change_role_moderators_title">"Керувати модераторами"</string>
<string name="screen_room_change_role_owners_title">"Оберіть власників"</string>
<string name="screen_room_change_role_section_administrators">"Адміністратори"</string>
<string name="screen_room_change_role_section_moderators">"Модератори"</string>
<string name="screen_room_change_role_section_users">"Учасники"</string>
<string name="screen_room_change_role_unsaved_changes_description">"У вас є не збережені зміни."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Зберегти зміни?"</string>
<string name="screen_room_member_list_banned_empty">"У цій кімнаті немає заблокованих користувачів."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d особа"</item>
<item quantity="few">"%1$d особи"</item>
<item quantity="many">"%1$d осіб"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Вилучити й заблокувати учасника"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Лише вилучити учасника"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Розблокувати"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Вони зможуть знову приєднатися до цієї кімнати, якщо їх запросять."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Розблокувати в кімнаті"</string>
<string name="screen_room_member_list_mode_banned">"Заблоковані"</string>
<string name="screen_room_member_list_mode_members">"Учасники"</string>
<string name="screen_room_member_list_role_administrator">"Тільки для адміністраторів"</string>
<string name="screen_room_member_list_role_moderator">"Адміністратори та модератори"</string>
<string name="screen_room_member_list_role_owner">"Власник"</string>
<string name="screen_room_member_list_room_members_header_title">"Учасники кімнати"</string>
<string name="screen_room_member_list_unbanning_user">"Розблокування %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Адміністратори"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Адміністратори та власники"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Змінити мою роль"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Понизити до учасника"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Понизити до модератора"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Модерація учасників"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Повідомлення та зміст"</string>
<string name="screen_room_roles_and_permissions_moderators">"Модератори"</string>
<string name="screen_room_roles_and_permissions_owners">"Власники"</string>
<string name="screen_room_roles_and_permissions_reset">"Скинути дозволи"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Після скидання дозволів ви втратите поточні налаштування."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Скинути дозволи?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Ролі"</string>
<string name="screen_room_roles_and_permissions_room_details">"Деталі кімнати"</string>
<string name="screen_room_roles_and_permissions_title">"Ролі та дозволи"</string>
</resources>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"صرف منتظمین"</string>
<string name="screen_room_change_permissions_ban_people">"لوگوں کو محظور کریں"</string>
<string name="screen_room_change_permissions_delete_messages">"پیغامات ہٹائیں"</string>
<string name="screen_room_change_permissions_invite_people">"لوگوں کو مدعو کریں اور شمولیت کی درخواستیں قبول کریں"</string>
<string name="screen_room_change_permissions_messages_and_content">"پیغامات اور مواد"</string>
<string name="screen_room_change_permissions_moderators">"منتظمین اور ناظمین"</string>
<string name="screen_room_change_permissions_remove_people">"لوگوں کو ہٹا دیں اور شمولیت کی درخواستیں مسترد کریں"</string>
<string name="screen_room_change_permissions_room_avatar">"کمرے کا اوتار بدلیں"</string>
<string name="screen_room_change_permissions_room_details">"کمرے میں ترمیم کریں"</string>
<string name="screen_room_change_permissions_room_name">"کمرے کا نام بدلیں"</string>
<string name="screen_room_change_permissions_room_topic">"کمرے کا موضوع بدلیں"</string>
<string name="screen_room_change_permissions_send_messages">"پیغامات بھیجیں"</string>
<string name="screen_room_change_role_administrators_title">"منتظمین میں ترمیم کریں"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"آپ اس کارروائی کو کالعدم نہیں کرسکیں گے۔ آپ صارف کو اپنی جیسی طاقت کی سطح رکھنے کے لئے فروغ دے رہے ہیں۔"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"منتظم شمال کریں؟"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"تنزل کریں"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"آپ اس تبدیلی کو کالعدم نہیں کرسکیں گے کیونکہ آپ اپنے آپ کو تنزل کر رہے ہیں، اگر آپ کمرے میں آخری مراعات یافتہ صارف ہیں تو مراعات پھر حاصل کرنا ناممکن ہو جائے گا۔"</string>
<string name="screen_room_change_role_confirm_demote_self_title">"اپنے آپ کو تنزل کریں؟"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (زیر التواء)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(زیر التواء)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"منتظمین کے پاس خودکاراً ناظمین مراعات ہوتی ہیں"</string>
<string name="screen_room_change_role_moderators_title">"ناظمین میں ترمیم کریں"</string>
<string name="screen_room_change_role_section_administrators">"منتظمین"</string>
<string name="screen_room_change_role_section_moderators">"ناظمین"</string>
<string name="screen_room_change_role_section_users">"اراکین"</string>
<string name="screen_room_change_role_unsaved_changes_description">"آپکے پاس غیر محفوظ تبدیلیاں ہیں"</string>
<string name="screen_room_change_role_unsaved_changes_title">"تبدیلیاں محفوظ کریں؟"</string>
<string name="screen_room_member_list_banned_empty">"اس کمرے میں کوئی محظور صارفین نہیں ہیں۔"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d شخص"</item>
<item quantity="other">"%1$d اشخاص"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"کمرے سے محظور کریں"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"رکن کو صرف ہٹائیں"</string>
<string name="screen_room_member_list_manage_member_unban_action">"غیر محظور کریں"</string>
<string name="screen_room_member_list_manage_member_unban_message">"اگر وہ مدعو کیا جائیں تو وہ دوبارہ اس کمرے میں شامل ہوسکیں گے۔"</string>
<string name="screen_room_member_list_mode_banned">"محظور"</string>
<string name="screen_room_member_list_mode_members">"اراکین"</string>
<string name="screen_room_member_list_role_administrator">"صرف منتظمین"</string>
<string name="screen_room_member_list_role_moderator">"منتظمین اور ناظمین"</string>
<string name="screen_room_member_list_room_members_header_title">"کمرے کے ارکان"</string>
<string name="screen_room_member_list_unbanning_user">"%1$s کو غیر محظور کر رہا ہے"</string>
<string name="screen_room_roles_and_permissions_admins">"منتظمین"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"میرا کردار تبدیل کریں"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"تا رکن تنزلی کریں"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"تا ناظم تنزلی کریں"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"ارکان کا اعتدال"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"پیغامات اور مواد"</string>
<string name="screen_room_roles_and_permissions_moderators">"ناظمین"</string>
<string name="screen_room_roles_and_permissions_reset">"اجازتیں بحال کریں"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"ایک بار جب آپ اجازتیں بحال کردیں گے، آپ موجودہ ترتیبات کھو دیں گے۔"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"اجازتیں بحال کریں؟"</string>
<string name="screen_room_roles_and_permissions_roles_header">"کردارہا"</string>
<string name="screen_room_roles_and_permissions_room_details">"کمرے کی تفصیلات"</string>
<string name="screen_room_roles_and_permissions_title">"کردارہا اور اجازتیں"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Faqat adminlar"</string>
<string name="screen_room_change_permissions_ban_people">"Odamlarni taqiqlash"</string>
<string name="screen_room_change_permissions_delete_messages">"Xabarlarni olib tashlash"</string>
<string name="screen_room_change_permissions_invite_people">"Odamlarni taklif qiling va qoshilish sorovlarini qabul qiling"</string>
<string name="screen_room_change_permissions_messages_and_content">"Xabarlar va kontent"</string>
<string name="screen_room_change_permissions_moderators">"Adminlar va moderatorlar"</string>
<string name="screen_room_change_permissions_remove_people">"Odamlarni olib tashlash va qoʻshilish soʻrovlarini rad etish"</string>
<string name="screen_room_change_permissions_room_avatar">"Xona avatarini oʻzgartirish"</string>
<string name="screen_room_change_permissions_room_details">"Xonani tahrirlash"</string>
<string name="screen_room_change_permissions_room_name">"Xona nomini oʻzgartirish"</string>
<string name="screen_room_change_permissions_room_topic">"Xona mavzusini almashtirish"</string>
<string name="screen_room_change_permissions_send_messages">"Xabarlar yuborish"</string>
<string name="screen_room_change_role_administrators_title">"Administratorlarni tahrirlash"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Bu amalni bekor qila olmaysiz. Siz foydalanuvchini ozingiz bilan bir xil quvvat darajasiga ega bolishga undayapsiz."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Admin qoshilsinmi?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Bu amalni bekor qila olmaysiz. Siz egalikni tanlangan foydalanuvchilarga otkazmoqdasiz. Tark etsangiz, bu doimiy boladi."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Egalik huquqini otkazasizmi?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Pastga tushirish"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"Siz oʻzingizni imtiyozlardan mahrum qilayotganingiz sababli, bu ozgarishni bekor qila olmaysiz. Agar xonadagi songgi imtiyozli foydalanuvchi bolsangiz, imtiyozlarni qayta tiklash imkonsiz boladi."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Oz darajangizni pasaytirmoqchimisiz?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Jarayonda)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Kutilmoqda)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratorlar avtomatik ravishda moderator imtiyozlariga ega"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Egalar avtomatik ravishda administrator huquqlariga ega."</string>
<string name="screen_room_change_role_moderators_title">"Moderatorlarni tahrirlash"</string>
<string name="screen_room_change_role_owners_title">"Egalarni tanlang"</string>
<string name="screen_room_change_role_section_administrators">"Adminlar"</string>
<string name="screen_room_change_role_section_moderators">"Moderatorlar"</string>
<string name="screen_room_change_role_section_users">"Azolar"</string>
<string name="screen_room_change_role_unsaved_changes_description">"Sizda saqlanmagan oʻzgarishlar bor"</string>
<string name="screen_room_change_role_unsaved_changes_title">"Ozgartirishlarni saqlaysizmi?"</string>
<string name="screen_room_member_list_banned_empty">"Bu xonada taqiqlangan foydalanuvchilar yoʻq."</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$dodam"</item>
<item quantity="other">"%1$dodamlar"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Xonadan chetlashtirish"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Faqat aʻzoni olib tashlash"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Taqiqni bekor qilish"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Agar taklif qilinsa, ular bu xonaga qayta qoshilishlari mumkin."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Xonadan taqiqni olib tashlash"</string>
<string name="screen_room_member_list_mode_banned">"Taqiqlangan"</string>
<string name="screen_room_member_list_mode_members">"Azolar"</string>
<string name="screen_room_member_list_role_administrator">"Faqat adminlar"</string>
<string name="screen_room_member_list_role_moderator">"Adminlar va moderatorlar"</string>
<string name="screen_room_member_list_role_owner">"Egasi"</string>
<string name="screen_room_member_list_room_members_header_title">"Xona a\'zolari"</string>
<string name="screen_room_member_list_unbanning_user">"Taqiqni bekor qilish %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Adminlar"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Adminlar va egalari"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Rolimni ozgartirish"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Aʼzolikka tushirish"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Moderatorga pasaytirish"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Aʻzo moderatsiyasi"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Xabarlar va kontent"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderatorlar"</string>
<string name="screen_room_roles_and_permissions_owners">"Egalari"</string>
<string name="screen_room_roles_and_permissions_reset">"Ruxsatlarni tiklash"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Ruxsatlarni asliga qaytargach, joriy sozlamalarni yoʻqotasiz."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Ruxsatlar asliga qaytarilsinmi?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Rollar"</string>
<string name="screen_room_roles_and_permissions_room_details">"Xona tafsilotlari"</string>
<string name="screen_room_roles_and_permissions_title">"Rollar va ruxsatlar"</string>
</resources>
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"管理員"</string>
<string name="screen_room_change_permissions_ban_people">"管理黑名單"</string>
<string name="screen_room_change_permissions_delete_messages">"移除訊息"</string>
<string name="screen_room_change_permissions_everyone">"成員"</string>
<string name="screen_room_change_permissions_invite_people">"邀請夥伴"</string>
<string name="screen_room_change_permissions_member_moderation">"管理成員"</string>
<string name="screen_room_change_permissions_messages_and_content">"訊息與內容"</string>
<string name="screen_room_change_permissions_moderators">"版主"</string>
<string name="screen_room_change_permissions_remove_people">"移除夥伴"</string>
<string name="screen_room_change_permissions_room_avatar">"變更聊天室大頭照"</string>
<string name="screen_room_change_permissions_room_details">"編輯詳細資訊"</string>
<string name="screen_room_change_permissions_room_name">"變更聊天室名稱"</string>
<string name="screen_room_change_permissions_room_topic">"變更聊天室主題"</string>
<string name="screen_room_change_permissions_send_messages">"傳送訊息"</string>
<string name="screen_room_change_role_administrators_title">"編輯管理員"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"您將無法復原此動作。您正將使用者提昇至與您相同的權力等級。"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"要新增管理員嗎?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"您將無法撤銷此動作。您正在將所有權轉移給選定的使用者。一旦您離開,此動作將永久有效。"</string>
<string name="screen_room_change_role_confirm_change_owners_title">"轉移所有權?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"降級"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"當您自行降級時,您將無法復原此變更,若您是聊天室中的最後一位特權使用者,則無法重新獲得權限。"</string>
<string name="screen_room_change_role_confirm_demote_self_title">"將自己降級?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s(擱置中)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(擱置中)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"管理員自動擁有版主權限"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"擁有者自動擁有管理員權限。"</string>
<string name="screen_room_change_role_moderators_title">"編輯版主"</string>
<string name="screen_room_change_role_owners_title">"選擇擁有者"</string>
<string name="screen_room_change_role_section_administrators">"管理員"</string>
<string name="screen_room_change_role_section_moderators">"版主"</string>
<string name="screen_room_change_role_section_users">"成員"</string>
<string name="screen_room_change_role_unsaved_changes_description">"您有尚未儲存的變更"</string>
<string name="screen_room_change_role_unsaved_changes_title">"是否儲存變更?"</string>
<string name="screen_room_member_list_banned_empty">"沒有被封鎖的使用者。"</string>
<string name="screen_room_member_list_empty_search_subtitle">"檢查拼字或嘗試新搜尋"</string>
<string name="screen_room_member_list_empty_search_title">"找不到「%1$s」"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="other">"%1$d 個人"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"踢出並加入黑名單"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"僅移除成員"</string>
<string name="screen_room_member_list_manage_member_unban_action">"解除黑名單"</string>
<string name="screen_room_member_list_manage_member_unban_message">"如果收到邀請,他們能再次加入聊天室。"</string>
<string name="screen_room_member_list_manage_member_unban_title">"從聊天室解除封鎖"</string>
<string name="screen_room_member_list_mode_banned">"黑名單"</string>
<string name="screen_room_member_list_mode_members">"成員"</string>
<string name="screen_room_member_list_pending_status">"擱置中"</string>
<string name="screen_room_member_list_role_administrator">"管理員"</string>
<string name="screen_room_member_list_role_moderator">"版主"</string>
<string name="screen_room_member_list_role_owner">"擁有者"</string>
<string name="screen_room_member_list_room_members_header_title">"聊天室成員"</string>
<string name="screen_room_member_list_unbanning_user">"正在解除黑名單 %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"管理員"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"管理員與擁有者"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"變更我的身份"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"降級為普通成員"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"降級為版主"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"成員管理"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"訊息與內容"</string>
<string name="screen_room_roles_and_permissions_moderators">"版主"</string>
<string name="screen_room_roles_and_permissions_owners">"擁有者"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"權限"</string>
<string name="screen_room_roles_and_permissions_reset">"重設權限"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"重設之後,您會遺失當前的設定。"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"確定要重設權限嗎?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"身份"</string>
<string name="screen_room_roles_and_permissions_room_details">"聊天室資訊"</string>
<string name="screen_room_roles_and_permissions_space_details">"空間詳細資訊"</string>
<string name="screen_room_roles_and_permissions_title">"角色與權限"</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"仅限管理员"</string>
<string name="screen_room_change_permissions_ban_people">"封禁成员"</string>
<string name="screen_room_change_permissions_delete_messages">"移除消息"</string>
<string name="screen_room_change_permissions_invite_people">"邀请他人及接受加入请求"</string>
<string name="screen_room_change_permissions_messages_and_content">"消息和内容"</string>
<string name="screen_room_change_permissions_moderators">"管理员和协管员"</string>
<string name="screen_room_change_permissions_remove_people">"移除成员及拒绝加入请求"</string>
<string name="screen_room_change_permissions_room_avatar">"更改聊天室头像"</string>
<string name="screen_room_change_permissions_room_details">"编辑聊天室"</string>
<string name="screen_room_change_permissions_room_name">"更改聊天室名称"</string>
<string name="screen_room_change_permissions_room_topic">"更改聊天室主题"</string>
<string name="screen_room_change_permissions_send_messages">"发送消息"</string>
<string name="screen_room_change_role_administrators_title">"编辑管理员"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"您将无法撤消此操作。您正在提升用户的权限,使其拥有与您平权。"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"添加管理员?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"此操作无法撤销。您正在将所有权转移给所选用户。一旦离开此界面,该操作将永久生效。"</string>
<string name="screen_room_change_role_confirm_change_owners_title">"转让所有权"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"降级"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"您正在降级,此更改将无法撤消。如果您是聊天室中的最后一个特权用户,则无法重新获得权限。"</string>
<string name="screen_room_change_role_confirm_demote_self_title">"降级自己?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s(待处理)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(已邀请)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"管理员自动拥有协管员权限"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"所有者自动拥有管理员权限。"</string>
<string name="screen_room_change_role_moderators_title">"编辑协管员"</string>
<string name="screen_room_change_role_owners_title">"选择所有者"</string>
<string name="screen_room_change_role_section_administrators">"管理员"</string>
<string name="screen_room_change_role_section_moderators">"协管员"</string>
<string name="screen_room_change_role_section_users">"成员"</string>
<string name="screen_room_change_role_unsaved_changes_description">"您有未保存的更改。"</string>
<string name="screen_room_change_role_unsaved_changes_title">"保存更改?"</string>
<string name="screen_room_member_list_banned_empty">"没有被封禁的用户。"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="other">"%1$d 人"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"移除并封禁成员"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"仅移除成员"</string>
<string name="screen_room_member_list_manage_member_unban_action">"取消封禁"</string>
<string name="screen_room_member_list_manage_member_unban_message">"如果受到邀请,他们可以重新加入聊天室。"</string>
<string name="screen_room_member_list_manage_member_unban_title">"从房间取消解封"</string>
<string name="screen_room_member_list_mode_banned">"已封禁用户"</string>
<string name="screen_room_member_list_mode_members">"成员"</string>
<string name="screen_room_member_list_role_administrator">"仅限管理员"</string>
<string name="screen_room_member_list_role_moderator">"管理员和协管员"</string>
<string name="screen_room_member_list_role_owner">"所有者"</string>
<string name="screen_room_member_list_room_members_header_title">"聊天室成员"</string>
<string name="screen_room_member_list_unbanning_user">"解除封禁 %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"管理员"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"管理员和所有者"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"更改我的角色"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"降级为成员"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"降级为协管员"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"成员权限"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"消息和内容"</string>
<string name="screen_room_roles_and_permissions_moderators">"协管员"</string>
<string name="screen_room_roles_and_permissions_owners">"所有者"</string>
<string name="screen_room_roles_and_permissions_reset">"重置权限"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"重置权限后,您将丢失当前设置。"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"重置权限?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"角色"</string>
<string name="screen_room_roles_and_permissions_room_details">"聊天室详情"</string>
<string name="screen_room_roles_and_permissions_space_details">"空间详情"</string>
<string name="screen_room_roles_and_permissions_title">"角色与权限"</string>
</resources>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Admin"</string>
<string name="screen_room_change_permissions_ban_people">"Ban people"</string>
<string name="screen_room_change_permissions_delete_messages">"Remove messages"</string>
<string name="screen_room_change_permissions_everyone">"Member"</string>
<string name="screen_room_change_permissions_invite_people">"Invite people"</string>
<string name="screen_room_change_permissions_member_moderation">"Manage members"</string>
<string name="screen_room_change_permissions_messages_and_content">"Messages and content"</string>
<string name="screen_room_change_permissions_moderators">"Moderator"</string>
<string name="screen_room_change_permissions_remove_people">"Remove people"</string>
<string name="screen_room_change_permissions_room_avatar">"Change avatar"</string>
<string name="screen_room_change_permissions_room_details">"Edit details"</string>
<string name="screen_room_change_permissions_room_name">"Change name"</string>
<string name="screen_room_change_permissions_room_topic">"Change topic"</string>
<string name="screen_room_change_permissions_send_messages">"Send messages"</string>
<string name="screen_room_change_role_administrators_title">"Edit Admins"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"You will not be able to undo this action. You are promoting the user to have the same power level as you."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Add Admin?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"You will not be able to undo this action. You are transferring the ownership to the selected users. Once you leave this will be permanent."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Transfer ownership?"</string>
<string name="screen_room_change_role_confirm_demote_self_action">"Demote"</string>
<string name="screen_room_change_role_confirm_demote_self_description">"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Demote yourself?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Pending)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Pending)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Admins automatically have moderator privileges"</string>
<string name="screen_room_change_role_moderators_owner_section_footer">"Owners automatically have admin privileges."</string>
<string name="screen_room_change_role_moderators_title">"Edit Moderators"</string>
<string name="screen_room_change_role_owners_title">"Choose Owners"</string>
<string name="screen_room_change_role_section_administrators">"Admins"</string>
<string name="screen_room_change_role_section_moderators">"Moderators"</string>
<string name="screen_room_change_role_section_users">"Members"</string>
<string name="screen_room_change_role_unsaved_changes_description">"You have unsaved changes."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Save changes?"</string>
<string name="screen_room_member_list_banned_empty">"There are no banned users."</string>
<plurals name="screen_room_member_list_banned_header_title">
<item quantity="one">"%1$d Banned"</item>
<item quantity="other">"%1$d Banned"</item>
</plurals>
<string name="screen_room_member_list_empty_search_subtitle">"Check the spelling or try a new search"</string>
<string name="screen_room_member_list_empty_search_title">"No results for “%1$s”"</string>
<plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d Person"</item>
<item quantity="other">"%1$d People"</item>
</plurals>
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Ban user"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Only remove member"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Unban"</string>
<string name="screen_room_member_list_manage_member_unban_message">"They will be able to join this room again if invited."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Unban user"</string>
<string name="screen_room_member_list_mode_banned">"Banned"</string>
<string name="screen_room_member_list_mode_members">"Members"</string>
<plurals name="screen_room_member_list_pending_header_title">
<item quantity="one">"%1$d Invited"</item>
<item quantity="other">"%1$d Invited"</item>
</plurals>
<string name="screen_room_member_list_pending_status">"Pending"</string>
<string name="screen_room_member_list_role_administrator">"Admin"</string>
<string name="screen_room_member_list_role_moderator">"Moderator"</string>
<string name="screen_room_member_list_role_owner">"Owner"</string>
<string name="screen_room_member_list_room_members_header_title">"Room members"</string>
<string name="screen_room_member_list_unbanning_user">"Unbanning %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Admins"</string>
<string name="screen_room_roles_and_permissions_admins_and_owners">"Admins and owners"</string>
<string name="screen_room_roles_and_permissions_change_my_role">"Change my role"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_member">"Demote to member"</string>
<string name="screen_room_roles_and_permissions_change_role_demote_to_moderator">"Demote to moderator"</string>
<string name="screen_room_roles_and_permissions_member_moderation">"Member moderation"</string>
<string name="screen_room_roles_and_permissions_messages_and_content">"Messages and content"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderators"</string>
<string name="screen_room_roles_and_permissions_owners">"Owners"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Permissions"</string>
<string name="screen_room_roles_and_permissions_reset">"Reset permissions"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Once you reset permissions, you will lose the current settings."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Reset permissions?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Roles"</string>
<string name="screen_room_roles_and_permissions_room_details">"Room details"</string>
<string name="screen_room_roles_and_permissions_space_details">"Space details"</string>
<string name="screen_room_roles_and_permissions_title">"Roles &amp; permissions"</string>
</resources>
@@ -0,0 +1,283 @@
/*
* 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.rolesandpermissions.impl.permissions
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.Event
import app.cash.turbine.TurbineTestContext
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.room.RoomMember.Role.Admin
import io.element.android.libraries.matrix.api.room.RoomMember.Role.Moderator
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
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.defaultRoomPowerLevelValues
import io.element.android.services.analytics.test.FakeAnalyticsService
import kotlinx.coroutines.test.runTest
import org.junit.Test
class ChangeRoomPermissionsPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createChangeRoomPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initial state, no permissions loaded
awaitItem().run {
assertThat(this.currentPermissions).isNull()
assertThat(this.itemsBySection).isNotEmpty()
assertThat(this.hasChanges).isFalse()
assertThat(this.saveAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(this.confirmExitAction).isEqualTo(AsyncAction.Uninitialized)
}
// Updated state, permissions loaded
assertThat(awaitItem().currentPermissions).isEqualTo(defaultPermissions())
}
}
@Test
fun `present - items by section are correct for room`() = runTest {
val presenter = createChangeRoomPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val itemsBySection = awaitUpdatedItem().itemsBySection
assertThat(itemsBySection[RoomPermissionsSection.RoomDetails]).containsExactly(
RoomPermissionType.ROOM_NAME,
RoomPermissionType.ROOM_AVATAR,
RoomPermissionType.ROOM_TOPIC,
)
assertThat(itemsBySection[RoomPermissionsSection.MessagesAndContent]).containsExactly(
RoomPermissionType.SEND_EVENTS,
RoomPermissionType.REDACT_EVENTS,
)
assertThat(itemsBySection[RoomPermissionsSection.MembershipModeration]).containsExactly(
RoomPermissionType.INVITE,
RoomPermissionType.KICK,
RoomPermissionType.BAN,
)
}
}
@Test
fun `present - ChangeMinimumRoleForAction updates the current permissions and hasChanges`() = runTest {
val presenter = createChangeRoomPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitUpdatedItem()
assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel)
assertThat(state.hasChanges).isFalse()
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator))
awaitItem().run {
assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel)
assertThat(hasChanges).isTrue()
}
}
}
@Test
fun `present - ChangeMinimumRoleForAction works for all actions`() = runTest {
val presenter = createChangeRoomPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitUpdatedItem()
val initialPermissions = defaultPermissions()
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, SelectableRole.Moderator))
val itemsBySection = cancelAndConsumeRemainingEvents()
(itemsBySection.last() as? Event.Item<ChangeRoomPermissionsState>)?.value?.run {
assertThat(currentPermissions).isEqualTo(
RoomPowerLevelsValues(
invite = Moderator.powerLevel,
kick = Moderator.powerLevel,
ban = Moderator.powerLevel,
redactEvents = Moderator.powerLevel,
sendEvents = Moderator.powerLevel,
roomName = Moderator.powerLevel,
roomAvatar = Moderator.powerLevel,
roomTopic = Moderator.powerLevel,
spaceChild = initialPermissions.spaceChild
)
)
}
}
}
@Test
fun `present - Save updates the current permissions and resets hasChanges`() = runTest {
val analyticsService = FakeAnalyticsService()
val presenter = createChangeRoomPermissionsPresenter(
analyticsService = analyticsService,
room = FakeJoinedRoom(
updatePowerLevelsResult = { Result.success(Unit) },
baseRoom = FakeBaseRoom(powerLevelsResult = { Result.success(defaultPermissions()) }),
),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitUpdatedItem()
assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel)
assertThat(state.hasChanges).isFalse()
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, SelectableRole.Moderator))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, SelectableRole.Everyone))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, SelectableRole.Admin))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, SelectableRole.Admin))
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, SelectableRole.Admin))
skipItems(7)
assertThat(awaitItem().hasChanges).isTrue()
state.eventSink(ChangeRoomPermissionsEvent.Save)
assertThat(awaitItem().saveAction).isEqualTo(AsyncAction.Loading)
assertThat(awaitItem().hasChanges).isFalse()
awaitItem().run {
assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel)
assertThat(saveAction).isEqualTo(AsyncAction.Success(Unit))
}
assertThat(analyticsService.capturedEvents).containsExactlyElementsIn(
listOf(
RoomModeration(RoomModeration.Action.ChangePermissionsRoomName, RoomModeration.Role.Moderator),
RoomModeration(RoomModeration.Action.ChangePermissionsRoomAvatar, RoomModeration.Role.Moderator),
RoomModeration(RoomModeration.Action.ChangePermissionsRoomTopic, RoomModeration.Role.Moderator),
RoomModeration(RoomModeration.Action.ChangePermissionsSendMessages, RoomModeration.Role.Moderator),
RoomModeration(RoomModeration.Action.ChangePermissionsRedactMessages, RoomModeration.Role.User),
RoomModeration(RoomModeration.Action.ChangePermissionsKickMembers, RoomModeration.Role.Administrator),
RoomModeration(RoomModeration.Action.ChangePermissionsBanMembers, RoomModeration.Role.Administrator),
RoomModeration(RoomModeration.Action.ChangePermissionsInviteUsers, RoomModeration.Role.Administrator),
)
)
}
}
@Test
fun `present - Save will fail if there are not current permissions`() = runTest {
val room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(powerLevelsResult = { Result.failure(IllegalStateException("Failed to load power levels")) }),
)
val presenter = createChangeRoomPermissionsPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitItem()
assertThat(state.currentPermissions).isNull()
state.eventSink(ChangeRoomPermissionsEvent.Save)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
}
}
@Test
fun `present - Save can handle failures and they can be cleared`() = runTest {
val room = FakeJoinedRoom(
updatePowerLevelsResult = { Result.failure(IllegalStateException("Failed to update power levels")) },
baseRoom = FakeBaseRoom(powerLevelsResult = { Result.success(defaultPermissions()) }),
)
val presenter = createChangeRoomPermissionsPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitUpdatedItem()
assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel)
assertThat(state.hasChanges).isFalse()
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator))
assertThat(awaitItem().hasChanges).isTrue()
state.eventSink(ChangeRoomPermissionsEvent.Save)
assertThat(awaitItem().saveAction).isEqualTo(AsyncAction.Loading)
awaitItem().run {
assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel)
// Couldn't save the changes, so they're still pending
assertThat(hasChanges).isTrue()
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
}
state.eventSink(ChangeRoomPermissionsEvent.ResetPendingActions)
awaitItem().run {
assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel)
assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(hasChanges).isTrue()
}
}
}
@Test
fun `present - Exit does not need a confirmation when there are no pending changes`() = runTest {
val presenter = createChangeRoomPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitUpdatedItem()
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator))
assertThat(awaitItem().hasChanges).isTrue()
state.eventSink(ChangeRoomPermissionsEvent.Exit)
assertThat(awaitItem().confirmExitAction).isEqualTo(AsyncAction.ConfirmingNoParams)
state.eventSink(ChangeRoomPermissionsEvent.Exit)
assertThat(awaitItem().confirmExitAction).isEqualTo(AsyncAction.Success(Unit))
}
}
@Test
fun `present - Exit needs confirmation when there are pending changes`() = runTest {
val presenter = createChangeRoomPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = awaitUpdatedItem()
state.eventSink(ChangeRoomPermissionsEvent.Exit)
assertThat(awaitItem().confirmExitAction).isEqualTo(AsyncAction.Success(Unit))
}
}
private fun createChangeRoomPermissionsPresenter(
room: FakeJoinedRoom = FakeJoinedRoom(
baseRoom = FakeBaseRoom(powerLevelsResult = { Result.success(defaultPermissions()) }),
),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
) = ChangeRoomPermissionsPresenter(
room = room,
analyticsService = analyticsService,
)
private fun defaultPermissions() = defaultRoomPowerLevelValues()
private suspend fun TurbineTestContext<ChangeRoomPermissionsState>.awaitUpdatedItem(): ChangeRoomPermissionsState {
skipItems(1)
return awaitItem()
}
}
@@ -0,0 +1,172 @@
/*
* 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.rolesandpermissions.impl.permissions
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.rolesandpermissions.impl.R
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.clickOnFirst
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.pressBackKey
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ChangeRoomPermissionsViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `click on back icon invokes Exit`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
eventSink = recorder
)
)
rule.pressBack()
recorder.assertSingle(ChangeRoomPermissionsEvent.Exit)
}
@Test
fun `click on back key invokes Exit`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
eventSink = recorder
)
)
rule.pressBackKey()
recorder.assertSingle(ChangeRoomPermissionsEvent.Exit)
}
@Test
fun `when confirming exit with pending changes, using the back key actually exits`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
hasChanges = true,
eventSink = recorder,
),
)
rule.pressBackKey()
recorder.assertSingle(ChangeRoomPermissionsEvent.Exit)
}
@Test
fun `when confirming exit with pending changes, clicking on 'discard' button in the dialog actually exits`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
hasChanges = true,
confirmExitAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOn(CommonStrings.action_discard)
recorder.assertSingle(ChangeRoomPermissionsEvent.Exit)
}
@Test
fun `when confirming exit with pending changes, clicking on 'save' button in the dialog saves the changes`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
hasChanges = true,
confirmExitAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOnFirst(CommonStrings.action_save)
recorder.assertSingle(ChangeRoomPermissionsEvent.Save)
}
@Test
fun `click on a role item triggers ChangeRole event`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
itemsBySection = persistentMapOf(
// Makes sure there is only one item to click on
RoomPermissionsSection.RoomDetails to persistentListOf(RoomPermissionType.ROOM_NAME)
),
eventSink = recorder,
)
)
rule.clickOn(R.string.screen_room_change_permissions_room_name)
rule.clickOn(R.string.screen_room_change_permissions_everyone)
recorder.assertSingle(
ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Everyone),
)
}
@Test
fun `click on the Save menu item triggers Save event`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
hasChanges = true,
eventSink = recorder,
),
)
rule.clickOn(CommonStrings.action_save)
recorder.assertSingle(ChangeRoomPermissionsEvent.Save)
}
@Test
fun `a successful save exits the screen`() {
ensureCalledOnceWithParam(true) { callback ->
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
hasChanges = true,
saveAction = AsyncAction.Success(Unit),
),
onComplete = callback
)
rule.clickOn(CommonStrings.action_save)
}
}
@Test
fun `click on the Ok option in save error dialog triggers ResetPendingAction event`() {
val recorder = EventsRecorder<ChangeRoomPermissionsEvent>()
rule.setChangeRoomPermissionsRule(
state = aChangeRoomPermissionsState(
hasChanges = true,
saveAction = AsyncAction.Failure(IllegalStateException("Failed to set room power levels")),
eventSink = recorder,
),
)
rule.clickOn(CommonStrings.action_ok)
recorder.assertSingle(ChangeRoomPermissionsEvent.ResetPendingActions)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setChangeRoomPermissionsRule(
state: ChangeRoomPermissionsState = aChangeRoomPermissionsState(),
onComplete: (Boolean) -> Unit = EnsureNeverCalledWithParam(),
) {
setContent {
ChangeRoomPermissionsView(
state = state,
onComplete = onComplete,
)
}
}
@@ -0,0 +1,26 @@
/*
* 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.rolesandpermissions.impl.roles
import com.google.common.truth.Truth.assertThat
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.libraries.matrix.api.room.RoomMember
import org.junit.Test
class ChangeRolesNodeTest {
@Test
fun `test toRoomMemberRole`() {
assertThat(ChangeRoomMemberRolesListType.Admins.toRoomMemberRole())
.isEqualTo(RoomMember.Role.Admin)
assertThat(ChangeRoomMemberRolesListType.Moderators.toRoomMemberRole())
.isEqualTo(RoomMember.Role.Moderator)
assertThat(ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving.toRoomMemberRole())
.isEqualTo(RoomMember.Role.Owner(false))
}
}
@@ -0,0 +1,584 @@
/*
* 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.rolesandpermissions.impl.roles
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.features.rolesandpermissions.impl.RoomMemberListDataSource
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.UserId
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.powerlevels.RoomPowerLevels
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.A_USER_ID_2
import io.element.android.libraries.matrix.test.A_USER_ID_3
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.aRoomInfo
import io.element.android.libraries.matrix.test.room.aRoomMember
import io.element.android.libraries.matrix.test.room.defaultRoomPowerLevelValues
import io.element.android.libraries.previewutils.room.aRoomMemberList
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.test
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
class ChangeRolesPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createChangeRolesPresenter()
presenter.test {
with(awaitItem()) {
assertThat(role).isEqualTo(RoomMember.Role.Admin)
assertThat(query).isNull()
assertThat(isSearchActive).isFalse()
assertThat(searchResults).isInstanceOf(SearchBarResultState.Initial::class.java)
assertThat(selectedUsers).isEmpty()
assertThat(hasPendingChanges).isFalse()
assertThat(savingState).isEqualTo(AsyncAction.Uninitialized)
}
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - initial results are loaded automatically`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
assertThat(awaitItem().searchResults).isInstanceOf(SearchBarResultState.Results::class.java)
}
}
@Test
fun `present - canChangeRole of users with lower power level unless they are owners - privilegedCreatorRole is true`() = runTest {
val creatorUserId = UserId("@creator:matrix.org")
val superAdminUserId = UserId("@super_admin:matrix.org")
val room = FakeJoinedRoom().apply {
// User is a creator, so they can change roles of other members. So is `creatorUserId`.
givenRoomInfo(
aRoomInfo(
privilegedCreatorRole = true,
roomCreators = listOf(sessionId, creatorUserId),
roomPowerLevels = RoomPowerLevels(
defaultRoomPowerLevelValues(),
users = persistentMapOf(
// bob is Admin
A_USER_ID_2 to RoomMember.Role.Admin.powerLevel,
// carol is Moderator
A_USER_ID_3 to RoomMember.Role.Moderator.powerLevel,
// super_admin is Owner - Superadmin
superAdminUserId to RoomMember.Role.Owner(isCreator = false).powerLevel,
)
)
)
)
val roomMemberList = aRoomMemberList() + listOf(
// Owner - superadmin
aRoomMember(userId = superAdminUserId, role = RoomMember.Role.Owner(isCreator = false)),
// Owner - creator
aRoomMember(userId = creatorUserId, role = RoomMember.Role.Owner(isCreator = true))
)
givenRoomMembersState(RoomMembersState.Ready(roomMemberList.toImmutableList()))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
awaitItem().run {
assertThat(canChangeMemberRole(A_USER_ID_2)).isTrue() // Admin
assertThat(canChangeMemberRole(A_USER_ID_3)).isTrue() // Moderator
assertThat(canChangeMemberRole(superAdminUserId)).isTrue() // Super admin
assertThat(canChangeMemberRole(creatorUserId)).isFalse() // Owner
}
}
}
@Test
fun `present - canChangeRole of users with lower power level unless they are owners - privilegedCreatorRole is false`() = runTest {
val creatorUserId = UserId("@creator:matrix.org")
val superAdminUserId = UserId("@super_admin:matrix.org")
val room = FakeJoinedRoom().apply {
// User is a creator, so they can change roles of other members. So is `creatorUserId`.
givenRoomInfo(
aRoomInfo(
privilegedCreatorRole = false,
roomCreators = listOf(sessionId, creatorUserId),
roomPowerLevels = RoomPowerLevels(
defaultRoomPowerLevelValues(),
users = persistentMapOf(
// Creator is an admin
sessionId to RoomMember.Role.Admin.powerLevel,
creatorUserId to RoomMember.Role.Admin.powerLevel,
// bob is Admin
A_USER_ID_2 to RoomMember.Role.Admin.powerLevel,
// carol is Moderator
A_USER_ID_3 to RoomMember.Role.Moderator.powerLevel,
// super_admin is Owner - Superadmin
superAdminUserId to RoomMember.Role.Owner(isCreator = false).powerLevel,
)
)
)
)
val roomMemberList = aRoomMemberList() + listOf(
// Owner - superadmin
aRoomMember(userId = superAdminUserId, role = RoomMember.Role.Owner(isCreator = false)),
// Owner - creator
aRoomMember(userId = creatorUserId, role = RoomMember.Role.Owner(isCreator = true))
)
givenRoomMembersState(RoomMembersState.Ready(roomMemberList.toImmutableList()))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
awaitItem().run {
assertThat(canChangeMemberRole(A_USER_ID_2)).isFalse() // Creator cannot update Admin in this case
assertThat(canChangeMemberRole(A_USER_ID_3)).isTrue() // Moderator
assertThat(canChangeMemberRole(creatorUserId)).isFalse() // Owner
}
}
}
@Test
fun `present - when modifying admins, creators are displayed too`() = runTest {
val room = FakeJoinedRoom().apply {
val creatorUserId = UserId("@creator:matrix.org")
val memberList = aRoomMemberList()
.plus(aRoomMember(displayName = "CREATOR", role = RoomMember.Role.Owner(isCreator = true), userId = creatorUserId))
.toImmutableList()
givenRoomInfo(aRoomInfo(roomCreators = listOf(creatorUserId)))
givenRoomMembersState(RoomMembersState.Ready(memberList))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(2)
awaitItem().searchResults.run {
assertThat(this).isInstanceOf(SearchBarResultState.Results::class.java)
val results = (this as SearchBarResultState.Results).results
assertThat(results.admins).isNotEmpty()
assertThat(results.owners).isNotEmpty()
assertThat(results.owners.last().role).isEqualTo(RoomMember.Role.Owner(isCreator = true))
}
}
}
@Test
fun `present - ToggleSearchActive changes the value`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(ChangeRolesEvent.ToggleSearchActive)
assertThat(awaitItem().isSearchActive).isTrue()
initialState.eventSink(ChangeRolesEvent.ToggleSearchActive)
assertThat(awaitItem().isSearchActive).isFalse()
}
}
@Test
fun `present - QueryChanged produces new results`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
val initialState = awaitItem()
val initialResults = (awaitItem().searchResults as? SearchBarResultState.Results)?.results
assertThat(initialResults?.members).hasSize(8)
assertThat(initialResults?.moderators).hasSize(1)
assertThat(initialResults?.admins).hasSize(1)
initialState.eventSink(ChangeRolesEvent.QueryChanged("Alice"))
skipItems(1)
val searchResults = (awaitItem().searchResults as? SearchBarResultState.Results)?.results
assertThat(searchResults?.admins).hasSize(1)
assertThat(searchResults?.moderators).isEmpty()
assertThat(searchResults?.members).isEmpty()
assertThat(searchResults?.admins?.firstOrNull()?.userId).isEqualTo(A_USER_ID)
}
}
@Test
fun `present - changes in the room members produce new results`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
val initialResults = (awaitItem().searchResults as? SearchBarResultState.Results)?.results
assertThat(initialResults?.members).hasSize(8)
assertThat(initialResults?.moderators).hasSize(1)
assertThat(initialResults?.admins).hasSize(1)
room.givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList().take(1).toImmutableList()))
skipItems(1)
val searchResults = (awaitItem().searchResults as? SearchBarResultState.Results)?.results
assertThat(searchResults?.admins).hasSize(1)
assertThat(searchResults?.moderators).isEmpty()
assertThat(searchResults?.members).isEmpty()
assertThat(searchResults?.admins?.firstOrNull()?.userId).isEqualTo(A_USER_ID)
}
}
@Test
fun `present - UserSelectionToggle adds and removes users from the selected user list`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.selectedUsers).hasSize(1)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
assertThat(awaitItem().selectedUsers).hasSize(2)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
assertThat(awaitItem().selectedUsers).hasSize(1)
}
}
@Test
fun `present - hasPendingChanges is true when the initial selected users don't match the new ones`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.hasPendingChanges).isFalse()
assertThat(initialState.selectedUsers).hasSize(1)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
with(awaitItem()) {
assertThat(selectedUsers).hasSize(2)
assertThat(hasPendingChanges).isTrue()
}
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
with(awaitItem()) {
assertThat(selectedUsers).hasSize(1)
assertThat(hasPendingChanges).isFalse()
}
}
}
@Test
fun `present - Exit will display success false if no pending changes`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.hasPendingChanges).isFalse()
assertThat(initialState.savingState).isEqualTo(AsyncAction.Uninitialized)
initialState.eventSink(ChangeRolesEvent.Exit)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(false))
}
}
@Test
fun `present - CloseDialog will remove exit confirmation`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.hasPendingChanges).isFalse()
assertThat(initialState.savingState).isEqualTo(AsyncAction.Uninitialized)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().eventSink(ChangeRolesEvent.Exit)
val confirmingState = awaitItem()
assertThat(confirmingState.savingState).isEqualTo(AsyncAction.ConfirmingCancellation)
confirmingState.eventSink(ChangeRolesEvent.CloseDialog)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Uninitialized)
}
}
@Test
fun `present - Exit will display a confirmation dialog if there are pending changes, calling it again will actually exit`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.hasPendingChanges).isFalse()
assertThat(initialState.savingState).isEqualTo(AsyncAction.Uninitialized)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
val updatedState = awaitItem()
assertThat(updatedState.hasPendingChanges).isTrue()
skipItems(1)
updatedState.eventSink(ChangeRolesEvent.Exit)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.ConfirmingCancellation)
updatedState.eventSink(ChangeRolesEvent.Exit)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(false))
}
}
@Test
fun `present - Save will display a confirmation when adding admins`() = runTest {
val room = FakeJoinedRoom(
updateUserRoleResult = { Result.success(Unit) },
baseRoom = FakeBaseRoom(updateMembersResult = { Result.success(Unit) }),
).apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(role = RoomMember.Role.Admin, room = room)
presenter.test {
skipItems(2)
val initialState = awaitItem()
assertThat(initialState.selectedUsers).hasSize(1)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().eventSink(ChangeRolesEvent.Save)
val confirmingState = awaitItem()
assertThat(confirmingState.savingState).isEqualTo(ConfirmingModifyingAdmins)
confirmingState.eventSink(ChangeRolesEvent.Save)
assertThat(awaitItem().savingState).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(true))
}
}
@Test
fun `present - CloseDialog will remove the confirmation dialog`() = runTest {
val room = FakeJoinedRoom().apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Admin)))
}
val presenter = createChangeRolesPresenter(role = RoomMember.Role.Admin, room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.selectedUsers).hasSize(1)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().eventSink(ChangeRolesEvent.Save)
val confirmingState = awaitItem()
assertThat(confirmingState.savingState).isEqualTo(ConfirmingModifyingAdmins)
confirmingState.eventSink(ChangeRolesEvent.CloseDialog)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Uninitialized)
}
}
@Test
fun `present - Save will just save the data for moderators`() = runTest {
val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom(
updateUserRoleResult = { Result.success(Unit) },
baseRoom = FakeBaseRoom(updateMembersResult = { Result.success(Unit) }),
).apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(RoomMember.Role.Moderator)))
}
val presenter = createChangeRolesPresenter(
role = RoomMember.Role.Moderator,
room = room,
analyticsService = analyticsService
)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.selectedUsers).isEmpty()
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().also {
assertThat(it.selectedUsers).hasSize(1)
it.eventSink(ChangeRolesEvent.Save)
}
assertThat(awaitItem().savingState).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(true))
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.ChangeMemberRole, RoomModeration.Role.Moderator))
}
}
@Test
fun `present - Save will ask for confirmation before assigning new owners`() = runTest {
val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom(
updateUserRoleResult = { Result.success(Unit) },
baseRoom = FakeBaseRoom(updateMembersResult = { Result.success(Unit) }),
).apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(
aRoomInfo(
roomCreators = listOf(sessionId),
roomPowerLevels = roomPowerLevelsWithRoles(
A_USER_ID to RoomMember.Role.Owner(isCreator = false),
A_USER_ID_2 to RoomMember.Role.Admin,
)
)
)
}
val presenter = createChangeRolesPresenter(
role = RoomMember.Role.Owner(isCreator = false),
room = room,
analyticsService = analyticsService
)
presenter.test {
val initialState = awaitItem()
assertThat(initialState.selectedUsers).isEmpty()
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().also {
assertThat(it.selectedUsers).hasSize(1)
it.eventSink(ChangeRolesEvent.Save)
}
assertThat(awaitItem().savingState.isConfirming()).isTrue()
}
}
@Test
fun `present - Save will just save the changes if the current user is a room creator and the selected users are not`() = runTest {
val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom(
updateUserRoleResult = { Result.success(Unit) },
baseRoom = FakeBaseRoom(updateMembersResult = { Result.success(Unit) }),
).apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(
aRoomInfo(
roomCreators = listOf(sessionId),
roomPowerLevels = roomPowerLevelsWithRole(role = RoomMember.Role.Admin, userId = A_USER_ID_2)
)
)
}
val presenter = createChangeRolesPresenter(
role = RoomMember.Role.Admin,
room = room,
analyticsService = analyticsService
)
presenter.test {
skipItems(2)
val initialState = awaitItem()
assertThat(initialState.selectedUsers).hasSize(2)
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().also {
assertThat(it.selectedUsers).hasSize(1)
it.eventSink(ChangeRolesEvent.Save)
}
val loadingState = awaitItem()
assertThat(loadingState.savingState).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(true))
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.ChangeMemberRole, RoomModeration.Role.User))
}
}
@Test
fun `present - Save can handle failures and CloseDialog clears them`() = runTest {
val room = FakeJoinedRoom(
updateUserRoleResult = { Result.failure(IllegalStateException("Failed")) }
).apply {
givenRoomMembersState(RoomMembersState.Ready(aRoomMemberList()))
givenRoomInfo(aRoomInfo(roomPowerLevels = roomPowerLevelsWithRole(role = RoomMember.Role.Moderator, userId = A_USER_ID)))
}
val presenter = createChangeRolesPresenter(role = RoomMember.Role.Moderator, room = room)
presenter.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.selectedUsers).isEmpty()
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
awaitItem().also {
assertThat(it.selectedUsers).hasSize(1)
it.eventSink(ChangeRolesEvent.Save)
}
val loadingState = awaitItem()
assertThat(loadingState.savingState).isInstanceOf(AsyncAction.Loading::class.java)
val failedState = awaitItem()
assertThat(failedState.savingState).isInstanceOf(AsyncAction.Failure::class.java)
failedState.eventSink(ChangeRolesEvent.CloseDialog)
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Uninitialized)
}
}
@Test
fun `test analytics mapping`() = runTest {
val presenter = createChangeRolesPresenter()
with(presenter) {
assertThat(RoomMember.Role.User.toAnalyticsMemberRole()).isEqualTo(RoomModeration.Role.User)
assertThat(RoomMember.Role.Moderator.toAnalyticsMemberRole()).isEqualTo(RoomModeration.Role.Moderator)
assertThat(RoomMember.Role.Admin.toAnalyticsMemberRole()).isEqualTo(RoomModeration.Role.Administrator)
assertThat(RoomMember.Role.Owner(isCreator = false).toAnalyticsMemberRole()).isEqualTo(RoomModeration.Role.Administrator)
assertThat(RoomMember.Role.Owner(isCreator = true).toAnalyticsMemberRole()).isEqualTo(RoomModeration.Role.Administrator)
}
}
private fun roomPowerLevelsWithRole(
role: RoomMember.Role,
userId: UserId = A_USER_ID,
): RoomPowerLevels {
return RoomPowerLevels(
values = defaultRoomPowerLevelValues(),
users = persistentMapOf(userId to role.powerLevel)
)
}
private fun roomPowerLevelsWithRoles(vararg pairs: Pair<UserId, RoomMember.Role>): RoomPowerLevels {
return RoomPowerLevels(
values = defaultRoomPowerLevelValues(),
users = pairs.associate { (userId, role) -> userId to role.powerLevel }.toImmutableMap()
)
}
}
internal fun TestScope.createChangeRolesPresenter(
role: RoomMember.Role = RoomMember.Role.Admin,
room: FakeJoinedRoom = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {})),
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
): ChangeRolesPresenter {
return ChangeRolesPresenter(
role = role,
room = room,
dataSource = RoomMemberListDataSource(room, dispatchers),
analyticsService = analyticsService,
roomCoroutineScope = this,
)
}
@@ -0,0 +1,308 @@
/*
* 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.rolesandpermissions.impl.roles
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.toMatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.pressBackKey
import kotlinx.collections.immutable.toImmutableList
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@RunWith(AndroidJUnit4::class)
class ChangeRolesViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `passing a 'User' role throws an exception`() {
val exception = runCatchingExceptions {
rule.setChangeRolesContent(
state = aChangeRolesState(
role = RoomMember.Role.User,
eventSink = EnsureNeverCalledWithParam(),
),
)
}.exceptionOrNull()
assertThat(exception).isNotNull()
}
@Test
fun `back key - with search active toggles the search`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
isSearchActive = true,
eventSink = eventsRecorder,
),
)
rule.pressBackKey()
eventsRecorder.assertSingle(ChangeRolesEvent.ToggleSearchActive)
}
@Test
fun `back key - with search inactive exits the screen`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
isSearchActive = false,
eventSink = eventsRecorder,
),
)
rule.pressBackKey()
eventsRecorder.assertList(listOf(ChangeRolesEvent.QueryChanged(""), ChangeRolesEvent.Exit))
}
@Test
fun `back button - exits the screen`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
isSearchActive = false,
eventSink = eventsRecorder,
),
)
rule.pressBack()
eventsRecorder.assertList(listOf(ChangeRolesEvent.QueryChanged(""), ChangeRolesEvent.Exit))
}
@Test
fun `save button - with changes, it saves them`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
hasPendingChanges = true,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_save)
eventsRecorder.assertList(listOf(ChangeRolesEvent.QueryChanged(""), ChangeRolesEvent.Save))
}
@Test
fun `save button - with no changes, does nothing`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
hasPendingChanges = false,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_save)
eventsRecorder.assertList(listOf(ChangeRolesEvent.QueryChanged("")))
}
@Test
fun `exit confirmation dialog - submit exits the screen`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
isSearchActive = true,
savingState = AsyncAction.ConfirmingCancellation,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_ok)
eventsRecorder.assertSingle(ChangeRolesEvent.Exit)
}
@Test
fun `exit confirmation dialog - cancel removes the dialog`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
isSearchActive = true,
savingState = AsyncAction.ConfirmingCancellation,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog)
}
@Test
fun `save admins confirmation dialog - submit saves the changes`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
role = RoomMember.Role.Admin,
isSearchActive = true,
savingState = ConfirmingModifyingAdmins,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_ok)
eventsRecorder.assertSingle(ChangeRolesEvent.Save)
}
@Test
fun `save owners confirmation dialog - continue saves the changes`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
role = RoomMember.Role.Owner(isCreator = false),
isSearchActive = true,
savingState = ConfirmingModifyingOwners,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_continue)
eventsRecorder.assertSingle(ChangeRolesEvent.Save)
}
@Test
fun `save admins confirmation dialog - cancel removes the dialog`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
role = RoomMember.Role.Admin,
isSearchActive = true,
savingState = ConfirmingModifyingAdmins,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog)
}
@Test
fun `save owners confirmation dialog - cancel removes the dialog`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
role = RoomMember.Role.Owner(isCreator = false),
isSearchActive = true,
savingState = ConfirmingModifyingOwners,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog)
}
@Test
fun `error dialog - dismissing removes the dialog`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
rule.setChangeRolesContent(
state = aChangeRolesState(
isSearchActive = true,
savingState = AsyncAction.Failure(IllegalStateException("boom")),
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_ok)
eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog)
}
@Test
fun `testing removing user from selected list emits the expected event`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
val selectedUsers = aMatrixUserList().take(2)
val userToDeselect = selectedUsers[1]
assertThat(userToDeselect.displayName).isEqualTo("Bob")
rule.setChangeRolesContent(
state = aChangeRolesStateWithSelectedUsers().copy(
selectedUsers = selectedUsers.toImmutableList(),
eventSink = eventsRecorder,
),
)
// Unselect the user from the row list
val contentDescription = rule.activity.getString(CommonStrings.action_remove)
rule.onNodeWithContentDescription(
label = contentDescription,
useUnmergedTree = true,
).performClick()
eventsRecorder.assertList(
listOf(
ChangeRolesEvent.QueryChanged(""),
ChangeRolesEvent.UserSelectionToggled(userToDeselect),
)
)
}
@Test
@Config(qualifiers = "h1000dp")
fun `testing adding user to the selected list emits the expected event`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
val selectedUsers = aMatrixUserList().take(2)
val state = aChangeRolesStateWithSelectedUsers().copy(
selectedUsers = selectedUsers.toImmutableList(),
eventSink = eventsRecorder,
)
val userToSelect = (state.searchResults as SearchBarResultState.Results).results.members.first().toMatrixUser()
assertThat(userToSelect.displayName).isEqualTo("Carol")
rule.setChangeRolesContent(
state = state,
)
// Select the user from the user list
rule.onNodeWithText("Carol").performClick()
eventsRecorder.assertList(
listOf(
ChangeRolesEvent.QueryChanged(""),
ChangeRolesEvent.UserSelectionToggled(userToSelect),
)
)
}
@Test
fun `testing removing user to the selected list emits the expected event`() {
val eventsRecorder = EventsRecorder<ChangeRolesEvent>()
val selectedUsers = aMatrixUserList().take(2)
val state = aChangeRolesStateWithSelectedUsers().copy(
selectedUsers = selectedUsers.toImmutableList(),
eventSink = eventsRecorder,
)
val userToSelect = (state.searchResults as SearchBarResultState.Results).results.moderators.first().toMatrixUser()
assertThat(userToSelect.displayName).isEqualTo("Bob")
rule.setChangeRolesContent(
state = state,
)
// Unselect the user from the user list
rule.onAllNodesWithText(
text = "Bob",
useUnmergedTree = true,
)[1].performClick()
eventsRecorder.assertList(
listOf(
ChangeRolesEvent.QueryChanged(""),
ChangeRolesEvent.UserSelectionToggled(userToSelect),
)
)
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setChangeRolesContent(
state: ChangeRolesState,
) {
setContent {
ChangeRolesView(
state = state,
)
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.rolesandpermissions.impl.roles
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.bumble.appyx.core.modality.BuildContext
import com.google.common.truth.Truth.assertThat
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.tests.testutils.node.TestParentNode
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DefaultChangeRoomMemberRolesEntyPointTest {
@Test
fun `test node builder`() = runTest {
val entryPoint = DefaultChangeRoomMemberRolesEntyPoint()
val parentNode = TestParentNode.create { buildContext, plugins ->
ChangeRoomMemberRolesRootNode(
buildContext = buildContext,
plugins = plugins,
roomGraphFactory = { },
)
}
val room = FakeJoinedRoom()
val listType = ChangeRoomMemberRolesListType.Admins
val result = entryPoint.createNode(
parentNode = parentNode,
buildContext = BuildContext.root(null),
room = FakeJoinedRoom(),
listType = listType,
)
assertThat(result).isInstanceOf(ChangeRoomMemberRolesRootNode::class.java)
// Search for the Inputs plugin
val input = result.plugins.filterIsInstance<ChangeRoomMemberRolesRootNode.Inputs>().single()
assertThat(input.joinedRoom.roomId).isEqualTo(room.roomId)
assertThat(input.listType).isEqualTo(listType)
}
}
@@ -0,0 +1,79 @@
/*
* 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.rolesandpermissions.impl.roles
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.A_USER_ID_3
import io.element.android.libraries.matrix.test.A_USER_ID_4
import io.element.android.libraries.matrix.test.A_USER_ID_5
import io.element.android.libraries.matrix.test.A_USER_ID_6
import io.element.android.libraries.matrix.test.A_USER_ID_7
import io.element.android.libraries.matrix.test.room.aRoomMember
import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator
import kotlinx.collections.immutable.persistentListOf
import org.junit.Test
class MembersByRoleTest {
@Test
fun `constructor - with single member list categorizes and sorts members`() {
val members = listOf(
aRoomMember(A_USER_ID_2, displayName = "Bob", role = RoomMember.Role.Admin),
aRoomMember(A_USER_ID, displayName = "Alice", role = RoomMember.Role.Admin),
aRoomMember(A_USER_ID_3, displayName = "Carol", role = RoomMember.Role.User),
aRoomMember(A_USER_ID_5, displayName = "Eve", role = RoomMember.Role.User),
aRoomMember(A_USER_ID_4, displayName = "David", role = RoomMember.Role.User),
aRoomMember(A_USER_ID_6, displayName = "Justin", role = RoomMember.Role.Owner(isCreator = true)),
aRoomMember(A_USER_ID_7, displayName = "Mallory", role = RoomMember.Role.Owner(isCreator = false)),
)
val membersByRole = MembersByRole(members = members, comparator = PowerLevelRoomMemberComparator())
assertThat(membersByRole.owners).containsExactly(
aRoomMember(A_USER_ID_6, displayName = "Justin", role = RoomMember.Role.Owner(isCreator = true)),
aRoomMember(A_USER_ID_7, displayName = "Mallory", role = RoomMember.Role.Owner(isCreator = false)),
)
assertThat(membersByRole.admins).containsExactly(
aRoomMember(A_USER_ID, displayName = "Alice", role = RoomMember.Role.Admin),
aRoomMember(A_USER_ID_2, displayName = "Bob", role = RoomMember.Role.Admin),
)
assertThat(membersByRole.moderators).isEmpty()
assertThat(membersByRole.members).containsExactly(
aRoomMember(A_USER_ID_3, displayName = "Carol", role = RoomMember.Role.User),
aRoomMember(A_USER_ID_4, displayName = "David", role = RoomMember.Role.User),
aRoomMember(A_USER_ID_5, displayName = "Eve", role = RoomMember.Role.User),
)
}
@Test
fun `isEmpty - only returns true with no members of any role`() {
val emptyMembersByRole = MembersByRole()
assertThat(emptyMembersByRole.isEmpty()).isTrue()
val membersByRoleWithOwners = MembersByRole(
owners = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.Admin)),
)
assertThat(membersByRoleWithOwners.isEmpty()).isFalse()
val membersByRoleWithAdmins = MembersByRole(
admins = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.Admin)),
)
assertThat(membersByRoleWithAdmins.isEmpty()).isFalse()
val membersByRoleWithModerators = MembersByRole(
moderators = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.Moderator)),
)
assertThat(membersByRoleWithModerators.isEmpty()).isFalse()
val membersByRoleWithMembers = MembersByRole(
members = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.User)),
)
assertThat(membersByRoleWithMembers.isEmpty()).isFalse()
}
}
@@ -0,0 +1,166 @@
/*
* 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.rolesandpermissions.impl.root
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.RoomModeration
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
class RolesAndPermissionPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createRolesAndPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
with(awaitItem()) {
assertThat(adminCount).isEqualTo(0)
assertThat(moderatorCount).isEqualTo(0)
assertThat(changeOwnRoleAction).isEqualTo(AsyncAction.Uninitialized)
}
}
}
@Test
fun `present - ChangeOwnRole presents a confirmation dialog`() = runTest {
val presenter = createRolesAndPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RolesAndPermissionsEvents.ChangeOwnRole)
assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.ConfirmingNoParams)
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `present - DemoteSelfTo changes own role to the specified one`() = runTest(StandardTestDispatcher()) {
val presenter = createRolesAndPermissionsPresenter(
dispatchers = testCoroutineDispatchers(),
room = FakeJoinedRoom(
updateUserRoleResult = { Result.success(Unit) }
),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator))
runCurrent()
assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Loading)
runCurrent()
assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Success(Unit))
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `present - DemoteSelfTo can handle failures and clean them`() = runTest(StandardTestDispatcher()) {
val room = FakeJoinedRoom(
updateUserRoleResult = { Result.failure(Exception("Failed to update role")) }
)
val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator))
runCurrent()
assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Loading)
runCurrent()
assertThat(awaitItem().changeOwnRoleAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(RolesAndPermissionsEvents.CancelPendingAction)
assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Uninitialized)
}
}
@Test
fun `present - CancelPendingAction dismisses confirmation dialog too`() = runTest {
val presenter = createRolesAndPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RolesAndPermissionsEvents.ChangeOwnRole)
awaitItem().eventSink(RolesAndPermissionsEvents.CancelPendingAction)
assertThat(awaitItem().changeOwnRoleAction).isEqualTo(AsyncAction.Uninitialized)
}
}
@Test
fun `present - ResetPermissions needs confirmation, then resets permissions`() = runTest {
val analyticsService = FakeAnalyticsService()
val presenter = createRolesAndPermissionsPresenter(
analyticsService = analyticsService,
room = FakeJoinedRoom(
resetPowerLevelsResult = { Result.success(Unit) }
)
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RolesAndPermissionsEvents.ResetPermissions)
// Confirmation
awaitItem().eventSink(RolesAndPermissionsEvents.ResetPermissions)
assertThat(awaitItem().resetPermissionsAction).isEqualTo(AsyncAction.Loading)
assertThat(awaitItem().resetPermissionsAction).isEqualTo(AsyncAction.Success(Unit))
assertThat(analyticsService.capturedEvents.last()).isEqualTo(RoomModeration(RoomModeration.Action.ResetPermissions))
}
}
@Test
fun `present - ResetPermissions confirmation can be cancelled`() = runTest {
val presenter = createRolesAndPermissionsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RolesAndPermissionsEvents.ResetPermissions)
awaitItem().eventSink(RolesAndPermissionsEvents.CancelPendingAction)
assertThat(awaitItem().resetPermissionsAction).isEqualTo(AsyncAction.Uninitialized)
}
}
private fun TestScope.createRolesAndPermissionsPresenter(
room: FakeJoinedRoom = FakeJoinedRoom(),
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService()
): RolesAndPermissionsPresenter {
return RolesAndPermissionsPresenter(
room = room,
dispatchers = dispatchers,
analyticsService = analyticsService
)
}
}
@@ -0,0 +1,199 @@
/*
* 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.rolesandpermissions.impl.root
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.rolesandpermissions.impl.R
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledTimes
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.setSafeContent
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@RunWith(AndroidJUnit4::class)
class RolesAndPermissionsViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `click on back invokes expected callback`() {
ensureCalledOnce { callback ->
rule.setRolesAndPermissionsView(
goBack = callback,
)
rule.pressBack()
}
}
@Test
fun `tapping on Admins opens admin list`() {
ensureCalledOnce { callback ->
rule.setRolesAndPermissionsView(
aRolesAndPermissionsState(
roomSupportsOwners = false,
eventSink = EventsRecorder(expectEvents = false)
),
openAdminList = callback,
)
rule.clickOn(R.string.screen_room_roles_and_permissions_admins)
}
}
@Test
fun `tapping on Admins and Owners opens admin list`() {
ensureCalledOnce { callback ->
rule.setRolesAndPermissionsView(
aRolesAndPermissionsState(
roomSupportsOwners = true,
eventSink = EventsRecorder(expectEvents = false)
),
openAdminList = callback,
)
rule.clickOn(R.string.screen_room_roles_and_permissions_admins_and_owners)
}
}
@Test
fun `tapping on Moderators opens moderators list`() {
ensureCalledOnce { callback ->
rule.setRolesAndPermissionsView(
openModeratorList = callback,
)
rule.clickOn(R.string.screen_room_roles_and_permissions_moderators)
}
}
@Test
@Config(qualifiers = "h640dp")
fun `tapping permission item open the change permissions screen`() {
ensureCalledTimes(1) { callback ->
rule.setRolesAndPermissionsView(
openEditPermissions = callback,
)
rule.clickOn(R.string.screen_room_roles_and_permissions_permissions_header)
}
}
@Test
@Config(qualifiers = "h640dp")
fun `tapping on reset permissions triggers ResetPermissions event`() {
val recorder = EventsRecorder<RolesAndPermissionsEvents>()
rule.setRolesAndPermissionsView(
state = aRolesAndPermissionsState(
eventSink = recorder,
),
)
rule.clickOn(R.string.screen_room_roles_and_permissions_reset)
recorder.assertSingle(RolesAndPermissionsEvents.ResetPermissions)
}
@Test
fun `tapping on Reset in the reset permissions confirmation dialog triggers ResetPermissions event`() {
val recorder = EventsRecorder<RolesAndPermissionsEvents>()
rule.setRolesAndPermissionsView(
state = aRolesAndPermissionsState(
resetPermissionsAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOn(CommonStrings.action_reset)
recorder.assertSingle(RolesAndPermissionsEvents.ResetPermissions)
}
@Test
fun `tapping on Cancel in the reset permissions confirmation dialog triggers CancelPendingAction event`() {
val recorder = EventsRecorder<RolesAndPermissionsEvents>()
rule.setRolesAndPermissionsView(
state = aRolesAndPermissionsState(
resetPermissionsAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOn(CommonStrings.action_cancel)
recorder.assertSingle(RolesAndPermissionsEvents.CancelPendingAction)
}
@Test
fun `tapping on 'Demote to moderator' in the demote self bottom sheet triggers the right event`() {
val recorder = EventsRecorder<RolesAndPermissionsEvents>()
rule.setRolesAndPermissionsView(
state = aRolesAndPermissionsState(
changeOwnRoleAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator)
rule.mainClock.advanceTimeBy(1_000L)
recorder.assertSingle(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator))
}
@Test
fun `tapping on 'Demote to member' in the demote self bottom sheet triggers the right event`() = runTest {
val recorder = EventsRecorder<RolesAndPermissionsEvents>()
rule.setRolesAndPermissionsView(
state = aRolesAndPermissionsState(
changeOwnRoleAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_member)
rule.mainClock.advanceTimeBy(1_000L)
recorder.assertSingle(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.User))
}
@Test
fun `tapping on 'Cancel' in the demote self bottom sheet triggers the right event`() {
val recorder = EventsRecorder<RolesAndPermissionsEvents>()
rule.setRolesAndPermissionsView(
state = aRolesAndPermissionsState(
changeOwnRoleAction = AsyncAction.ConfirmingNoParams,
eventSink = recorder,
),
)
rule.clickOn(CommonStrings.action_cancel)
rule.mainClock.advanceTimeBy(1_000L)
recorder.assertSingle(RolesAndPermissionsEvents.CancelPendingAction)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRolesAndPermissionsView(
state: RolesAndPermissionsState = aRolesAndPermissionsState(
roomSupportsOwners = false,
eventSink = EventsRecorder(expectEvents = false),
),
goBack: () -> Unit = EnsureNeverCalled(),
openAdminList: () -> Unit = EnsureNeverCalled(),
openModeratorList: () -> Unit = EnsureNeverCalled(),
openEditPermissions: () -> Unit = EnsureNeverCalled(),
) {
setSafeContent {
RolesAndPermissionsView(
state = state,
rolesAndPermissionsNavigator = object : RolesAndPermissionsNavigator {
override fun onBackClick() = goBack()
override fun openAdminList() = openAdminList()
override fun openModeratorList() = openModeratorList()
override fun openEditPermissions() = openEditPermissions()
}
)
}
}