From fb337dacd60b5e0305f042855ade8cdab4b323c5 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 10 Jun 2021 15:42:53 +0200 Subject: [PATCH 1/9] Add space alias in creation wizard --- .../api/session/room/RoomDirectoryService.kt | 4 + .../sdk/api/session/space/SpaceService.kt | 6 +- .../room/DefaultRoomDirectoryService.kt | 18 +++- .../alias/RoomAliasAvailabilityChecker.kt | 4 +- .../session/space/DefaultSpaceService.kt | 3 +- .../app/features/form/FormEditTextItem.kt | 24 +++++- .../features/spaces/SpaceCreationActivity.kt | 3 + .../spaces/create/CreateSpaceAction.kt | 1 + .../create/CreateSpaceDetailsFragment.kt | 4 + .../spaces/create/CreateSpaceEvents.kt | 1 + .../spaces/create/CreateSpaceState.kt | 4 + .../spaces/create/CreateSpaceViewModel.kt | 82 +++++++++++++++++-- .../spaces/create/CreateSpaceViewModelTask.kt | 4 +- .../create/SpaceDetailEpoxyController.kt | 50 ++++++++--- vector/src/main/res/values/strings.xml | 1 + 15 files changed, 183 insertions(+), 26 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt index 176de8e408..cb74115f54 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.api.session.room +import arrow.core.Either +import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse @@ -40,4 +42,6 @@ interface RoomDirectoryService { * Set the visibility of a room in the directory */ suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) + + suspend fun checkAliasAvailability(aliasLocalPart: String?) : Result } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt index fedf38fe06..e5288e4045 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt @@ -36,7 +36,11 @@ interface SpaceService { /** * Just a shortcut for space creation for ease of use */ - suspend fun createSpace(name: String, topic: String?, avatarUri: Uri?, isPublic: Boolean): String + suspend fun createSpace(name: String, + topic: String?, + avatarUri: Uri?, + isPublic: Boolean, + roomAliasLocalPart: String? = null): String /** * Get a space from a roomId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt index 218d846afb..5cd5d0adb2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt @@ -16,10 +16,15 @@ package org.matrix.android.sdk.internal.session.room +import arrow.core.Either +import arrow.core.Right import org.matrix.android.sdk.api.session.room.RoomDirectoryService +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError +import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse +import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask @@ -28,7 +33,8 @@ import javax.inject.Inject internal class DefaultRoomDirectoryService @Inject constructor( private val getPublicRoomTask: GetPublicRoomTask, private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask, - private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask + private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask, + private val roomAliasAvailabilityChecker: RoomAliasAvailabilityChecker ) : RoomDirectoryService { override suspend fun getPublicRooms(server: String?, @@ -43,4 +49,14 @@ internal class DefaultRoomDirectoryService @Inject constructor( override suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) { setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility)) } + + + override suspend fun checkAliasAvailability(aliasLocalPart: String?): Result { + return try { + roomAliasAvailabilityChecker.check(aliasLocalPart) + Result.success(Unit) + } catch (failure: RoomAliasError) { + Result.failure(failure) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt index b39cbaa582..66164c5280 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt @@ -51,13 +51,13 @@ internal class RoomAliasAvailabilityChecker @Inject constructor( } catch (throwable: Throwable) { if (throwable is Failure.ServerError && throwable.httpCode == 404) { // This is a 404, so the alias is available: nominal case - null + return } else { // Other error, propagate it throw throwable } } - ?.let { + .let { // Alias already exists: error case throw RoomAliasError.AliasNotAvailable } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index 9c6153b349..0c5c0416f9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -66,12 +66,13 @@ internal class DefaultSpaceService @Inject constructor( return createRoomTask.executeRetry(params, 3) } - override suspend fun createSpace(name: String, topic: String?, avatarUri: Uri?, isPublic: Boolean): String { + override suspend fun createSpace(name: String, topic: String?, avatarUri: Uri?, isPublic: Boolean, roomAliasLocalPart: String?): String { return createSpace(CreateSpaceParams().apply { this.name = name this.topic = topic this.avatarUri = avatarUri if (isPublic) { + this.roomAliasName = roomAliasLocalPart this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( invite = 0 ) diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index e2203057ab..8960ddcd02 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -40,6 +40,9 @@ abstract class FormEditTextItem : VectorEpoxyModel() { @EpoxyAttribute var value: String? = null + @EpoxyAttribute + var forceUpdateValue: Boolean = false + @EpoxyAttribute var errorMessage: String? = null @@ -64,12 +67,23 @@ abstract class FormEditTextItem : VectorEpoxyModel() { @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var editorActionListener: TextView.OnEditorActionListener? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var onFocusChange: ((Boolean) -> Unit)? = null + + @EpoxyAttribute + var inputPrefix: String? = null + + @EpoxyAttribute + var inputSuffix: String? = null + private val onTextChangeListener = object : SimpleTextWatcher() { override fun afterTextChanged(s: Editable) { onTextChange?.invoke(s.toString()) } } + private val onFocusChangedListener = View.OnFocusChangeListener { _, hasFocus -> onFocusChange?.invoke(hasFocus) } + override fun bind(holder: Holder) { super.bind(holder) holder.textInputLayout.isEnabled = enabled @@ -77,7 +91,14 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputLayout.error = errorMessage holder.textInputLayout.endIconMode = endIconMode ?: TextInputLayout.END_ICON_NONE - holder.setValueOnce(holder.textInputEditText, value) + holder.textInputLayout.prefixText = inputPrefix + holder.textInputLayout.suffixText = inputSuffix + + if (forceUpdateValue) { + holder.textInputEditText.setText(value) + } else { + holder.setValueOnce(holder.textInputEditText, value) + } holder.textInputEditText.isEnabled = enabled inputType?.let { holder.textInputEditText.inputType = it } @@ -86,6 +107,7 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputEditText.addTextChangedListenerOnce(onTextChangeListener) holder.textInputEditText.setOnEditorActionListener(editorActionListener) + holder.textInputEditText.onFocusChangeListener = onFocusChangedListener } override fun shouldSaveViewState(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt index 6bf31dd5ce..a02755a155 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt @@ -113,6 +113,9 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac CreateSpaceEvents.HideModalLoading -> { hideWaitingView() } + is CreateSpaceEvents.ShowModalLoading -> { + showWaitingView(it.message) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt index cd31b40354..1f0ed6428f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt @@ -23,6 +23,7 @@ sealed class CreateSpaceAction : VectorViewModelAction { data class SetRoomType(val type: SpaceType) : CreateSpaceAction() data class NameChanged(val name: String) : CreateSpaceAction() data class TopicChanged(val topic: String) : CreateSpaceAction() + data class SpaceAliasChanged(val aliasLocalPart: String) : CreateSpaceAction() data class SetAvatar(val uri: Uri?) : CreateSpaceAction() object OnBackPressed : CreateSpaceAction() object NextFromDetails : CreateSpaceAction() diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt index b5925eec59..544c33948b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt @@ -84,6 +84,10 @@ class CreateSpaceDetailsFragment @Inject constructor( sharedViewModel.handle(CreateSpaceAction.TopicChanged(newTopic)) } + override fun setAliasLocalPart(aliasLocalPart: String) { + sharedViewModel.handle(CreateSpaceAction.SpaceAliasChanged(aliasLocalPart)) + } + override fun onBackPressed(toolbarButton: Boolean): Boolean { sharedViewModel.handle(CreateSpaceAction.OnBackPressed) return true diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt index c3fa2b2068..073531353f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt @@ -27,4 +27,5 @@ sealed class CreateSpaceEvents : VectorViewEvents { data class FinishSuccess(val spaceId: String, val defaultRoomId: String?, val topology: SpaceTopology?) : CreateSpaceEvents() data class ShowModalError(val errorMessage: String) : CreateSpaceEvents() object HideModalLoading : CreateSpaceEvents() + data class ShowModalLoading(val message: String?) : CreateSpaceEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt index 147fd3a616..39a69e837b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt @@ -28,6 +28,10 @@ data class CreateSpaceState( val step: Step = Step.ChooseType, val spaceType: SpaceType? = null, val spaceTopology: SpaceTopology? = null, + val homeServerName: String? = null, + val aliasLocalPart: String? = null, + val aliasManuallyModified: Boolean = false, + val aliasVerificationTask: Async = Uninitialized, val nameInlineError: String? = null, val defaultRooms: Map? = null, val creationResult: Async = Uninitialized diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index aff342cea7..7b430dd411 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -35,14 +35,25 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure class CreateSpaceViewModel @AssistedInject constructor( @Assisted initialState: CreateSpaceState, + private val session: Session, private val stringProvider: StringProvider, private val createSpaceViewModelTask: CreateSpaceViewModelTask, private val errorFormatter: ErrorFormatter ) : VectorViewModel(initialState) { + init { + setState { + copy( + homeServerName = session.myUserId.substringAfter(":") + ) + } + } + @AssistedFactory interface Factory { fun create(initialState: CreateSpaceState): CreateSpaceViewModel @@ -80,10 +91,15 @@ class CreateSpaceViewModel @AssistedInject constructor( _viewEvents.post(CreateSpaceEvents.NavigateToDetails) } is CreateSpaceAction.NameChanged -> { + val tentativeAlias = + getAliasFromName(action.name) + setState { copy( nameInlineError = null, - name = action.name + name = action.name, + aliasLocalPart = tentativeAlias, + aliasVerificationTask = Uninitialized ) } } @@ -94,6 +110,15 @@ class CreateSpaceViewModel @AssistedInject constructor( ) } } + is CreateSpaceAction.SpaceAliasChanged -> { + setState { + copy( + aliasManuallyModified = true, + aliasLocalPart = action.aliasLocalPart, + aliasVerificationTask = Uninitialized + ) + } + } CreateSpaceAction.OnBackPressed -> { handleBackNavigation() } @@ -121,6 +146,12 @@ class CreateSpaceViewModel @AssistedInject constructor( }.exhaustive } + private fun getAliasFromName(name: String): String { + return Regex("\\s").replace(name.lowercase(), "_").let { + "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") + } + } + private fun handleSetTopology(action: CreateSpaceAction.SetSpaceTopology) { when (action.topology) { SpaceTopology.JustMe -> { @@ -204,12 +235,31 @@ class CreateSpaceViewModel @AssistedInject constructor( } _viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType) } else { + // it'a public space, let's check alias + val aliasLocalPart = if (state.aliasManuallyModified) state.aliasLocalPart else getAliasFromName(state.name) + _viewEvents.post(CreateSpaceEvents.ShowModalLoading(null)) setState { - copy( - step = CreateSpaceState.Step.AddRooms + copy(aliasVerificationTask = Loading()) + } + viewModelScope.launch { + session.checkAliasAvailability(aliasLocalPart).fold( + { + setState { + copy( + step = CreateSpaceState.Step.AddRooms + ) + } + _viewEvents.post(CreateSpaceEvents.HideModalLoading) + _viewEvents.post(CreateSpaceEvents.NavigateToAddRooms) + }, + { + setState { + copy(aliasVerificationTask = Fail(it)) + } + _viewEvents.post(CreateSpaceEvents.HideModalLoading) + } ) } - _viewEvents.post(CreateSpaceEvents.NavigateToAddRooms) } } } @@ -221,6 +271,9 @@ class CreateSpaceViewModel @AssistedInject constructor( } viewModelScope.launch(Dispatchers.IO) { try { + val alias = if (state.spaceType == SpaceType.Public) { + if (state.aliasManuallyModified) state.aliasLocalPart else getAliasFromName(state.name) + } else null val result = createSpaceViewModelTask.execute( CreateSpaceTaskParams( spaceName = spaceName, @@ -230,7 +283,8 @@ class CreateSpaceViewModel @AssistedInject constructor( defaultRooms = state.defaultRooms ?.entries ?.sortedBy { it.key } - ?.mapNotNull { it.value } ?: emptyList() + ?.mapNotNull { it.value } ?: emptyList(), + spaceAlias = alias ) ) when (result) { @@ -260,10 +314,22 @@ class CreateSpaceViewModel @AssistedInject constructor( ) } is CreateSpaceTaskResult.FailedToCreateSpace -> { - setState { - copy(creationResult = Fail(result.failure)) + if (result.failure is CreateRoomFailure.AliasError) { + setState { + copy( + step = CreateSpaceState.Step.SetDetails, + aliasVerificationTask = Fail(result.failure.aliasError), + creationResult = Uninitialized + ) + } + _viewEvents.post(CreateSpaceEvents.HideModalLoading) + _viewEvents.post(CreateSpaceEvents.NavigateToDetails) + } else { + setState { + copy(creationResult = Fail(result.failure)) + } + _viewEvents.post(CreateSpaceEvents.ShowModalError(errorFormatter.toHumanReadable(result.failure))) } - _viewEvents.post(CreateSpaceEvents.ShowModalError(errorFormatter.toHumanReadable(result.failure))) } } } catch (failure: Throwable) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt index 9ce1089f6c..ae9ab4ca63 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -45,6 +45,7 @@ data class CreateSpaceTaskParams( val spaceName: String, val spaceTopic: String?, val spaceAvatar: Uri? = null, + val spaceAlias: String? = null, val isPublic: Boolean, val defaultRooms: List = emptyList() ) @@ -57,7 +58,8 @@ class CreateSpaceViewModelTask @Inject constructor( override suspend fun execute(params: CreateSpaceTaskParams): CreateSpaceTaskResult { val spaceID = try { - session.spaceService().createSpace(params.spaceName, params.spaceTopic, params.spaceAvatar, params.isPublic) + session.spaceService().createSpace(params.spaceName, params.spaceTopic, params.spaceAvatar, + params.isPublic, params.spaceAlias) } catch (failure: Throwable) { return CreateSpaceTaskResult.FailedToCreateSpace(failure) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index 9b3686c513..36a04ef6fa 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -17,24 +17,39 @@ package im.vector.app.features.spaces.create import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Fail import im.vector.app.R +import im.vector.app.core.epoxy.TextListener import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableSquareAvatarItem import im.vector.app.features.form.formMultiLineEditTextItem import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject class SpaceDetailEpoxyController @Inject constructor( private val stringProvider: StringProvider, - private val avatarRenderer: AvatarRenderer + private val avatarRenderer: AvatarRenderer, + private val roomAliasErrorFormatter: RoomAliasErrorFormatter ) : TypedEpoxyController() { var listener: Listener? = null -// var shouldForceFocusOnce = true + /** + * Alias text can be automatically set when changing the room name, + * We have to be able to make a difference between a programming change versus + * a user change. + */ + var aliasTextIsFocused = false + private val aliasTextWatcher: TextListener = { + if (aliasTextIsFocused) { + listener?.setAliasLocalPart(it) + } + } override fun buildModels(data: CreateSpaceState?) { val host = this @@ -65,20 +80,32 @@ class SpaceDetailEpoxyController @Inject constructor( value(data?.name) hint(host.stringProvider.getString(R.string.create_room_name_hint)) errorMessage(data?.nameInlineError) -// onBind { _, view, _ -> -// if (shouldForceFocusOnce && data?.name.isNullOrBlank()) { -// shouldForceFocusOnce = false -// // sad face :( -// view.textInputEditText.post { -// view.textInputEditText.showKeyboard(true) -// } -// } -// } onTextChange { text -> host.listener?.onNameChange(text) } } + if (data?.spaceType == SpaceType.Public) { + formEditTextItem { + id("alias") + enabled(true) + forceUpdateValue(!data.aliasManuallyModified) + value(data.aliasLocalPart) + hint(host.stringProvider.getString(R.string.create_space_alias_hint)) + inputSuffix(":" + data.homeServerName) + inputPrefix("#") + onFocusChange { hasFocus -> + host.aliasTextIsFocused = hasFocus + } + errorMessage( + host.roomAliasErrorFormatter.format( + (((data.aliasVerificationTask as? Fail)?.error) as? RoomAliasError)) + ) + onTextChange(host.aliasTextWatcher) + showBottomSeparator(false) + } + } + formMultiLineEditTextItem { id("topic") enabled(true) @@ -96,5 +123,6 @@ class SpaceDetailEpoxyController @Inject constructor( fun onAvatarChange() fun onNameChange(newName: String) fun onTopicChange(newTopic: String) + fun setAliasLocalPart(aliasLocalPart: String) } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 621ecb7aee..0f012ef31d 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2585,6 +2585,7 @@ You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later. Room address + Space address This address is already in use Please provide a room address Some characters are not allowed From b1ab6eb58f3eaa23b475f00a8728c338a3116c57 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 10 Jun 2021 16:17:24 +0200 Subject: [PATCH 2/9] Add alias management in space settings --- .../sdk/api/session/room/RoomDirectoryService.kt | 2 -- .../session/room/DefaultRoomDirectoryService.kt | 4 ---- .../roomprofile/alias/RoomAliasController.kt | 5 ++++- .../features/spaces/manage/SpaceManageActivity.kt | 10 ++++++++++ .../spaces/manage/SpaceManageSharedViewModel.kt | 1 + .../spaces/manage/SpaceManagedSharedAction.kt | 1 + .../spaces/manage/SpaceManagedSharedViewEvents.kt | 1 + .../spaces/manage/SpaceSettingsController.kt | 12 ++++++++++++ .../features/spaces/manage/SpaceSettingsFragment.kt | 4 ++++ vector/src/main/res/values/strings.xml | 2 ++ 10 files changed, 35 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt index cb74115f54..4b19e7ba96 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt @@ -16,8 +16,6 @@ package org.matrix.android.sdk.api.session.room -import arrow.core.Either -import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt index 5cd5d0adb2..7da1da27ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt @@ -16,11 +16,8 @@ package org.matrix.android.sdk.internal.session.room -import arrow.core.Either -import arrow.core.Right import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.alias.RoomAliasError -import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse @@ -50,7 +47,6 @@ internal class DefaultRoomDirectoryService @Inject constructor( setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility)) } - override suspend fun checkAliasAvailability(aliasLocalPart: String?): Result { return try { roomAliasAvailabilityChecker.check(aliasLocalPart) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 97050e9c6d..0689cf2f56 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -39,6 +39,7 @@ import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomType import javax.inject.Inject class RoomAliasController @Inject constructor( @@ -71,7 +72,9 @@ class RoomAliasController @Inject constructor( // Published alias buildPublishInfo(data) // Room directory visibility - buildRoomDirectoryVisibility(data) + if (data.roomSummary.invoke()?.roomType != RoomType.SPACE) { + buildRoomDirectoryVisibility(data) + } // Local alias buildLocalInfo(data) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt index 334c7bd64d..630c578069 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt @@ -39,6 +39,7 @@ import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomdirectory.createroom.CreateRoomArgs import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment import im.vector.app.features.roomprofile.RoomProfileArgs +import im.vector.app.features.roomprofile.alias.RoomAliasFragment import kotlinx.parcelize.Parcelize import javax.inject.Inject @@ -155,6 +156,15 @@ class SpaceManageActivity : VectorBaseActivity(), ) } } + SpaceManagedSharedViewEvents.NavigateToAliasSettings -> { + args?.spaceId?.let { spaceId -> + addFragmentToBackstack( + R.id.simpleFragmentContainer, + RoomAliasFragment::class.java, + RoomProfileArgs(spaceId) + ) + } + } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt index 57c47250f9..f1d041056f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt @@ -56,6 +56,7 @@ class SpaceManageSharedViewModel @AssistedInject constructor( SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading) SpaceManagedSharedAction.CreateRoom -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateRoom) SpaceManagedSharedAction.ManageRooms -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToManageRooms) + SpaceManagedSharedAction.OpenSpaceAliasesSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToAliasSettings) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt index 0b413a3b8a..77143470bc 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt @@ -24,4 +24,5 @@ sealed class SpaceManagedSharedAction : VectorViewModelAction { object HideLoading : SpaceManagedSharedAction() object CreateRoom : SpaceManagedSharedAction() object ManageRooms : SpaceManagedSharedAction() + object OpenSpaceAliasesSettings : SpaceManagedSharedAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt index da6f01d205..ab993764c6 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt @@ -24,4 +24,5 @@ sealed class SpaceManagedSharedViewEvents : VectorViewEvents { object HideLoading : SpaceManagedSharedViewEvents() object NavigateToCreateRoom : SpaceManagedSharedViewEvents() object NavigateToManageRooms : SpaceManagedSharedViewEvents() + object NavigateToAliasSettings : SpaceManagedSharedViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt index 08b21db025..10dd11527d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt @@ -52,6 +52,7 @@ class SpaceSettingsController @Inject constructor( fun onDevRoomSettings() fun onManageRooms() fun setIsPublic(public: Boolean) + fun onRoomAliasesClicked() } var callback: Callback? = null @@ -139,6 +140,17 @@ class SpaceSettingsController @Inject constructor( if (data.actionPermissions.canAddChildren) callback?.onManageRooms() } ) + if (roomSummary.isPublic) { + buildProfileAction( + id = "alias", + title = stringProvider.getString(R.string.space_settings_alias_title), + subtitle = stringProvider.getString(R.string.space_settings_alias_subtitle), + dividerColor = dividerColor, + divider = true, + editable = true, + action = { callback?.onRoomAliasesClicked() } + ) + } if (vectorPreferences.developerMode()) { buildProfileAction( diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt index 350c0bdb4a..e831732bcc 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt @@ -233,6 +233,10 @@ class SpaceSettingsFragment @Inject constructor( } } + override fun onRoomAliasesClicked() { + sharedViewModel.handle(SpaceManagedSharedAction.OpenSpaceAliasesSettings) + } + override fun onImageReady(uri: Uri?) { uri ?: return viewModel.handle( diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 0f012ef31d..54356db664 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1414,6 +1414,8 @@ Room addresses See and managed addresses of this room, and its visibility in the room directory. + Space addresses + See and managed addresses of this space. Room Addresses Published Addresses From 28a6caf754bd8e8331afc0f9bf9c58b14fbbf9d4 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 10 Jun 2021 17:07:58 +0200 Subject: [PATCH 3/9] cleaning + rebase --- .../app/features/form/FormEditTextItem.kt | 1 + .../createroom/CreateRoomController.kt | 6 +- .../createroom/RoomAliasEditItem.kt | 82 ------------------- .../roomprofile/alias/RoomAliasController.kt | 7 +- .../create/SpaceDetailEpoxyController.kt | 1 - .../spaces/manage/SpaceSettingsController.kt | 8 +- .../res/layout/item_room_alias_text_input.xml | 54 ------------ 7 files changed, 13 insertions(+), 146 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasEditItem.kt delete mode 100644 vector/src/main/res/layout/item_room_alias_text_input.xml diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index 8960ddcd02..5607ddcd5d 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -17,6 +17,7 @@ package im.vector.app.features.form import android.text.Editable +import android.view.View import android.view.inputmethod.EditorInfo import android.widget.TextView import com.airbnb.epoxy.EpoxyAttribute diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index 61b34a1499..3ac61f1803 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -99,11 +99,13 @@ class CreateRoomController @Inject constructor( } if (viewState.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public) { // Room alias for public room - roomAliasEditItem { + formEditTextItem { id("alias") enabled(enableFormElement) value(viewState.roomVisibilityType.aliasLocalPart) - homeServer(":" + viewState.homeServerName) + inputSuffix(":" + viewState.homeServerName) + inputPrefix("#") + hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage( host.roomAliasErrorFormatter.format( (((viewState.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasEditItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasEditItem.kt deleted file mode 100644 index 9cb6f79a56..0000000000 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasEditItem.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.roomdirectory.createroom - -import android.text.Editable -import android.widget.TextView -import com.airbnb.epoxy.EpoxyAttribute -import com.airbnb.epoxy.EpoxyModelClass -import com.google.android.material.textfield.TextInputEditText -import com.google.android.material.textfield.TextInputLayout -import im.vector.app.R -import im.vector.app.core.epoxy.TextListener -import im.vector.app.core.epoxy.VectorEpoxyHolder -import im.vector.app.core.epoxy.VectorEpoxyModel -import im.vector.app.core.epoxy.addTextChangedListenerOnce -import im.vector.app.core.epoxy.setValueOnce -import im.vector.app.core.platform.SimpleTextWatcher - -@EpoxyModelClass(layout = R.layout.item_room_alias_text_input) -abstract class RoomAliasEditItem : VectorEpoxyModel() { - - @EpoxyAttribute - var value: String? = null - - @EpoxyAttribute - var errorMessage: String? = null - - @EpoxyAttribute - var homeServer: String? = null - - @EpoxyAttribute - var enabled: Boolean = true - - @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) - var onTextChange: TextListener? = null - - private val onTextChangeListener = object : SimpleTextWatcher() { - override fun afterTextChanged(s: Editable) { - onTextChange?.invoke(s.toString()) - } - } - - override fun bind(holder: Holder) { - super.bind(holder) - holder.textInputLayout.isEnabled = enabled - holder.textInputLayout.error = errorMessage - - holder.setValueOnce(holder.textInputEditText, value) - holder.textInputEditText.isEnabled = enabled - holder.textInputEditText.addTextChangedListenerOnce(onTextChangeListener) - holder.homeServerText.text = homeServer - } - - override fun shouldSaveViewState(): Boolean { - return false - } - - override fun unbind(holder: Holder) { - super.unbind(holder) - holder.textInputEditText.removeTextChangedListener(onTextChangeListener) - } - - class Holder : VectorEpoxyHolder() { - val textInputLayout by bind(R.id.itemRoomAliasTextInputLayout) - val textInputEditText by bind(R.id.itemRoomAliasTextInputEditText) - val homeServerText by bind(R.id.itemRoomAliasHomeServer) - } -} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 0689cf2f56..50bc3d1e5c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -36,7 +36,6 @@ import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formSwitchItem import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter -import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomType @@ -246,10 +245,12 @@ class RoomAliasController @Inject constructor( } } is RoomAliasViewState.AddAliasState.Editing -> { - roomAliasEditItem { + formEditTextItem { id("newLocalAlias") value(data.newLocalAliasState.value) - homeServer(":" + data.homeServerName) + inputSuffix(":" + data.homeServerName) + inputPrefix("#") + hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage(host.roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError)) onTextChange { value -> host.callback?.setNewLocalAliasLocalPart(value) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index 36a04ef6fa..ad544e2648 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -102,7 +102,6 @@ class SpaceDetailEpoxyController @Inject constructor( (((data.aliasVerificationTask as? Fail)?.error) as? RoomAliasError)) ) onTextChange(host.aliasTextWatcher) - showBottomSeparator(false) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt index 10dd11527d..11a4a982a0 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt @@ -104,6 +104,7 @@ class SpaceSettingsController @Inject constructor( } } + val isPublic = (data.newRoomJoinRules.newJoinRules ?: data.currentRoomJoinRules) == RoomJoinRules.PUBLIC if (vectorPreferences.labsUseExperimentalRestricted()) { buildProfileAction( id = "joinRule", @@ -114,7 +115,6 @@ class SpaceSettingsController @Inject constructor( action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() } ) } else { - val isPublic = (data.newRoomJoinRules.newJoinRules ?: data.currentRoomJoinRules) == RoomJoinRules.PUBLIC formSwitchItem { id("isPublic") enabled(data.actionPermissions.canChangeJoinRule) @@ -134,18 +134,18 @@ class SpaceSettingsController @Inject constructor( id = "manage_rooms", title = stringProvider.getString(R.string.space_settings_manage_rooms), // subtitle = data.getJoinRuleWording(stringProvider), - divider = vectorPreferences.developerMode(), + divider = true, editable = data.actionPermissions.canAddChildren, action = { if (data.actionPermissions.canAddChildren) callback?.onManageRooms() } ) - if (roomSummary.isPublic) { + + if (isPublic) { buildProfileAction( id = "alias", title = stringProvider.getString(R.string.space_settings_alias_title), subtitle = stringProvider.getString(R.string.space_settings_alias_subtitle), - dividerColor = dividerColor, divider = true, editable = true, action = { callback?.onRoomAliasesClicked() } diff --git a/vector/src/main/res/layout/item_room_alias_text_input.xml b/vector/src/main/res/layout/item_room_alias_text_input.xml deleted file mode 100644 index 1cac4763c4..0000000000 --- a/vector/src/main/res/layout/item_room_alias_text_input.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - From b6b80120b2d2084139ded75f2231d8fb4bf55037 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 10 Jun 2021 17:13:26 +0200 Subject: [PATCH 4/9] update change log --- newsfragment/3483.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragment/3483.feature diff --git a/newsfragment/3483.feature b/newsfragment/3483.feature new file mode 100644 index 0000000000..e6c27992ea --- /dev/null +++ b/newsfragment/3483.feature @@ -0,0 +1 @@ +Add option to set aliases for public spaces \ No newline at end of file From 88119a15bf5cab5cb9de6046f756ebed00d232f0 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 15 Jun 2021 11:22:29 +0200 Subject: [PATCH 5/9] Core review rename input Suffix|Prefix --- .../java/im/vector/app/features/form/FormEditTextItem.kt | 8 ++++---- .../roomdirectory/createroom/CreateRoomController.kt | 4 ++-- .../app/features/roomprofile/alias/RoomAliasController.kt | 4 ++-- .../features/spaces/create/SpaceDetailEpoxyController.kt | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index 5607ddcd5d..2b2fddd0c9 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -72,10 +72,10 @@ abstract class FormEditTextItem : VectorEpoxyModel() { var onFocusChange: ((Boolean) -> Unit)? = null @EpoxyAttribute - var inputPrefix: String? = null + var prefixText: String? = null @EpoxyAttribute - var inputSuffix: String? = null + var suffixText: String? = null private val onTextChangeListener = object : SimpleTextWatcher() { override fun afterTextChanged(s: Editable) { @@ -92,8 +92,8 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputLayout.error = errorMessage holder.textInputLayout.endIconMode = endIconMode ?: TextInputLayout.END_ICON_NONE - holder.textInputLayout.prefixText = inputPrefix - holder.textInputLayout.suffixText = inputSuffix + holder.textInputLayout.prefixText = prefixText + holder.textInputLayout.suffixText = suffixText if (forceUpdateValue) { holder.textInputEditText.setText(value) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index 3ac61f1803..6c441c355c 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -103,8 +103,8 @@ class CreateRoomController @Inject constructor( id("alias") enabled(enableFormElement) value(viewState.roomVisibilityType.aliasLocalPart) - inputSuffix(":" + viewState.homeServerName) - inputPrefix("#") + suffixText(":" + viewState.homeServerName) + prefixText("#") hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage( host.roomAliasErrorFormatter.format( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 50bc3d1e5c..4a683b6292 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -248,8 +248,8 @@ class RoomAliasController @Inject constructor( formEditTextItem { id("newLocalAlias") value(data.newLocalAliasState.value) - inputSuffix(":" + data.homeServerName) - inputPrefix("#") + suffixText(":" + data.homeServerName) + prefixText("#") hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage(host.roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError)) onTextChange { value -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index ad544e2648..27c08d1f6f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -92,8 +92,8 @@ class SpaceDetailEpoxyController @Inject constructor( forceUpdateValue(!data.aliasManuallyModified) value(data.aliasLocalPart) hint(host.stringProvider.getString(R.string.create_space_alias_hint)) - inputSuffix(":" + data.homeServerName) - inputPrefix("#") + suffixText(":" + data.homeServerName) + prefixText("#") onFocusChange { hasFocus -> host.aliasTextIsFocused = hasFocus } From 9190e1bfb5f12a5cb37c2d06188f042522a95d65 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 15 Jun 2021 11:22:44 +0200 Subject: [PATCH 6/9] Code Review Formatting --- .../features/spaces/create/CreateSpaceViewModelTask.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt index ae9ab4ca63..f1731caf76 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -58,8 +58,13 @@ class CreateSpaceViewModelTask @Inject constructor( override suspend fun execute(params: CreateSpaceTaskParams): CreateSpaceTaskResult { val spaceID = try { - session.spaceService().createSpace(params.spaceName, params.spaceTopic, params.spaceAvatar, - params.isPublic, params.spaceAlias) + session.spaceService().createSpace( + params.spaceName, + params.spaceTopic, + params.spaceAvatar, + params.isPublic, + params.spaceAlias + ) } catch (failure: Throwable) { return CreateSpaceTaskResult.FailedToCreateSpace(failure) } From 19a2b56011d4e3d31e53a4cfb816bf32dabdb888 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 15 Jun 2021 11:22:56 +0200 Subject: [PATCH 7/9] Code review AliasAvailabilityResult --- .../session/room/AliasAvailabilityResult.kt | 24 +++++++++++++++++++ .../api/session/room/RoomDirectoryService.kt | 2 +- .../room/DefaultRoomDirectoryService.kt | 7 +++--- .../spaces/create/CreateSpaceViewModel.kt | 20 +++++++++++----- 4 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt new file mode 100644 index 0000000000..d5c0d06215 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.room + +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError + +sealed class AliasAvailabilityResult { + object Available: AliasAvailabilityResult() + data class NotAvailable(val roomAliasError: RoomAliasError) : AliasAvailabilityResult() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt index 4b19e7ba96..f3e3913bc1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt @@ -41,5 +41,5 @@ interface RoomDirectoryService { */ suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) - suspend fun checkAliasAvailability(aliasLocalPart: String?) : Result + suspend fun checkAliasAvailability(aliasLocalPart: String?) : AliasAvailabilityResult } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt index 7da1da27ed..7330c91c20 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room +import org.matrix.android.sdk.api.session.room.AliasAvailabilityResult import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility @@ -47,12 +48,12 @@ internal class DefaultRoomDirectoryService @Inject constructor( setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility)) } - override suspend fun checkAliasAvailability(aliasLocalPart: String?): Result { + override suspend fun checkAliasAvailability(aliasLocalPart: String?): AliasAvailabilityResult { return try { roomAliasAvailabilityChecker.check(aliasLocalPart) - Result.success(Unit) + AliasAvailabilityResult.Available } catch (failure: RoomAliasError) { - Result.failure(failure) + AliasAvailabilityResult.NotAvailable(failure) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index 7b430dd411..960d428581 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -36,6 +36,7 @@ import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.AliasAvailabilityResult import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure class CreateSpaceViewModel @AssistedInject constructor( @@ -242,8 +243,9 @@ class CreateSpaceViewModel @AssistedInject constructor( copy(aliasVerificationTask = Loading()) } viewModelScope.launch { - session.checkAliasAvailability(aliasLocalPart).fold( - { + try { + when (val result = session.checkAliasAvailability(aliasLocalPart)) { + AliasAvailabilityResult.Available -> { setState { copy( step = CreateSpaceState.Step.AddRooms @@ -251,14 +253,20 @@ class CreateSpaceViewModel @AssistedInject constructor( } _viewEvents.post(CreateSpaceEvents.HideModalLoading) _viewEvents.post(CreateSpaceEvents.NavigateToAddRooms) - }, - { + } + is AliasAvailabilityResult.NotAvailable -> { setState { - copy(aliasVerificationTask = Fail(it)) + copy(aliasVerificationTask = Fail(result.roomAliasError)) } _viewEvents.post(CreateSpaceEvents.HideModalLoading) } - ) + } + } catch (failure: Throwable) { + setState { + copy(aliasVerificationTask = Fail(failure)) + } + _viewEvents.post(CreateSpaceEvents.HideModalLoading) + } } } } From 301251262d755fc8a576ec38ec66278e9fccd02a Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 15 Jun 2021 12:20:26 +0200 Subject: [PATCH 8/9] Code review cleaning --- .../spaces/create/CreateSpaceViewModel.kt | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index 960d428581..1e47f9b690 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -92,16 +92,24 @@ class CreateSpaceViewModel @AssistedInject constructor( _viewEvents.post(CreateSpaceEvents.NavigateToDetails) } is CreateSpaceAction.NameChanged -> { - val tentativeAlias = - getAliasFromName(action.name) setState { - copy( - nameInlineError = null, - name = action.name, - aliasLocalPart = tentativeAlias, - aliasVerificationTask = Uninitialized - ) + if (aliasManuallyModified) { + copy( + nameInlineError = null, + name = action.name, + aliasVerificationTask = Uninitialized + ) + } else { + val tentativeAlias = + getAliasFromName(action.name) + copy( + nameInlineError = null, + name = action.name, + aliasLocalPart = tentativeAlias, + aliasVerificationTask = Uninitialized + ) + } } } is CreateSpaceAction.TopicChanged -> { @@ -112,6 +120,8 @@ class CreateSpaceViewModel @AssistedInject constructor( } } is CreateSpaceAction.SpaceAliasChanged -> { + // This called only when the alias is change manually + // not when programmatically changed via a change on name setState { copy( aliasManuallyModified = true, @@ -237,7 +247,7 @@ class CreateSpaceViewModel @AssistedInject constructor( _viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType) } else { // it'a public space, let's check alias - val aliasLocalPart = if (state.aliasManuallyModified) state.aliasLocalPart else getAliasFromName(state.name) + val aliasLocalPart = state.aliasLocalPart _viewEvents.post(CreateSpaceEvents.ShowModalLoading(null)) setState { copy(aliasVerificationTask = Loading()) @@ -280,7 +290,7 @@ class CreateSpaceViewModel @AssistedInject constructor( viewModelScope.launch(Dispatchers.IO) { try { val alias = if (state.spaceType == SpaceType.Public) { - if (state.aliasManuallyModified) state.aliasLocalPart else getAliasFromName(state.name) + state.aliasLocalPart } else null val result = createSpaceViewModelTask.execute( CreateSpaceTaskParams( From 4180b4ccd3f5fc5ab6214230a0fe4f76fb6b8e44 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 18 Jun 2021 10:47:21 +0200 Subject: [PATCH 9/9] Code review --- .../java/org/matrix/android/sdk/api/MatrixPatterns.kt | 6 ++++++ .../sdk/api/session/room/AliasAvailabilityResult.kt | 2 +- .../app/features/spaces/create/CreateSpaceViewModel.kt | 10 ++-------- .../features/spaces/manage/SpaceSettingsController.kt | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt index 841e833271..7f5f3f54ef 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt @@ -157,4 +157,10 @@ object MatrixPatterns { fun isValidOrderString(order: String?) : Boolean { return order != null && order.length < 50 && order matches ORDER_STRING_REGEX } + + fun candidateAliasFromRoomName(name: String): String { + return Regex("\\s").replace(name.lowercase(), "_").let { + "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt index d5c0d06215..6f607569c0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index 1e47f9b690..60110b7dd5 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -35,6 +35,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.AliasAvailabilityResult import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure @@ -92,7 +93,6 @@ class CreateSpaceViewModel @AssistedInject constructor( _viewEvents.post(CreateSpaceEvents.NavigateToDetails) } is CreateSpaceAction.NameChanged -> { - setState { if (aliasManuallyModified) { copy( @@ -102,7 +102,7 @@ class CreateSpaceViewModel @AssistedInject constructor( ) } else { val tentativeAlias = - getAliasFromName(action.name) + MatrixPatterns.candidateAliasFromRoomName(action.name) copy( nameInlineError = null, name = action.name, @@ -157,12 +157,6 @@ class CreateSpaceViewModel @AssistedInject constructor( }.exhaustive } - private fun getAliasFromName(name: String): String { - return Regex("\\s").replace(name.lowercase(), "_").let { - "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") - } - } - private fun handleSetTopology(action: CreateSpaceAction.SetSpaceTopology) { when (action.topology) { SpaceTopology.JustMe -> { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt index 11a4a982a0..27204be8a6 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt @@ -134,7 +134,7 @@ class SpaceSettingsController @Inject constructor( id = "manage_rooms", title = stringProvider.getString(R.string.space_settings_manage_rooms), // subtitle = data.getJoinRuleWording(stringProvider), - divider = true, + divider = vectorPreferences.developerMode() || isPublic, editable = data.actionPermissions.canAddChildren, action = { if (data.actionPermissions.canAddChildren) callback?.onManageRooms() @@ -146,7 +146,7 @@ class SpaceSettingsController @Inject constructor( id = "alias", title = stringProvider.getString(R.string.space_settings_alias_title), subtitle = stringProvider.getString(R.string.space_settings_alias_subtitle), - divider = true, + divider = vectorPreferences.developerMode(), editable = true, action = { callback?.onRoomAliasesClicked() } )