feature(security&privacy): iterate on SpaceMember option
This commit is contained in:
parent
96745c765a
commit
1930877a81
13 changed files with 226 additions and 88 deletions
|
|
@ -12,12 +12,14 @@ 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.Lifecycle
|
||||||
|
import androidx.lifecycle.coroutineScope
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
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
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
|
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||||
import dev.zacsweers.metro.Assisted
|
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
|
||||||
|
|
@ -31,12 +33,15 @@ 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.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.use
|
import io.element.android.libraries.matrix.api.room.powerlevels.use
|
||||||
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
|
|
@ -61,7 +66,7 @@ class SecurityAndPrivacyFlowNode(
|
||||||
data object EditRoomAddress : NavTarget
|
data object EditRoomAddress : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object ManageAuthorizedSpaces : NavTarget
|
data class ManageAuthorizedSpaces(val initialSelection: List<RoomId>) : NavTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callback: SecurityAndPrivacyEntryPoint.Callback = callback()
|
private val callback: SecurityAndPrivacyEntryPoint.Callback = callback()
|
||||||
|
|
@ -83,6 +88,18 @@ class SecurityAndPrivacyFlowNode(
|
||||||
callback.onDone()
|
callback.onDone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
whenChildrenAttached { commonLifecycle: Lifecycle,
|
||||||
|
securityAndPrivacyNode: SecurityAndPrivacyNode,
|
||||||
|
manageAuthorizedSpacesNode: ManageAuthorizedSpacesNode ->
|
||||||
|
commonLifecycle.coroutineScope.launch {
|
||||||
|
val authorizedSpacesData = securityAndPrivacyNode.getAuthorizedSpacesData()
|
||||||
|
val selectedSpaces = manageAuthorizedSpacesNode.waitForCompletion(authorizedSpacesData)
|
||||||
|
withContext(NonCancellable) {
|
||||||
|
backstack.pop()
|
||||||
|
securityAndPrivacyNode.onAuthorizedSpacesSelected(selectedSpaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
|
|
@ -93,7 +110,7 @@ class SecurityAndPrivacyFlowNode(
|
||||||
NavTarget.EditRoomAddress -> {
|
NavTarget.EditRoomAddress -> {
|
||||||
createNode<EditRoomAddressNode>(buildContext, plugins = listOf(navigator))
|
createNode<EditRoomAddressNode>(buildContext, plugins = listOf(navigator))
|
||||||
}
|
}
|
||||||
NavTarget.ManageAuthorizedSpaces -> {
|
is NavTarget.ManageAuthorizedSpaces -> {
|
||||||
createNode<ManageAuthorizedSpacesNode>(buildContext, plugins = listOf(navigator))
|
createNode<ManageAuthorizedSpacesNode>(buildContext, plugins = listOf(navigator))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,13 @@ import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint
|
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
interface SecurityAndPrivacyNavigator : Plugin {
|
interface SecurityAndPrivacyNavigator : Plugin {
|
||||||
fun onDone()
|
fun onDone()
|
||||||
fun openEditRoomAddress()
|
fun openEditRoomAddress()
|
||||||
fun closeEditRoomAddress()
|
fun closeEditRoomAddress()
|
||||||
fun openManageAuthorizedSpaces()
|
fun openManageAuthorizedSpaces(initialSelection: List<RoomId>)
|
||||||
fun closeManageAuthorizedSpaces()
|
fun closeManageAuthorizedSpaces()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,8 +39,8 @@ class BackstackSecurityAndPrivacyNavigator(
|
||||||
backStack.pop()
|
backStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openManageAuthorizedSpaces() {
|
override fun openManageAuthorizedSpaces(initialSelection: List<RoomId>) {
|
||||||
backStack.push(SecurityAndPrivacyFlowNode.NavTarget.ManageAuthorizedSpaces)
|
backStack.push(SecurityAndPrivacyFlowNode.NavTarget.ManageAuthorizedSpaces(initialSelection))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun closeManageAuthorizedSpaces() {
|
override fun closeManageAuthorizedSpaces() {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ package io.element.android.features.securityandprivacy.impl.manageauthorizedspac
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
sealed interface ManageAuthorizedSpacesEvent {
|
sealed interface ManageAuthorizedSpacesEvent {
|
||||||
|
data class SetData(val data: AuthorizedSpacesSelection) : ManageAuthorizedSpacesEvent
|
||||||
data object Done : ManageAuthorizedSpacesEvent
|
data object Done : ManageAuthorizedSpacesEvent
|
||||||
data class ToggleSpace(val roomId: RoomId) : ManageAuthorizedSpacesEvent
|
data class ToggleSpace(val roomId: RoomId) : ManageAuthorizedSpacesEvent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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
|
||||||
|
|
@ -18,7 +20,12 @@ 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.impl.SecurityAndPrivacyNavigator
|
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
|
||||||
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
import io.element.android.libraries.architecture.appyx.launchMolecule
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
|
|
@ -27,12 +34,24 @@ class ManageAuthorizedSpacesNode(
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
presenterFactory: ManageAuthorizedSpacesPresenter.Factory,
|
presenterFactory: ManageAuthorizedSpacesPresenter.Factory,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
|
data class Params(
|
||||||
|
val initialSelection: List<RoomId>
|
||||||
|
) : NodeInputs
|
||||||
|
|
||||||
private val navigator = plugins<SecurityAndPrivacyNavigator>().first()
|
private val navigator = plugins<SecurityAndPrivacyNavigator>().first()
|
||||||
private val presenter = presenterFactory.create(navigator)
|
private val presenter = presenterFactory.create(navigator)
|
||||||
|
|
||||||
|
private val stateFlow = launchMolecule { presenter.present() }
|
||||||
|
|
||||||
|
suspend fun waitForCompletion(data: AuthorizedSpacesSelection): ImmutableList<RoomId> {
|
||||||
|
stateFlow.value.eventSink(ManageAuthorizedSpacesEvent.SetData(data))
|
||||||
|
return stateFlow.first { it.isSelectionComplete }.selectedIds
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state by stateFlow.collectAsState()
|
||||||
ManageAuthorizedSpacesView(
|
ManageAuthorizedSpacesView(
|
||||||
state = state,
|
state = state,
|
||||||
onBackClick = ::navigateUp,
|
onBackClick = ::navigateUp,
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,21 @@
|
||||||
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
|
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class ManageAuthorizedSpacesPresenter(
|
class ManageAuthorizedSpacesPresenter(
|
||||||
|
|
@ -33,19 +38,33 @@ class ManageAuthorizedSpacesPresenter(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): ManageAuthorizedSpacesState {
|
override fun present(): ManageAuthorizedSpacesState {
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
var currentSelection: ImmutableList<RoomId> by remember { mutableStateOf(persistentListOf()) }
|
||||||
|
var spacesData by remember { mutableStateOf(AuthorizedSpacesSelection()) }
|
||||||
|
var isSelectionComplete by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
fun handleEvent(event: ManageAuthorizedSpacesEvent) {
|
fun handleEvent(event: ManageAuthorizedSpacesEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
ManageAuthorizedSpacesEvent.Done -> TODO()
|
ManageAuthorizedSpacesEvent.Done -> {
|
||||||
is ManageAuthorizedSpacesEvent.ToggleSpace -> TODO()
|
isSelectionComplete = true
|
||||||
|
}
|
||||||
|
is ManageAuthorizedSpacesEvent.ToggleSpace -> {
|
||||||
|
currentSelection = if (currentSelection.contains(event.roomId)) {
|
||||||
|
currentSelection.minus(event.roomId).toPersistentList()
|
||||||
|
} else {
|
||||||
|
currentSelection.plus(event.roomId).toPersistentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ManageAuthorizedSpacesEvent.SetData -> {
|
||||||
|
spacesData = event.data
|
||||||
|
currentSelection = event.data.initialSelectedIds
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ManageAuthorizedSpacesState(
|
return ManageAuthorizedSpacesState(
|
||||||
joinedSpaces = persistentListOf(),
|
selection = spacesData,
|
||||||
unknownSpaceIds = persistentListOf(),
|
selectedIds = currentSelection,
|
||||||
currentSelection = persistentListOf(),
|
isSelectionComplete = isSelectionComplete,
|
||||||
initialSelection = persistentListOf(),
|
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,17 @@ package io.element.android.features.securityandprivacy.impl.manageauthorizedspac
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
data class ManageAuthorizedSpacesState(
|
data class ManageAuthorizedSpacesState(
|
||||||
val joinedSpaces: ImmutableList<SpaceRoom>,
|
val selection: AuthorizedSpacesSelection,
|
||||||
val unknownSpaceIds: ImmutableList<RoomId>,
|
val selectedIds: ImmutableList<RoomId>,
|
||||||
val currentSelection: ImmutableList<RoomId>,
|
val isSelectionComplete: Boolean,
|
||||||
val initialSelection: ImmutableList<RoomId>,
|
|
||||||
val eventSink: (ManageAuthorizedSpacesEvent) -> Unit
|
val eventSink: (ManageAuthorizedSpacesEvent) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class AuthorizedSpacesSelection(
|
||||||
|
val joinedSpaces: ImmutableList<SpaceRoom> = persistentListOf(),
|
||||||
|
val unknownSpaceIds: ImmutableList<RoomId> = persistentListOf(),
|
||||||
|
val initialSelectedIds: ImmutableList<RoomId> = persistentListOf()
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,15 @@ open class ManageAuthorizedSpacesStateProvider : PreviewParameterProvider<Manage
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aManageAuthorizedSpacesState(),
|
aManageAuthorizedSpacesState(),
|
||||||
aManageAuthorizedSpacesState(
|
aManageAuthorizedSpacesState(
|
||||||
unknownSpaceIds = listOf(aRoomId(99))
|
authorizedSpacesData = anAuthorizedSpacesData(
|
||||||
|
unknownSpaceIds = listOf(aRoomId(99))
|
||||||
|
)
|
||||||
),
|
),
|
||||||
aManageAuthorizedSpacesState(
|
aManageAuthorizedSpacesState(
|
||||||
currentSelection = listOf(aRoomId(1), aRoomId(3)),
|
currentSelection = listOf(aRoomId(1), aRoomId(3)),
|
||||||
initialSelection = listOf(aRoomId(1)),
|
authorizedSpacesData = anAuthorizedSpacesData(
|
||||||
|
initialSelection = listOf(aRoomId(1)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -45,17 +49,24 @@ private fun aSpaceRoomList(count: Int): List<SpaceRoom> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun aManageAuthorizedSpacesState(
|
fun anAuthorizedSpacesData(
|
||||||
joinedSpaces: List<SpaceRoom> = aSpaceRoomList(5),
|
joinedSpaces: List<SpaceRoom> = aSpaceRoomList(5),
|
||||||
unknownSpaceIds: List<RoomId> = emptyList(),
|
unknownSpaceIds: List<RoomId> = emptyList(),
|
||||||
currentSelection: List<RoomId> = emptyList(),
|
|
||||||
initialSelection: List<RoomId> = emptyList(),
|
initialSelection: List<RoomId> = emptyList(),
|
||||||
eventSink: (ManageAuthorizedSpacesEvent) -> Unit = {},
|
) = AuthorizedSpacesSelection(
|
||||||
) = ManageAuthorizedSpacesState(
|
|
||||||
joinedSpaces = joinedSpaces.toImmutableList(),
|
joinedSpaces = joinedSpaces.toImmutableList(),
|
||||||
unknownSpaceIds = unknownSpaceIds.toImmutableList(),
|
unknownSpaceIds = unknownSpaceIds.toImmutableList(),
|
||||||
currentSelection = currentSelection.toImmutableList(),
|
initialSelectedIds = initialSelection.toImmutableList(),
|
||||||
initialSelection = initialSelection.toImmutableList(),
|
)
|
||||||
|
|
||||||
|
private fun aManageAuthorizedSpacesState(
|
||||||
|
authorizedSpacesData: AuthorizedSpacesSelection = anAuthorizedSpacesData(),
|
||||||
|
currentSelection: List<RoomId> = emptyList(),
|
||||||
|
eventSink: (ManageAuthorizedSpacesEvent) -> Unit = {},
|
||||||
|
) = ManageAuthorizedSpacesState(
|
||||||
|
selection = authorizedSpacesData,
|
||||||
|
selectedIds = currentSelection.toImmutableList(),
|
||||||
|
isSelectionComplete = false,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,12 @@ fun ManageAuthorizedSpacesView(
|
||||||
hasDivider = false,
|
hasDivider = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
items(items = state.joinedSpaces) { space ->
|
items(items = state.selection.joinedSpaces) { space ->
|
||||||
CheckableSpaceListItem(
|
CheckableSpaceListItem(
|
||||||
headlineText = space.displayName,
|
headlineText = space.displayName,
|
||||||
supportingText = space.canonicalAlias?.value,
|
supportingText = space.canonicalAlias?.value,
|
||||||
avatarData = space.getAvatarData(AvatarSize.SpaceMember),
|
avatarData = space.getAvatarData(AvatarSize.SpaceMember),
|
||||||
checked = state.currentSelection.contains(space.roomId),
|
checked = state.selectedIds.contains(space.roomId),
|
||||||
onCheckedChange = { _ ->
|
onCheckedChange = { _ ->
|
||||||
state.eventSink(
|
state.eventSink(
|
||||||
ManageAuthorizedSpacesEvent.ToggleSpace(space.roomId)
|
ManageAuthorizedSpacesEvent.ToggleSpace(space.roomId)
|
||||||
|
|
@ -79,19 +79,19 @@ fun ManageAuthorizedSpacesView(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.unknownSpaceIds.isNotEmpty()) {
|
if (state.selection.unknownSpaceIds.isNotEmpty()) {
|
||||||
item {
|
item {
|
||||||
ListSectionHeader(
|
ListSectionHeader(
|
||||||
title = stringResource(R.string.screen_manage_authorized_spaces_unknown_spaces_section_title),
|
title = stringResource(R.string.screen_manage_authorized_spaces_unknown_spaces_section_title),
|
||||||
hasDivider = true,
|
hasDivider = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
items(items = state.unknownSpaceIds) {
|
items(items = state.selection.unknownSpaceIds) {
|
||||||
CheckableSpaceListItem(
|
CheckableSpaceListItem(
|
||||||
headlineText = stringResource(R.string.screen_manage_authorized_spaces_unknown_space),
|
headlineText = stringResource(R.string.screen_manage_authorized_spaces_unknown_space),
|
||||||
supportingText = it.value,
|
supportingText = it.value,
|
||||||
avatarData = null,
|
avatarData = null,
|
||||||
checked = state.currentSelection.contains(it),
|
checked = state.selectedIds.contains(it),
|
||||||
onCheckedChange = { _ ->
|
onCheckedChange = { _ ->
|
||||||
state.eventSink(
|
state.eventSink(
|
||||||
ManageAuthorizedSpacesEvent.ToggleSpace(it)
|
ManageAuthorizedSpacesEvent.ToggleSpace(it)
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,12 @@ import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
|
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
|
||||||
|
import io.element.android.features.securityandprivacy.impl.manageauthorizedspaces.AuthorizedSpacesSelection
|
||||||
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
||||||
import io.element.android.libraries.architecture.appyx.launchMolecule
|
import io.element.android.libraries.architecture.appyx.launchMolecule
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
|
|
@ -43,6 +46,16 @@ class SecurityAndPrivacyNode(
|
||||||
activity.openUrlInChromeCustomTab(null, darkTheme, url)
|
activity.openUrlInChromeCustomTab(null, darkTheme, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAuthorizedSpacesData(): AuthorizedSpacesSelection{
|
||||||
|
return stateFlow.value.getAuthorizedSpaceData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onAuthorizedSpacesSelected(selectedSpaces: ImmutableList<RoomId>) {
|
||||||
|
stateFlow.value.eventSink(
|
||||||
|
SecurityAndPrivacyEvent.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.SpaceMember(selectedSpaces))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val activity = requireNotNull(LocalActivity.current)
|
val activity = requireNotNull(LocalActivity.current)
|
||||||
|
|
@ -56,4 +69,5 @@ class SecurityAndPrivacyNode(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||||
|
|
@ -130,9 +131,9 @@ class SecurityAndPrivacyPresenter(
|
||||||
value = (joinedParentSpaces + nonParentJoinedSpaces).toImmutableSet()
|
value = (joinedParentSpaces + nonParentJoinedSpaces).toImmutableSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
val spaceSelection by remember {
|
val spaceSelectionMode by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
getSpaceSelection(selectableJoinedSpaces, savedSettings.roomAccess)
|
getSpaceSelectionMode(selectableJoinedSpaces, savedSettings.roomAccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,9 +192,12 @@ class SecurityAndPrivacyPresenter(
|
||||||
SecurityAndPrivacyEvent.DismissExitConfirmation -> {
|
SecurityAndPrivacyEvent.DismissExitConfirmation -> {
|
||||||
saveAction.value = AsyncAction.Uninitialized
|
saveAction.value = AsyncAction.Uninitialized
|
||||||
}
|
}
|
||||||
SecurityAndPrivacyEvent.ManageAuthorizedSpaces -> navigator.openManageAuthorizedSpaces()
|
SecurityAndPrivacyEvent.ManageAuthorizedSpaces -> {
|
||||||
|
navigator.openManageAuthorizedSpaces(editedSettings.roomAccess.spaceIds())
|
||||||
|
}
|
||||||
SecurityAndPrivacyEvent.SelectSpaceMemberAccess -> handleSpaceMemberAccessSelection(
|
SecurityAndPrivacyEvent.SelectSpaceMemberAccess -> handleSpaceMemberAccessSelection(
|
||||||
spaceSelection = spaceSelection,
|
spaceSelectionMode = spaceSelectionMode,
|
||||||
|
spaceIds = editedSettings.roomAccess.spaceIds(),
|
||||||
editedAccess = editedRoomAccess,
|
editedAccess = editedRoomAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +220,7 @@ class SecurityAndPrivacyPresenter(
|
||||||
isSpace = roomInfo.isSpace,
|
isSpace = roomInfo.isSpace,
|
||||||
isSpaceSettingsEnabled = isSpaceSettingsEnabled,
|
isSpaceSettingsEnabled = isSpaceSettingsEnabled,
|
||||||
selectableJoinedSpaces = selectableJoinedSpaces,
|
selectableJoinedSpaces = selectableJoinedSpaces,
|
||||||
spaceSelection = spaceSelection,
|
spaceSelectionMode = spaceSelectionMode,
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -241,42 +245,45 @@ class SecurityAndPrivacyPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSpaceMemberAccessSelection(
|
private fun handleSpaceMemberAccessSelection(
|
||||||
spaceSelection: SpaceSelection,
|
spaceSelectionMode: SpaceSelectionMode,
|
||||||
|
spaceIds: List<RoomId>,
|
||||||
editedAccess: MutableState<SecurityAndPrivacyRoomAccess>,
|
editedAccess: MutableState<SecurityAndPrivacyRoomAccess>,
|
||||||
) {
|
) {
|
||||||
if (editedAccess.value is SecurityAndPrivacyRoomAccess.SpaceMember) {
|
if (editedAccess.value is SecurityAndPrivacyRoomAccess.SpaceMember) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
when (spaceSelection) {
|
when (spaceSelectionMode) {
|
||||||
is SpaceSelection.None -> Unit
|
is SpaceSelectionMode.None -> Unit
|
||||||
is SpaceSelection.Multiple -> navigator.openManageAuthorizedSpaces()
|
is SpaceSelectionMode.Multiple -> navigator.openManageAuthorizedSpaces(
|
||||||
is SpaceSelection.Single -> {
|
initialSelection = spaceIds ,
|
||||||
|
)
|
||||||
|
is SpaceSelectionMode.Single -> {
|
||||||
val newRoomAccess = SecurityAndPrivacyRoomAccess.SpaceMember(
|
val newRoomAccess = SecurityAndPrivacyRoomAccess.SpaceMember(
|
||||||
spaceIds = persistentListOf(spaceSelection.spaceId)
|
spaceIds = persistentListOf(spaceSelectionMode.spaceId)
|
||||||
)
|
)
|
||||||
editedAccess.value = newRoomAccess
|
editedAccess.value = newRoomAccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSpaceSelection(
|
private fun getSpaceSelectionMode(
|
||||||
selectableJoinedSpaces: Set<SpaceRoom>,
|
selectableJoinedSpaces: Set<SpaceRoom>,
|
||||||
savedAccess: SecurityAndPrivacyRoomAccess,
|
savedAccess: SecurityAndPrivacyRoomAccess,
|
||||||
): SpaceSelection {
|
): SpaceSelectionMode {
|
||||||
val selectableSpacesCount = (selectableJoinedSpaces.map { it.roomId } + savedAccess.spaceIds()).toSet().size
|
val selectableSpacesCount = (selectableJoinedSpaces.map { it.roomId } + savedAccess.spaceIds()).toSet().size
|
||||||
return when {
|
return when {
|
||||||
selectableSpacesCount == 0 -> SpaceSelection.None
|
selectableSpacesCount == 0 -> SpaceSelectionMode.None
|
||||||
selectableSpacesCount > 1 -> SpaceSelection.Multiple
|
selectableSpacesCount > 1 -> SpaceSelectionMode.Multiple
|
||||||
else -> {
|
else -> {
|
||||||
val joinedSpace = selectableJoinedSpaces.firstOrNull()
|
val joinedSpace = selectableJoinedSpaces.firstOrNull()
|
||||||
if (joinedSpace != null) {
|
if (joinedSpace != null) {
|
||||||
SpaceSelection.Single(joinedSpace.roomId, joinedSpace)
|
SpaceSelectionMode.Single(joinedSpace.roomId, joinedSpace)
|
||||||
} else {
|
} else {
|
||||||
val spaceId = savedAccess.spaceIds().firstOrNull()
|
val spaceId = savedAccess.spaceIds().firstOrNull()
|
||||||
if (spaceId == null) {
|
if (spaceId == null) {
|
||||||
SpaceSelection.None
|
SpaceSelectionMode.None
|
||||||
} else {
|
} else {
|
||||||
SpaceSelection.Single(spaceId, null)
|
SpaceSelectionMode.Single(spaceId, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,11 @@
|
||||||
|
|
||||||
package io.element.android.features.securityandprivacy.impl.root
|
package io.element.android.features.securityandprivacy.impl.root
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyPermissions
|
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyPermissions
|
||||||
|
import io.element.android.features.securityandprivacy.impl.R
|
||||||
|
import io.element.android.features.securityandprivacy.impl.manageauthorizedspaces.AuthorizedSpacesSelection
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -25,16 +29,32 @@ data class SecurityAndPrivacyState(
|
||||||
val editedSettings: SecurityAndPrivacySettings,
|
val editedSettings: SecurityAndPrivacySettings,
|
||||||
val homeserverName: String,
|
val homeserverName: String,
|
||||||
val showEnableEncryptionConfirmation: Boolean,
|
val showEnableEncryptionConfirmation: Boolean,
|
||||||
val isKnockEnabled: Boolean,
|
private val isKnockEnabled: Boolean,
|
||||||
val isSpaceSettingsEnabled: Boolean,
|
private val isSpaceSettingsEnabled: Boolean,
|
||||||
val saveAction: AsyncAction<Unit>,
|
val saveAction: AsyncAction<Unit>,
|
||||||
val isSpace: Boolean,
|
val isSpace: Boolean,
|
||||||
private val permissions: SecurityAndPrivacyPermissions,
|
private val permissions: SecurityAndPrivacyPermissions,
|
||||||
private val selectableJoinedSpaces: ImmutableSet<SpaceRoom>,
|
private val selectableJoinedSpaces: ImmutableSet<SpaceRoom>,
|
||||||
private val spaceSelection: SpaceSelection,
|
private val spaceSelectionMode: SpaceSelectionMode,
|
||||||
val eventSink: (SecurityAndPrivacyEvent) -> Unit
|
val eventSink: (SecurityAndPrivacyEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val isSpaceMemberSelectable = isSpaceSettingsEnabled && spaceSelectionMode != SpaceSelectionMode.None
|
||||||
|
|
||||||
|
// Show SpaceMember option in two cases:
|
||||||
|
// - the SpaceSettings FF is enabled
|
||||||
|
// - SpaceMember is the current saved value
|
||||||
|
val showSpaceMemberOption = savedSettings.roomAccess is SecurityAndPrivacyRoomAccess.SpaceMember || isSpaceMemberSelectable
|
||||||
|
|
||||||
|
val showManageSpaceAction = spaceSelectionMode is SpaceSelectionMode.Multiple && editedSettings.roomAccess is SecurityAndPrivacyRoomAccess.SpaceMember
|
||||||
|
|
||||||
|
val isAskToJoinSelectable = isKnockEnabled
|
||||||
|
|
||||||
|
// Show Ask to join option in two cases:
|
||||||
|
// - the Knock FF is enabled
|
||||||
|
// - AskToJoin is the current saved value
|
||||||
|
val showAskToJoinOption = savedSettings.roomAccess == SecurityAndPrivacyRoomAccess.AskToJoin || isAskToJoinSelectable
|
||||||
|
|
||||||
val canBeSaved = savedSettings != editedSettings
|
val canBeSaved = savedSettings != editedSettings
|
||||||
|
|
||||||
// Logic is in https://github.com/element-hq/element-meta/issues/3029
|
// Logic is in https://github.com/element-hq/element-meta/issues/3029
|
||||||
|
|
@ -57,6 +77,32 @@ data class SecurityAndPrivacyState(
|
||||||
|
|
||||||
val showHistoryVisibilitySection = permissions.canChangeHistoryVisibility && !isSpace
|
val showHistoryVisibilitySection = permissions.canChangeHistoryVisibility && !isSpace
|
||||||
val showEncryptionSection = permissions.canChangeEncryption && !isSpace
|
val showEncryptionSection = permissions.canChangeEncryption && !isSpace
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun spaceMemberDescription(): String {
|
||||||
|
return if (isSpaceMemberSelectable) {
|
||||||
|
when (spaceSelectionMode) {
|
||||||
|
is SpaceSelectionMode.Single -> {
|
||||||
|
val spaceName = spaceSelectionMode.spaceRoom?.displayName ?: spaceSelectionMode.spaceId.value
|
||||||
|
stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_single_parent_description, spaceName)
|
||||||
|
}
|
||||||
|
is SpaceSelectionMode.None,
|
||||||
|
is SpaceSelectionMode.Multiple -> stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_multiple_parents_description)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_unavailable_description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAuthorizedSpaceData(): AuthorizedSpacesSelection {
|
||||||
|
return AuthorizedSpacesSelection(
|
||||||
|
joinedSpaces = selectableJoinedSpaces.toImmutableList(),
|
||||||
|
unknownSpaceIds = savedSettings.roomAccess.spaceIds().filter { spaceId ->
|
||||||
|
selectableJoinedSpaces.none { it.roomId == spaceId }
|
||||||
|
}.toImmutableList(),
|
||||||
|
initialSelectedIds = editedSettings.roomAccess.spaceIds().toImmutableList()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SecurityAndPrivacySettings(
|
data class SecurityAndPrivacySettings(
|
||||||
|
|
@ -85,10 +131,10 @@ enum class SecurityAndPrivacyHistoryVisibility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface SpaceSelection {
|
sealed interface SpaceSelectionMode {
|
||||||
data object None : SpaceSelection
|
data object None : SpaceSelectionMode
|
||||||
data class Single(val spaceId: RoomId, val spaceRoom: SpaceRoom?) : SpaceSelection
|
data class Single(val spaceId: RoomId, val spaceRoom: SpaceRoom?) : SpaceSelectionMode
|
||||||
data object Multiple : SpaceSelection
|
data object Multiple : SpaceSelectionMode
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface SecurityAndPrivacyRoomAccess {
|
sealed interface SecurityAndPrivacyRoomAccess {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
|
||||||
import kotlinx.collections.immutable.toImmutableSet
|
import kotlinx.collections.immutable.toImmutableSet
|
||||||
|
|
||||||
open class SecurityAndPrivacyStateProvider : PreviewParameterProvider<SecurityAndPrivacyState> {
|
open class SecurityAndPrivacyStateProvider : PreviewParameterProvider<SecurityAndPrivacyState> {
|
||||||
|
|
@ -122,7 +121,7 @@ fun aSecurityAndPrivacyState(
|
||||||
isKnockEnabled: Boolean = true,
|
isKnockEnabled: Boolean = true,
|
||||||
isSpace: Boolean = false,
|
isSpace: Boolean = false,
|
||||||
selectableJoinedSpaces: Set<SpaceRoom> = emptySet(),
|
selectableJoinedSpaces: Set<SpaceRoom> = emptySet(),
|
||||||
spaceSelection: SpaceSelection = SpaceSelection.None,
|
spaceSelectionMode: SpaceSelectionMode = SpaceSelectionMode.None,
|
||||||
isSpaceSettingsEnabled: Boolean = true,
|
isSpaceSettingsEnabled: Boolean = true,
|
||||||
eventSink: (SecurityAndPrivacyEvent) -> Unit = {}
|
eventSink: (SecurityAndPrivacyEvent) -> Unit = {}
|
||||||
) = SecurityAndPrivacyState(
|
) = SecurityAndPrivacyState(
|
||||||
|
|
@ -135,7 +134,7 @@ fun aSecurityAndPrivacyState(
|
||||||
permissions = permissions,
|
permissions = permissions,
|
||||||
isSpace = isSpace,
|
isSpace = isSpace,
|
||||||
selectableJoinedSpaces = selectableJoinedSpaces.toImmutableSet(),
|
selectableJoinedSpaces = selectableJoinedSpaces.toImmutableSet(),
|
||||||
spaceSelection = SpaceSelection.None,
|
spaceSelectionMode = spaceSelectionMode,
|
||||||
isSpaceSettingsEnabled = isSpaceSettingsEnabled,
|
isSpaceSettingsEnabled = isSpaceSettingsEnabled,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,8 @@ fun SecurityAndPrivacyView(
|
||||||
) {
|
) {
|
||||||
if (state.showRoomAccessSection) {
|
if (state.showRoomAccessSection) {
|
||||||
RoomAccessSection(
|
RoomAccessSection(
|
||||||
|
state = state,
|
||||||
modifier = Modifier.padding(top = 24.dp),
|
modifier = Modifier.padding(top = 24.dp),
|
||||||
edited = state.editedSettings.roomAccess,
|
|
||||||
saved = state.savedSettings.roomAccess,
|
|
||||||
isKnockEnabled = state.isKnockEnabled,
|
|
||||||
isSpaceSettingsEnabled = state.isSpaceSettingsEnabled,
|
|
||||||
onSelectOption = { state.eventSink(SecurityAndPrivacyEvent.ChangeRoomAccess(it)) },
|
|
||||||
onManageSpacesClick = { state.eventSink(SecurityAndPrivacyEvent.ManageAuthorizedSpaces) },
|
|
||||||
onSpaceMemberAccessClick = { state.eventSink(SecurityAndPrivacyEvent.SelectSpaceMemberAccess) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.showRoomVisibilitySections) {
|
if (state.showRoomVisibilitySections) {
|
||||||
|
|
@ -211,15 +205,25 @@ private fun SecurityAndPrivacySection(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RoomAccessSection(
|
private fun RoomAccessSection(
|
||||||
edited: SecurityAndPrivacyRoomAccess,
|
state: SecurityAndPrivacyState,
|
||||||
saved: SecurityAndPrivacyRoomAccess,
|
|
||||||
isKnockEnabled: Boolean,
|
|
||||||
isSpaceSettingsEnabled: Boolean,
|
|
||||||
onSelectOption: (SecurityAndPrivacyRoomAccess) -> Unit,
|
|
||||||
onSpaceMemberAccessClick: () -> Unit,
|
|
||||||
onManageSpacesClick: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val edited = state.editedSettings.roomAccess
|
||||||
|
val saved = state.savedSettings.roomAccess
|
||||||
|
|
||||||
|
fun onSelectOption(option: SecurityAndPrivacyRoomAccess) {
|
||||||
|
state.eventSink(SecurityAndPrivacyEvent.ChangeRoomAccess(option))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSpaceMemberAccessClick() {
|
||||||
|
state.eventSink(SecurityAndPrivacyEvent.SelectSpaceMemberAccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onManageSpacesClick() {
|
||||||
|
state.eventSink(SecurityAndPrivacyEvent.ManageAuthorizedSpaces)
|
||||||
|
}
|
||||||
|
|
||||||
SecurityAndPrivacySection(
|
SecurityAndPrivacySection(
|
||||||
title = stringResource(R.string.screen_security_and_privacy_room_access_section_header),
|
title = stringResource(R.string.screen_security_and_privacy_room_access_section_header),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -231,31 +235,25 @@ private fun RoomAccessSection(
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Public())),
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Public())),
|
||||||
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.Anyone) },
|
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.Anyone) },
|
||||||
)
|
)
|
||||||
// Show SpaceMember option in two cases:
|
if (state.showSpaceMemberOption)
|
||||||
// - the SpaceSettings FF is enabled
|
|
||||||
// - SpaceMember is the current saved value
|
|
||||||
if (saved is SecurityAndPrivacyRoomAccess.SpaceMember || isSpaceSettingsEnabled)
|
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_title)) },
|
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_title)) },
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Text(text = stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_unavailable_description))
|
Text(text = state.spaceMemberDescription())
|
||||||
},
|
},
|
||||||
trailingContent = ListItemContent.RadioButton(selected = edited is SecurityAndPrivacyRoomAccess.SpaceMember),
|
trailingContent = ListItemContent.RadioButton(selected = state.editedSettings.roomAccess is SecurityAndPrivacyRoomAccess.SpaceMember),
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Space())),
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Space())),
|
||||||
onClick = onSpaceMemberAccessClick,
|
onClick = ::onSpaceMemberAccessClick,
|
||||||
enabled = isSpaceSettingsEnabled,
|
enabled = state.isSpaceMemberSelectable,
|
||||||
)
|
)
|
||||||
// Show Ask to join option in two cases:
|
if (state.showAskToJoinOption) {
|
||||||
// - the Knock FF is enabled
|
|
||||||
// - AskToJoin is the current saved value
|
|
||||||
if (saved == SecurityAndPrivacyRoomAccess.AskToJoin || isKnockEnabled) {
|
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_title)) },
|
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_title)) },
|
||||||
supportingContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_description)) },
|
supportingContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_description)) },
|
||||||
trailingContent = ListItemContent.RadioButton(selected = edited == SecurityAndPrivacyRoomAccess.AskToJoin),
|
trailingContent = ListItemContent.RadioButton(selected = edited == SecurityAndPrivacyRoomAccess.AskToJoin),
|
||||||
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.AskToJoin) },
|
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.AskToJoin) },
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserAdd())),
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserAdd())),
|
||||||
enabled = isKnockEnabled,
|
enabled = state.isAskToJoinSelectable,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ListItem(
|
ListItem(
|
||||||
|
|
@ -265,11 +263,11 @@ private fun RoomAccessSection(
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
||||||
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.InviteOnly) },
|
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.InviteOnly) },
|
||||||
)
|
)
|
||||||
if (edited is SecurityAndPrivacyRoomAccess.SpaceMember) {
|
if (state.showManageSpaceAction) {
|
||||||
val footerText = stringWithLink(
|
val footerText = stringWithLink(
|
||||||
textRes = R.string.screen_security_and_privacy_room_access_footer,
|
textRes = R.string.screen_security_and_privacy_room_access_footer,
|
||||||
url = stringResource(R.string.screen_security_and_privacy_room_access_footer_manage_spaces_action),
|
url = stringResource(R.string.screen_security_and_privacy_room_access_footer_manage_spaces_action),
|
||||||
onLinkClick = { onManageSpacesClick() },
|
onLinkClick = {onManageSpacesClick()},
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = footerText,
|
text = footerText,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue