feat(room address) : extract some reusable code
This commit is contained in:
@@ -17,7 +17,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
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.RoomPreset
|
||||
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.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.mediaupload.api.MediaPreProcessor
|
||||
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 kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.Optional
|
||||
import javax.inject.Inject
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.jvm.optionals.getOrDefault
|
||||
|
||||
class ConfigureRoomPresenter @Inject constructor(
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
config: CreateRoomConfig,
|
||||
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.matrix.api.core.RoomId
|
||||
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 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.ui.components.aMatrixUserList
|
||||
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.aPermissionsState
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.SelectedUsersRowList
|
||||
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.ui.strings.CommonStrings
|
||||
|
||||
@@ -142,10 +142,12 @@ fun ConfigureRoomView(
|
||||
)
|
||||
RoomAddressField(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
address = state.config.roomVisibility.roomAddress,
|
||||
address = state.config.roomVisibility.roomAddress.value,
|
||||
homeserverName = state.homeserverName,
|
||||
addressValidity = state.roomAddressValidity,
|
||||
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)
|
||||
}
|
||||
@@ -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
|
||||
@Composable
|
||||
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.
|
||||
*/
|
||||
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
package io.element.android.libraries.matrix.ui.room.address
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user