feat(room address) : extract some reusable code
This commit is contained in:
parent
5a37a2eeef
commit
8406840169
7 changed files with 142 additions and 83 deletions
|
|
@ -17,7 +17,6 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
|
||||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||||
|
|
@ -32,9 +31,10 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
||||||
import io.element.android.libraries.matrix.api.createroom.RoomPreset
|
import io.element.android.libraries.matrix.api.createroom.RoomPreset
|
||||||
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
||||||
import io.element.android.libraries.matrix.api.roomAliasFromName
|
|
||||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidityEffect
|
||||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||||
|
|
@ -42,12 +42,10 @@ import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
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.delay
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Optional
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrDefault
|
||||||
|
|
||||||
class ConfigureRoomPresenter @Inject constructor(
|
class ConfigureRoomPresenter @Inject constructor(
|
||||||
private val dataStore: CreateRoomDataStore,
|
private val dataStore: CreateRoomDataStore,
|
||||||
|
|
@ -96,7 +94,12 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomAddressValidityEffect(createRoomConfig.roomVisibility.roomAddress()) { newRoomAddressValidity ->
|
RoomAddressValidityEffect(
|
||||||
|
client = matrixClient,
|
||||||
|
roomAliasHelper = roomAliasHelper,
|
||||||
|
newRoomAddress = createRoomConfig.roomVisibility.roomAddress().getOrDefault(""),
|
||||||
|
knownRoomAddress = null,
|
||||||
|
) { newRoomAddressValidity ->
|
||||||
roomAddressValidity.value = newRoomAddressValidity
|
roomAddressValidity.value = newRoomAddressValidity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,39 +149,6 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RoomAddressValidityEffect(
|
|
||||||
roomAddress: Optional<String>,
|
|
||||||
onRoomAddressValidityChange: (RoomAddressValidity) -> Unit,
|
|
||||||
) {
|
|
||||||
val onChange by rememberUpdatedState(onRoomAddressValidityChange)
|
|
||||||
LaunchedEffect(roomAddress) {
|
|
||||||
val roomAliasName = roomAddress.getOrNull().orEmpty()
|
|
||||||
if (roomAliasName.isEmpty()) {
|
|
||||||
onChange(RoomAddressValidity.Unknown)
|
|
||||||
return@LaunchedEffect
|
|
||||||
}
|
|
||||||
// debounce the room address validation
|
|
||||||
delay(300)
|
|
||||||
val roomAlias = matrixClient.roomAliasFromName(roomAliasName).getOrNull()
|
|
||||||
if (roomAlias == null || !roomAliasHelper.isRoomAliasValid(roomAlias)) {
|
|
||||||
onChange(RoomAddressValidity.InvalidSymbols)
|
|
||||||
} else {
|
|
||||||
matrixClient.resolveRoomAlias(roomAlias)
|
|
||||||
.onSuccess { resolved ->
|
|
||||||
if (resolved.isPresent) {
|
|
||||||
onChange(RoomAddressValidity.NotAvailable)
|
|
||||||
} else {
|
|
||||||
onChange(RoomAddressValidity.Valid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onFailure {
|
|
||||||
onChange(RoomAddressValidity.Valid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.createRoom(
|
private fun CoroutineScope.createRoom(
|
||||||
config: CreateRoomConfig,
|
config: CreateRoomConfig,
|
||||||
createRoomAction: MutableState<AsyncAction<RoomId>>
|
createRoomAction: MutableState<AsyncAction<RoomId>>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
import io.element.android.libraries.permissions.api.PermissionsState
|
import io.element.android.libraries.permissions.api.PermissionsState
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||||
import io.element.android.libraries.permissions.api.PermissionsState
|
import io.element.android.libraries.permissions.api.PermissionsState
|
||||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
|
@ -58,6 +57,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
|
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
|
||||||
import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList
|
import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList
|
||||||
import io.element.android.libraries.matrix.ui.components.UnsavedAvatar
|
import io.element.android.libraries.matrix.ui.components.UnsavedAvatar
|
||||||
|
import io.element.android.libraries.matrix.ui.room.address.RoomAddressField
|
||||||
import io.element.android.libraries.permissions.api.PermissionsView
|
import io.element.android.libraries.permissions.api.PermissionsView
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -142,10 +142,12 @@ fun ConfigureRoomView(
|
||||||
)
|
)
|
||||||
RoomAddressField(
|
RoomAddressField(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
address = state.config.roomVisibility.roomAddress,
|
address = state.config.roomVisibility.roomAddress.value,
|
||||||
homeserverName = state.homeserverName,
|
homeserverName = state.homeserverName,
|
||||||
addressValidity = state.roomAddressValidity,
|
addressValidity = state.roomAddressValidity,
|
||||||
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
|
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
|
||||||
|
label = stringResource(R.string.screen_create_room_room_address_section_title),
|
||||||
|
supportingText = stringResource(R.string.screen_create_room_room_address_section_footer),
|
||||||
)
|
)
|
||||||
Spacer(Modifier)
|
Spacer(Modifier)
|
||||||
}
|
}
|
||||||
|
|
@ -318,47 +320,6 @@ private fun RoomAccessOptions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RoomAddressField(
|
|
||||||
address: RoomAddress,
|
|
||||||
homeserverName: String,
|
|
||||||
addressValidity: RoomAddressValidity,
|
|
||||||
onAddressChange: (String) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
value = address.value,
|
|
||||||
label = stringResource(R.string.screen_create_room_room_address_section_title),
|
|
||||||
leadingIcon = {
|
|
||||||
Text(
|
|
||||||
text = "#",
|
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
|
||||||
color = ElementTheme.colors.textSecondary,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingIcon = {
|
|
||||||
Text(
|
|
||||||
text = homeserverName,
|
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
|
||||||
color = ElementTheme.colors.textSecondary,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
supportingText = when (addressValidity) {
|
|
||||||
RoomAddressValidity.InvalidSymbols -> {
|
|
||||||
stringResource(CommonStrings.error_room_address_invalid_symbols)
|
|
||||||
}
|
|
||||||
RoomAddressValidity.NotAvailable -> {
|
|
||||||
stringResource(CommonStrings.error_room_address_already_exists)
|
|
||||||
}
|
|
||||||
else -> stringResource(R.string.screen_create_room_room_address_section_footer)
|
|
||||||
},
|
|
||||||
isError = addressValidity.isError(),
|
|
||||||
onValueChange = onAddressChange,
|
|
||||||
singleLine = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewWithLargeHeight
|
@PreviewWithLargeHeight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
|
internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.ui.room.address
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RoomAddressField(
|
||||||
|
address: String,
|
||||||
|
homeserverName: String,
|
||||||
|
addressValidity: RoomAddressValidity,
|
||||||
|
onAddressChange: (String) -> Unit,
|
||||||
|
label: String,
|
||||||
|
supportingText: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
modifier = modifier,
|
||||||
|
value = address,
|
||||||
|
label = label,
|
||||||
|
leadingIcon = {
|
||||||
|
Text(
|
||||||
|
text = "#",
|
||||||
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
Text(
|
||||||
|
text = homeserverName,
|
||||||
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingText = when (addressValidity) {
|
||||||
|
RoomAddressValidity.InvalidSymbols -> {
|
||||||
|
stringResource(CommonStrings.error_room_address_invalid_symbols)
|
||||||
|
}
|
||||||
|
RoomAddressValidity.NotAvailable -> {
|
||||||
|
stringResource(CommonStrings.error_room_address_already_exists)
|
||||||
|
}
|
||||||
|
else -> supportingText
|
||||||
|
},
|
||||||
|
isError = addressValidity.isError(),
|
||||||
|
onValueChange = onAddressChange,
|
||||||
|
singleLine = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewsDayNight
|
||||||
|
@Composable
|
||||||
|
fun RoomAddressFieldPreview() = ElementPreview {
|
||||||
|
RoomAddressField(
|
||||||
|
address = "room",
|
||||||
|
homeserverName = "element.io",
|
||||||
|
addressValidity = RoomAddressValidity.Valid,
|
||||||
|
onAddressChange = {},
|
||||||
|
label = "Room address",
|
||||||
|
supportingText = "This is the address that people will use to join your room",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.createroom.impl.configureroom
|
package io.element.android.libraries.matrix.ui.room.address
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.ui.room.address
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
||||||
|
import io.element.android.libraries.matrix.api.roomAliasFromName
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RoomAddressValidityEffect(
|
||||||
|
client: MatrixClient,
|
||||||
|
roomAliasHelper: RoomAliasHelper,
|
||||||
|
newRoomAddress: String,
|
||||||
|
knownRoomAddress: String?,
|
||||||
|
onRoomAddressValidityChange: (RoomAddressValidity) -> Unit,
|
||||||
|
) {
|
||||||
|
val onChange by rememberUpdatedState(onRoomAddressValidityChange)
|
||||||
|
LaunchedEffect(newRoomAddress) {
|
||||||
|
if (newRoomAddress.isEmpty() || newRoomAddress == knownRoomAddress) {
|
||||||
|
onChange(RoomAddressValidity.Unknown)
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
// debounce the room address validation
|
||||||
|
delay(300)
|
||||||
|
val roomAlias = client.roomAliasFromName(newRoomAddress).getOrNull()
|
||||||
|
if (roomAlias == null || !roomAliasHelper.isRoomAliasValid(roomAlias)) {
|
||||||
|
onChange(RoomAddressValidity.InvalidSymbols)
|
||||||
|
} else {
|
||||||
|
client.resolveRoomAlias(roomAlias)
|
||||||
|
.onSuccess { resolved ->
|
||||||
|
if (resolved.isPresent) {
|
||||||
|
onChange(RoomAddressValidity.NotAvailable)
|
||||||
|
} else {
|
||||||
|
onChange(RoomAddressValidity.Valid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onFailure {
|
||||||
|
onChange(RoomAddressValidity.Valid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue