feature(security&privacy): start ManageAuthorizedSpacesView
This commit is contained in:
parent
d868bf64bf
commit
7b8950a51b
4 changed files with 171 additions and 6 deletions
|
|
@ -18,6 +18,7 @@ import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNav
|
||||||
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.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class EditRoomAddressPresenter(
|
class EditRoomAddressPresenter(
|
||||||
|
|
@ -33,7 +34,6 @@ class EditRoomAddressPresenter(
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): ManageAuthorizedSpacesState {
|
override fun present(): ManageAuthorizedSpacesState {
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
|
|
||||||
fun handleEvent(event: ManageAuthorizedSpacesEvent) {
|
fun handleEvent(event: ManageAuthorizedSpacesEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
ManageAuthorizedSpacesEvent.Done -> TODO()
|
ManageAuthorizedSpacesEvent.Done -> TODO()
|
||||||
|
|
@ -42,6 +42,10 @@ class EditRoomAddressPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
return ManageAuthorizedSpacesState(
|
return ManageAuthorizedSpacesState(
|
||||||
|
joinedSpaces = persistentListOf(),
|
||||||
|
unknownSpaceIds = persistentListOf(),
|
||||||
|
currentSelection = persistentListOf(),
|
||||||
|
initialSelection = persistentListOf(),
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,14 @@
|
||||||
|
|
||||||
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ManageAuthorizedSpacesState(
|
data class ManageAuthorizedSpacesState(
|
||||||
|
val joinedSpaces: ImmutableList<SpaceRoom>,
|
||||||
|
val unknownSpaceIds: ImmutableList<RoomId>,
|
||||||
|
val currentSelection: ImmutableList<RoomId>,
|
||||||
|
val initialSelection: ImmutableList<RoomId>,
|
||||||
val eventSink: (ManageAuthorizedSpacesEvent) -> Unit
|
val eventSink: (ManageAuthorizedSpacesEvent) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,53 @@
|
||||||
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
|
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
open class ManageAuthorizedSpacesStateProvider : PreviewParameterProvider<ManageAuthorizedSpacesState> {
|
open class ManageAuthorizedSpacesStateProvider : PreviewParameterProvider<ManageAuthorizedSpacesState> {
|
||||||
override val values: Sequence<ManageAuthorizedSpacesState>
|
override val values: Sequence<ManageAuthorizedSpacesState>
|
||||||
get() = sequenceOf()
|
get() = sequenceOf(
|
||||||
|
aManageAuthorizedSpacesState(),
|
||||||
|
aManageAuthorizedSpacesState(
|
||||||
|
unknownSpaceIds = listOf(aRoomId(99))
|
||||||
|
),
|
||||||
|
aManageAuthorizedSpacesState(
|
||||||
|
currentSelection = listOf(aRoomId(1), aRoomId(3)),
|
||||||
|
initialSelection = listOf(aRoomId(1)),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun aRoomId(index: Int) = RoomId("!roomId$index:matrix.org")
|
||||||
|
|
||||||
|
private fun aSpaceRoomList(count: Int): List<SpaceRoom> {
|
||||||
|
return (1..count).map { index ->
|
||||||
|
aSpaceRoom(
|
||||||
|
roomId = aRoomId(index),
|
||||||
|
displayName = "Space $index",
|
||||||
|
canonicalAlias = if (index % 2 == 0) {
|
||||||
|
RoomAlias("#space$index:matrix.org")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun aManageAuthorizedSpacesState(
|
||||||
|
joinedSpaces: List<SpaceRoom> = aSpaceRoomList(5),
|
||||||
|
unknownSpaceIds: List<RoomId> = emptyList(),
|
||||||
|
currentSelection: List<RoomId> = emptyList(),
|
||||||
|
initialSelection: List<RoomId> = emptyList(),
|
||||||
|
eventSink: (ManageAuthorizedSpacesEvent) -> Unit = {},
|
||||||
|
) = ManageAuthorizedSpacesState(
|
||||||
|
joinedSpaces = joinedSpaces.toImmutableList(),
|
||||||
|
unknownSpaceIds = unknownSpaceIds.toImmutableList(),
|
||||||
|
currentSelection = currentSelection.toImmutableList(),
|
||||||
|
initialSelection = initialSelection.toImmutableList(),
|
||||||
|
eventSink = eventSink,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,34 @@ package io.element.android.features.securityandprivacy.impl.manageauthorizedspac
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
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.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
|
import io.element.android.features.securityandprivacy.impl.R
|
||||||
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
|
import io.element.android.libraries.designsystem.components.BigIcon
|
||||||
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
|
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
|
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -43,11 +60,105 @@ fun ManageAuthorizedSpacesView(
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.padding(padding)
|
modifier = Modifier.padding(padding)
|
||||||
) {
|
) {
|
||||||
|
headerItem()
|
||||||
|
item {
|
||||||
|
ListSectionHeader(
|
||||||
|
title = stringResource(R.string.screen_manage_authorized_spaces_your_spaces_section_title),
|
||||||
|
hasDivider = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
items(items = state.joinedSpaces) { space ->
|
||||||
|
CheckableSpaceListItem(
|
||||||
|
headlineText = space.displayName,
|
||||||
|
supportingText = space.canonicalAlias?.value,
|
||||||
|
avatarData = space.getAvatarData(AvatarSize.SpaceMember),
|
||||||
|
checked = state.currentSelection.contains(space.roomId),
|
||||||
|
onCheckedChange = { _ ->
|
||||||
|
state.eventSink(
|
||||||
|
ManageAuthorizedSpacesEvent.ToggleSpace(space.roomId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(state.unknownSpaceIds.isNotEmpty()){
|
||||||
|
item {
|
||||||
|
ListSectionHeader(
|
||||||
|
title = stringResource(R.string.screen_manage_authorized_spaces_unknown_spaces_section_title),
|
||||||
|
hasDivider = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
items(items = state.unknownSpaceIds) {
|
||||||
|
CheckableSpaceListItem(
|
||||||
|
headlineText = stringResource(R.string.screen_manage_authorized_spaces_unknown_space),
|
||||||
|
supportingText = it.value,
|
||||||
|
avatarData = null,
|
||||||
|
checked = state.currentSelection.contains(it),
|
||||||
|
onCheckedChange = { _ ->
|
||||||
|
state.eventSink(
|
||||||
|
ManageAuthorizedSpacesEvent.ToggleSpace(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.headerItem() {
|
||||||
|
item(key = "header", contentType = "header") {
|
||||||
|
IconTitleSubtitleMolecule(
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
vertical = 16.dp,
|
||||||
|
horizontal = 24.dp
|
||||||
|
),
|
||||||
|
title = stringResource(R.string.screen_manage_authorized_spaces_header),
|
||||||
|
subTitle = null,
|
||||||
|
iconStyle = BigIcon.Style.Default(
|
||||||
|
vectorIcon = CompoundIcons.SpaceSolid(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CheckableSpaceListItem(
|
||||||
|
headlineText: String,
|
||||||
|
supportingText: String?,
|
||||||
|
avatarData: AvatarData?,
|
||||||
|
checked: Boolean,
|
||||||
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = {
|
||||||
|
Text(text = headlineText)
|
||||||
|
},
|
||||||
|
supportingContent = supportingText?.let {
|
||||||
|
@Composable {
|
||||||
|
Text(text = supportingText)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leadingContent = avatarData?.let{
|
||||||
|
ListItemContent.Custom {
|
||||||
|
Avatar(
|
||||||
|
avatarData = avatarData,
|
||||||
|
avatarType = AvatarType.Space(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailingContent = ListItemContent.Checkbox(
|
||||||
|
checked = checked,
|
||||||
|
enabled = enabled,
|
||||||
|
),
|
||||||
|
enabled = enabled,
|
||||||
|
onClick = { onCheckedChange(!checked) },
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ManageAuthorizedSpacesTopBar(
|
private fun ManageAuthorizedSpacesTopBar(
|
||||||
|
|
@ -57,7 +168,7 @@ private fun ManageAuthorizedSpacesTopBar(
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
titleStr = stringResource(CommonStrings.screen_manage_authorized_spaces_title),
|
titleStr = stringResource(R.string.screen_manage_authorized_spaces_title),
|
||||||
navigationIcon = { BackButton(onClick = onBackClick) },
|
navigationIcon = { BackButton(onClick = onBackClick) },
|
||||||
actions = {
|
actions = {
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue