diff --git a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt index 1e9fe6a1c4..bd3cea0439 100644 --- a/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt +++ b/features/rolesandpermissions/api/src/main/kotlin/io/element/android/features/rolesandpermissions/api/RolesAndPermissionsEntryPoint.kt @@ -8,6 +8,19 @@ package io.element.android.features.rolesandpermissions.api -import io.element.android.libraries.architecture.SimpleFeatureEntryPoint +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint -fun interface RolesAndPermissionsEntryPoint : SimpleFeatureEntryPoint +fun interface RolesAndPermissionsEntryPoint : FeatureEntryPoint { + interface Callback : Plugin { + fun onDone() + } + + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node +} diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt index 2f281a596a..5f27365857 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/DefaultRolesAndPermissionsEntryPoint.kt @@ -17,7 +17,11 @@ import io.element.android.libraries.di.RoomScope @ContributesBinding(RoomScope::class) class DefaultRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext): Node { - return parentNode.createNode(buildContext) + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: RolesAndPermissionsEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt index 5966a4f0ed..4bd88071c3 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/RolesAndPermissionsFlowNode.kt @@ -14,7 +14,10 @@ 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.Lifecycle import androidx.lifecycle.coroutineScope +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -25,17 +28,24 @@ 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.api.RolesAndPermissionsEntryPoint 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.callback 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.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.api.room.powerlevels.canEditRolesAndPermissions +import io.element.android.libraries.matrix.api.room.powerlevels.permissionsFlow import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -44,6 +54,7 @@ import kotlinx.parcelize.Parcelize class RolesAndPermissionsFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val room: JoinedRoom, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -66,6 +77,7 @@ class RolesAndPermissionsFlowNode( data object ChangeRoomPermissions : NavTarget } + private val callback: RolesAndPermissionsEntryPoint.Callback = callback() private val asyncIndicatorState = AsyncIndicatorState() override fun onBuilt() { @@ -76,6 +88,15 @@ class RolesAndPermissionsFlowNode( onChangeComplete(changesSaved) } } + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + room.permissionsFlow(false) { perms -> perms.canEditRolesAndPermissions() } + .filter { canEdit -> !canEdit } + .first() + // If the user can no longer edit roles and permissions, exit the flow + callback.onDone() + } + } } private fun onChangeComplete(changesSaved: Boolean) { diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt index 9963a751f1..546d54c21a 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/analytics/AnalyticUtils.kt @@ -34,8 +34,8 @@ internal fun AnalyticsService.trackPermissionChangeAnalytics(initial: RoomPowerL 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.eventsDefault != initial?.eventsDefault) { + capture(RoomModeration(RoomModeration.Action.ChangePermissionsSendMessages, analyticsMemberRoleForPowerLevel(updated.eventsDefault))) } if (updated.redactEvents != initial?.redactEvents) { capture(RoomModeration(RoomModeration.Action.ChangePermissionsRedactMessages, analyticsMemberRoleForPowerLevel(updated.redactEvents))) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt index 99f5723bf9..d085be5920 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt @@ -36,8 +36,7 @@ class ChangeRoomPermissionsPresenter( ) : Presenter { companion object { private fun itemsForSection(section: RoomPermissionsSection) = when (section) { - RoomPermissionsSection.SpaceDetails, - RoomPermissionsSection.RoomDetails -> persistentListOf( + RoomPermissionsSection.EditDetails -> persistentListOf( RoomPermissionType.ROOM_NAME, RoomPermissionType.ROOM_AVATAR, RoomPermissionType.ROOM_TOPIC, @@ -46,19 +45,23 @@ class ChangeRoomPermissionsPresenter( RoomPermissionType.SEND_EVENTS, RoomPermissionType.REDACT_EVENTS, ) - RoomPermissionsSection.MembershipModeration -> persistentListOf( + RoomPermissionsSection.ManageMembers -> persistentListOf( RoomPermissionType.INVITE, RoomPermissionType.KICK, RoomPermissionType.BAN, ) + RoomPermissionsSection.ManageSpace -> persistentListOf( + RoomPermissionType.SPACE_MANAGE_ROOMS, + RoomPermissionType.CHANGE_SETTINGS, + ) } private fun RoomPermissionsSection.shouldShow(isSpace: Boolean): Boolean { return when (this) { - RoomPermissionsSection.RoomDetails -> !isSpace - RoomPermissionsSection.MembershipModeration -> true + RoomPermissionsSection.EditDetails -> true + RoomPermissionsSection.ManageMembers -> true RoomPermissionsSection.MessagesAndContent -> !isSpace - RoomPermissionsSection.SpaceDetails -> isSpace + RoomPermissionsSection.ManageSpace -> isSpace } } @@ -99,11 +102,13 @@ class ChangeRoomPermissionsPresenter( 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.SEND_EVENTS -> currentPermissions?.copy(eventsDefault = 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) + RoomPermissionType.SPACE_MANAGE_ROOMS -> currentPermissions?.copy(spaceChild = powerLevel) + RoomPermissionType.CHANGE_SETTINGS -> currentPermissions?.copy(stateDefault = powerLevel) } } is ChangeRoomPermissionsEvent.Save -> coroutineScope.save() diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt index 0dde54db89..88309ea023 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt @@ -32,11 +32,13 @@ data class ChangeRoomPermissionsState( 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.SEND_EVENTS -> RoomMember.Role.forPowerLevel(currentPermissions.eventsDefault) 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) + RoomPermissionType.SPACE_MANAGE_ROOMS -> RoomMember.Role.forPowerLevel(currentPermissions.spaceChild) + RoomPermissionType.CHANGE_SETTINGS -> RoomMember.Role.forPowerLevel(currentPermissions.stateDefault) } return when (role) { is RoomMember.Role.Owner, @@ -48,10 +50,10 @@ data class ChangeRoomPermissionsState( } enum class RoomPermissionsSection { - SpaceDetails, - RoomDetails, + ManageMembers, + EditDetails, MessagesAndContent, - MembershipModeration, + ManageSpace } enum class SelectableRole : DropdownOption { @@ -80,5 +82,7 @@ enum class RoomPermissionType { REDACT_EVENTS, ROOM_NAME, ROOM_AVATAR, - ROOM_TOPIC + ROOM_TOPIC, + SPACE_MANAGE_ROOMS, + CHANGE_SETTINGS, } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt index 6280e4fd8f..6c4e5aa379 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt @@ -26,6 +26,7 @@ class ChangeRoomPermissionsStateProvider : PreviewParameterProvider 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) + RoomPermissionsSection.EditDetails -> stringResource(R.string.screen_room_change_permissions_room_details) + RoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_change_permissions_messages_and_content) + RoomPermissionsSection.ManageMembers -> stringResource(R.string.screen_room_change_permissions_member_moderation) + RoomPermissionsSection.ManageSpace -> stringResource(R.string.screen_room_change_permissions_manage_space) } @Composable @@ -126,6 +126,8 @@ private fun titleForType(type: RoomPermissionType): String = when (type) { 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) + RoomPermissionType.SPACE_MANAGE_ROOMS -> stringResource(R.string.screen_room_change_permissions_manage_space_rooms) + RoomPermissionType.CHANGE_SETTINGS -> stringResource(R.string.screen_room_change_permissions_change_settings) } @PreviewsDayNight diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt index 4469eb8f37..da69ee52a9 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsNode.kt @@ -11,7 +11,6 @@ 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 @@ -20,14 +19,6 @@ 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 @@ -35,7 +26,6 @@ class RolesAndPermissionsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val presenter: RolesAndPermissionsPresenter, - private val room: BaseRoom, ) : Node(buildContext, plugins = plugins), RolesAndPermissionsNavigator { interface Callback : Plugin, RolesAndPermissionsNavigator { override fun openAdminList() @@ -54,22 +44,6 @@ class RolesAndPermissionsNode( } } - 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() diff --git a/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml index 3df8e0afbb..87936de3a7 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml @@ -2,9 +2,12 @@ "Správce" "Vykázat lidi" + "Změnit nastavení" "Odstranit zprávy" "Člen" "Pozvat přátele" + "Správa prostoru" + "Spravovat místnosti" "Spravovat členy" "Zprávy a obsah" "Moderátor" @@ -14,6 +17,7 @@ "Změnit název místnosti" "Změnit téma místnosti" "Odeslat zprávy" + "Oprávnění" "Upravit správce" "Tuto akci nebudete moci vrátit zpět. Upravujete oprávnění uživatele, tak aby měl stejnou úroveň jako vy." "Přidat správce?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml index 582109c717..4a2754c1e0 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml @@ -2,9 +2,12 @@ "Peakasutajad" "Suhtluskeelu seadmine" + "Muuda seadistusi" "Eemalda sõnumid" "Liikmed" "Osalejate kutsumine" + "Halda kogukonda" + "Halda jututuba" "Liikmete haldus" "Sõnumid ja sisu" "Moderaatorid" @@ -14,6 +17,7 @@ "Jututoa nime muutmine" "Jututoa teema muutmine" "Sõnumite saatmine" + "Õigused" "Muuda peakasutajaid" "Kuna sa annad teisele kasutajale sinu õigustega võrreldes samad õigused, siis sa ei saa seda muudatust hiljem tagasi pöörata." "Lisame peakasutaja?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml index 1322089dc8..29331bcd13 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml @@ -2,9 +2,12 @@ "Ylläpitäjä" "Porttikieltojen antaminen" + "Asetusten muuttaminen" "Viestien poistaminen" "Jäsen" "Ihmisten kutsuminen ja liittymispyyntöjen hyväksyminen" + "Tilan hallitseminen" + "Huoneiden hallitseminen" "Jäsenien hallinta" "Viestit ja sisältö" "Valvoja" @@ -14,6 +17,7 @@ "Huoneen nimen vaihtaminen" "Huoneen aiheen vaihtaminen" "Viestien lähettäminen" + "Oikeudet" "Muokkaa ylläpitäjiä" "Et voi peruuttaa tätä toimenpidettä. Ylennät käyttäjän samalle oikeustasolle kuin sinä." "Lisätäänkö ylläpitäjä?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-fr/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fr/translations.xml index f44072366d..874b6710cd 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-fr/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-fr/translations.xml @@ -2,9 +2,12 @@ "Administrateurs" "Bannir des participants" + "Changer les paramètres" "Supprimer des messages" "Membre" "Inviter des personnes" + "Gérer l’espace" + "Gérer les salons" "Gérer les membres" "Messages et contenus" "Modérateurs" @@ -14,6 +17,7 @@ "Changer le nom du salon" "Changer le sujet du salon" "Envoyer des messages" + "Autorisations" "Modifier les administrateurs" "Vous ne pourrez pas annuler cette action. Vous êtes en train de promouvoir l’utilisateur pour qu’il ait le même niveau que vous." "Ajouter un administrateur ?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml index 0f80fbb227..4160039a0a 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml @@ -2,9 +2,12 @@ "Adminisztrátor" "Emberek kitiltása" + "Beállítások módosítása" "Üzenetek eltávolítása" "Tag" "Emberek meghívása" + "Tér kezelése" + "Szobák kezelése" "Tagok kezelése" "Üzenetek és tartalom" "Moderátor" @@ -14,6 +17,7 @@ "Szoba nevének módosítása" "Szoba témájának módosítása" "Üzenetek küldése" + "Jogosultságok" "Adminisztrátorok szerkesztése" "Ezt a műveletet nem fogja tudja visszavonni. Ugyanarra a szintre lépteti elő a felhasználót, mint amellyel Ön is rendelkezik." "Adminisztrátor hozzáadása?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml index 2b439a6c61..b1dea12151 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml @@ -2,9 +2,12 @@ "Amministratore" "Escludi membri" + "Modifica impostazioni" "Rimuovi messaggi" "Membro" "Invita persone" + "Gestire lo spazio" + "Gestisci le stanze" "Gestisci membri" "Messaggi e contenuti" "Moderatore" @@ -14,6 +17,7 @@ "Cambia il nome della stanza" "Cambiare l\'argomento della stanza" "Inviare messaggi" + "Autorizzazioni" "Modifica amministratori" "Non potrai annullare questa azione. Stai promuovendo l\'utente al tuo stesso livello di potere." "Aggiungi amministratore?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-pt-rBR/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-pt-rBR/translations.xml index 886d3c541d..43fa8d83f2 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-pt-rBR/translations.xml @@ -2,9 +2,12 @@ "Administradores" "Banir pessoas" + "Alterar configurações" "Remover mensagens" "Membro" "Convidar pessoas" + "Gerenciar espaço" + "Gerenciar salas" "Gerenciar membros" "Mensagens e conteúdo" "Moderador" @@ -14,6 +17,7 @@ "Alterar nome da sala" "Alterar tópico da sala" "Enviar mensagens" + "Permissões" "Editar administradores" "Você não poderá desfazer essa ação. Você está promovendo o usuário a ter o mesmo nível de poder que você." "Adicionar administrador?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml index f54b984ea3..d922e58550 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml @@ -2,9 +2,12 @@ "Только администраторы" "Блокировать людей могут" + "Изменить настройки" "Удалить сообщения" "Участник" "Пригласить людей" + "Управление пространством" + "Управление комнатами" "Список участников" "Сообщения и содержание" "Модератор" @@ -14,6 +17,7 @@ "Менять название комнаты могут" "Менять тему комнаты могут" "Отправлять сообщения могут" + "Разрешения" "Редактировать роль администраторов" "Вы не сможете отменить это действие. Вы устанавливаете уровень пользователю соответствующий вашему." "Добавить администратора?" diff --git a/features/rolesandpermissions/impl/src/main/res/values-zh-rTW/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-zh-rTW/translations.xml index 50555bf9ed..7bc662e2fc 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-zh-rTW/translations.xml @@ -2,9 +2,12 @@ "管理員" "管理黑名單" + "變更設定" "移除訊息" "成員" "邀請夥伴" + "管理空間" + "管理聊天室" "管理成員" "訊息與內容" "版主" @@ -14,6 +17,7 @@ "變更聊天室名稱" "變更聊天室主題" "傳送訊息" + "權限" "編輯管理員" "您將無法復原此動作。您正將使用者提昇至與您相同的權力等級。" "要新增管理員嗎?" diff --git a/features/rolesandpermissions/impl/src/main/res/values/localazy.xml b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml index 70efa48e5a..e5ab3f1cd7 100644 --- a/features/rolesandpermissions/impl/src/main/res/values/localazy.xml +++ b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml @@ -2,9 +2,12 @@ "Admin" "Ban people" + "Change settings" "Remove messages" "Member" "Invite people" + "Manage space" + "Manage rooms" "Manage members" "Messages and content" "Moderator" @@ -14,6 +17,7 @@ "Change name" "Change topic" "Send messages" + "Permissions" "Edit Admins" "You will not be able to undo this action. You are promoting the user to have the same power level as you." "Add Admin?" diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt index d666ec3ccc..9d94c4831e 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt @@ -53,7 +53,7 @@ class ChangeRoomPermissionsPresenterTest { presenter.present() }.test { val itemsBySection = awaitUpdatedItem().itemsBySection - assertThat(itemsBySection[RoomPermissionsSection.RoomDetails]).containsExactly( + assertThat(itemsBySection[RoomPermissionsSection.EditDetails]).containsExactly( RoomPermissionType.ROOM_NAME, RoomPermissionType.ROOM_AVATAR, RoomPermissionType.ROOM_TOPIC, @@ -62,7 +62,7 @@ class ChangeRoomPermissionsPresenterTest { RoomPermissionType.SEND_EVENTS, RoomPermissionType.REDACT_EVENTS, ) - assertThat(itemsBySection[RoomPermissionsSection.MembershipModeration]).containsExactly( + assertThat(itemsBySection[RoomPermissionsSection.ManageMembers]).containsExactly( RoomPermissionType.INVITE, RoomPermissionType.KICK, RoomPermissionType.BAN, @@ -77,13 +77,13 @@ class ChangeRoomPermissionsPresenterTest { presenter.present() }.test { val state = awaitUpdatedItem() - assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel) + assertThat(state.currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) assertThat(state.hasChanges).isFalse() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Admin)) awaitItem().run { - assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) + assertThat(currentPermissions?.roomName).isEqualTo(Admin.powerLevel) assertThat(hasChanges).isTrue() } } @@ -115,8 +115,9 @@ class ChangeRoomPermissionsPresenterTest { invite = Moderator.powerLevel, kick = Moderator.powerLevel, ban = Moderator.powerLevel, + stateDefault = Moderator.powerLevel, redactEvents = Moderator.powerLevel, - sendEvents = Moderator.powerLevel, + eventsDefault = Moderator.powerLevel, roomName = Moderator.powerLevel, roomAvatar = Moderator.powerLevel, roomTopic = Moderator.powerLevel, @@ -141,14 +142,14 @@ class ChangeRoomPermissionsPresenterTest { presenter.present() }.test { val state = awaitUpdatedItem() - assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel) + assertThat(state.currentPermissions?.roomName).isEqualTo(Moderator.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.ROOM_NAME, SelectableRole.Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_AVATAR, SelectableRole.Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_TOPIC, SelectableRole.Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.SEND_EVENTS, SelectableRole.Admin)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.REDACT_EVENTS, SelectableRole.Admin)) state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.KICK, SelectableRole.Admin)) state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.BAN, SelectableRole.Admin)) state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.INVITE, SelectableRole.Admin)) @@ -160,16 +161,16 @@ class ChangeRoomPermissionsPresenterTest { assertThat(awaitItem().saveAction).isEqualTo(AsyncAction.Loading) assertThat(awaitItem().hasChanges).isFalse() awaitItem().run { - assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) + assertThat(currentPermissions?.roomName).isEqualTo(Admin.powerLevel) assertThat(saveAction).isEqualTo(AsyncAction.Success(true)) } 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.ChangePermissionsRoomName, RoomModeration.Role.Administrator), + RoomModeration(RoomModeration.Action.ChangePermissionsRoomAvatar, RoomModeration.Role.Administrator), + RoomModeration(RoomModeration.Action.ChangePermissionsRoomTopic, RoomModeration.Role.Administrator), + RoomModeration(RoomModeration.Action.ChangePermissionsSendMessages, RoomModeration.Role.Administrator), + RoomModeration(RoomModeration.Action.ChangePermissionsRedactMessages, RoomModeration.Role.Administrator), RoomModeration(RoomModeration.Action.ChangePermissionsKickMembers, RoomModeration.Role.Administrator), RoomModeration(RoomModeration.Action.ChangePermissionsBanMembers, RoomModeration.Role.Administrator), RoomModeration(RoomModeration.Action.ChangePermissionsInviteUsers, RoomModeration.Role.Administrator), @@ -206,17 +207,17 @@ class ChangeRoomPermissionsPresenterTest { presenter.present() }.test { val state = awaitUpdatedItem() - assertThat(state.currentPermissions?.roomName).isEqualTo(Admin.powerLevel) + assertThat(state.currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) assertThat(state.hasChanges).isFalse() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Admin)) assertThat(awaitItem().hasChanges).isTrue() state.eventSink(ChangeRoomPermissionsEvent.Save) assertThat(awaitItem().saveAction).isEqualTo(AsyncAction.Loading) awaitItem().run { - assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) + assertThat(currentPermissions?.roomName).isEqualTo(Admin.powerLevel) // Couldn't save the changes, so they're still pending assertThat(hasChanges).isTrue() assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java) @@ -224,7 +225,7 @@ class ChangeRoomPermissionsPresenterTest { state.eventSink(ChangeRoomPermissionsEvent.ResetPendingActions) awaitItem().run { - assertThat(currentPermissions?.roomName).isEqualTo(Moderator.powerLevel) + assertThat(currentPermissions?.roomName).isEqualTo(Admin.powerLevel) assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized) assertThat(hasChanges).isTrue() } @@ -238,7 +239,7 @@ class ChangeRoomPermissionsPresenterTest { presenter.present() }.test { val state = awaitUpdatedItem() - state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Moderator)) + state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Admin)) assertThat(awaitItem().hasChanges).isTrue() state.eventSink(ChangeRoomPermissionsEvent.Exit) diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt index 6b637efbed..f28c9c150f 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt @@ -104,7 +104,7 @@ class ChangeRoomPermissionsViewTest { state = aChangeRoomPermissionsState( itemsBySection = persistentMapOf( // Makes sure there is only one item to click on - RoomPermissionsSection.RoomDetails to persistentListOf(RoomPermissionType.ROOM_NAME) + RoomPermissionsSection.EditDetails to persistentListOf(RoomPermissionType.ROOM_NAME) ), eventSink = recorder, ) diff --git a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt index 01f2787778..62cf3285f3 100644 --- a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt @@ -14,7 +14,7 @@ import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEn import io.element.android.tests.testutils.lambda.lambdaError class FakeRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext): Node { + override fun createNode(parentNode: Node, buildContext: BuildContext, callback: RolesAndPermissionsEntryPoint.Callback): Node { lambdaError() } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 9ea060ef6c..03adbded1b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -349,7 +349,16 @@ class RoomDetailsFlowNode( } is NavTarget.AdminSettings -> { - rolesAndPermissionsEntryPoint.createNode(this, buildContext) + val callback = object : RolesAndPermissionsEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + } + rolesAndPermissionsEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } NavTarget.PinnedMessagesList -> { val params = MessagesEntryPoint.Params( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index 83840ad604..07b76d41b9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import dev.zacsweers.metro.Inject import im.vector.app.features.analytics.plan.Interaction +import io.element.android.features.knockrequests.api.KnockRequestPermissions import io.element.android.features.knockrequests.api.knockRequestPermissions import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState @@ -26,6 +27,7 @@ import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.features.roomdetailsedit.api.RoomDetailsEditPermissions import io.element.android.features.roomdetailsedit.api.roomDetailsEditPermissions +import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyPermissions import io.element.android.features.securityandprivacy.api.securityAndPrivacyPermissions import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.architecture.Presenter @@ -119,7 +121,10 @@ class RoomDetailsPresenter( room.knockRequestsFlow.collect { value = it.size } } val canShowKnockRequests by remember { - derivedStateOf { isKnockRequestsEnabled && permissions.canManageKnockRequests && joinRule == JoinRule.Knock } + derivedStateOf { isKnockRequestsEnabled && permissions.knockRequestsPermissions.hasAny && joinRule == JoinRule.Knock } + } + val canShowSecurityAndPrivacy by remember { + derivedStateOf { !isDm && permissions.securityAndPrivacyPermissions.hasAny(isSpace = false, joinRule = joinRule) } } val isDeveloperModeEnabled by remember { appPreferencesStore.isDeveloperModeEnabledFlow() @@ -186,7 +191,7 @@ class RoomDetailsPresenter( snackbarMessage = snackbarMessage, canShowKnockRequests = canShowKnockRequests, knockRequestsCount = knockRequestsCount, - canShowSecurityAndPrivacy = !isDm && permissions.canEditSecurityAndPrivacy, + canShowSecurityAndPrivacy = canShowSecurityAndPrivacy, hasMemberVerificationViolations = hasMemberVerificationViolations, canReportRoom = canReportRoom, isTombstoned = roomInfo.successorRoom != null, @@ -221,9 +226,9 @@ class RoomDetailsPresenter( private data class Permissions( val canInvite: Boolean = false, val editDetailsPermissions: RoomDetailsEditPermissions = RoomDetailsEditPermissions.DEFAULT, - val canManageKnockRequests: Boolean = false, + val knockRequestsPermissions: KnockRequestPermissions = KnockRequestPermissions.DEFAULT, + val securityAndPrivacyPermissions: SecurityAndPrivacyPermissions = SecurityAndPrivacyPermissions.DEFAULT, val canEditRolesAndPermissions: Boolean = false, - val canEditSecurityAndPrivacy: Boolean = false, ) @Composable @@ -232,9 +237,9 @@ class RoomDetailsPresenter( Permissions( canInvite = perms.canOwnUserInvite(), editDetailsPermissions = perms.roomDetailsEditPermissions(), - canManageKnockRequests = perms.knockRequestPermissions().hasAny, + knockRequestsPermissions = perms.knockRequestPermissions(), canEditRolesAndPermissions = perms.canEditRolesAndPermissions(), - canEditSecurityAndPrivacy = perms.securityAndPrivacyPermissions().hasAny, + securityAndPrivacyPermissions = perms.securityAndPrivacyPermissions(), ) } } diff --git a/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt b/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt index 268b30cb31..aedb9bd16b 100644 --- a/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt +++ b/features/roomdetailsedit/impl/src/main/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditView.kt @@ -110,6 +110,7 @@ fun RoomDetailsEditView( } else { AvatarType.Room() }, + enabled = state.canChangeAvatar, onAvatarClick = ::onAvatarClick, modifier = Modifier.fillMaxWidth(), ) diff --git a/features/securityandprivacy/api/src/main/kotlin/io/element/android/features/securityandprivacy/api/SecurityAndPrivacyPermissions.kt b/features/securityandprivacy/api/src/main/kotlin/io/element/android/features/securityandprivacy/api/SecurityAndPrivacyPermissions.kt index bacd863ca6..3601f2c4e6 100644 --- a/features/securityandprivacy/api/src/main/kotlin/io/element/android/features/securityandprivacy/api/SecurityAndPrivacyPermissions.kt +++ b/features/securityandprivacy/api/src/main/kotlin/io/element/android/features/securityandprivacy/api/SecurityAndPrivacyPermissions.kt @@ -9,6 +9,7 @@ package io.element.android.features.securityandprivacy.api import io.element.android.libraries.matrix.api.room.StateEventType +import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions data class SecurityAndPrivacyPermissions( @@ -17,10 +18,19 @@ data class SecurityAndPrivacyPermissions( val canChangeEncryption: Boolean, val canChangeRoomVisibility: Boolean, ) { - val hasAny = canChangeRoomAccess || - canChangeHistoryVisibility || - canChangeEncryption || - canChangeRoomVisibility + fun hasAny(isSpace: Boolean, joinRule: JoinRule?): Boolean { + val canChangeRoomVisibility = when (joinRule) { + is JoinRule.Public, + is JoinRule.Knock, + is JoinRule.KnockRestricted -> canChangeRoomVisibility + else -> false + } + return if (isSpace) { + canChangeRoomAccess || canChangeRoomVisibility + } else { + canChangeRoomAccess || canChangeRoomVisibility || canChangeHistoryVisibility || canChangeEncryption + } + } companion object { val DEFAULT = SecurityAndPrivacyPermissions( diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt index 13c5b454a2..3d62e7b5d7 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt @@ -11,6 +11,9 @@ package io.element.android.features.securityandprivacy.impl import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -19,6 +22,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint +import io.element.android.features.securityandprivacy.api.securityAndPrivacyPermissions import io.element.android.features.securityandprivacy.impl.editroomaddress.EditRoomAddressNode import io.element.android.features.securityandprivacy.impl.root.SecurityAndPrivacyNode import io.element.android.libraries.architecture.BackstackView @@ -26,6 +30,12 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.api.room.powerlevels.use +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) @@ -33,6 +43,7 @@ import kotlinx.parcelize.Parcelize class SecurityAndPrivacyFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val room: JoinedRoom, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.SecurityAndPrivacy, @@ -52,6 +63,24 @@ class SecurityAndPrivacyFlowNode( private val callback: SecurityAndPrivacyEntryPoint.Callback = callback() private val navigator = BackstackSecurityAndPrivacyNavigator(callback, backstack) + override fun onBuilt() { + super.onBuilt() + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + room.roomInfoFlow + .map { roomInfo -> + room.roomPermissions().use(false) { perms -> + perms.securityAndPrivacyPermissions().hasAny(roomInfo.isSpace, roomInfo.joinRule) + } + } + .filter { canEdit -> !canEdit } + .first() + // If the user can no longer edit security and privacy, exit the flow + callback.onDone() + } + } + } + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.SecurityAndPrivacy -> { diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt index 81bfe22697..472f71ddfb 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt @@ -182,9 +182,20 @@ class SecurityAndPrivacyPresenter( eventSink = ::handleEvent, ) - // If the history visibility is not available for the current access, use the fallback. - LaunchedEffect(state.availableHistoryVisibilities) { - if (editedSettings.historyVisibility !in state.availableHistoryVisibilities) { + // Revert changes that the user is not allowed to make anymore + LaunchedEffect(permissions, state.editedSettings.roomAccess) { + if (!state.showRoomAccessSection) { + editedRoomAccess = savedSettings.roomAccess + } + if (!state.showEncryptionSection) { + editedIsEncrypted = savedSettings.isEncrypted + } + if (!state.showRoomVisibilitySections) { + editedVisibleInRoomDirectory = savedSettings.isVisibleInRoomDirectory + } + if (!state.showHistoryVisibilitySection) { + editedHistoryVisibility = savedSettings.historyVisibility + } else if (editedSettings.historyVisibility !in state.availableHistoryVisibilities) { editedHistoryVisibility = editedSettings.historyVisibility.fallback() } } diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyPresenterTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyPresenterTest.kt index a4542b02a1..5f3d14958c 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyPresenterTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyPresenterTest.kt @@ -244,7 +244,7 @@ class SecurityAndPrivacyPresenterTest { navigator = navigator, ) presenter.test { - skipItems(2) + skipItems(1) with(awaitItem()) { assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.InviteOnly) eventSink(SecurityAndPrivacyEvent.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.Anyone)) @@ -312,7 +312,7 @@ class SecurityAndPrivacyPresenterTest { ) val presenter = createSecurityAndPrivacyPresenter(room = room) presenter.test { - skipItems(2) + skipItems(1) with(awaitItem()) { assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.InviteOnly) eventSink(SecurityAndPrivacyEvent.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.Anyone)) diff --git a/features/space/impl/build.gradle.kts b/features/space/impl/build.gradle.kts index 4ee6822bc5..03c8cda4ac 100644 --- a/features/space/impl/build.gradle.kts +++ b/features/space/impl/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(projects.libraries.previewutils) implementation(projects.features.securityandprivacy.api) implementation(projects.features.rolesandpermissions.api) + implementation(projects.features.roomdetailsedit.api) api(projects.features.space.api) testCommonDependencies(libs, true) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index 5fe646aaeb..4c91da0301 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -65,7 +65,7 @@ class SpaceFlowNode( data object Root : NavTarget @Parcelize - data object Settings : NavTarget + data class Settings(val initialTarget: SpaceSettingsFlowNode.NavTarget = SpaceSettingsFlowNode.NavTarget.Root) : NavTarget @Parcelize data object Leave : NavTarget @@ -89,7 +89,7 @@ class SpaceFlowNode( } override fun navigateToRolesAndPermissions() { - // TODO + backstack.push(NavTarget.Settings(SpaceSettingsFlowNode.NavTarget.RolesAndPermissions)) } } createNode(buildContext, listOf(callback)) @@ -101,7 +101,7 @@ class SpaceFlowNode( } override fun navigateToSpaceSettings() { - backstack.push(NavTarget.Settings) + backstack.push(NavTarget.Settings()) } override fun navigateToRoomMemberList() { @@ -114,8 +114,10 @@ class SpaceFlowNode( } createNode(buildContext, listOf(callback)) } - NavTarget.Settings -> { + is NavTarget.Settings -> { val callback = object : SpaceSettingsFlowNode.Callback { + override fun initialTarget() = navTarget.initialTarget + override fun navigateToSpaceMembers() { callback.navigateToRoomMemberList() } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt index 02598f25ed..d405162b88 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpaceView.kt @@ -132,8 +132,7 @@ fun LeaveSpaceView( state.eventSink(LeaveSpaceEvents.LeaveSpace) }, onCancel = onCancel, - // TODO enable when navigation is ready - showRolesAndPermissionsButton = false, // state.isLastAdmin, + showRolesAndPermissionsButton = state.isLastAdmin, onRolesAndPermissionsClick = onRolesAndPermissionsClick, ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt index 3d36de0cea..309747d2c9 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -11,17 +11,20 @@ package io.element.android.features.space.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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 androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject -import im.vector.app.features.analytics.plan.JoinedRoom +import im.vector.app.features.analytics.plan.JoinedRoom.Trigger import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.toInviteData +import io.element.android.features.space.impl.settings.SpaceSettingsPermissions +import io.element.android.features.space.impl.settings.spaceSettingsPermissions import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.mapState @@ -31,8 +34,10 @@ import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias +import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.join.JoinRoom +import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoomList import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar @@ -50,6 +55,7 @@ import kotlin.jvm.optionals.getOrNull @Inject class SpacePresenter( private val spaceRoomList: SpaceRoomList, + private val room: BaseRoom, private val client: MatrixClient, private val seenInvitesStore: SeenInvitesStore, private val joinRoom: JoinRoom, @@ -82,10 +88,17 @@ class SpacePresenter( } }.collectAsState() + val permissions by room.permissionsAsState(SpaceSettingsPermissions.DEFAULT) { perms -> + perms.spaceSettingsPermissions() + } val isSpaceSettingsEnabled by remember { featureFlagService.isFeatureEnabledFlow(FeatureFlags.SpaceSettings) }.collectAsState(false) + val roomInfo by room.roomInfoFlow.collectAsState() + val canAccessSpaceSettings by remember { + derivedStateOf { isSpaceSettingsEnabled && permissions.hasAny(roomInfo.joinRule) } + } val currentSpace by spaceRoomList.currentSpaceFlow.collectAsState() val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap>()) } @@ -136,7 +149,7 @@ class SpacePresenter( joinActions = joinActions.toImmutableMap(), acceptDeclineInviteState = acceptDeclineInviteState, topicViewerState = topicViewerState, - canAccessSpaceSettings = isSpaceSettingsEnabled, + canAccessSpaceSettings = canAccessSpaceSettings, eventSink = ::handleEvent, ) } @@ -150,7 +163,7 @@ class SpacePresenter( joinRoom.invoke( roomIdOrAlias = spaceRoom.roomId.toRoomIdOrAlias(), serverNames = spaceRoom.via, - trigger = JoinedRoom.Trigger.SpaceHierarchy, + trigger = Trigger.SpaceHierarchy, ).onFailure { setJoinActions(joinActions + mapOf(spaceRoom.roomId to AsyncAction.Failure(it))) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsFlowNode.kt index d299a43791..8fc7f51aa7 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsFlowNode.kt @@ -20,6 +20,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEntryPoint +import io.element.android.features.roomdetailsedit.api.RoomDetailsEditEntryPoint import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint import io.element.android.features.space.impl.di.SpaceFlowScope import io.element.android.libraries.architecture.BackstackView @@ -35,15 +36,17 @@ class SpaceSettingsFlowNode( @Assisted plugins: List, private val securityAndPrivacyEntryPoint: SecurityAndPrivacyEntryPoint, private val rolesAndPermissionsEntryPoint: RolesAndPermissionsEntryPoint, + private val roomDetailsEditEntryPoint: RoomDetailsEditEntryPoint ) : BaseFlowNode( backstack = BackStack( - initialElement = NavTarget.Root, + initialElement = initialElement(plugins), savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, plugins = plugins, ) { interface Callback : Plugin { + fun initialTarget(): NavTarget = NavTarget.Root fun navigateToSpaceMembers() fun startLeaveSpaceFlow() fun closeSettings() @@ -53,6 +56,9 @@ class SpaceSettingsFlowNode( @Parcelize data object Root : NavTarget + @Parcelize + data object EditDetails : NavTarget + @Parcelize data object SecurityAndPrivacy : NavTarget @@ -71,7 +77,7 @@ class SpaceSettingsFlowNode( } override fun navigateToEditDetails() { - // TODO + backstack.push(NavTarget.EditDetails) } override fun navigateToSpaceMembers() { @@ -108,9 +114,21 @@ class SpaceSettingsFlowNode( ) } is NavTarget.RolesAndPermissions -> { + val callback = object : RolesAndPermissionsEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + } rolesAndPermissionsEntryPoint.createNode( parentNode = this, buildContext = buildContext, + callback = callback, + ) + } + NavTarget.EditDetails -> { + roomDetailsEditEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, ) } } @@ -121,3 +139,7 @@ class SpaceSettingsFlowNode( BackstackView(modifier) } } + +fun initialElement(plugins: List): SpaceSettingsFlowNode.NavTarget { + return plugins.callback().initialTarget() +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPermissions.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPermissions.kt new file mode 100644 index 0000000000..e3ec70a51d --- /dev/null +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPermissions.kt @@ -0,0 +1,44 @@ +/* + * 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.space.impl.settings + +import io.element.android.features.roomdetailsedit.api.RoomDetailsEditPermissions +import io.element.android.features.roomdetailsedit.api.roomDetailsEditPermissions +import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyPermissions +import io.element.android.features.securityandprivacy.api.securityAndPrivacyPermissions +import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions +import io.element.android.libraries.matrix.api.room.powerlevels.canEditRolesAndPermissions + +data class SpaceSettingsPermissions( + val editDetailsPermissions: RoomDetailsEditPermissions, + val canEditRolesAndPermissions: Boolean, + val securityAndPrivacyPermissions: SecurityAndPrivacyPermissions, +) { + fun hasAny(joinRule: JoinRule?): Boolean { + return editDetailsPermissions.hasAny || + canEditRolesAndPermissions || + securityAndPrivacyPermissions.hasAny(isSpace = true, joinRule = joinRule) + } + + companion object { + val DEFAULT = SpaceSettingsPermissions( + editDetailsPermissions = RoomDetailsEditPermissions.DEFAULT, + canEditRolesAndPermissions = false, + securityAndPrivacyPermissions = SecurityAndPrivacyPermissions.DEFAULT, + ) + } +} + +fun RoomPermissions.spaceSettingsPermissions(): SpaceSettingsPermissions { + return SpaceSettingsPermissions( + editDetailsPermissions = roomDetailsEditPermissions(), + canEditRolesAndPermissions = canEditRolesAndPermissions(), + securityAndPrivacyPermissions = securityAndPrivacyPermissions(), + ) +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt index 5238914c56..565008a778 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsPresenter.kt @@ -10,11 +10,13 @@ package io.element.android.features.space.impl.settings import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin +import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState @Inject class SpaceSettingsPresenter( @@ -23,15 +25,22 @@ class SpaceSettingsPresenter( @Composable override fun present(): SpaceSettingsState { val roomInfo by room.roomInfoFlow.collectAsState() - val isUserAdmin = room.isOwnUserAdmin() + val permissions by room.permissionsAsState(SpaceSettingsPermissions.DEFAULT) { perms -> + perms.spaceSettingsPermissions() + } + val showSecurityAndPrivacy by remember { + derivedStateOf { permissions.securityAndPrivacyPermissions.hasAny(isSpace = false, joinRule = roomInfo.joinRule) } + } + return SpaceSettingsState( roomId = room.roomId, name = roomInfo.name.orEmpty(), canonicalAlias = roomInfo.canonicalAlias, avatarUrl = roomInfo.avatarUrl, memberCount = roomInfo.activeMembersCount, - showRolesAndPermissions = isUserAdmin, - showSecurityAndPrivacy = isUserAdmin, + canEditDetails = permissions.editDetailsPermissions.hasAny, + showRolesAndPermissions = permissions.canEditRolesAndPermissions, + showSecurityAndPrivacy = showSecurityAndPrivacy, eventSink = {}, ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt index 40ceadc112..5c7e9b5c6e 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsState.kt @@ -17,6 +17,7 @@ data class SpaceSettingsState( val canonicalAlias: RoomAlias?, val avatarUrl: String?, val memberCount: Long, + val canEditDetails: Boolean, val showRolesAndPermissions: Boolean, val showSecurityAndPrivacy: Boolean, val eventSink: (SpaceSettingsEvents) -> Unit diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt index 2abe7efebe..2030b6885a 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt @@ -30,6 +30,7 @@ fun aSpaceSettingsState( memberCount: Long = 100, showRolesAndPermissions: Boolean = false, showSecurityAndPrivacy: Boolean = false, + canEditDetails: Boolean = false, eventSink: (SpaceSettingsEvents) -> Unit = {}, ) = SpaceSettingsState( roomId = roomId, @@ -37,6 +38,7 @@ fun aSpaceSettingsState( canonicalAlias = alias, avatarUrl = avatarUrl, memberCount = memberCount, + canEditDetails = canEditDetails, showRolesAndPermissions = showRolesAndPermissions, showSecurityAndPrivacy = showSecurityAndPrivacy, eventSink = eventSink, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt index 379d75f1dd..5a8aac4a30 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsView.kt @@ -73,6 +73,7 @@ fun SpaceSettingsView( name = state.name, avatarUrl = state.avatarUrl, canonicalAlias = state.canonicalAlias?.value, + canEditDetails = state.canEditDetails, onSpaceInfoClick = onSpaceInfoClick, ) Section(isVisible = state.showSecurityAndPrivacy, content = { @@ -101,12 +102,13 @@ private fun SpaceInfoSection( name: String, avatarUrl: String?, canonicalAlias: String?, + canEditDetails: Boolean, onSpaceInfoClick: () -> Unit, ) { Row( modifier = Modifier .fillMaxWidth() - .clickable(onClick = onSpaceInfoClick) + .clickable(enabled = canEditDetails, onClick = onSpaceInfoClick) .padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt index 9b6df0d258..917aceb262 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias +import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoomList @@ -31,7 +32,9 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom +import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList import io.element.android.libraries.previewutils.room.aSpaceRoom import io.element.android.tests.testutils.EventsRecorder @@ -71,8 +74,25 @@ class SpacePresenterTest { } @Test - fun `present - canAccessSpaceSettings when space settings ff is enabled`() = runTest { + fun `present - canAccessSpaceSettings false when space settings ff is enabled but no permissions`() = runTest { val presenter = createSpacePresenter(spaceSettingsEnabled = true) + presenter.test { + val state = awaitItem() + assertThat(state.canAccessSpaceSettings).isFalse() + } + } + + @Test + fun `present - canAccessSpaceSettings true when space settings ff is enabled and has permissions`() = runTest { + val room = FakeBaseRoom( + roomPermissions = FakeRoomPermissions( + canSendState = { true } + ) + ) + val presenter = createSpacePresenter( + room = room, + spaceSettingsEnabled = true, + ) presenter.test { skipItems(1) val state = awaitItem() @@ -335,7 +355,10 @@ class SpacePresenterTest { private fun TestScope.createSpacePresenter( client: MatrixClient = FakeMatrixClient(), - spaceRoomList: SpaceRoomList = FakeSpaceRoomList(), + room: BaseRoom = FakeBaseRoom(), + spaceRoomList: SpaceRoomList = FakeSpaceRoomList( + paginateResult = { Result.success(Unit) } + ), seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(), joinRoom: JoinRoom = FakeJoinRoom( lambda = { _, _, _ -> Result.success(Unit) }, @@ -345,6 +368,7 @@ class SpacePresenterTest { ): SpacePresenter { return SpacePresenter( client = client, + room = room, spaceRoomList = spaceRoomList, seenInvitesStore = seenInvitesStore, joinRoom = joinRoom, diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt index 4a35f99db4..e949dcaf63 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt @@ -10,8 +10,11 @@ package io.element.android.libraries.architecture import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins inline fun Node.callback(): I { - return requireNotNull(plugins().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" } + return plugins.callback() +} + +inline fun List.callback(): I { + return requireNotNull(filterIsInstance().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt index 638bf5449c..6b9815d6c9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPermissions.kt @@ -23,6 +23,9 @@ import kotlinx.coroutines.flow.map * Provides information about the permissions of users in a room. */ interface RoomPermissions : AutoCloseable { + /** + * Returns true if the current user is able to ban from the room. + */ fun canOwnUserBan(): Boolean /** @@ -31,7 +34,7 @@ interface RoomPermissions : AutoCloseable { fun canOwnUserInvite(): Boolean /** - * Returns true if the current user is able to kick in the room. + * Returns true if the current user is able to kick from the room. */ fun canOwnUserKick(): Boolean @@ -128,10 +131,18 @@ interface RoomPermissions : AutoCloseable { fun canUserTriggerRoomNotification(userId: UserId): Boolean } +/** + * Returns true if the current user can edit roles and permissions in the room ie. can send + * a power levels state event. + */ fun RoomPermissions.canEditRolesAndPermissions(): Boolean { return canOwnUserSendState(StateEventType.ROOM_POWER_LEVELS) } +/** + * Returns true if the current user can start a call in the room ie. can send + * a call member state event. + */ fun RoomPermissions.canCall(): Boolean { return canOwnUserSendState(StateEventType.CALL_MEMBER) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt index a7eebbb99c..e8f88ed86d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt @@ -12,7 +12,8 @@ data class RoomPowerLevelsValues( val ban: Long, val invite: Long, val kick: Long, - val sendEvents: Long, + val eventsDefault: Long, + val stateDefault: Long, val redactEvents: Long, val roomName: Long, val roomAvatar: Long, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index 943b5ee1d5..770efb3918 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -397,10 +397,12 @@ class JoinedRustRoom( invite = roomPowerLevelsValues.invite, kick = roomPowerLevelsValues.kick, redact = roomPowerLevelsValues.redactEvents, - eventsDefault = roomPowerLevelsValues.sendEvents, + stateDefault = roomPowerLevelsValues.stateDefault, + eventsDefault = roomPowerLevelsValues.eventsDefault, roomName = roomPowerLevelsValues.roomName, roomAvatar = roomPowerLevelsValues.roomAvatar, roomTopic = roomPowerLevelsValues.roomTopic, + spaceChild = roomPowerLevelsValues.spaceChild, ) innerRoom.applyPowerLevelChanges(changes) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt index 081d08457e..5e2a1c82da 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt @@ -19,7 +19,8 @@ object RoomPowerLevelsValuesMapper { ban = values.ban, invite = values.invite, kick = values.kick, - sendEvents = values.eventsDefault, + eventsDefault = values.eventsDefault, + stateDefault = values.stateDefault, redactEvents = values.redact, roomName = values.roomName, roomAvatar = values.roomAvatar, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt index 72464827ed..c47c4406b6 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt @@ -25,11 +25,11 @@ fun defaultFfiRoomPowerLevelValues() = RoomPowerLevelsValues( invite = 0, kick = 50, eventsDefault = 0, + stateDefault = 50, redact = 50, - roomName = 100, - roomAvatar = 100, - roomTopic = 100, - stateDefault = 0, + roomName = 50, + roomAvatar = 50, + roomTopic = 50, + spaceChild = 50, usersDefault = 0, - spaceChild = 100, ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt index 3c100283a1..f298da8b42 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt @@ -37,8 +37,9 @@ class RoomPowerLevelsValuesMapperTest { ban = 1, invite = 2, kick = 3, - sendEvents = 5, redactEvents = 4, + eventsDefault = 5, + stateDefault = 6, roomName = 8, roomAvatar = 9, roomTopic = 10, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt index b56066af73..78765d1ec4 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt @@ -211,10 +211,11 @@ fun defaultRoomPowerLevelValues() = RoomPowerLevelsValues( ban = 50, invite = 0, kick = 50, - sendEvents = 0, + eventsDefault = 0, + stateDefault = 50, redactEvents = 50, - roomName = 100, - roomAvatar = 100, - roomTopic = 100, - spaceChild = 100, + roomName = 50, + roomAvatar = 50, + roomTopic = 50, + spaceChild = 50, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt index 7a005402e2..284ffcbab1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt @@ -56,6 +56,7 @@ fun EditableAvatarView( avatarType: AvatarType, onAvatarClick: () -> Unit, modifier: Modifier = Modifier, + enabled: Boolean = true, ) { val a11yAvatar = stringResource(CommonStrings.a11y_avatar) val editIconRadius = 15.dp @@ -66,6 +67,7 @@ fun EditableAvatarView( .wrapContentSize() .size(height = parentHeight, width = parentWidth) .clickable( + enabled = enabled, interactionSource = remember { MutableInteractionSource() }, onClickLabel = stringResource(CommonStrings.a11y_edit_avatar), onClick = onAvatarClick, diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 53429fb1df..176175a04b 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -453,10 +453,6 @@ Opravdu chcete pokračovat?" "Vaše zpráva nebyla odeslána, protože%1$s neověřil(a) všechna zařízení" "Jedno nebo více vašich zařízení není ověřeno. Zprávu můžete přesto odeslat, nebo ji můžete prozatím zrušit a zkusit to znovu později, až ověříte všechna svá zařízení." "Vaše zpráva nebyla odeslána, protože jste neověřili jedno nebo více zařízení" - "Změnit nastavení" - "Správa prostoru" - "Spravovat místnosti" - "Oprávnění" "Upravit správce nebo vlastníky" "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nepodařilo se načíst údaje o uživateli" diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index 7d58b7bfb6..6de4dc7ebd 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -445,10 +445,6 @@ Er du sikker på, at du vil fortsætte?" "Din besked blev ikke sendt, fordi %1$s ikke har bekræftet alle enheder" "En eller flere af dine enheder er ikke verificeret. Du kan sende beskeden alligevel, eller du kan annullere for nu og prøve igen senere, når du har verificeret alle dine enheder." "Din besked blev ikke sendt, fordi du ikke har verificeret en eller flere af dine enheder" - "Skift indstillinger" - "Administrér gruppe" - "Administrer rum" - "Tilladelser" "Rediger administratorer eller ejere" "Det lykkedes ikke at behandle medier til upload. Prøv venligst igen." "Kunne ikke hente brugeroplysninger" diff --git a/libraries/ui-strings/src/main/res/values-et/translations.xml b/libraries/ui-strings/src/main/res/values-et/translations.xml index 593fc9af11..e7444c442b 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -483,10 +483,6 @@ Kas sa oled kindel, et soovid jätkata?" "Sinu sõnum on saatmata, kuna %1$s pole verifitseerinud kõiki oma seadmeid" "Üks või enam sinu seadet on verifitseerimata. Sa võid sõnumi ikkagi ära saata või katkestad saatmise ning proovid uuesti, kui oled kõik oma seadmed verifitseerinud." "Kuna sul on üks või enam verifitseerimata seadet, siis sinu sõnum jäi saatmata" - "Muuda seadistusi" - "Halda kogukonda" - "Halda jututuba" - "Õigused" "Muuda peakasutajaid või omanikke" "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti." "Kasutaja andmete laadimine ei õnnestunud" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 8c725904d7..97e0c7b037 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -446,10 +446,6 @@ Haluatko varmasti jatkaa?" "Viestiäsi ei lähetetty, koska %1$s ei ole vahvistanut kaikkia laitteitaan." "Yksi tai useampi laitteistasi on vahvistamaton. Voit lähettää viestin silti tai peruuttaa sen toistaiseksi ja yrittää uudelleen myöhemmin, kun olet vahvistanut kaikki laitteesi." "Viestiäsi ei lähetetty, koska et ole vahvistanut yhtä tai useampaa laitettasi." - "Asetusten muuttaminen" - "Tilan hallitseminen" - "Huoneiden hallitseminen" - "Oikeudet" "Muokkaa ylläpitäjiä tai omistajia" "Median käsittely epäonnistui, yritä uudelleen." "Käyttäjän tietojen hakeminen epäonnistui" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index f4b55e8d80..5cf19b8dfe 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -453,10 +453,6 @@ Raison : %1$s." "Votre message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils" "Un ou plusieurs de vos appareils ne sont pas vérifiés. Vous pouvez quand même envoyer le message, ou vous pouvez annuler et réessayer plus tard après avoir vérifié tous vos appareils." "Votre message n’a pas été envoyé car vous n’avez pas vérifié tous vos appareils" - "Changer les paramètres" - "Gérer l’espace" - "Gérer les salons" - "Autorisations" "Modifier les administrateurs ou les propriétaires" "Échec du traitement des médias à télécharger, veuillez réessayer." "Impossible de récupérer les détails de l’utilisateur" diff --git a/libraries/ui-strings/src/main/res/values-hr/translations.xml b/libraries/ui-strings/src/main/res/values-hr/translations.xml index e350ab9f73..5c3290e187 100644 --- a/libraries/ui-strings/src/main/res/values-hr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hr/translations.xml @@ -492,10 +492,6 @@ Jeste li sigurni da želite nastaviti?" "Vaša poruka nije poslana jer %1$s nije potvrdio sve uređaje" "Jedan vaš uređaj ili više njih nije potvrđeno. Možete svejedno poslati poruku ili za sada otkazati i pokušati ponovno poslije nakon što potvrdite sve svoje uređaje." "Vaša poruka nije poslana jer niste potvrdili jedan svoj uređaj ili više njih" - "Promijeni postavke" - "Upravljaj prostorom" - "Upravljaj sobama" - "Dopuštenja" "Uredi administratore ili vlasnike" "Prijenos medija za obradu nije uspio, pokušajte ponovno." "Nije moguće dohvatiti korisničke podatke" diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index bc235cccd4..0dd7b35b51 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -445,10 +445,6 @@ Biztos, hogy folytatja?" "Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét" "Egy vagy több eszköze nincs ellenőrizve. Így is elküldheti az üzenetet, vagy egyelőre megszakíthatja, és később, az összes eszköz ellenőrzése után újrapróbálkozhat." "Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte" - "Beállítások módosítása" - "Tér kezelése" - "Szobák kezelése" - "Jogosultságok" "Adminisztrátorok vagy tulajdonosok szerkesztése" "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült letölteni a felhasználói adatokat" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 1e2765a190..ec359908ce 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -446,10 +446,6 @@ Sei sicuro di voler continuare?" "Il tuo messaggio non è stato inviato perché %1$s non ha verificato tutti i dispositivi." "Uno o più dispositivi non sono verificati. Puoi inviare il messaggio comunque, oppure annullarlo e riprovare più tardi dopo aver verificato tutti i tuoi dispositivi." "Il tuo messaggio non è stato inviato perché non hai verificato uno o più dispositivi." - "Modifica impostazioni" - "Gestire lo spazio" - "Gestisci le stanze" - "Autorizzazioni" "Modifica amministratori o proprietari" "Elaborazione del file multimediale da caricare fallita, riprova." "Impossibile recuperare i dettagli dell\'utente" diff --git a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml index d7fc53da97..c61dadbd7a 100644 --- a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml @@ -446,10 +446,6 @@ Você tem certeza de que deseja continuar?" "Sua mensagem não foi enviada porque %1$s não verificou todos os dispositivos" "Um ou mais de seus dispositivos não foram verificados. Você pode enviar a mensagem mesmo assim ou pode cancelar por enquanto e tentar novamente mais tarde, depois de ter verificado todos os seus dispositivos." "Sua mensagem não foi enviada porque você não verificou um ou mais de seus dispositivos" - "Alterar configurações" - "Gerenciar espaço" - "Gerenciar salas" - "Permissões" "Editar administradores ou proprietários" "Falha ao processar a mídia para o envio. Tente novamente." "Não foi possível buscar os detalhes do usuário" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 203f2f893a..bf82feec26 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -492,10 +492,6 @@ Sunteți sigur că doriți să continuați?" "Mesajul dvs. nu a fost trimis deoarece %1$s nu si-a verificat toate dispozitivele" "Unul sau mai multe dispozitive nu sunt verificate. Puteți trimite mesajul oricum sau puteți anula deocamdată și încercați din nou mai târziu după ce ați verificat toate dispozitivele." "Mesajul dumneavoastră nu a fost trimis deoarece nu ați verificat unul sau mai multe dispozitive" - "Modificați setările" - "Gestionați spațiul" - "Gestionați camerele" - "Permisiuni" "Editați administratorii sau proprietarii" "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Nu am putut găsi detaliile utilizatorului" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index d319fcc053..d0fca61375 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -455,10 +455,6 @@ "Ваше сообщение не было отправлено, потому что %1$s не проверил одно или несколько устройств" "Одно или несколько ваших устройств не проверены. Вы можете отправить сообщение в любом случае или отменить его пока и повторить попытку позже, проверив все свои устройства." "Ваше сообщение не было отправлено, поскольку вы не подтвердили одно или несколько своих устройств." - "Изменить настройки" - "Управление пространством" - "Управление комнатами" - "Разрешения" "Редактировать роль владельца и администратора" "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось получить данные о пользователе" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 694b679f45..d225eecc4f 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -462,10 +462,6 @@ Naozaj chcete pokračovať?" "Vaša správa nebola odoslaná, pretože %1$s neoveril/a všetky zariadenia." "Jedno alebo viac vašich zariadení nie je overených. Správu môžete odoslať aj tak, alebo môžete zatiaľ zrušiť a skúsiť to znova neskôr po overení všetkých svojich zariadení." "Vaša správa nebola odoslaná, pretože ste neoverili jedno alebo viac svojich zariadení" - "Zmeniť nastavenia" - "Spravovať priestor" - "Spravovať miestnosti" - "Povolenia" "Upraviť správcov alebo vlastníkov" "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa získať údaje o používateľovi" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 6707ca7950..e4dfb2805c 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -437,10 +437,6 @@ "未傳送您的訊息,因為 %1$s 尚未驗證所有裝置。" "您的一個或多個裝置未經驗證。您仍可傳送訊息,也可以取消並在您驗證您的所有裝置後再試一次。" "因為您尚未驗證一個或多個裝置,因為未傳送您的訊息" - "變更設定" - "管理空間" - "管理聊天室" - "權限" "編輯管理員或擁有者" "無法處理要上傳的媒體,請再試一次。" "無法擷取使用者詳細資訊" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 58fbc36794..ac6157e5e2 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -483,10 +483,6 @@ Are you sure you want to continue?" "Your message was not sent because %1$s has not verified all devices" "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices." "Your message was not sent because you have not verified one or more of your devices" - "Change settings" - "Manage space" - "Manage rooms" - "Permissions" "Edit Admins or Owners" "Failed processing media to upload, please try again." "Could not retrieve user details" diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png index 76a24059e1..49f38a28fd 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc850fc418dc1407311120e7aa096d83d6ad4cff5abb2d15335d700175585ee6 -size 52471 +oid sha256:cd8976b009775ac64496007232f9771bba6b8afbb588c621c574a91b399bc4bb +size 49734 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png index 4c6e7d3755..f3ef799045 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c93e3409aad7e2bc4cf9b0a9ae871fa22cdf1c0f80f7e5997cf4205a7bbb27bf -size 52396 +oid sha256:f82f648e9f5d67abfe2cf783df7e70568a74ff5dcae876e80b54995981a65886 +size 49650 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png index 1537cb69c2..83b5995ef4 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e88b49f64d50f47716f17b41007af13ed40a3b3892c1a22b909a57bee4d3251e -size 45645 +oid sha256:0ebf86fc0ac80375edd1730caab4a669aa2cc545b2ea946981901fd360114320 +size 44555 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png index a6e775a2b6..0c1593bc85 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd9b90c305f5192f7c45910541c2e576227c939e5416821e152e4ea95fa39114 -size 43157 +oid sha256:fae74539f53342110e50a077ad78b8422d08ba931b948b2b167d90f6cbe8ade6 +size 43535 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png index 684c54aab0..5a6d410ad7 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7ec3aa60b762fce4463e0e3ef4607eb9e04b0ce6d1cab6726361c4b45114a4f -size 49964 +oid sha256:61bced1ea61f5952e83956b4c4447a0371fe99c874e2a20df431bc59e02dc838 +size 50316 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en.png new file mode 100644 index 0000000000..16c2a363d8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73d9461a1d51964ba1a912376658e3e550e952821f7aa1d9bff765ed11deb920 +size 49123 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png index fc1442fd2e..865c938ba5 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f62f14d6db6ebd24739ab8e6bd63f0ccda5603b9b425d19a5f08d3de4f706fc -size 51008 +oid sha256:1f096dcda405a17c90bad22dc6340543d8774c9ba65f1cd8cbd03a2077186614 +size 48626 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png index 64b5723ed7..cb24aa2578 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afb783af20ae5131e8b6c7982c3152f1121fd90ba5f6cd06c4371f18c38d2870 -size 50909 +oid sha256:192fce45ea8d83d8f7aa40f9337b2dd92c3b1194da611fed1c42fd771d74fe0a +size 48534 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png index 5db46a1c93..b16e4b3ba3 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52cf090186c65692a28b6702576a18dc64c22bcf5406b2e89931b22304aaaa00 -size 44188 +oid sha256:b3ed16d04a1106b82183c4fe6ae741b598448050e2ddae2fa842b0b01d475e0a +size 43230 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png index 2ce42f90d0..7ae9ff70b6 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e72fed367e540d65c58bc30dbfd7307c2eaa8b45f32f114c04af8d7394a82188 -size 41076 +oid sha256:0cb2f7d7cad98d111ed2c1c0b8120e5837f2586f845594e581b41b7cb96e00f2 +size 41555 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png index 6fa7228b9b..1aa08a4e12 100644 --- a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b5ad3057f13e64a7962dd33d96914cc6e08ad092924e727135bba1aee06fad1 -size 47822 +oid sha256:0862da8d120e276afd6bdef6a9b247d3976ae70adab47077d8749600dd695884 +size 48280 diff --git a/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en.png new file mode 100644 index 0000000000..25c50a55cb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48e5f7460a13798d2f2963930e4e7a95c6a1ffa704d034ec21e33ba1bde6606c +size 48123 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png index 9f8cf23b73..930061aba1 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:264d55c8b65befa6e242e6864ba9be09ac9c3ed46e4033630445415f3c5ade83 -size 26111 +oid sha256:08e629078ddc8fe761305d7e3b1ce22344187a9b6bf4e729b85cfc6c59d2fde2 +size 33271 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png index 4f49ec35cf..e4293e23a2 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.leave_LeaveSpaceView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5bfbe783ade0202f2c3e38451cc0868f858425457a19651bd37e10f35277327 -size 25774 +oid sha256:5fc7f07a583f9dedb887ca83c67543e904d7bab092ebf70e06bc5292bf1e95b3 +size 32414 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index f7463a6396..b7db5f05e6 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -381,6 +381,7 @@ "name" : ":features:rolesandpermissions:impl", "includeRegex" : [ "screen_room_change_.*", + "screen\\.room_change_permissions\\..*", "screen_room_roles_.*", "screen\\.room_roles_and_permissions\\..*", "screen_room_member_list.*",