create room : start adding new options in ui

This commit is contained in:
ganfra
2024-10-25 18:27:23 +02:00
parent 5674d10d9b
commit f6938fa737
15 changed files with 447 additions and 110 deletions

View File

@@ -8,7 +8,7 @@
package io.element.android.features.createroom.impl
import android.net.Uri
import io.element.android.features.createroom.impl.configureroom.RoomPrivacy
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@@ -18,5 +18,5 @@ data class CreateRoomConfig(
val topic: String? = null,
val avatarUri: Uri? = null,
val invites: ImmutableList<MatrixUser> = persistentListOf(),
val privacy: RoomPrivacy = RoomPrivacy.Private,
val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private,
)

View File

@@ -8,7 +8,11 @@
package io.element.android.features.createroom.impl
import android.net.Uri
import io.element.android.features.createroom.impl.configureroom.RoomPrivacy
import io.element.android.features.createroom.impl.configureroom.RoomAccess
import io.element.android.features.createroom.impl.configureroom.RoomAccessItem
import io.element.android.features.createroom.impl.configureroom.RoomAddress
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState
import io.element.android.features.createroom.impl.di.CreateRoomScope
import io.element.android.features.createroom.impl.userlist.UserListDataStore
import io.element.android.libraries.androidutils.file.safeDelete
@@ -17,7 +21,9 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.getAndUpdate
import java.io.File
import java.text.Normalizer
import javax.inject.Inject
@SingleIn(CreateRoomScope::class)
@@ -31,28 +37,87 @@ class CreateRoomDataStore @Inject constructor(
field = value
}
fun getCreateRoomConfig(): Flow<CreateRoomConfig> = combine(
val createRoomConfig: Flow<CreateRoomConfig> = combine(
selectedUserListDataStore.selectedUsers(),
createRoomConfigFlow,
) { selectedUsers, config ->
config.copy(invites = selectedUsers.toImmutableList())
}
fun setRoomName(roomName: String?) {
createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(roomName = roomName?.takeIf { it.isNotEmpty() }))
fun setRoomName(roomName: String) {
createRoomConfigFlow.getAndUpdate { config ->
val newVisibility = when (config.roomVisibility) {
is RoomVisibilityState.Public -> {
val roomAddress = config.roomVisibility.roomAddress
if (roomAddress is RoomAddress.AutoFilled || roomName.isEmpty()) {
config.roomVisibility.copy(
roomAddress = RoomAddress.AutoFilled(roomName),
)
} else {
config.roomVisibility
}
}
else -> config.roomVisibility
}
config.copy(
roomName = roomName.takeIf { it.isNotEmpty() },
roomVisibility = newVisibility,
)
}
}
fun setTopic(topic: String?) {
createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(topic = topic?.takeIf { it.isNotEmpty() }))
fun setTopic(topic: String) {
createRoomConfigFlow.getAndUpdate { config ->
config.copy(topic = topic.takeIf { it.isNotEmpty() })
}
}
fun setAvatarUri(uri: Uri?, cached: Boolean = false) {
cachedAvatarUri = uri.takeIf { cached }
createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(avatarUri = uri))
createRoomConfigFlow.getAndUpdate { config ->
config.copy(avatarUri = uri)
}
}
fun setPrivacy(privacy: RoomPrivacy) {
createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(privacy = privacy))
fun setRoomVisibility(visibility: RoomVisibilityItem) {
createRoomConfigFlow.getAndUpdate { config ->
config.copy(
roomVisibility = when (visibility) {
RoomVisibilityItem.Private -> RoomVisibilityState.Private
RoomVisibilityItem.Public -> RoomVisibilityState.Public(
roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()),
roomAccess = RoomAccess.Anyone,
)
}
)
}
}
fun setRoomAddress(address: String) {
createRoomConfigFlow.getAndUpdate { config ->
config.copy(
roomVisibility = when (config.roomVisibility) {
is RoomVisibilityState.Public -> config.roomVisibility.copy(roomAddress = RoomAddress.Edited(address))
else -> config.roomVisibility
}
)
}
}
fun setRoomAccess(access: RoomAccessItem) {
createRoomConfigFlow.getAndUpdate { config ->
config.copy(
roomVisibility = when (config.roomVisibility) {
is RoomVisibilityState.Public -> {
when (access) {
RoomAccessItem.Anyone -> config.roomVisibility.copy(roomAccess = RoomAccess.Anyone)
RoomAccessItem.AskToJoin -> config.roomVisibility.copy(roomAccess = RoomAccess.Knocking)
}
}
else -> config.roomVisibility
}
)
}
}
fun clearCachedData() {

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.createroom.impl.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.selectable
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.createroom.impl.configureroom.RoomAccessItem
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.RadioButton
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun RoomAccessOption(
roomAccessItem: RoomAccessItem,
onOptionClick: (RoomAccessItem) -> Unit,
modifier: Modifier = Modifier,
isSelected: Boolean = false,
) {
Row(
modifier
.fillMaxWidth()
.selectable(
selected = isSelected,
onClick = { onOptionClick(roomAccessItem) },
role = Role.RadioButton,
)
) {
Column(Modifier.weight(1f)) {
Text(
text = stringResource(roomAccessItem.title),
style = ElementTheme.typography.fontBodyLgRegular,
color = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.size(8.dp))
Text(
text = stringResource(roomAccessItem.description),
style = ElementTheme.typography.fontBodySmRegular,
color = MaterialTheme.colorScheme.tertiary,
)
}
RadioButton(
modifier = Modifier
.align(Alignment.CenterVertically)
.size(48.dp),
selected = isSelected,
// null recommended for accessibility with screenreaders
onClick = null
)
}
}
@PreviewsDayNight
@Composable
internal fun RoomAccessOptionPreview() = ElementPreview {
val aRoomAccessItem = RoomAccessItem.Anyone
Column {
RoomAccessOption(
roomAccessItem = aRoomAccessItem,
onOptionClick = {},
isSelected = true,
)
RoomAccessOption(
roomAccessItem = aRoomAccessItem,
onOptionClick = {},
isSelected = false,
)
}
}

View File

@@ -7,6 +7,8 @@
package io.element.android.features.createroom.impl.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -14,15 +16,17 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.createroom.impl.configureroom.RoomPrivacyItem
import io.element.android.features.createroom.impl.configureroom.roomPrivacyItems
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -30,9 +34,9 @@ import io.element.android.libraries.designsystem.theme.components.RadioButton
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun RoomPrivacyOption(
roomPrivacyItem: RoomPrivacyItem,
onOptionClick: (RoomPrivacyItem) -> Unit,
fun RoomVisibilityOption(
roomPrivacyItem: RoomVisibilityItem,
onOptionClick: (RoomVisibilityItem) -> Unit,
modifier: Modifier = Modifier,
isSelected: Boolean = false,
) {
@@ -44,28 +48,31 @@ fun RoomPrivacyOption(
onClick = { onOptionClick(roomPrivacyItem) },
role = Role.RadioButton,
)
.padding(8.dp),
) {
Icon(
modifier = Modifier.padding(horizontal = 8.dp),
resourceId = roomPrivacyItem.icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.secondary,
)
Column(
Modifier
.weight(1f)
.padding(horizontal = 8.dp)
Box(
modifier = modifier
.size(30.dp)
.clip(RoundedCornerShape(8.dp))
.background(ElementTheme.colors.bgSubtleSecondary)
.padding(3.dp),
contentAlignment = Alignment.Center,
) {
Icon(
resourceId = roomPrivacyItem.icon,
contentDescription = null,
tint = if(isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary,
)
}
Spacer(Modifier.size(16.dp))
Column(Modifier.weight(1f)) {
Text(
text = roomPrivacyItem.title,
text = stringResource(roomPrivacyItem.title),
style = ElementTheme.typography.fontBodyLgRegular,
color = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.size(3.dp))
Text(
text = roomPrivacyItem.description,
text = stringResource(roomPrivacyItem.description),
style = ElementTheme.typography.fontBodySmRegular,
color = MaterialTheme.colorScheme.tertiary,
)
@@ -85,14 +92,14 @@ fun RoomPrivacyOption(
@PreviewsDayNight
@Composable
internal fun RoomPrivacyOptionPreview() = ElementPreview {
val aRoomPrivacyItem = roomPrivacyItems().first()
val aRoomPrivacyItem = RoomVisibilityItem.Private
Column {
RoomPrivacyOption(
RoomVisibilityOption(
roomPrivacyItem = aRoomPrivacyItem,
onOptionClick = {},
isSelected = true,
)
RoomPrivacyOption(
RoomVisibilityOption(
roomPrivacyItem = aRoomPrivacyItem,
onOptionClick = {},
isSelected = false,

View File

@@ -14,8 +14,10 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
sealed interface ConfigureRoomEvents {
data class RoomNameChanged(val name: String) : ConfigureRoomEvents
data class TopicChanged(val topic: String) : ConfigureRoomEvents
data class RoomPrivacyChanged(val privacy: RoomPrivacy) : ConfigureRoomEvents
data class RemoveFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents
data class RoomVisibilityChanged(val visibilityItem: RoomVisibilityItem) : ConfigureRoomEvents
data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents
data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents
data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents
data class CreateRoom(val config: CreateRoomConfig) : ConfigureRoomEvents
data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents
data object CancelCreateRoom : ConfigureRoomEvents

View File

@@ -54,7 +54,7 @@ class ConfigureRoomPresenter @Inject constructor(
@Composable
override fun present(): ConfigureRoomState {
val cameraPermissionState = cameraPermissionPresenter.present()
val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig())
val createRoomConfig = dataStore.createRoomConfig.collectAsState(CreateRoomConfig())
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
onResult = { uri -> if (uri != null) dataStore.setAvatarUri(uri = uri, cached = true) },
@@ -92,8 +92,10 @@ class ConfigureRoomPresenter @Inject constructor(
when (event) {
is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name)
is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic)
is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy)
is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser)
is ConfigureRoomEvents.RoomVisibilityChanged -> dataStore.setRoomVisibility(event.visibilityItem)
is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser)
is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess)
is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress)
is ConfigureRoomEvents.CreateRoom -> createRoom(event.config)
is ConfigureRoomEvents.HandleAvatarAction -> {
when (event.action) {
@@ -109,6 +111,7 @@ class ConfigureRoomPresenter @Inject constructor(
}
ConfigureRoomEvents.CancelCreateRoom -> createRoomAction.value = AsyncAction.Uninitialized
}
}
@@ -130,10 +133,10 @@ class ConfigureRoomPresenter @Inject constructor(
val params = CreateRoomParameters(
name = config.roomName,
topic = config.topic,
isEncrypted = config.privacy == RoomPrivacy.Private,
isEncrypted = config.roomVisibility is RoomVisibilityState.Private,
isDirect = false,
visibility = if (config.privacy == RoomPrivacy.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE,
preset = if (config.privacy == RoomPrivacy.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT,
visibility = if (config.roomVisibility is RoomVisibilityState.Public) RoomVisibility.PUBLIC else RoomVisibility.PRIVATE,
preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT,
invite = config.invites.map { it.userId },
avatar = avatarUrl,
)

View File

@@ -24,7 +24,10 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
roomName = "Room 101",
topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldnt be more than 3 lines",
invites = aMatrixUserList().toImmutableList(),
privacy = RoomPrivacy.Public,
roomVisibility = RoomVisibilityState.Public(
roomAddress = RoomAddress.AutoFilled("Room 101"),
roomAccess = RoomAccess.Knocking
),
),
),
)

View File

@@ -11,16 +11,22 @@ import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -29,22 +35,26 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.createroom.impl.R
import io.element.android.features.createroom.impl.components.RoomPrivacyOption
import io.element.android.features.createroom.impl.components.RoomAccessOption
import io.element.android.features.createroom.impl.components.RoomVisibilityOption
import io.element.android.libraries.designsystem.components.LabelledTextField
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.modifiers.clearFocusOnTap
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
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.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
@@ -103,23 +113,38 @@ fun ConfigureRoomView(
)
if (state.config.invites.isNotEmpty()) {
SelectedUsersRowList(
modifier = Modifier.padding(bottom = 16.dp),
contentPadding = PaddingValues(horizontal = 24.dp),
selectedUsers = state.config.invites,
onUserRemove = {
focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it))
state.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(it))
},
)
}
RoomPrivacyOptions(
modifier = Modifier.padding(bottom = 40.dp),
selected = state.config.privacy,
RoomVisibilityOptions(
selected = when (state.config.roomVisibility) {
is RoomVisibilityState.Private -> RoomVisibilityItem.Private
is RoomVisibilityState.Public -> RoomVisibilityItem.Public
},
onOptionClick = {
focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy))
state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(it))
},
)
if (state.config.roomVisibility is RoomVisibilityState.Public) {
RoomAccessOptions(
selected = state.config.roomVisibility.roomAccess,
onOptionClick = {
focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it))
},
)
RoomAddress(
modifier = Modifier.padding(horizontal = 16.dp),
address = state.config.roomVisibility.roomAddress,
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
)
}
}
}
@@ -221,24 +246,125 @@ private fun RoomTopic(
}
@Composable
private fun RoomPrivacyOptions(
selected: RoomPrivacy?,
onOptionClick: (RoomPrivacyItem) -> Unit,
private fun ConfigureRoomOptions(
title: String,
verticalArrangement: Arrangement.Vertical,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit,
) {
Column(
modifier = modifier
.selectableGroup()
.padding(horizontal = 12.dp),
verticalArrangement = verticalArrangement,
) {
Text(
text = title,
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textPrimary,
)
content()
}
}
@Composable
private fun RoomVisibilityOptions(
selected: RoomVisibilityItem,
onOptionClick: (RoomVisibilityItem) -> Unit,
modifier: Modifier = Modifier,
) {
val items = roomPrivacyItems()
Column(modifier = modifier.selectableGroup()) {
items.forEach { item ->
RoomPrivacyOption(
ConfigureRoomOptions(
title = "Room visibility",
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
RoomVisibilityItem.entries.forEach { item ->
RoomVisibilityOption(
roomPrivacyItem = item,
isSelected = selected == item.privacy,
isSelected = item == selected,
onOptionClick = onOptionClick,
)
}
}
}
@Composable
private fun RoomAccessOptions(
selected: RoomAccess,
onOptionClick: (RoomAccessItem) -> Unit,
modifier: Modifier = Modifier,
) {
ConfigureRoomOptions(
title = "Room access",
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
RoomAccessItem.entries.forEach { item ->
RoomAccessOption(
roomAccessItem = item,
isSelected = when (item) {
RoomAccessItem.Anyone -> selected == RoomAccess.Anyone
RoomAccessItem.AskToJoin -> selected == RoomAccess.Knocking
},
onOptionClick = onOptionClick,
)
}
}
}
@Composable
private fun RoomAddress(
address: RoomAddress,
onAddressChange: (String) -> Unit,
modifier: Modifier = Modifier,
){
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular,
color = MaterialTheme.colorScheme.primary,
text = "Room address",
)
TextField(
modifier = Modifier.fillMaxWidth(),
value = when(address) {
is RoomAddress.AutoFilled -> address.address
is RoomAddress.Edited -> address.address
},
leadingIcon = {
Text(
text = "#",
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textSecondary,
)
},
trailingIcon = {
Text(
text = ":myserver.com",
style = ElementTheme.typography.fontBodyLgMedium,
color = ElementTheme.colors.textSecondary,
modifier = Modifier.padding(end = 16.dp)
)
},
supportingText = {
Text(
text = "In order for this room to be visible in the public room directory, you will need to a room address. ",
style = ElementTheme.typography.fontBodySmRegular,
color = ElementTheme.colors.textSecondary,
)
},
onValueChange = onAddressChange,
singleLine = true,
)
}
}
@PreviewsDayNight
@PreviewWithLargeHeight
@Composable
internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview {
ConfigureRoomView(

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
@@ -7,7 +7,6 @@
package io.element.android.features.createroom.impl.configureroom
enum class RoomPrivacy {
Private,
Public,
enum class RoomAccess {
Anyone, Knocking, Invite
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.createroom.impl.configureroom
import androidx.annotation.StringRes
import io.element.android.libraries.ui.strings.CommonStrings
enum class RoomAccessItem(
@StringRes val title: Int,
@StringRes val description: Int
) {
Anyone(
title = CommonStrings.screen_create_room_access_section_anyone_option_title,
description = CommonStrings.screen_create_room_access_section_anyone_option_description,
),
AskToJoin(
title = CommonStrings.screen_create_room_access_section_knocking_option_title,
description = CommonStrings.screen_create_room_access_section_knocking_option_description,
)
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.createroom.impl.configureroom
sealed interface RoomAddress {
data class AutoFilled(val address: String) : RoomAddress
data class Edited(val address: String) : RoomAddress
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.createroom.impl.configureroom
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.features.createroom.impl.R
import io.element.android.libraries.designsystem.icons.CompoundDrawables
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
data class RoomPrivacyItem(
val privacy: RoomPrivacy,
@DrawableRes val icon: Int,
val title: String,
val description: String,
)
@Composable
fun roomPrivacyItems(): ImmutableList<RoomPrivacyItem> {
return RoomPrivacy.entries
.map {
when (it) {
RoomPrivacy.Private -> RoomPrivacyItem(
privacy = it,
icon = CompoundDrawables.ic_compound_lock_solid,
title = stringResource(R.string.screen_create_room_private_option_title),
description = stringResource(R.string.screen_create_room_private_option_description),
)
RoomPrivacy.Public -> RoomPrivacyItem(
privacy = it,
icon = CompoundDrawables.ic_compound_public,
title = stringResource(R.string.screen_create_room_public_option_title),
description = stringResource(R.string.screen_create_room_public_option_description),
)
}
}
.toImmutableList()
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.createroom.impl.configureroom
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import io.element.android.features.createroom.impl.R
import io.element.android.libraries.designsystem.icons.CompoundDrawables
enum class RoomVisibilityItem(
@DrawableRes val icon: Int,
@StringRes val title: Int,
@StringRes val description: Int
) {
Private(
icon = CompoundDrawables.ic_compound_lock,
title = R.string.screen_create_room_private_option_title,
description = R.string.screen_create_room_private_option_description,
),
Public(
icon = CompoundDrawables.ic_compound_public,
title = R.string.screen_create_room_public_option_title,
description = R.string.screen_create_room_public_option_description,
)
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.features.createroom.impl.configureroom
sealed interface RoomVisibilityState {
val roomAccess: RoomAccess
data object Private : RoomVisibilityState {
override val roomAccess: RoomAccess = RoomAccess.Invite
}
data class Public(
val roomAddress: RoomAddress,
override val roomAccess: RoomAccess
) : RoomVisibilityState
}

View File

@@ -103,7 +103,7 @@ class ConfigureRoomPresenterTest {
assertThat(initialState.config.topic).isNull()
assertThat(initialState.config.invites).isEmpty()
assertThat(initialState.config.avatarUri).isNull()
assertThat(initialState.config.privacy).isEqualTo(RoomPrivacy.Private)
assertThat(initialState.config.roomVisibility).isEqualTo(RoomVisibilityState.Private)
}
}
@@ -200,13 +200,13 @@ class ConfigureRoomPresenterTest {
assertThat(newState.config).isEqualTo(expectedConfig)
// Room privacy
newState.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(RoomPrivacy.Public))
newState.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityState.Public))
newState = awaitItem()
expectedConfig = expectedConfig.copy(privacy = RoomPrivacy.Public)
expectedConfig = expectedConfig.copy(roomVisibility = RoomVisibilityState.Public)
assertThat(newState.config).isEqualTo(expectedConfig)
// Remove user
newState.eventSink(ConfigureRoomEvents.RemoveFromSelection(selectedUser1))
newState.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(selectedUser1))
newState = awaitItem()
expectedConfig = expectedConfig.copy(invites = expectedConfig.invites.minus(selectedUser1).toImmutableList())
assertThat(newState.config).isEqualTo(expectedConfig)