change(space settings): manage permissions
This commit is contained in:
parent
de52c991fe
commit
dfc3ebb718
6 changed files with 62 additions and 8 deletions
|
|
@ -17,11 +17,14 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Inject
|
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.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
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.AcceptDeclineInviteEvents
|
||||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||||
import io.element.android.features.invite.api.toInviteData
|
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.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.mapState
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
|
|
@ -33,6 +36,7 @@ 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.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
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.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.SpaceRoom
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
|
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
|
||||||
|
|
@ -50,6 +54,7 @@ import kotlin.jvm.optionals.getOrNull
|
||||||
@Inject
|
@Inject
|
||||||
class SpacePresenter(
|
class SpacePresenter(
|
||||||
private val spaceRoomList: SpaceRoomList,
|
private val spaceRoomList: SpaceRoomList,
|
||||||
|
private val room: JoinedRoom,
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val seenInvitesStore: SeenInvitesStore,
|
private val seenInvitesStore: SeenInvitesStore,
|
||||||
private val joinRoom: JoinRoom,
|
private val joinRoom: JoinRoom,
|
||||||
|
|
@ -82,6 +87,9 @@ class SpacePresenter(
|
||||||
}
|
}
|
||||||
}.collectAsState()
|
}.collectAsState()
|
||||||
|
|
||||||
|
val permissions by room.permissionsAsState(SpaceSettingsPermissions.DEFAULT) { perms ->
|
||||||
|
perms.spaceSettingsPermissions()
|
||||||
|
}
|
||||||
val isSpaceSettingsEnabled by remember {
|
val isSpaceSettingsEnabled by remember {
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.SpaceSettings)
|
featureFlagService.isFeatureEnabledFlow(FeatureFlags.SpaceSettings)
|
||||||
}.collectAsState(false)
|
}.collectAsState(false)
|
||||||
|
|
@ -136,7 +144,7 @@ class SpacePresenter(
|
||||||
joinActions = joinActions.toImmutableMap(),
|
joinActions = joinActions.toImmutableMap(),
|
||||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||||
topicViewerState = topicViewerState,
|
topicViewerState = topicViewerState,
|
||||||
canAccessSpaceSettings = isSpaceSettingsEnabled,
|
canAccessSpaceSettings = isSpaceSettingsEnabled && permissions.hasAny,
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +158,7 @@ class SpacePresenter(
|
||||||
joinRoom.invoke(
|
joinRoom.invoke(
|
||||||
roomIdOrAlias = spaceRoom.roomId.toRoomIdOrAlias(),
|
roomIdOrAlias = spaceRoom.roomId.toRoomIdOrAlias(),
|
||||||
serverNames = spaceRoom.via,
|
serverNames = spaceRoom.via,
|
||||||
trigger = JoinedRoom.Trigger.SpaceHierarchy,
|
trigger = Trigger.SpaceHierarchy,
|
||||||
).onFailure {
|
).onFailure {
|
||||||
setJoinActions(joinActions + mapOf(spaceRoom.roomId to AsyncAction.Failure(it)))
|
setJoinActions(joinActions + mapOf(spaceRoom.roomId to AsyncAction.Failure(it)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.securityandprivacy.api.securityAndPrivacyPermissions
|
||||||
|
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||||
|
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions
|
||||||
|
|
||||||
|
data class SpaceSettingsPermissions(
|
||||||
|
val canEditDetails: Boolean,
|
||||||
|
val canManageRolesAndPermissions: Boolean,
|
||||||
|
val canManageSecurityAndPrivacy: Boolean,
|
||||||
|
){
|
||||||
|
|
||||||
|
val hasAny = canEditDetails || canManageRolesAndPermissions || canManageSecurityAndPrivacy
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val DEFAULT = SpaceSettingsPermissions(
|
||||||
|
canEditDetails = false,
|
||||||
|
canManageRolesAndPermissions = false,
|
||||||
|
canManageSecurityAndPrivacy = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RoomPermissions.spaceSettingsPermissions(): SpaceSettingsPermissions {
|
||||||
|
return SpaceSettingsPermissions(
|
||||||
|
canEditDetails = roomDetailsEditPermissions().hasAny,
|
||||||
|
canManageRolesAndPermissions = canOwnUserSendState(StateEventType.ROOM_POWER_LEVELS),
|
||||||
|
canManageSecurityAndPrivacy = securityAndPrivacyPermissions().hasAny,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ import androidx.compose.runtime.getValue
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
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
|
@Inject
|
||||||
class SpaceSettingsPresenter(
|
class SpaceSettingsPresenter(
|
||||||
|
|
@ -23,15 +23,18 @@ class SpaceSettingsPresenter(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): SpaceSettingsState {
|
override fun present(): SpaceSettingsState {
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val isUserAdmin = room.isOwnUserAdmin()
|
val permissions by room.permissionsAsState(SpaceSettingsPermissions.DEFAULT) { perms ->
|
||||||
|
perms.spaceSettingsPermissions()
|
||||||
|
}
|
||||||
return SpaceSettingsState(
|
return SpaceSettingsState(
|
||||||
roomId = room.roomId,
|
roomId = room.roomId,
|
||||||
name = roomInfo.name.orEmpty(),
|
name = roomInfo.name.orEmpty(),
|
||||||
canonicalAlias = roomInfo.canonicalAlias,
|
canonicalAlias = roomInfo.canonicalAlias,
|
||||||
avatarUrl = roomInfo.avatarUrl,
|
avatarUrl = roomInfo.avatarUrl,
|
||||||
memberCount = roomInfo.activeMembersCount,
|
memberCount = roomInfo.activeMembersCount,
|
||||||
showRolesAndPermissions = isUserAdmin,
|
canEditDetails = permissions.canEditDetails,
|
||||||
showSecurityAndPrivacy = isUserAdmin,
|
showRolesAndPermissions = permissions.canManageRolesAndPermissions,
|
||||||
|
showSecurityAndPrivacy = permissions.canManageSecurityAndPrivacy,
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ data class SpaceSettingsState(
|
||||||
val canonicalAlias: RoomAlias?,
|
val canonicalAlias: RoomAlias?,
|
||||||
val avatarUrl: String?,
|
val avatarUrl: String?,
|
||||||
val memberCount: Long,
|
val memberCount: Long,
|
||||||
|
val canEditDetails: Boolean,
|
||||||
val showRolesAndPermissions: Boolean,
|
val showRolesAndPermissions: Boolean,
|
||||||
val showSecurityAndPrivacy: Boolean,
|
val showSecurityAndPrivacy: Boolean,
|
||||||
val eventSink: (SpaceSettingsEvents) -> Unit
|
val eventSink: (SpaceSettingsEvents) -> Unit
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ fun aSpaceSettingsState(
|
||||||
memberCount: Long = 100,
|
memberCount: Long = 100,
|
||||||
showRolesAndPermissions: Boolean = false,
|
showRolesAndPermissions: Boolean = false,
|
||||||
showSecurityAndPrivacy: Boolean = false,
|
showSecurityAndPrivacy: Boolean = false,
|
||||||
|
canEditDetails: Boolean = false,
|
||||||
eventSink: (SpaceSettingsEvents) -> Unit = {},
|
eventSink: (SpaceSettingsEvents) -> Unit = {},
|
||||||
) = SpaceSettingsState(
|
) = SpaceSettingsState(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
|
@ -37,6 +38,7 @@ fun aSpaceSettingsState(
|
||||||
canonicalAlias = alias,
|
canonicalAlias = alias,
|
||||||
avatarUrl = avatarUrl,
|
avatarUrl = avatarUrl,
|
||||||
memberCount = memberCount,
|
memberCount = memberCount,
|
||||||
|
canEditDetails = canEditDetails,
|
||||||
showRolesAndPermissions = showRolesAndPermissions,
|
showRolesAndPermissions = showRolesAndPermissions,
|
||||||
showSecurityAndPrivacy = showSecurityAndPrivacy,
|
showSecurityAndPrivacy = showSecurityAndPrivacy,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ fun SpaceSettingsView(
|
||||||
name = state.name,
|
name = state.name,
|
||||||
avatarUrl = state.avatarUrl,
|
avatarUrl = state.avatarUrl,
|
||||||
canonicalAlias = state.canonicalAlias?.value,
|
canonicalAlias = state.canonicalAlias?.value,
|
||||||
|
canEditDetails = state.canEditDetails,
|
||||||
onSpaceInfoClick = onSpaceInfoClick,
|
onSpaceInfoClick = onSpaceInfoClick,
|
||||||
)
|
)
|
||||||
Section(isVisible = state.showSecurityAndPrivacy, content = {
|
Section(isVisible = state.showSecurityAndPrivacy, content = {
|
||||||
|
|
@ -101,12 +102,13 @@ private fun SpaceInfoSection(
|
||||||
name: String,
|
name: String,
|
||||||
avatarUrl: String?,
|
avatarUrl: String?,
|
||||||
canonicalAlias: String?,
|
canonicalAlias: String?,
|
||||||
|
canEditDetails: Boolean,
|
||||||
onSpaceInfoClick: () -> Unit,
|
onSpaceInfoClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(onClick = onSpaceInfoClick)
|
.clickable(enabled = canEditDetails, onClick = onSpaceInfoClick)
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue