change(room permissions): ensure closing screen without permissions
This commit is contained in:
parent
6e2863ded6
commit
d06f3a0798
8 changed files with 88 additions and 30 deletions
|
|
@ -8,6 +8,19 @@
|
||||||
|
|
||||||
package io.element.android.features.rolesandpermissions.api
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@ import io.element.android.libraries.di.RoomScope
|
||||||
|
|
||||||
@ContributesBinding(RoomScope::class)
|
@ContributesBinding(RoomScope::class)
|
||||||
class DefaultRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint {
|
class DefaultRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint {
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
override fun createNode(
|
||||||
return parentNode.createNode<RolesAndPermissionsFlowNode>(buildContext)
|
parentNode: Node,
|
||||||
|
buildContext: BuildContext,
|
||||||
|
callback: RolesAndPermissionsEntryPoint.Callback,
|
||||||
|
): Node {
|
||||||
|
return parentNode.createNode<RolesAndPermissionsFlowNode>(buildContext, listOf(callback))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.coroutineScope
|
import androidx.lifecycle.coroutineScope
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
|
@ -25,17 +28,24 @@ import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
|
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.permissions.ChangeRoomPermissionsNode
|
||||||
import io.element.android.features.rolesandpermissions.impl.roles.ChangeRolesNode
|
import io.element.android.features.rolesandpermissions.impl.roles.ChangeRolesNode
|
||||||
import io.element.android.features.rolesandpermissions.impl.root.RolesAndPermissionsNode
|
import io.element.android.features.rolesandpermissions.impl.root.RolesAndPermissionsNode
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
import io.element.android.libraries.architecture.BaseFlowNode
|
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.architecture.createNode
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncIndicator
|
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.AsyncIndicatorHost
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncIndicatorState
|
import io.element.android.libraries.designsystem.components.async.AsyncIndicatorState
|
||||||
import io.element.android.libraries.di.RoomScope
|
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 io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
|
@ -44,6 +54,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
class RolesAndPermissionsFlowNode(
|
class RolesAndPermissionsFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val room: JoinedRoom,
|
||||||
) : BaseFlowNode<RolesAndPermissionsFlowNode.NavTarget>(
|
) : BaseFlowNode<RolesAndPermissionsFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Root,
|
initialElement = NavTarget.Root,
|
||||||
|
|
@ -66,6 +77,7 @@ class RolesAndPermissionsFlowNode(
|
||||||
data object ChangeRoomPermissions : NavTarget
|
data object ChangeRoomPermissions : NavTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val callback: RolesAndPermissionsEntryPoint.Callback = callback()
|
||||||
private val asyncIndicatorState = AsyncIndicatorState()
|
private val asyncIndicatorState = AsyncIndicatorState()
|
||||||
|
|
||||||
override fun onBuilt() {
|
override fun onBuilt() {
|
||||||
|
|
@ -76,6 +88,15 @@ class RolesAndPermissionsFlowNode(
|
||||||
onChangeComplete(changesSaved)
|
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) {
|
private fun onChangeComplete(changesSaved: Boolean) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ package io.element.android.features.rolesandpermissions.impl.root
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
|
@ -21,13 +20,6 @@ import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.architecture.callback
|
import io.element.android.libraries.architecture.callback
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
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)
|
@ContributesNode(RoomScope::class)
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
|
|
@ -54,22 +46,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
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import io.element.android.features.rolesandpermissions.api.RolesAndPermissionsEn
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
class FakeRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint {
|
class FakeRolesAndPermissionsEntryPoint : RolesAndPermissionsEntryPoint {
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
override fun createNode(parentNode: Node, buildContext: BuildContext, callback: RolesAndPermissionsEntryPoint.Callback): Node {
|
||||||
lambdaError()
|
lambdaError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,16 @@ class RoomDetailsFlowNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
is NavTarget.AdminSettings -> {
|
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 -> {
|
NavTarget.PinnedMessagesList -> {
|
||||||
val params = MessagesEntryPoint.Params(
|
val params = MessagesEntryPoint.Params(
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ package io.element.android.features.securityandprivacy.impl
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
|
@ -19,6 +22,7 @@ import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint
|
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.editroomaddress.EditRoomAddressNode
|
||||||
import io.element.android.features.securityandprivacy.impl.root.SecurityAndPrivacyNode
|
import io.element.android.features.securityandprivacy.impl.root.SecurityAndPrivacyNode
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
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.callback
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.di.RoomScope
|
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
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
|
|
@ -33,6 +43,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
class SecurityAndPrivacyFlowNode(
|
class SecurityAndPrivacyFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val room: JoinedRoom,
|
||||||
) : BaseFlowNode<SecurityAndPrivacyFlowNode.NavTarget>(
|
) : BaseFlowNode<SecurityAndPrivacyFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.SecurityAndPrivacy,
|
initialElement = NavTarget.SecurityAndPrivacy,
|
||||||
|
|
@ -52,6 +63,24 @@ class SecurityAndPrivacyFlowNode(
|
||||||
private val callback: SecurityAndPrivacyEntryPoint.Callback = callback()
|
private val callback: SecurityAndPrivacyEntryPoint.Callback = callback()
|
||||||
private val navigator = BackstackSecurityAndPrivacyNavigator(callback, backstack)
|
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 {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.SecurityAndPrivacy -> {
|
NavTarget.SecurityAndPrivacy -> {
|
||||||
|
|
|
||||||
|
|
@ -113,9 +113,15 @@ class SpaceSettingsFlowNode(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is NavTarget.RolesAndPermissions -> {
|
is NavTarget.RolesAndPermissions -> {
|
||||||
|
val callback = object : RolesAndPermissionsEntryPoint.Callback {
|
||||||
|
override fun onDone() {
|
||||||
|
backstack.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
rolesAndPermissionsEntryPoint.createNode(
|
rolesAndPermissionsEntryPoint.createNode(
|
||||||
parentNode = this,
|
parentNode = this,
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
|
callback = callback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
NavTarget.EditDetails -> {
|
NavTarget.EditDetails -> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue