create room : start integrating the address field

This commit is contained in:
ganfra 2024-10-30 18:20:57 +01:00
parent 93bb02ef3b
commit 2c107eeab3
11 changed files with 65 additions and 25 deletions

View file

@ -19,4 +19,6 @@ data class CreateRoomConfig(
val avatarUri: Uri? = null, val avatarUri: Uri? = null,
val invites: ImmutableList<MatrixUser> = persistentListOf(), val invites: ImmutableList<MatrixUser> = persistentListOf(),
val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private, val roomVisibility: RoomVisibilityState = RoomVisibilityState.Private,
) ) {
val isValid = roomName.isNullOrEmpty().not() && roomVisibility.isValid()
}

View file

@ -11,6 +11,7 @@ import android.net.Uri
import io.element.android.features.createroom.impl.configureroom.RoomAccess 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.RoomAccessItem
import io.element.android.features.createroom.impl.configureroom.RoomAddress import io.element.android.features.createroom.impl.configureroom.RoomAddress
import io.element.android.features.createroom.impl.configureroom.RoomAddressErrorState
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem 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.configureroom.RoomVisibilityState
import io.element.android.features.createroom.impl.di.CreateRoomScope import io.element.android.features.createroom.impl.di.CreateRoomScope
@ -23,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.getAndUpdate
import java.io.File import java.io.File
import java.text.Normalizer
import javax.inject.Inject import javax.inject.Inject
@SingleIn(CreateRoomScope::class) @SingleIn(CreateRoomScope::class)
@ -86,6 +86,7 @@ class CreateRoomDataStore @Inject constructor(
RoomVisibilityItem.Private -> RoomVisibilityState.Private RoomVisibilityItem.Private -> RoomVisibilityState.Private
RoomVisibilityItem.Public -> RoomVisibilityState.Public( RoomVisibilityItem.Public -> RoomVisibilityState.Public(
roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()), roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()),
roomAddressErrorState = RoomAddressErrorState.None,
roomAccess = RoomAccess.Anyone, roomAccess = RoomAccess.Anyone,
) )
} }

View file

@ -22,7 +22,7 @@ import io.element.android.features.createroom.impl.CreateRoomConfig
import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.createroom.impl.CreateRoomDataStore
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
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.core.RoomId
@ -39,7 +39,7 @@ import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Optional import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class ConfigureRoomPresenter @Inject constructor( class ConfigureRoomPresenter @Inject constructor(
@ -132,7 +132,7 @@ class ConfigureRoomPresenter @Inject constructor(
config: CreateRoomConfig, config: CreateRoomConfig,
createRoomAction: MutableState<AsyncAction<RoomId>> createRoomAction: MutableState<AsyncAction<RoomId>>
) = launch { ) = launch {
suspend { runUpdatingState(createRoomAction) {
val avatarUrl = config.avatarUri?.let { uploadAvatar(it) } val avatarUrl = config.avatarUri?.let { uploadAvatar(it) }
val params = CreateRoomParameters( val params = CreateRoomParameters(
name = config.roomName, name = config.roomName,
@ -144,13 +144,17 @@ class ConfigureRoomPresenter @Inject constructor(
preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT, preset = if (config.roomVisibility is RoomVisibilityState.Public) RoomPreset.PUBLIC_CHAT else RoomPreset.PRIVATE_CHAT,
invite = config.invites.map { it.userId }, invite = config.invites.map { it.userId },
avatar = avatarUrl, avatar = avatarUrl,
canonicalAlias = config.roomVisibility.roomAddress()
) )
matrixClient.createRoom(params).getOrThrow() matrixClient.createRoom(params)
.also { .onFailure { failure ->
Timber.e(failure, "Failed to create room")
}
.onSuccess {
dataStore.clearCachedData() dataStore.clearCachedData()
analyticsService.capture(CreatedRoom(isDM = false)) analyticsService.capture(CreatedRoom(isDM = false))
} }
}.runCatchingUpdatingState(createRoomAction) }
} }
private suspend fun uploadAvatar(avatarUri: Uri): String { private suspend fun uploadAvatar(avatarUri: Uri): String {

View file

@ -21,6 +21,4 @@ data class ConfigureRoomState(
val cameraPermissionState: PermissionsState, val cameraPermissionState: PermissionsState,
val homeserverName: String, val homeserverName: String,
val eventSink: (ConfigureRoomEvents) -> Unit val eventSink: (ConfigureRoomEvents) -> Unit
) { )
val isCreateButtonEnabled: Boolean = config.roomName.isNullOrEmpty().not()
}

View file

@ -26,7 +26,8 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
invites = aMatrixUserList().toImmutableList(), invites = aMatrixUserList().toImmutableList(),
roomVisibility = RoomVisibilityState.Public( roomVisibility = RoomVisibilityState.Public(
roomAddress = RoomAddress.AutoFilled("Room 101"), roomAddress = RoomAddress.AutoFilled("Room 101"),
roomAccess = RoomAccess.Knocking roomAccess = RoomAccess.Knocking,
roomAddressErrorState = RoomAddressErrorState.None,
), ),
), ),
), ),

