Merge pull request #5908 from element-hq/feature/fga/space_settings_iteration

Change : space settings iteration
This commit is contained in:
ganfra
2025-12-16 22:41:03 +01:00
committed by GitHub
78 changed files with 428 additions and 205 deletions

View File

@@ -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
}

View File

@@ -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<RolesAndPermissionsFlowNode>(buildContext)
override fun createNode(
parentNode: Node,
buildContext: BuildContext,
callback: RolesAndPermissionsEntryPoint.Callback,
): Node {
return parentNode.createNode<RolesAndPermissionsFlowNode>(buildContext, listOf(callback))
}
}

View File

@@ -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<Plugin>,
private val room: JoinedRoom,
) : BaseFlowNode<RolesAndPermissionsFlowNode.NavTarget>(
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) {

View File

@@ -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)))

View File

@@ -36,8 +36,7 @@ class ChangeRoomPermissionsPresenter(
) : Presenter<ChangeRoomPermissionsState> {
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()

View File

@@ -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,
}

View File

@@ -26,6 +26,7 @@ class ChangeRoomPermissionsStateProvider : PreviewParameterProvider<ChangeRoomPe
saveAction = AsyncAction.Failure(IllegalStateException("Failed to save changes"))
),
aChangeRoomPermissionsState(hasChanges = true, saveAction = AsyncAction.ConfirmingCancellation),
aChangeRoomPermissionsState(itemsBySection = ChangeRoomPermissionsPresenter.buildItems(isSpace = true)),
)
}
@@ -51,12 +52,13 @@ private fun previewPermissions(): RoomPowerLevelsValues {
ban = RoomMember.Role.User.powerLevel,
// MessagesAndContent section
redactEvents = RoomMember.Role.Moderator.powerLevel,
sendEvents = RoomMember.Role.Admin.powerLevel,
eventsDefault = RoomMember.Role.Admin.powerLevel,
// RoomDetails section
roomName = RoomMember.Role.Admin.powerLevel,
roomAvatar = RoomMember.Role.Moderator.powerLevel,
roomTopic = RoomMember.Role.User.powerLevel,
// SpaceManagement section
spaceChild = RoomMember.Role.Moderator.powerLevel,
stateDefault = RoomMember.Role.Moderator.powerLevel,
)
}

View File

