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 invites: ImmutableList<MatrixUser> = persistentListOf(),
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.RoomAccessItem
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.RoomVisibilityState
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.getAndUpdate
import java.io.File
import java.text.Normalizer
import javax.inject.Inject
@SingleIn(CreateRoomScope::class)
@@ -86,6 +86,7 @@ class CreateRoomDataStore @Inject constructor(
RoomVisibilityItem.Private -> RoomVisibilityState.Private
RoomVisibilityItem.Public -> RoomVisibilityState.Public(
roomAddress = RoomAddress.AutoFilled(config.roomName.orEmpty()),
roomAddressErrorState = RoomAddressErrorState.None,
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.libraries.architecture.AsyncAction
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.matrix.api.MatrixClient
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.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.util.Optional
import timber.log.Timber
import javax.inject.Inject
class ConfigureRoomPresenter @Inject constructor(
@@ -132,7 +132,7 @@ class ConfigureRoomPresenter @Inject constructor(
config: CreateRoomConfig,
createRoomAction: MutableState<AsyncAction<RoomId>>
) = launch {
suspend {
runUpdatingState(createRoomAction) {
val avatarUrl = config.avatarUri?.let { uploadAvatar(it) }
val params = CreateRoomParameters(
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,
invite = config.invites.map { it.userId },
avatar = avatarUrl,
canonicalAlias = config.roomVisibility.roomAddress()
)
matrixClient.createRoom(params).getOrThrow()
.also {
matrixClient.createRoom(params)
.onFailure { failure ->
Timber.e(failure, "Failed to create room")
}
.onSuccess {
dataStore.clearCachedData()
analyticsService.capture(CreatedRoom(isDM = false))
}
}.runCatchingUpdatingState(createRoomAction)
}
}
private suspend fun uploadAvatar(avatarUri: Uri): String {

View File

@@ -21,6 +21,4 @@ data class ConfigureRoomState(
val cameraPermissionState: PermissionsState,
val homeserverName: String,
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(),
roomVisibility = RoomVisibilityState.Public(
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),
topBar = {
ConfigureRoomToolbar(
isNextActionEnabled = state.isCreateButtonEnabled,
isNextActionEnabled = state.config.isValid,
onBackClick = onBackClick,
onNextClick = {
focusManager.clearFocus()
@@ -138,7 +138,7 @@ fun ConfigureRoomView(
state.eventSink(ConfigureRoomEvents.RoomAccessChanged(it))
},
)
RoomAddress(
RoomAddressField(
modifier = Modifier.padding(horizontal = 16.dp),
address = state.config.roomVisibility.roomAddress,
homeserverName = state.homeserverName,
@@ -310,7 +310,7 @@ private fun RoomAccessOptions(
}
@Composable
private fun RoomAddress(
private fun RoomAddressField(
address: RoomAddress,
homeserverName: String,
onAddressChange: (String) -> Unit,
@@ -326,13 +326,10 @@ private fun RoomAddress(
color = MaterialTheme.colorScheme.primary,
text = "Room address",
)
TextField(
modifier = Modifier.fillMaxWidth(),
value = when (address) {
is RoomAddress.AutoFilled -> address.address
is RoomAddress.Edited -> address.address
},
value = address.value,
leadingIcon = {
Text(
text = "#",

View File

@@ -7,7 +7,7 @@
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
sealed class RoomAddress(open val value: String) {
data class AutoFilled(override val value: String) : RoomAddress(value)
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
import java.util.Optional
sealed interface RoomVisibilityState {
data object Private : RoomVisibilityState
data class Public(
val roomAddress: RoomAddress,
val roomAccess: RoomAccess
val roomAddressErrorState: RoomAddressErrorState,
val roomAccess: RoomAccess,
) : 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
import io.element.android.libraries.matrix.api.core.UserId
import java.util.Optional
data class CreateRoomParameters(
val name: String?,
@@ -19,4 +20,5 @@ data class CreateRoomParameters(
val invite: List<UserId>? = null,
val avatar: String? = null,
val joinRuleOverride: JoinRuleOverride = JoinRuleOverride.None,
val canonicalAlias: Optional<String> = Optional.empty(),
)

View File

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