View file

@ -78,7 +78,7 @@ fun ConfigureRoomView(
modifier = modifier.clearFocusOnTap(focusManager), modifier = modifier.clearFocusOnTap(focusManager),
topBar = { topBar = {
ConfigureRoomToolbar( ConfigureRoomToolbar(
isNextActionEnabled = state.isCreateButtonEnabled, isNextActionEnabled = state.config.isValid,
onBackClick = onBackClick, onBackClick = onBackClick,
onNextClick = { onNextClick = {
focusManager.clearFocus() focusManager.clearFocus()
@ -138,7 +138,7 @@ fun ConfigureRoomView(
state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it)) state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it))
}, },
) )
RoomAddress( RoomAddressField(
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
address = state.config.roomVisibility.roomAddress, address = state.config.roomVisibility.roomAddress,
homeserverName = state.homeserverName, homeserverName = state.homeserverName,
@ -310,7 +310,7 @@ private fun RoomAccessOptions(
} }
@Composable @Composable
private fun RoomAddress( private fun RoomAddressField(
address: RoomAddress, address: RoomAddress,
homeserverName: String, homeserverName: String,
onAddressChange: (String) -> Unit, onAddressChange: (String) -> Unit,
@ -329,10 +329,7 @@ private fun RoomAddress(
TextField( TextField(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
value = when (address) { value = address.value,
is RoomAddress.AutoFilled -> address.address
is RoomAddress.Edited -> address.address
},
leadingIcon = { leadingIcon = {
Text( Text(
text = "#", text = "#",

View file

@ -7,7 +7,7 @@
package io.element.android.features.createroom.impl.configureroom package io.element.android.features.createroom.impl.configureroom
sealed interface RoomAddress { sealed class RoomAddress(open val value: String) {
data class AutoFilled(val address: String) : RoomAddress data class AutoFilled(override val value: String) : RoomAddress(value)
data class Edited(val address: String) : RoomAddress data class Edited(override val value: String) : RoomAddress(value)
} }

View file

@ -0,0 +1,17 @@
/*
* 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
/**
* Represents the error state of a room address.
*/
sealed interface RoomAddressErrorState {
data object InvalidCharacters : RoomAddressErrorState
data object AlreadyExists : RoomAddressErrorState
data object None : RoomAddressErrorState
}

View file

@ -7,11 +7,28 @@
package io.element.android.features.createroom.impl.configureroom package io.element.android.features.createroom.impl.configureroom
import java.util.Optional
sealed interface RoomVisibilityState { sealed interface RoomVisibilityState {
data object Private : RoomVisibilityState data object Private : RoomVisibilityState
data class Public( data class Public(
val roomAddress: RoomAddress, val roomAddress: RoomAddress,
val roomAccess: RoomAccess val roomAddressErrorState: RoomAddressErrorState,
val roomAccess: RoomAccess,
) : RoomVisibilityState ) : RoomVisibilityState
fun roomAddress(): Optional<String> {
return when (this) {
is Private -> Optional.empty()
is Public -> Optional.of(roomAddress.value)
}
}
fun isValid(): Boolean {
return when (this) {
is Private -> true
is Public -> roomAddressErrorState is RoomAddressErrorState.None && roomAddress.value.isNotEmpty()
}
}
} }

View file

@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.api.createroom package io.element.android.libraries.matrix.api.createroom
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import java.util.Optional
data class CreateRoomParameters( data class CreateRoomParameters(
val name: String?, val name: String?,
@ -19,4 +20,5 @@ data class CreateRoomParameters(
val invite: List<UserId>? = null, val invite: List<UserId>? = null,
val avatar: String? = null, val avatar: String? = null,
val joinRuleOverride: JoinRuleOverride = JoinRuleOverride.None, val joinRuleOverride: JoinRuleOverride = JoinRuleOverride.None,
val canonicalAlias: Optional<String> = Optional.empty(),
) )

View file

@ -324,7 +324,8 @@ class RustMatrixClient(
joinRuleOverride = when (createRoomParams.joinRuleOverride) { joinRuleOverride = when (createRoomParams.joinRuleOverride) {
JoinRuleOverride.Knock -> RustJoinRule.Knock JoinRuleOverride.Knock -> RustJoinRule.Knock
JoinRuleOverride.None -> null JoinRuleOverride.None -> null
} },
canonicalAlias = createRoomParams.canonicalAlias.getOrNull(),
) )
val roomId = RoomId(client.createRoom(rustParams)) val roomId = RoomId(client.createRoom(rustParams))
// Wait to receive the room back from the sync but do not returns failure if it fails. // Wait to receive the room back from the sync but do not returns failure if it fails.