change(room permissions): fix securityAndPrivacy permissions computation

This commit is contained in:
ganfra
2025-12-11 11:54:12 +01:00
parent 03dd89a77f
commit 6e2863ded6
5 changed files with 62 additions and 27 deletions

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

@@ -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,7 @@ 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
@@ -94,6 +95,10 @@ class SpacePresenter(
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>>()) }
@@ -144,7 +149,7 @@ class SpacePresenter(
joinActions = joinActions.toImmutableMap(),
acceptDeclineInviteState = acceptDeclineInviteState,
topicViewerState = topicViewerState,
canAccessSpaceSettings = isSpaceSettingsEnabled && permissions.hasAny,
canAccessSpaceSettings = canAccessSpaceSettings,
eventSink = ::handleEvent,
)
}

View File

@@ -7,32 +7,41 @@
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.StateEventType
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 canEditDetails: Boolean,
val canManageRolesAndPermissions: Boolean,
val canManageSecurityAndPrivacy: Boolean,
){
val editDetailsPermissions: RoomDetailsEditPermissions,
val canEditRolesAndPermissions: Boolean,
val securityAndPrivacyPermissions: SecurityAndPrivacyPermissions,
) {
fun hasAny(joinRule: JoinRule?): Boolean {
return editDetailsPermissions.hasAny ||
canEditRolesAndPermissions ||
securityAndPrivacyPermissions.hasAny(isSpace = true, joinRule = joinRule)
}
val hasAny = canEditDetails || canManageRolesAndPermissions || canManageSecurityAndPrivacy
companion object {
val DEFAULT = SpaceSettingsPermissions(
canEditDetails = false,
canManageRolesAndPermissions = false,
canManageSecurityAndPrivacy = false,
editDetailsPermissions = RoomDetailsEditPermissions.DEFAULT,
canEditRolesAndPermissions = false,
securityAndPrivacyPermissions = SecurityAndPrivacyPermissions.DEFAULT,
)
}
}
fun RoomPermissions.spaceSettingsPermissions(): SpaceSettingsPermissions {
fun RoomPermissions.spaceSettingsPermissions(): SpaceSettingsPermissions {
return SpaceSettingsPermissions(
canEditDetails = roomDetailsEditPermissions().hasAny,
canManageRolesAndPermissions = canOwnUserSendState(StateEventType.ROOM_POWER_LEVELS),
canManageSecurityAndPrivacy = securityAndPrivacyPermissions().hasAny,
editDetailsPermissions = roomDetailsEditPermissions(),
canEditRolesAndPermissions = canEditRolesAndPermissions(),
securityAndPrivacyPermissions = securityAndPrivacyPermissions(),
)
}

View File

@@ -10,7 +10,9 @@ 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
@@ -26,15 +28,19 @@ class SpaceSettingsPresenter(
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,
canEditDetails = permissions.canEditDetails,
showRolesAndPermissions = permissions.canManageRolesAndPermissions,
showSecurityAndPrivacy = permissions.canManageSecurityAndPrivacy,
canEditDetails = permissions.editDetailsPermissions.hasAny,
showRolesAndPermissions = permissions.canEditRolesAndPermissions,
showSecurityAndPrivacy = showSecurityAndPrivacy,
eventSink = {},
)
}