@@ -110,10 +110,10 @@ fun ChangeRoomPermissionsView(
@Composable
private fun titleForSection(section: RoomPermissionsSection): String = when (section) {
RoomPermissionsSection.SpaceDetails -> stringResource(R.string.screen_room_roles_and_permissions_space_details)
RoomPermissionsSection.RoomDetails -> stringResource(R.string.screen_room_roles_and_permissions_room_details)
RoomPermissionsSection.MessagesAndContent -> stringResource(R.string.screen_room_roles_and_permissions_messages_and_content)
RoomPermissionsSection.MembershipModeration -> stringResource(R.string.screen_room_roles_and_permissions_member_moderation)
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

View File

@@ -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<Plugin>,
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()

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Správce"</string>
<string name="screen_room_change_permissions_ban_people">"Vykázat lidi"</string>
<string name="screen_room_change_permissions_change_settings">"Změnit nastavení"</string>
<string name="screen_room_change_permissions_delete_messages">"Odstranit zprávy"</string>
<string name="screen_room_change_permissions_everyone">"Člen"</string>
<string name="screen_room_change_permissions_invite_people">"Pozvat přátele"</string>
<string name="screen_room_change_permissions_manage_space">"Správa prostoru"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Spravovat místnosti"</string>
<string name="screen_room_change_permissions_member_moderation">"Spravovat členy"</string>
<string name="screen_room_change_permissions_messages_and_content">"Zprávy a obsah"</string>
<string name="screen_room_change_permissions_moderators">"Moderátor"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Změnit název místnosti"</string>
<string name="screen_room_change_permissions_room_topic">"Změnit téma místnosti"</string>
<string name="screen_room_change_permissions_send_messages">"Odeslat zprávy"</string>
<string name="screen_room_change_permissions_title">"Oprávnění"</string>
<string name="screen_room_change_role_administrators_title">"Upravit správce"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Tuto akci nebudete moci vrátit zpět. Upravujete oprávnění uživatele, tak aby měl stejnou úroveň jako vy."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Přidat správce?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Peakasutajad"</string>
<string name="screen_room_change_permissions_ban_people">"Suhtluskeelu seadmine"</string>
<string name="screen_room_change_permissions_change_settings">"Muuda seadistusi"</string>
<string name="screen_room_change_permissions_delete_messages">"Eemalda sõnumid"</string>
<string name="screen_room_change_permissions_everyone">"Liikmed"</string>
<string name="screen_room_change_permissions_invite_people">"Osalejate kutsumine"</string>
<string name="screen_room_change_permissions_manage_space">"Halda kogukonda"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Halda jututuba"</string>
<string name="screen_room_change_permissions_member_moderation">"Liikmete haldus"</string>
<string name="screen_room_change_permissions_messages_and_content">"Sõnumid ja sisu"</string>
<string name="screen_room_change_permissions_moderators">"Moderaatorid"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Jututoa nime muutmine"</string>
<string name="screen_room_change_permissions_room_topic">"Jututoa teema muutmine"</string>
<string name="screen_room_change_permissions_send_messages">"Sõnumite saatmine"</string>
<string name="screen_room_change_permissions_title">"Õigused"</string>
<string name="screen_room_change_role_administrators_title">"Muuda peakasutajaid"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Kuna sa annad teisele kasutajale sinu õigustega võrreldes samad õigused, siis sa ei saa seda muudatust hiljem tagasi pöörata."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisame peakasutaja?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Ylläpitäjä"</string>
<string name="screen_room_change_permissions_ban_people">"Porttikieltojen antaminen"</string>
<string name="screen_room_change_permissions_change_settings">"Asetusten muuttaminen"</string>
<string name="screen_room_change_permissions_delete_messages">"Viestien poistaminen"</string>
<string name="screen_room_change_permissions_everyone">"Jäsen"</string>
<string name="screen_room_change_permissions_invite_people">"Ihmisten kutsuminen ja liittymispyyntöjen hyväksyminen"</string>
<string name="screen_room_change_permissions_manage_space">"Tilan hallitseminen"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Huoneiden hallitseminen"</string>
<string name="screen_room_change_permissions_member_moderation">"Jäsenien hallinta"</string>
<string name="screen_room_change_permissions_messages_and_content">"Viestit ja sisältö"</string>
<string name="screen_room_change_permissions_moderators">"Valvoja"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Huoneen nimen vaihtaminen"</string>
<string name="screen_room_change_permissions_room_topic">"Huoneen aiheen vaihtaminen"</string>
<string name="screen_room_change_permissions_send_messages">"Viestien lähettäminen"</string>
<string name="screen_room_change_permissions_title">"Oikeudet"</string>
<string name="screen_room_change_role_administrators_title">"Muokkaa ylläpitäjiä"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Et voi peruuttaa tätä toimenpidettä. Ylennät käyttäjän samalle oikeustasolle kuin sinä."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Lisätäänkö ylläpitäjä?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Administrateurs"</string>
<string name="screen_room_change_permissions_ban_people">"Bannir des participants"</string>
<string name="screen_room_change_permissions_change_settings">"Changer les paramètres"</string>
<string name="screen_room_change_permissions_delete_messages">"Supprimer des messages"</string>
<string name="screen_room_change_permissions_everyone">"Membre"</string>
<string name="screen_room_change_permissions_invite_people">"Inviter des personnes"</string>
<string name="screen_room_change_permissions_manage_space">"Gérer lespace"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gérer les salons"</string>
<string name="screen_room_change_permissions_member_moderation">"Gérer les membres"</string>
<string name="screen_room_change_permissions_messages_and_content">"Messages et contenus"</string>
<string name="screen_room_change_permissions_moderators">"Modérateurs"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Changer le nom du salon"</string>
<string name="screen_room_change_permissions_room_topic">"Changer le sujet du salon"</string>
<string name="screen_room_change_permissions_send_messages">"Envoyer des messages"</string>
<string name="screen_room_change_permissions_title">"Autorisations"</string>
<string name="screen_room_change_role_administrators_title">"Modifier les administrateurs"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Vous ne pourrez pas annuler cette action. Vous êtes en train de promouvoir lutilisateur pour quil ait le même niveau que vous."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Ajouter un administrateur ?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Adminisztrátor"</string>
<string name="screen_room_change_permissions_ban_people">"Emberek kitiltása"</string>
<string name="screen_room_change_permissions_change_settings">"Beállítások módosítása"</string>
<string name="screen_room_change_permissions_delete_messages">"Üzenetek eltávolítása"</string>
<string name="screen_room_change_permissions_everyone">"Tag"</string>
<string name="screen_room_change_permissions_invite_people">"Emberek meghívása"</string>
<string name="screen_room_change_permissions_manage_space">"Tér kezelése"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Szobák kezelése"</string>
<string name="screen_room_change_permissions_member_moderation">"Tagok kezelése"</string>
<string name="screen_room_change_permissions_messages_and_content">"Üzenetek és tartalom"</string>
<string name="screen_room_change_permissions_moderators">"Moderátor"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Szoba nevének módosítása"</string>
<string name="screen_room_change_permissions_room_topic">"Szoba témájának módosítása"</string>
<string name="screen_room_change_permissions_send_messages">"Üzenetek küldése"</string>
<string name="screen_room_change_permissions_title">"Jogosultságok"</string>
<string name="screen_room_change_role_administrators_title">"Adminisztrátorok szerkesztése"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Ezt a műveletet nem fogja tudja visszavonni. Ugyanarra a szintre lépteti elő a felhasználót, mint amellyel Ön is rendelkezik."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adminisztrátor hozzáadása?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Amministratore"</string>
<string name="screen_room_change_permissions_ban_people">"Escludi membri"</string>
<string name="screen_room_change_permissions_change_settings">"Modifica impostazioni"</string>
<string name="screen_room_change_permissions_delete_messages">"Rimuovi messaggi"</string>
<string name="screen_room_change_permissions_everyone">"Membro"</string>
<string name="screen_room_change_permissions_invite_people">"Invita persone"</string>
<string name="screen_room_change_permissions_manage_space">"Gestire lo spazio"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gestisci le stanze"</string>
<string name="screen_room_change_permissions_member_moderation">"Gestisci membri"</string>
<string name="screen_room_change_permissions_messages_and_content">"Messaggi e contenuti"</string>
<string name="screen_room_change_permissions_moderators">"Moderatore"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Cambia il nome della stanza"</string>
<string name="screen_room_change_permissions_room_topic">"Cambiare l\'argomento della stanza"</string>
<string name="screen_room_change_permissions_send_messages">"Inviare messaggi"</string>
<string name="screen_room_change_permissions_title">"Autorizzazioni"</string>
<string name="screen_room_change_role_administrators_title">"Modifica amministratori"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Non potrai annullare questa azione. Stai promuovendo l\'utente al tuo stesso livello di potere."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Aggiungi amministratore?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Administradores"</string>
<string name="screen_room_change_permissions_ban_people">"Banir pessoas"</string>
<string name="screen_room_change_permissions_change_settings">"Alterar configurações"</string>
<string name="screen_room_change_permissions_delete_messages">"Remover mensagens"</string>
<string name="screen_room_change_permissions_everyone">"Membro"</string>
<string name="screen_room_change_permissions_invite_people">"Convidar pessoas"</string>
<string name="screen_room_change_permissions_manage_space">"Gerenciar espaço"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gerenciar salas"</string>
<string name="screen_room_change_permissions_member_moderation">"Gerenciar membros"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_change_permissions_moderators">"Moderador"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Alterar nome da sala"</string>
<string name="screen_room_change_permissions_room_topic">"Alterar tópico da sala"</string>
<string name="screen_room_change_permissions_send_messages">"Enviar mensagens"</string>
<string name="screen_room_change_permissions_title">"Permissões"</string>
<string name="screen_room_change_role_administrators_title">"Editar administradores"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Você não poderá desfazer essa ação. Você está promovendo o usuário a ter o mesmo nível de poder que você."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adicionar administrador?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Только администраторы"</string>
<string name="screen_room_change_permissions_ban_people">"Блокировать людей могут"</string>
<string name="screen_room_change_permissions_change_settings">"Изменить настройки"</string>
<string name="screen_room_change_permissions_delete_messages">"Удалить сообщения"</string>
<string name="screen_room_change_permissions_everyone">"Участник"</string>
<string name="screen_room_change_permissions_invite_people">"Пригласить людей"</string>
<string name="screen_room_change_permissions_manage_space">"Управление пространством"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Управление комнатами"</string>
<string name="screen_room_change_permissions_member_moderation">"Список участников"</string>
<string name="screen_room_change_permissions_messages_and_content">"Сообщения и содержание"</string>
<string name="screen_room_change_permissions_moderators">"Модератор"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Менять название комнаты могут"</string>
<string name="screen_room_change_permissions_room_topic">"Менять тему комнаты могут"</string>
<string name="screen_room_change_permissions_send_messages">"Отправлять сообщения могут"</string>
<string name="screen_room_change_permissions_title">"Разрешения"</string>
<string name="screen_room_change_role_administrators_title">"Редактировать роль администраторов"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не сможете отменить это действие. Вы устанавливаете уровень пользователю соответствующий вашему."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Добавить администратора?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"管理員"</string>
<string name="screen_room_change_permissions_ban_people">"管理黑名單"</string>
<string name="screen_room_change_permissions_change_settings">"變更設定"</string>
<string name="screen_room_change_permissions_delete_messages">"移除訊息"</string>
<string name="screen_room_change_permissions_everyone">"成員"</string>
<string name="screen_room_change_permissions_invite_people">"邀請夥伴"</string>
<string name="screen_room_change_permissions_manage_space">"管理空間"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"管理聊天室"</string>
<string name="screen_room_change_permissions_member_moderation">"管理成員"</string>
<string name="screen_room_change_permissions_messages_and_content">"訊息與內容"</string>
<string name="screen_room_change_permissions_moderators">"版主"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"變更聊天室名稱"</string>
<string name="screen_room_change_permissions_room_topic">"變更聊天室主題"</string>
<string name="screen_room_change_permissions_send_messages">"傳送訊息"</string>
<string name="screen_room_change_permissions_title">"權限"</string>
<string name="screen_room_change_role_administrators_title">"編輯管理員"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"您將無法復原此動作。您正將使用者提昇至與您相同的權力等級。"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"要新增管理員嗎?"</string>

View File

@@ -2,9 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Admin"</string>
<string name="screen_room_change_permissions_ban_people">"Ban people"</string>
<string name="screen_room_change_permissions_change_settings">"Change settings"</string>
<string name="screen_room_change_permissions_delete_messages">"Remove messages"</string>
<string name="screen_room_change_permissions_everyone">"Member"</string>
<string name="screen_room_change_permissions_invite_people">"Invite people"</string>
<string name="screen_room_change_permissions_manage_space">"Manage space"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Manage rooms"</string>
<string name="screen_room_change_permissions_member_moderation">"Manage members"</string>
<string name="screen_room_change_permissions_messages_and_content">"Messages and content"</string>
<string name="screen_room_change_permissions_moderators">"Moderator"</string>
@@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Change name"</string>
<string name="screen_room_change_permissions_room_topic">"Change topic"</string>
<string name="screen_room_change_permissions_send_messages">"Send messages"</string>
<string name="screen_room_change_permissions_title">"Permissions"</string>
<string name="screen_room_change_role_administrators_title">"Edit Admins"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"You will not be able to undo this action. You are promoting the user to have the same power level as you."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Add Admin?"</string>

View File

@@ -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)

View File

@@ -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,
)

