diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt index d29a7ffd20..79c0c68ad3 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyFlowNode.kt @@ -66,7 +66,7 @@ class SecurityAndPrivacyFlowNode( data object EditRoomAddress : NavTarget @Parcelize - data class ManageAuthorizedSpaces(val initialSelection: List) : NavTarget + data object ManageAuthorizedSpaces: NavTarget } private val callback: SecurityAndPrivacyEntryPoint.Callback = callback() @@ -95,7 +95,7 @@ class SecurityAndPrivacyFlowNode( val authorizedSpacesData = securityAndPrivacyNode.getAuthorizedSpacesData() val selectedSpaces = manageAuthorizedSpacesNode.waitForCompletion(authorizedSpacesData) withContext(NonCancellable) { - backstack.pop() + navigator.closeManageAuthorizedSpaces() securityAndPrivacyNode.onAuthorizedSpacesSelected(selectedSpaces) } } @@ -110,7 +110,7 @@ class SecurityAndPrivacyFlowNode( NavTarget.EditRoomAddress -> { createNode(buildContext, plugins = listOf(navigator)) } - is NavTarget.ManageAuthorizedSpaces -> { + NavTarget.ManageAuthorizedSpaces -> { createNode(buildContext, plugins = listOf(navigator)) } } diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyNavigator.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyNavigator.kt index da6ca379e8..274bf0b823 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyNavigator.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/SecurityAndPrivacyNavigator.kt @@ -13,13 +13,12 @@ import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyEntryPoint -import io.element.android.libraries.matrix.api.core.RoomId interface SecurityAndPrivacyNavigator : Plugin { fun onDone() fun openEditRoomAddress() fun closeEditRoomAddress() - fun openManageAuthorizedSpaces(initialSelection: List) + fun openManageAuthorizedSpaces() fun closeManageAuthorizedSpaces() } @@ -39,8 +38,8 @@ class BackstackSecurityAndPrivacyNavigator( backStack.pop() } - override fun openManageAuthorizedSpaces(initialSelection: List) { - backStack.push(SecurityAndPrivacyFlowNode.NavTarget.ManageAuthorizedSpaces(initialSelection)) + override fun openManageAuthorizedSpaces() { + backStack.push(SecurityAndPrivacyFlowNode.NavTarget.ManageAuthorizedSpaces) } override fun closeManageAuthorizedSpaces() { diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesNode.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesNode.kt index 5608c0ce16..b8f7150d86 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesNode.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesNode.kt @@ -20,7 +20,6 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode 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.matrix.api.core.RoomId @@ -32,16 +31,10 @@ import kotlinx.coroutines.flow.first class ManageAuthorizedSpacesNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - presenterFactory: ManageAuthorizedSpacesPresenter.Factory, + presenter: ManageAuthorizedSpacesPresenter, ) : Node(buildContext, plugins = plugins) { - data class Params( - val initialSelection: List - ) : NodeInputs - private val navigator = plugins().first() - private val presenter = presenterFactory.create(navigator) - private val stateFlow = launchMolecule { presenter.present() } suspend fun waitForCompletion(data: AuthorizedSpacesSelection): ImmutableList { @@ -54,7 +47,7 @@ class ManageAuthorizedSpacesNode( val state by stateFlow.collectAsState() ManageAuthorizedSpacesView( state = state, - onBackClick = ::navigateUp, + onBackClick = { navigator.closeManageAuthorizedSpaces() }, modifier = modifier ) } diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesPresenter.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesPresenter.kt index 09869cdf09..0679c72066 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesPresenter.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesPresenter.kt @@ -13,57 +13,42 @@ 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.AssistedFactory -import dev.zacsweers.metro.AssistedInject -import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator +import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.Presenter -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 kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList -@AssistedInject -class ManageAuthorizedSpacesPresenter( - @Assisted private val navigator: SecurityAndPrivacyNavigator, - private val client: MatrixClient, - private val room: JoinedRoom, -) : Presenter { - @AssistedFactory - interface Factory { - fun create(navigator: SecurityAndPrivacyNavigator): ManageAuthorizedSpacesPresenter - } +@Inject +class ManageAuthorizedSpacesPresenter() : Presenter { @Composable override fun present(): ManageAuthorizedSpacesState { - var currentSelection: ImmutableList by remember { mutableStateOf(persistentListOf()) } - var spacesData by remember { mutableStateOf(AuthorizedSpacesSelection()) } + var selectedIds: ImmutableList by remember { mutableStateOf(persistentListOf()) } + var spacesSelection by remember { mutableStateOf(AuthorizedSpacesSelection()) } var isSelectionComplete by remember { mutableStateOf(false) } fun handleEvent(event: ManageAuthorizedSpacesEvent) { when (event) { - ManageAuthorizedSpacesEvent.Done -> { - isSelectionComplete = true - } + ManageAuthorizedSpacesEvent.Done ->isSelectionComplete = true is ManageAuthorizedSpacesEvent.ToggleSpace -> { - currentSelection = if (currentSelection.contains(event.roomId)) { - currentSelection.minus(event.roomId).toPersistentList() + selectedIds = if (selectedIds.contains(event.roomId)) { + selectedIds.minus(event.roomId).toPersistentList() } else { - currentSelection.plus(event.roomId).toPersistentList() + selectedIds.plus(event.roomId).toPersistentList() } } is ManageAuthorizedSpacesEvent.SetData -> { - spacesData = event.data - currentSelection = event.data.initialSelectedIds + spacesSelection = event.data + selectedIds = event.data.initialSelectedIds } } } return ManageAuthorizedSpacesState( - selection = spacesData, - selectedIds = currentSelection, + selection = spacesSelection, + selectedIds = selectedIds, isSelectionComplete = isSelectionComplete, eventSink = ::handleEvent, ) diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesState.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesState.kt index 7564fabae9..291cd7c760 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesState.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesState.kt @@ -18,7 +18,9 @@ data class ManageAuthorizedSpacesState( val selectedIds: ImmutableList, val isSelectionComplete: Boolean, val eventSink: (ManageAuthorizedSpacesEvent) -> Unit -) +) { + val isDoneButtonEnabled = selectedIds.isNotEmpty() +} data class AuthorizedSpacesSelection( val joinedSpaces: ImmutableList = persistentListOf(), diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt index 1c4ff7bc77..022010f267 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesView.kt @@ -53,6 +53,7 @@ fun ManageAuthorizedSpacesView( onDoneClick = { state.eventSink(ManageAuthorizedSpacesEvent.Done) }, + isDoneButtonEnabled = state.isDoneButtonEnabled ) } ) { padding -> @@ -160,6 +161,7 @@ private fun CheckableSpaceListItem( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun ManageAuthorizedSpacesTopBar( + isDoneButtonEnabled: Boolean, onBackClick: () -> Unit, onDoneClick: () -> Unit, modifier: Modifier = Modifier, @@ -170,6 +172,7 @@ private fun ManageAuthorizedSpacesTopBar( navigationIcon = { BackButton(onClick = onBackClick) }, actions = { TextButton( + enabled = isDoneButtonEnabled, text = stringResource(CommonStrings.action_done), onClick = onDoneClick, ) diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyNode.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyNode.kt index 1e5e6e0943..8c41992e7a 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyNode.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyNode.kt @@ -47,7 +47,7 @@ class SecurityAndPrivacyNode( } fun getAuthorizedSpacesData(): AuthorizedSpacesSelection{ - return stateFlow.value.getAuthorizedSpaceData() + return stateFlow.value.getAuthorizedSpacesSelection() } fun onAuthorizedSpacesSelected(selectedSpaces: ImmutableList) { diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt index bc7688dab3..9e2bd6c375 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt @@ -118,7 +118,7 @@ class SecurityAndPrivacyPresenter( address = savedSettings.address, ) - val selectableJoinedSpaces by produceState(persistentSetOf()) { + val selectableJoinedSpaces by produceState(initialValue = persistentSetOf(), key1 = savedSettings.roomAccess.spaceIds()) { val joinedParentSpaces = matrixClient .spaceService .joinedParents(room.roomId) @@ -193,7 +193,7 @@ class SecurityAndPrivacyPresenter( saveAction.value = AsyncAction.Uninitialized } SecurityAndPrivacyEvent.ManageAuthorizedSpaces -> { - navigator.openManageAuthorizedSpaces(editedSettings.roomAccess.spaceIds()) + navigator.openManageAuthorizedSpaces() } SecurityAndPrivacyEvent.SelectSpaceMemberAccess -> handleSpaceMemberAccessSelection( spaceSelectionMode = spaceSelectionMode, @@ -254,9 +254,7 @@ class SecurityAndPrivacyPresenter( } when (spaceSelectionMode) { is SpaceSelectionMode.None -> Unit - is SpaceSelectionMode.Multiple -> navigator.openManageAuthorizedSpaces( - initialSelection = spaceIds , - ) + is SpaceSelectionMode.Multiple -> navigator.openManageAuthorizedSpaces() is SpaceSelectionMode.Single -> { val newRoomAccess = SecurityAndPrivacyRoomAccess.SpaceMember( spaceIds = persistentListOf(spaceSelectionMode.spaceId) diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt index d14d83b16b..d74ecb13d9 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt @@ -42,8 +42,8 @@ data class SecurityAndPrivacyState( val isSpaceMemberSelectable = isSpaceSettingsEnabled && spaceSelectionMode != SpaceSelectionMode.None // Show SpaceMember option in two cases: - // - the SpaceSettings FF is enabled // - SpaceMember is the current saved value + // - SpaceMember option is selectable (ie. the FF is enabled and there is at least one space to select) val showSpaceMemberOption = savedSettings.roomAccess is SecurityAndPrivacyRoomAccess.SpaceMember || isSpaceMemberSelectable val showManageSpaceAction = spaceSelectionMode is SpaceSelectionMode.Multiple && editedSettings.roomAccess is SecurityAndPrivacyRoomAccess.SpaceMember @@ -94,13 +94,16 @@ data class SecurityAndPrivacyState( } } - fun getAuthorizedSpaceData(): AuthorizedSpacesSelection { + fun getAuthorizedSpacesSelection(): AuthorizedSpacesSelection { return AuthorizedSpacesSelection( joinedSpaces = selectableJoinedSpaces.toImmutableList(), unknownSpaceIds = savedSettings.roomAccess.spaceIds().filter { spaceId -> selectableJoinedSpaces.none { it.roomId == spaceId } }.toImmutableList(), - initialSelectedIds = editedSettings.roomAccess.spaceIds().toImmutableList() + initialSelectedIds = when (editedSettings.roomAccess) { + is SecurityAndPrivacyRoomAccess.SpaceMember -> editedSettings.roomAccess.spaceIds + else -> savedSettings.roomAccess.spaceIds() + } ) } }