View File

@@ -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()
}
}

View File

@@ -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(

View File

@@ -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(),
)
}
}

View File

@@ -110,6 +110,7 @@ fun RoomDetailsEditView(
} else {
AvatarType.Room()
},
enabled = state.canChangeAvatar,
onAvatarClick = ::onAvatarClick,
modifier = Modifier.fillMaxWidth(),
)

View File

@@ -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(

View File

@@ -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<Plugin>,
private val room: JoinedRoom,
) : BaseFlowNode<SecurityAndPrivacyFlowNode.NavTarget>(
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 -> {

View File

@@ -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()
}
}

View File

@@ -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))

View File

@@ -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)

View File

@@ -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<LeaveSpaceNode>(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<SpaceNode>(buildContext, listOf(callback))
}
NavTarget.Settings -> {
is NavTarget.Settings -> {
val callback = object : SpaceSettingsFlowNode.Callback {
override fun initialTarget() = navTarget.initialTarget
override fun navigateToSpaceMembers() {
callback.navigateToRoomMemberList()
}

View File

@@ -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,
)
}

View File

@@ -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<RoomId, AsyncAction<Unit>>()) }
@@ -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)))
}

View File

@@ -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<Plugin>,
private val securityAndPrivacyEntryPoint: SecurityAndPrivacyEntryPoint,
private val rolesAndPermissionsEntryPoint: RolesAndPermissionsEntryPoint,
private val roomDetailsEditEntryPoint: RoomDetailsEditEntryPoint
) : BaseFlowNode<SpaceSettingsFlowNode.NavTarget>(
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<Plugin>): SpaceSettingsFlowNode.NavTarget {
return plugins.callback<SpaceSettingsFlowNode.Callback>().initialTarget()
}

View File

@@ -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(),
)
}

View File

@@ -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 = {},
)
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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,
) {

View File

@@ -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,

View File

@@ -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 <reified I : Plugin> Node.callback(): I {
return requireNotNull(plugins<I>().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" }
return plugins.callback()
}
inline fun <reified I : Plugin> List<Plugin>.callback(): I {
return requireNotNull(filterIsInstance<I>().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" }
}

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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,
)

View File

@@ -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,

View File

@@ -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,
)

View File

@@ -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,

View File

@@ -453,10 +453,6 @@ Opravdu chcete pokračovat?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Vaše zpráva nebyla odeslána, protože%1$s neověřil(a) všechna zařízení"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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í."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Vaše zpráva nebyla odeslána, protože jste neověřili jedno nebo více zařízení"</string>
<string name="screen_room_change_permissions_change_settings">"Změnit nastavení"</string>
<string name="screen_room_change_permissions_manage_space">"Správa prostoru"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Spravovat místnosti"</string>
<string name="screen_room_change_permissions_title">"Oprávnění"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Upravit správce nebo vlastníky"</string>
<string name="screen_room_error_failed_processing_media">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Nepodařilo se načíst údaje o uživateli"</string>

View File

@@ -445,10 +445,6 @@ Er du sikker på, at du vil fortsætte?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Din besked blev ikke sendt, fordi %1$s ikke har bekræftet alle enheder"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Din besked blev ikke sendt, fordi du ikke har verificeret en eller flere af dine enheder"</string>
<string name="screen_room_change_permissions_change_settings">"Skift indstillinger"</string>
<string name="screen_room_change_permissions_manage_space">"Administrér gruppe"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Administrer rum"</string>
<string name="screen_room_change_permissions_title">"Tilladelser"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Rediger administratorer eller ejere"</string>
<string name="screen_room_error_failed_processing_media">"Det lykkedes ikke at behandle medier til upload. Prøv venligst igen."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Kunne ikke hente brugeroplysninger"</string>

View File

@@ -483,10 +483,6 @@ Kas sa oled kindel, et soovid jätkata?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Sinu sõnum on saatmata, kuna %1$s pole verifitseerinud kõiki oma seadmeid"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"Ü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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Kuna sul on üks või enam verifitseerimata seadet, siis sinu sõnum jäi saatmata"</string>
<string name="screen_room_change_permissions_change_settings">"Muuda seadistusi"</string>
<string name="screen_room_change_permissions_manage_space">"Halda kogukonda"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Halda jututuba"</string>
<string name="screen_room_change_permissions_title">"Õigused"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Muuda peakasutajaid või omanikke"</string>
<string name="screen_room_error_failed_processing_media">"Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Kasutaja andmete laadimine ei õnnestunud"</string>

View File

@@ -446,10 +446,6 @@ Haluatko varmasti jatkaa?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Viestiäsi ei lähetetty, koska %1$s ei ole vahvistanut kaikkia laitteitaan."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Viestiäsi ei lähetetty, koska et ole vahvistanut yhtä tai useampaa laitettasi."</string>
<string name="screen_room_change_permissions_change_settings">"Asetusten muuttaminen"</string>
<string name="screen_room_change_permissions_manage_space">"Tilan hallitseminen"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Huoneiden hallitseminen"</string>
<string name="screen_room_change_permissions_title">"Oikeudet"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Muokkaa ylläpitäjiä tai omistajia"</string>
<string name="screen_room_error_failed_processing_media">"Median käsittely epäonnistui, yritä uudelleen."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Käyttäjän tietojen hakeminen epäonnistui"</string>

View File

@@ -453,10 +453,6 @@ Raison : %1$s."</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Votre message na pas été envoyé car %1$s na pas vérifié tous ses appareils"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Votre message na pas été envoyé car vous navez pas vérifié tous vos appareils"</string>
<string name="screen_room_change_permissions_change_settings">"Changer les paramètres"</string>
<string name="screen_room_change_permissions_manage_space">"Gérer lespace"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gérer les salons"</string>
<string name="screen_room_change_permissions_title">"Autorisations"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Modifier les administrateurs ou les propriétaires"</string>
<string name="screen_room_error_failed_processing_media">"Échec du traitement des médias à télécharger, veuillez réessayer."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Impossible de récupérer les détails de lutilisateur"</string>

View File

@@ -492,10 +492,6 @@ Jeste li sigurni da želite nastaviti?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Vaša poruka nije poslana jer %1$s nije potvrdio sve uređaje"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Vaša poruka nije poslana jer niste potvrdili jedan svoj uređaj ili više njih"</string>
<string name="screen_room_change_permissions_change_settings">"Promijeni postavke"</string>
<string name="screen_room_change_permissions_manage_space">"Upravljaj prostorom"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Upravljaj sobama"</string>
<string name="screen_room_change_permissions_title">"Dopuštenja"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Uredi administratore ili vlasnike"</string>
<string name="screen_room_error_failed_processing_media">"Prijenos medija za obradu nije uspio, pokušajte ponovno."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Nije moguće dohvatiti korisničke podatke"</string>

View File

@@ -445,10 +445,6 @@ Biztos, hogy folytatja?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte"</string>
<string name="screen_room_change_permissions_change_settings">"Beállítások módosítása"</string>
<string name="screen_room_change_permissions_manage_space">"Tér kezelése"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Szobák kezelése"</string>
<string name="screen_room_change_permissions_title">"Jogosultságok"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Adminisztrátorok vagy tulajdonosok szerkesztése"</string>
<string name="screen_room_error_failed_processing_media">"Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Nem sikerült letölteni a felhasználói adatokat"</string>

View File

@@ -446,10 +446,6 @@ Sei sicuro di voler continuare?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Il tuo messaggio non è stato inviato perché %1$s non ha verificato tutti i dispositivi."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Il tuo messaggio non è stato inviato perché non hai verificato uno o più dispositivi."</string>
<string name="screen_room_change_permissions_change_settings">"Modifica impostazioni"</string>
<string name="screen_room_change_permissions_manage_space">"Gestire lo spazio"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gestisci le stanze"</string>
<string name="screen_room_change_permissions_title">"Autorizzazioni"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Modifica amministratori o proprietari"</string>
<string name="screen_room_error_failed_processing_media">"Elaborazione del file multimediale da caricare fallita, riprova."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Impossibile recuperare i dettagli dell\'utente"</string>

View File

@@ -446,10 +446,6 @@ Você tem certeza de que deseja continuar?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Sua mensagem não foi enviada porque %1$s não verificou todos os dispositivos"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Sua mensagem não foi enviada porque você não verificou um ou mais de seus dispositivos"</string>
<string name="screen_room_change_permissions_change_settings">"Alterar configurações"</string>
<string name="screen_room_change_permissions_manage_space">"Gerenciar espaço"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gerenciar salas"</string>
<string name="screen_room_change_permissions_title">"Permissões"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Editar administradores ou proprietários"</string>
<string name="screen_room_error_failed_processing_media">"Falha ao processar a mídia para o envio. Tente novamente."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Não foi possível buscar os detalhes do usuário"</string>

View File

@@ -492,10 +492,6 @@ Sunteți sigur că doriți să continuați?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Mesajul dvs. nu a fost trimis deoarece %1$s nu si-a verificat toate dispozitivele"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Mesajul dumneavoastră nu a fost trimis deoarece nu ați verificat unul sau mai multe dispozitive"</string>
<string name="screen_room_change_permissions_change_settings">"Modificați setările"</string>
<string name="screen_room_change_permissions_manage_space">"Gestionați spațiul"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gestionați camerele"</string>
<string name="screen_room_change_permissions_title">"Permisiuni"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Editați administratorii sau proprietarii"</string>
<string name="screen_room_error_failed_processing_media">"Procesarea datelor media a eșuat, vă rugăm să încercați din nou."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Nu am putut găsi detaliile utilizatorului"</string>

View File

@@ -455,10 +455,6 @@
<string name="screen_resolve_send_failure_unsigned_device_title">"Ваше сообщение не было отправлено, потому что %1$s не проверил одно или несколько устройств"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"Одно или несколько ваших устройств не проверены. Вы можете отправить сообщение в любом случае или отменить его пока и повторить попытку позже, проверив все свои устройства."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Ваше сообщение не было отправлено, поскольку вы не подтвердили одно или несколько своих устройств."</string>
<string name="screen_room_change_permissions_change_settings">"Изменить настройки"</string>
<string name="screen_room_change_permissions_manage_space">"Управление пространством"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Управление комнатами"</string>
<string name="screen_room_change_permissions_title">"Разрешения"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Редактировать роль владельца и администратора"</string>
<string name="screen_room_error_failed_processing_media">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Не удалось получить данные о пользователе"</string>

View File

@@ -462,10 +462,6 @@ Naozaj chcete pokračovať?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Vaša správa nebola odoslaná, pretože %1$s neoveril/a všetky zariadenia."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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í."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Vaša správa nebola odoslaná, pretože ste neoverili jedno alebo viac svojich zariadení"</string>
<string name="screen_room_change_permissions_change_settings">"Zmeniť nastavenia"</string>
<string name="screen_room_change_permissions_manage_space">"Spravovať priestor"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Spravovať miestnosti"</string>
<string name="screen_room_change_permissions_title">"Povolenia"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Upraviť správcov alebo vlastníkov"</string>
<string name="screen_room_error_failed_processing_media">"Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Nepodarilo sa získať údaje o používateľovi"</string>

View File

@@ -437,10 +437,6 @@
<string name="screen_resolve_send_failure_unsigned_device_title">"未傳送您的訊息,因為 %1$s 尚未驗證所有裝置。"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"您的一個或多個裝置未經驗證。您仍可傳送訊息,也可以取消並在您驗證您的所有裝置後再試一次。"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"因為您尚未驗證一個或多個裝置,因為未傳送您的訊息"</string>
<string name="screen_room_change_permissions_change_settings">"變更設定"</string>
<string name="screen_room_change_permissions_manage_space">"管理空間"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"管理聊天室"</string>
<string name="screen_room_change_permissions_title">"權限"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"編輯管理員或擁有者"</string>
<string name="screen_room_error_failed_processing_media">"無法處理要上傳的媒體,請再試一次。"</string>
<string name="screen_room_error_failed_retrieving_user_details">"無法擷取使用者詳細資訊"</string>

View File

@@ -483,10 +483,6 @@ Are you sure you want to continue?"</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Your message was not sent because %1$s has not verified all devices"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"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."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Your message was not sent because you have not verified one or more of your devices"</string>
<string name="screen_room_change_permissions_change_settings">"Change settings"</string>
<string name="screen_room_change_permissions_manage_space">"Manage space"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Manage rooms"</string>
<string name="screen_room_change_permissions_title">"Permissions"</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Edit Admins or Owners"</string>
<string name="screen_room_error_failed_processing_media">"Failed processing media to upload, please try again."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Could not retrieve user details"</string>

View File

@@ -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.*",