diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt index d77dfcfe35..246813a524 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt @@ -22,16 +22,16 @@ import org.matrix.android.sdk.api.util.JsonDict @JsonClass(generateAdapter = true) data class ThirdPartyUser( - /* - Required. A Matrix User ID represting a third party user. + /** + * Required. A Matrix User ID representing a third party user. */ @Json(name = "userid") val userId: String, - /* - Required. The protocol ID that the third party location is a part of. + /** + * Required. The protocol ID that the third party location is a part of. */ @Json(name = "protocol") val protocol: String, - /* - Required. Information used to identify this third party location. + /** + * Required. Information used to identify this third party location. */ @Json(name = "fields") val fields: JsonDict ) diff --git a/newsfragment/1458.feature b/newsfragment/1458.feature new file mode 100644 index 0000000000..ded4f549ed --- /dev/null +++ b/newsfragment/1458.feature @@ -0,0 +1 @@ +Allow user to add custom "network" in room search \ No newline at end of file diff --git a/newsfragment/3196.feature b/newsfragment/3196.feature new file mode 100644 index 0000000000..ca84dd51c8 --- /dev/null +++ b/newsfragment/3196.feature @@ -0,0 +1 @@ +Add Gitter.im as a default in the Change Network menu \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/extensions/Activity.kt b/vector/src/main/java/im/vector/app/core/extensions/Activity.kt index 55ec8b605e..de469b9e3a 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Activity.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Activity.kt @@ -94,6 +94,10 @@ fun AppCompatActivity.addFragmentToBackstack( } } +fun AppCompatActivity.popBackstack() { + supportFragmentManager.popBackStack() +} + fun AppCompatActivity.resetBackstack() { repeat(supportFragmentManager.backStackEntryCount) { supportFragmentManager.popBackStack() diff --git a/vector/src/main/java/im/vector/app/core/ui/list/VerticalMarginItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/VerticalMarginItem.kt new file mode 100644 index 0000000000..ec99c7c215 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/list/VerticalMarginItem.kt @@ -0,0 +1,45 @@ +/* + * Copyright 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 im.vector.app.core.ui.list + +import android.view.View +import androidx.core.view.updateLayoutParams +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel + +/** + * A generic item with empty space. + */ +@EpoxyModelClass(layout = R.layout.item_vertical_margin) +abstract class VerticalMarginItem : VectorEpoxyModel() { + + @EpoxyAttribute + var heightInPx: Int = 0 + + override fun bind(holder: Holder) { + super.bind(holder) + holder.space.updateLayoutParams { + height = heightInPx + } + } + + class Holder : VectorEpoxyHolder() { + val space by bind(R.id.item_vertical_margin_space) + } +} diff --git a/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt b/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt index b59b24fe55..47059128a1 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt @@ -33,6 +33,9 @@ abstract class SettingsContinueCancelItem : EpoxyModelWithHolder() { var inputType: Int? = null @EpoxyAttribute - var singleLine: Boolean? = null + var singleLine: Boolean = true @EpoxyAttribute var imeOptions: Int? = null @@ -60,9 +61,13 @@ abstract class FormEditTextItem : VectorEpoxyModel() { @EpoxyAttribute var endIconMode: Int? = null - @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + // FIXME restore EpoxyAttribute.Option.DoNotHash and fix that properly + @EpoxyAttribute var onTextChange: ((String) -> Unit)? = null + @EpoxyAttribute + var editorActionListener: TextView.OnEditorActionListener? = null + private val onTextChangeListener = object : SimpleTextWatcher() { override fun afterTextChanged(s: Editable) { onTextChange?.invoke(s.toString()) @@ -80,10 +85,11 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputEditText.isEnabled = enabled inputType?.let { holder.textInputEditText.inputType = it } - holder.textInputEditText.isSingleLine = singleLine ?: false + holder.textInputEditText.isSingleLine = singleLine holder.textInputEditText.imeOptions = imeOptions ?: EditorInfo.IME_ACTION_NONE holder.textInputEditText.addTextChangedListener(onTextChangeListener) + holder.textInputEditText.setOnEditorActionListener(editorActionListener) holder.bottomSeparator.isVisible = showBottomSeparator } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 3abf01583c..ed57ccb04c 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -65,6 +65,7 @@ import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinArgs import im.vector.app.features.pin.PinMode import im.vector.app.features.roomdirectory.RoomDirectoryActivity +import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity import im.vector.app.features.roomdirectory.roompreview.RoomPreviewActivity import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData @@ -86,7 +87,6 @@ import im.vector.app.features.widgets.WidgetArgsBuilder import im.vector.app.space import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetType @@ -129,7 +129,7 @@ class DefaultNavigator @Inject constructor( } appStateHandler.setCurrentSpace(spaceId) when (postSwitchSpaceAction) { - Navigator.PostSwitchSpaceAction.None -> { + Navigator.PostSwitchSpaceAction.None -> { // go back to home if we are showing room details? // This is a bit ugly, but the navigator is supposed to know about the activity stack if (context is RoomDetailActivity) { @@ -139,7 +139,7 @@ class DefaultNavigator @Inject constructor( Navigator.PostSwitchSpaceAction.OpenAddExistingRooms -> { startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false) } - is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> { + is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> { val args = RoomDetailArgs( postSwitchSpaceAction.roomId, eventId = null, @@ -278,7 +278,7 @@ class DefaultNavigator @Inject constructor( val intent = RoomDirectoryActivity.getIntent(context, initialFilter) context.startActivity(intent) } - is RoomGroupingMethod.BySpace -> { + is RoomGroupingMethod.BySpace -> { val selectedSpace = groupingMethod.space() if (selectedSpace == null) { val intent = RoomDirectoryActivity.getIntent(context, initialFilter) @@ -320,7 +320,7 @@ class DefaultNavigator @Inject constructor( val intent = InviteUsersToRoomActivity.getIntent(context, roomId) context.startActivity(intent) } - is RoomGroupingMethod.BySpace -> { + is RoomGroupingMethod.BySpace -> { if (currentGroupingMethod.spaceSummary != null) { // let user decides if he does it from space or room (context as? AppCompatActivity)?.supportFragmentManager?.let { fm -> diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 444c48bddb..cf0263a1e8 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -26,11 +26,11 @@ import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.login.LoginConfig import im.vector.app.features.media.AttachmentData import im.vector.app.features.pin.PinMode +import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.util.MatrixItem @@ -44,7 +44,7 @@ interface Navigator { sealed class PostSwitchSpaceAction { object None : PostSwitchSpaceAction() data class OpenDefaultRoom(val roomId: String, val showShareSheet: Boolean) : PostSwitchSpaceAction() - object OpenAddExistingRooms: PostSwitchSpaceAction() + object OpenAddExistingRooms : PostSwitchSpaceAction() } fun switchToSpace(context: Context, spaceId: String, postSwitchSpaceAction: PostSwitchSpaceAction) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt index 16e5428b9c..fdab72caba 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsViewState.kt @@ -21,7 +21,6 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData data class PublicRoomsViewState( // The current filter diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryAction.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryAction.kt index a94cb7709f..77eec57ab3 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryAction.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roomdirectory import im.vector.app.core.platform.VectorViewModelAction -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData sealed class RoomDirectoryAction : VectorViewModelAction { data class SetRoomDirectoryData(val roomDirectoryData: RoomDirectoryData) : RoomDirectoryAction() diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt index d8edbcf503..9a63e81a2f 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt @@ -25,6 +25,7 @@ import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack +import im.vector.app.core.extensions.popBackstack import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment @@ -58,7 +59,7 @@ class RoomDirectoryActivity : VectorBaseActivity() { .observe() .subscribe { sharedAction -> when (sharedAction) { - is RoomDirectorySharedAction.Back -> onBackPressed() + is RoomDirectorySharedAction.Back -> popBackstack() is RoomDirectorySharedAction.CreateRoom -> { // Transmit the filter to the CreateRoomFragment withState(roomDirectoryViewModel) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/thirdparty/RoomDirectoryData.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt similarity index 75% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/thirdparty/RoomDirectoryData.kt rename to vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt index 91f429d773..49bb769460 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/thirdparty/RoomDirectoryData.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * 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. @@ -14,13 +14,12 @@ * limitations under the License. */ -package org.matrix.android.sdk.api.session.room.model.thirdparty +package im.vector.app.features.roomdirectory /** - * This class describes a rooms directory server. + * This class describes a rooms directory server protocol. */ data class RoomDirectoryData( - /** * The server name (might be null) * Set null when the server is the current user's home server. @@ -30,7 +29,12 @@ data class RoomDirectoryData( /** * The display name (the server description) */ - val displayName: String = DEFAULT_HOME_SERVER_NAME, + val displayName: String = MATRIX_PROTOCOL_NAME, + + /** + * the avatar url + */ + val avatarUrl: String? = null, /** * The third party server identifier @@ -40,15 +44,10 @@ data class RoomDirectoryData( /** * Tell if all the federated servers must be included */ - val includeAllNetworks: Boolean = false, - - /** - * the avatar url - */ - val avatarUrl: String? = null + val includeAllNetworks: Boolean = false ) { companion object { - const val DEFAULT_HOME_SERVER_NAME = "Matrix" + const val MATRIX_PROTOCOL_NAME = "Matrix" } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryServer.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryServer.kt new file mode 100644 index 0000000000..0f29ae5986 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryServer.kt @@ -0,0 +1,37 @@ +/* + * 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 im.vector.app.features.roomdirectory + +data class RoomDirectoryServer( + val serverName: String, + + /** + * True if this is the current user server + */ + val isUserServer: Boolean, + + /** + * True if manually added, so it can be removed by the user + */ + val isManuallyAdded: Boolean, + + /** + * Supported protocols + * TODO Rename RoomDirectoryData to RoomDirectoryProtocols + */ + val protocols: List +) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index f64105b759..dc1cbfc58d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -37,7 +37,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFilter import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -230,9 +229,7 @@ class RoomDirectoryViewModel @AssistedInject constructor( Timber.w("Try to join an already joining room. Should not happen") return@withState } - val viaServers = state.roomDirectoryData.homeServer - ?.let { listOf(it) } - .orEmpty() + val viaServers = listOfNotNull(state.roomDirectoryData.homeServer) viewModelScope.launch { try { session.joinRoom(action.roomId, viaServers = viaServers) 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 efb54650b8..aaf82c5791 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 @@ -75,6 +75,7 @@ class CreateRoomController @Inject constructor( id("topic") enabled(enableFormElement) value(viewState.roomTopic) + singleLine(false) hint(host.stringProvider.getString(R.string.create_room_topic_hint)) onTextChange { text -> diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt index 7b2e329b6a..7cf8e538ac 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt @@ -16,10 +16,12 @@ package im.vector.app.features.roomdirectory.picker +import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.core.view.isInvisible +import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R @@ -43,6 +45,9 @@ abstract class RoomDirectoryItem : VectorEpoxyModel() @EpoxyAttribute var includeAllNetworks: Boolean = false + @EpoxyAttribute + var checked: Boolean = false + @EpoxyAttribute var globalListener: (() -> Unit)? = null @@ -63,6 +68,7 @@ abstract class RoomDirectoryItem : VectorEpoxyModel() holder.nameView.text = directoryName holder.descriptionView.setTextOrHide(directoryDescription) + holder.checkedView.isVisible = checked } class Holder : VectorEpoxyHolder() { @@ -71,5 +77,6 @@ abstract class RoomDirectoryItem : VectorEpoxyModel() val avatarView by bind(R.id.itemRoomDirectoryAvatar) val nameView by bind(R.id.itemRoomDirectoryName) val descriptionView by bind(R.id.itemRoomDirectoryDescription) + val checkedView by bind(R.id.itemRoomDirectoryChecked) } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt index d51ad5040b..65d8f2d1cb 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt @@ -18,55 +18,110 @@ package im.vector.app.features.roomdirectory.picker import im.vector.app.R import im.vector.app.core.resources.StringArrayProvider +import im.vector.app.features.roomdirectory.RoomDirectoryData +import im.vector.app.features.roomdirectory.RoomDirectoryServer import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import javax.inject.Inject -class RoomDirectoryListCreator @Inject constructor(private val stringArrayProvider: StringArrayProvider, - private val session: Session) { +class RoomDirectoryListCreator @Inject constructor( + private val stringArrayProvider: StringArrayProvider, + private val session: Session +) { - fun computeDirectories(thirdPartyProtocolData: Map): List { - val result = ArrayList() + fun computeDirectories(thirdPartyProtocolData: Map, + customHomeservers: Set): List { + val result = ArrayList() + + val protocols = ArrayList() // Add user homeserver name val userHsName = session.myUserId.substringAfter(":") - result.add(RoomDirectoryData( - displayName = userHsName, - includeAllNetworks = true - )) - - // Add user's HS but for Matrix public rooms only - result.add(RoomDirectoryData()) - - // Add custom directory servers - val hsNamesList = stringArrayProvider.getStringArray(R.array.room_directory_servers) - hsNamesList.forEach { - if (it != userHsName) { - // Use the server name as a default display name - result.add(RoomDirectoryData( - homeServer = it, - displayName = it, - includeAllNetworks = true - )) - } - } + // Add default protocol + protocols.add( + RoomDirectoryData( + homeServer = null, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = false + ) + ) // Add result of the request thirdPartyProtocolData.forEach { it.value.instances?.forEach { thirdPartyProtocolInstance -> - result.add(RoomDirectoryData( - homeServer = null, - displayName = thirdPartyProtocolInstance.desc ?: "", - thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId, - includeAllNetworks = false, - // Default to protocol icon - avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon - )) + protocols.add( + RoomDirectoryData( + homeServer = null, + displayName = thirdPartyProtocolInstance.desc ?: "", + thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId, + includeAllNetworks = false, + // Default to protocol icon + avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon + ) + ) } } + // Add all rooms + protocols.add( + RoomDirectoryData( + homeServer = null, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = true + ) + ) + + result.add( + RoomDirectoryServer( + serverName = userHsName, + isUserServer = true, + isManuallyAdded = false, + protocols = protocols + ) + ) + + // Add custom directory servers, form the config file, excluding the current user homeserver + stringArrayProvider.getStringArray(R.array.room_directory_servers) + .filter { it != userHsName } + .forEach { + // Use the server name as a default display name + result.add( + RoomDirectoryServer( + serverName = it, + isUserServer = false, + isManuallyAdded = false, + protocols = listOf( + RoomDirectoryData( + homeServer = it, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = false + ) + ) + ) + ) + } + + // Add manually added server by the user + customHomeservers + .forEach { + // Use the server name as a default display name + result.add( + RoomDirectoryServer( + serverName = it, + isUserServer = false, + isManuallyAdded = true, + protocols = listOf( + RoomDirectoryData( + homeServer = it, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = false + ) + ) + ) + ) + } + return result } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerAction.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerAction.kt index 36f2cd4296..8be3c6b2b2 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerAction.kt @@ -17,7 +17,14 @@ package im.vector.app.features.roomdirectory.picker import im.vector.app.core.platform.VectorViewModelAction +import im.vector.app.features.roomdirectory.RoomDirectoryServer sealed class RoomDirectoryPickerAction : VectorViewModelAction { object Retry : RoomDirectoryPickerAction() + object EnterEditMode : RoomDirectoryPickerAction() + object ExitEditMode : RoomDirectoryPickerAction() + data class SetServerUrl(val url: String) : RoomDirectoryPickerAction() + data class RemoveServer(val roomDirectoryServer: RoomDirectoryServer) : RoomDirectoryPickerAction() + + object Submit : RoomDirectoryPickerAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt index 75e9807bd0..9a397c586d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt @@ -16,37 +16,62 @@ package im.vector.app.features.roomdirectory.picker +import android.text.InputType +import android.view.KeyEvent +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.TextView import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import im.vector.app.R +import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.join +import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData +import im.vector.app.core.ui.list.genericButtonItem +import im.vector.app.core.ui.list.verticalMarginItem +import im.vector.app.core.utils.DimensionConverter +import im.vector.app.features.discovery.settingsContinueCancelItem +import im.vector.app.features.discovery.settingsInformationItem +import im.vector.app.features.form.formEditTextItem +import im.vector.app.features.roomdirectory.RoomDirectoryData +import im.vector.app.features.roomdirectory.RoomDirectoryServer +import org.matrix.android.sdk.api.failure.Failure import javax.inject.Inject +import javax.net.ssl.HttpsURLConnection -class RoomDirectoryPickerController @Inject constructor(private val stringProvider: StringProvider, - private val errorFormatter: ErrorFormatter, - private val roomDirectoryListCreator: RoomDirectoryListCreator +class RoomDirectoryPickerController @Inject constructor( + private val stringProvider: StringProvider, + private val colorProvider: ColorProvider, + private val dimensionConverter: DimensionConverter, + private val errorFormatter: ErrorFormatter ) : TypedEpoxyController() { + var currentRoomDirectoryData: RoomDirectoryData? = null var callback: Callback? = null - var index = 0 + private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) - override fun buildModels(viewState: RoomDirectoryPickerViewState) { + override fun buildModels(data: RoomDirectoryPickerViewState) { val host = this - val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest - when (asyncThirdPartyProtocol) { + when (val asyncThirdPartyProtocol = data.asyncThirdPartyRequest) { is Success -> { - val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol()) - - directories.forEach { - buildDirectory(it) + data.directories.join( + each = { _, roomDirectoryServer -> buildDirectory(roomDirectoryServer) }, + between = { idx, _ -> buildDivider(idx) } + ) + buildForm(data) + verticalMarginItem { + id("space_bottom") + heightInPx(host.dimensionConverter.dpToPx(16)) } } is Incomplete -> { @@ -64,28 +89,131 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid } } - private fun buildDirectory(roomDirectoryData: RoomDirectoryData) { + private fun buildForm(data: RoomDirectoryPickerViewState) { + buildDivider(1000) val host = this - roomDirectoryItem { - id(host.index++) - - directoryName(roomDirectoryData.displayName) - - val description = when { - roomDirectoryData.includeAllNetworks -> - host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName) - "Matrix" == roomDirectoryData.displayName -> - host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName) - else -> - null + if (data.inEditMode) { + verticalMarginItem { + id("form_space") + heightInPx(host.dimensionConverter.dpToPx(16)) } + settingsInformationItem { + id("form_notice") + message(host.stringProvider.getString(R.string.directory_add_a_new_server_prompt)) + colorProvider(host.colorProvider) + } + verticalMarginItem { + id("form_space_2") + heightInPx(host.dimensionConverter.dpToPx(8)) + } + formEditTextItem { + id("edit") + showBottomSeparator(false) + value(data.enteredServer) + imeOptions(EditorInfo.IME_ACTION_DONE) + editorActionListener(object : TextView.OnEditorActionListener { + override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean { + if (actionId == EditorInfo.IME_ACTION_DONE) { + if (data.enteredServer.isNotEmpty()) { + host.callback?.onSubmitServer() + } + return true + } + return false + } + }) + hint(host.stringProvider.getString(R.string.directory_server_placeholder)) + inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI) + onTextChange { text -> + host.callback?.onEnterServerChange(text) + } + when (data.addServerAsync) { + Uninitialized -> enabled(true) + is Loading -> enabled(false) + is Success -> enabled(false) + is Fail -> { + enabled(true) + errorMessage(host.getErrorMessage(data.addServerAsync.error)) + } + } + } + when (data.addServerAsync) { + Uninitialized, + is Fail -> settingsContinueCancelItem { + id("continueCancel") + continueText(host.stringProvider.getString(R.string.ok)) + canContinue(data.enteredServer.isNotEmpty()) + continueOnClick { host.callback?.onSubmitServer() } + cancelOnClick { host.callback?.onCancelEnterServer() } + } + is Loading -> loadingItem { + id("addLoading") + } + is Success -> Unit /* This is a transitive state */ + } + } else { + genericButtonItem { + id("add") + text(host.stringProvider.getString(R.string.directory_add_a_new_server)) + textColor(host.colorProvider.getColor(R.color.riotx_accent)) + buttonClickAction(View.OnClickListener { + host.callback?.onStartEnterServer() + }) + } + } + } - directoryDescription(description) - directoryAvatarUrl(roomDirectoryData.avatarUrl) - includeAllNetworks(roomDirectoryData.includeAllNetworks) + private fun getErrorMessage(error: Throwable): String { + return if (error is Failure.ServerError + && error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) { + stringProvider.getString(R.string.directory_add_a_new_server_error) + } else { + errorFormatter.toHumanReadable(error) + } + } - globalListener { - host.callback?.onRoomDirectoryClicked(roomDirectoryData) + private fun buildDivider(idx: Int) { + val host = this + dividerItem { + id("divider_$idx") + color(host.dividerColor) + } + } + + private fun buildDirectory(roomDirectoryServer: RoomDirectoryServer) { + val host = this + roomDirectoryServerItem { + id("server_$roomDirectoryServer") + serverName(roomDirectoryServer.serverName) + canRemove(roomDirectoryServer.isManuallyAdded) + removeListener { host.callback?.onRemoveServer(roomDirectoryServer) } + + if (roomDirectoryServer.isUserServer) { + serverDescription(host.stringProvider.getString(R.string.directory_your_server)) + } + } + + roomDirectoryServer.protocols.forEach { roomDirectoryData -> + roomDirectoryItem { + id("server_${roomDirectoryServer}_proto_$roomDirectoryData") + directoryName( + if (roomDirectoryData.includeAllNetworks) { + host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryServer.serverName) + } else { + roomDirectoryData.displayName + } + ) + if (roomDirectoryData.displayName == RoomDirectoryData.MATRIX_PROTOCOL_NAME && !roomDirectoryData.includeAllNetworks) { + directoryDescription( + host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryServer.serverName) + ) + } + directoryAvatarUrl(roomDirectoryData.avatarUrl) + includeAllNetworks(roomDirectoryData.includeAllNetworks) + checked(roomDirectoryData == host.currentRoomDirectoryData) + globalListener { + host.callback?.onRoomDirectoryClicked(roomDirectoryData) + } } } } @@ -93,5 +221,10 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid interface Callback { fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) fun retry() + fun onStartEnterServer() + fun onEnterServerChange(server: String) + fun onSubmitServer() + fun onCancelEnterServer() + fun onRemoveServer(roomDirectoryServer: RoomDirectoryServer) } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index 7f205078f1..a32a3a897f 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -18,7 +18,6 @@ package im.vector.app.features.roomdirectory.picker import android.os.Bundle import android.view.LayoutInflater -import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity @@ -28,21 +27,22 @@ import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentRoomDirectoryPickerBinding import im.vector.app.features.roomdirectory.RoomDirectoryAction +import im.vector.app.features.roomdirectory.RoomDirectoryData +import im.vector.app.features.roomdirectory.RoomDirectoryServer import im.vector.app.features.roomdirectory.RoomDirectorySharedAction import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomdirectory.RoomDirectoryViewModel - -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import timber.log.Timber import javax.inject.Inject -// TODO Menu to add custom room directory (not done in RiotWeb so far...) class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerViewModelFactory: RoomDirectoryPickerViewModel.Factory, private val roomDirectoryPickerController: RoomDirectoryPickerController ) : VectorBaseFragment(), + OnBackPressed, RoomDirectoryPickerController.Callback { private val viewModel: RoomDirectoryViewModel by activityViewModel() @@ -65,6 +65,11 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) setupRecyclerView() + + // Give the current data to our controller. There maybe a better way to do that... + withState(viewModel) { + roomDirectoryPickerController.currentRoomDirectoryData = it.roomDirectoryData + } } override fun onDestroyView() { @@ -73,18 +78,6 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie super.onDestroyView() } - override fun getMenuRes() = R.menu.menu_directory_server_picker - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == R.id.action_add_custom_hs) { - // TODO - vectorBaseActivity.notImplemented("Entering custom homeserver") - return true - } - - return super.onOptionsItemSelected(item) - } - private fun setupRecyclerView() { views.roomDirectoryPickerList.configureWith(roomDirectoryPickerController) roomDirectoryPickerController.callback = this @@ -97,6 +90,26 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie sharedActionViewModel.post(RoomDirectorySharedAction.Back) } + override fun onStartEnterServer() { + pickerViewModel.handle(RoomDirectoryPickerAction.EnterEditMode) + } + + override fun onCancelEnterServer() { + pickerViewModel.handle(RoomDirectoryPickerAction.ExitEditMode) + } + + override fun onEnterServerChange(server: String) { + pickerViewModel.handle(RoomDirectoryPickerAction.SetServerUrl(server)) + } + + override fun onSubmitServer() { + pickerViewModel.handle(RoomDirectoryPickerAction.Submit) + } + + override fun onRemoveServer(roomDirectoryServer: RoomDirectoryServer) { + pickerViewModel.handle(RoomDirectoryPickerAction.RemoveServer(roomDirectoryServer)) + } + override fun onResume() { super.onResume() (activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.select_room_directory) @@ -111,4 +124,16 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie // Populate list with Epoxy roomDirectoryPickerController.setData(state) } + + override fun onBackPressed(toolbarButton: Boolean): Boolean { + // Leave the add server mode if started + return withState(pickerViewModel) { + if (it.inEditMode) { + pickerViewModel.handle(RoomDirectoryPickerAction.ExitEditMode) + true + } else { + false + } + } + } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt index d85b7937a2..2558715834 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt @@ -22,18 +22,28 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.R +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.ui.UiStateRepository import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams -class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initialState: RoomDirectoryPickerViewState, - private val session: Session) - : VectorViewModel(initialState) { +class RoomDirectoryPickerViewModel @AssistedInject constructor( + @Assisted initialState: RoomDirectoryPickerViewState, + private val session: Session, + private val uiStateRepository: UiStateRepository, + private val stringProvider: StringProvider, + private val roomDirectoryListCreator: RoomDirectoryListCreator +) : VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -50,7 +60,22 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initial } init { + observeAndCompute() load() + loadCustomRoomDirectoryHomeservers() + } + + private fun observeAndCompute() { + selectSubscribe( + RoomDirectoryPickerViewState::asyncThirdPartyRequest, + RoomDirectoryPickerViewState::customHomeservers + ) { async, custom -> + async()?.let { + setState { + copy(directories = roomDirectoryListCreator.computeDirectories(it, custom)) + } + } + } } private fun load() { @@ -71,9 +96,101 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor(@Assisted initial } } + private fun loadCustomRoomDirectoryHomeservers() { + setState { + copy( + customHomeservers = uiStateRepository.getCustomRoomDirectoryHomeservers(session.sessionId) + ) + } + } + override fun handle(action: RoomDirectoryPickerAction) { when (action) { - RoomDirectoryPickerAction.Retry -> load() + RoomDirectoryPickerAction.Retry -> load() + RoomDirectoryPickerAction.EnterEditMode -> handleEnterEditMode() + RoomDirectoryPickerAction.ExitEditMode -> handleExitEditMode() + is RoomDirectoryPickerAction.SetServerUrl -> handleSetServerUrl(action) + RoomDirectoryPickerAction.Submit -> handleSubmit() + is RoomDirectoryPickerAction.RemoveServer -> handleRemoveServer(action) + }.exhaustive + } + + private fun handleEnterEditMode() { + setState { + copy( + inEditMode = true, + enteredServer = "", + addServerAsync = Uninitialized + ) + } + } + + private fun handleExitEditMode() { + setState { + copy( + inEditMode = false, + enteredServer = "", + addServerAsync = Uninitialized + ) + } + } + + private fun handleSetServerUrl(action: RoomDirectoryPickerAction.SetServerUrl) { + setState { + copy( + enteredServer = action.url + ) + } + } + + private fun handleSubmit() = withState { state -> + // First avoid duplicate + val enteredServer = state.enteredServer + + val existingServerList = state.directories.map { it.serverName } + + if (enteredServer in existingServerList) { + setState { + copy(addServerAsync = Fail(Throwable(stringProvider.getString(R.string.directory_add_a_new_server_error_already_added)))) + } + return@withState + } + + viewModelScope.launch { + setState { + copy(addServerAsync = Loading()) + } + try { + session.getPublicRooms( + server = enteredServer, + publicRoomsParams = PublicRoomsParams(limit = 1) + ) + // Success, let add the server to our local repository, and update the state + val newSet = uiStateRepository.getCustomRoomDirectoryHomeservers(session.sessionId) + enteredServer + uiStateRepository.setCustomRoomDirectoryHomeservers(session.sessionId, newSet) + setState { + copy( + inEditMode = false, + enteredServer = "", + addServerAsync = Uninitialized, + customHomeservers = newSet + ) + } + } catch (failure: Throwable) { + setState { + copy(addServerAsync = Fail(failure)) + } + } + } + } + + private fun handleRemoveServer(action: RoomDirectoryPickerAction.RemoveServer) { + val newSet = uiStateRepository.getCustomRoomDirectoryHomeservers(session.sessionId) - action.roomDirectoryServer.serverName + uiStateRepository.setCustomRoomDirectoryHomeservers(session.sessionId, newSet) + setState { + copy( + customHomeservers = newSet + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt index 61cf50e8dd..5cdee862ab 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewState.kt @@ -19,8 +19,15 @@ package im.vector.app.features.roomdirectory.picker import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomdirectory.RoomDirectoryServer import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol data class RoomDirectoryPickerViewState( - val asyncThirdPartyRequest: Async> = Uninitialized + val asyncThirdPartyRequest: Async> = Uninitialized, + val customHomeservers: Set = emptySet(), + val inEditMode: Boolean = false, + val enteredServer: String = "", + val addServerAsync: Async = Uninitialized, + // computed + val directories: List = emptyList() ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryServerItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryServerItem.kt new file mode 100644 index 0000000000..6efb41d5b1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryServerItem.kt @@ -0,0 +1,59 @@ +/* + * Copyright 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 im.vector.app.features.roomdirectory.picker + +import android.view.View +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.ClickListener +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.onClick +import im.vector.app.core.extensions.setTextOrHide + +@EpoxyModelClass(layout = R.layout.item_room_directory_server) +abstract class RoomDirectoryServerItem : VectorEpoxyModel() { + + @EpoxyAttribute + var serverName: String? = null + + @EpoxyAttribute + var serverDescription: String? = null + + @EpoxyAttribute + var canRemove: Boolean = false + + @EpoxyAttribute + var removeListener: ClickListener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.nameView.text = serverName + holder.descriptionView.setTextOrHide(serverDescription) + holder.deleteView.isVisible = canRemove + holder.deleteView.onClick(removeListener) + } + + class Holder : VectorEpoxyHolder() { + val nameView by bind(R.id.itemRoomDirectoryServerName) + val descriptionView by bind(R.id.itemRoomDirectoryServerDescription) + val deleteView by bind(R.id.itemRoomDirectoryServerRemove) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 445d02d6e2..f9cf8e6dd7 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -25,9 +25,9 @@ import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding +import im.vector.app.features.roomdirectory.RoomDirectoryData import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.util.MatrixItem import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index 24836bc504..a07589d501 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -102,6 +102,7 @@ class RoomSettingsController @Inject constructor( id("topic") enabled(data.actionPermissions.canChangeTopic) value(data.newTopic ?: roomSummary.topic) + singleLine(false) hint(host.stringProvider.getString(R.string.room_settings_topic_hint)) onTextChange { text -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt index a8b85c9887..3f712a4fda 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt @@ -69,7 +69,6 @@ class SpaceDefaultRoomEpoxyController @Inject constructor( id("roomName1") enabled(true) value(firstRoomName) - singleLine(true) hint(host.stringProvider.getString(R.string.create_room_name_section)) endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT) showBottomSeparator(false) @@ -83,7 +82,6 @@ class SpaceDefaultRoomEpoxyController @Inject constructor( id("roomName2") enabled(true) value(secondRoomName) - singleLine(true) hint(host.stringProvider.getString(R.string.create_room_name_section)) endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT) showBottomSeparator(false) @@ -97,7 +95,6 @@ class SpaceDefaultRoomEpoxyController @Inject constructor( id("roomName3") enabled(true) value(thirdRoomName) - singleLine(true) hint(host.stringProvider.getString(R.string.create_room_name_section)) endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT) showBottomSeparator(false) 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 6ab35d3bf6..662b272d40 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 @@ -64,7 +64,6 @@ class SpaceDetailEpoxyController @Inject constructor( enabled(true) value(data?.name) hint(host.stringProvider.getString(R.string.create_room_name_hint)) - singleLine(true) showBottomSeparator(false) errorMessage(data?.nameInlineError) // onBind { _, view, _ -> diff --git a/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt b/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt index 1ec3a8ab46..e46c3516ca 100644 --- a/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt +++ b/vector/src/main/java/im/vector/app/features/ui/SharedPreferencesUiStateRepository.kt @@ -39,7 +39,7 @@ class SharedPreferencesUiStateRepository @Inject constructor( override fun getDisplayMode(): RoomListDisplayMode { return when (sharedPreferences.getInt(KEY_DISPLAY_MODE, VALUE_DISPLAY_MODE_CATCHUP)) { VALUE_DISPLAY_MODE_PEOPLE -> RoomListDisplayMode.PEOPLE - VALUE_DISPLAY_MODE_ROOMS -> RoomListDisplayMode.ROOMS + VALUE_DISPLAY_MODE_ROOMS -> RoomListDisplayMode.ROOMS else -> if (vectorPreferences.labAddNotificationTab()) { RoomListDisplayMode.NOTIFICATIONS } else { @@ -89,6 +89,18 @@ class SharedPreferencesUiStateRepository @Inject constructor( return sharedPreferences.getBoolean("$KEY_SELECTED_METHOD@$sessionId", true) } + override fun setCustomRoomDirectoryHomeservers(sessionId: String, servers: Set) { + sharedPreferences.edit { + putStringSet("$KEY_CUSTOM_DIRECTORY_HOMESERVER@$sessionId", servers) + } + } + + override fun getCustomRoomDirectoryHomeservers(sessionId: String): Set { + return sharedPreferences.getStringSet("$KEY_CUSTOM_DIRECTORY_HOMESERVER@$sessionId", null) + .orEmpty() + .toSet() + } + companion object { private const val KEY_DISPLAY_MODE = "UI_STATE_DISPLAY_MODE" private const val VALUE_DISPLAY_MODE_CATCHUP = 0 @@ -98,5 +110,7 @@ class SharedPreferencesUiStateRepository @Inject constructor( private const val KEY_SELECTED_SPACE = "UI_STATE_SELECTED_SPACE" private const val KEY_SELECTED_GROUP = "UI_STATE_SELECTED_GROUP" private const val KEY_SELECTED_METHOD = "UI_STATE_SELECTED_METHOD" + + private const val KEY_CUSTOM_DIRECTORY_HOMESERVER = "KEY_CUSTOM_DIRECTORY_HOMESERVER" } } diff --git a/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt b/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt index 935da83f5d..3c48f8972d 100644 --- a/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt +++ b/vector/src/main/java/im/vector/app/features/ui/UiStateRepository.kt @@ -32,6 +32,7 @@ interface UiStateRepository { fun storeDisplayMode(displayMode: RoomListDisplayMode) + // TODO Handle SharedPreference per session in a better way, also to cleanup when login out fun storeSelectedSpace(spaceId: String?, sessionId: String) fun storeSelectedGroup(groupId: String?, sessionId: String) @@ -40,4 +41,7 @@ interface UiStateRepository { fun getSelectedSpace(sessionId: String): String? fun getSelectedGroup(sessionId: String): String? fun isGroupingMethodSpace(sessionId: String): Boolean + + fun setCustomRoomDirectoryHomeservers(sessionId: String, servers: Set) + fun getCustomRoomDirectoryHomeservers(sessionId: String): Set } diff --git a/vector/src/main/res/layout/item_room_directory.xml b/vector/src/main/res/layout/item_room_directory.xml index 391f52ad92..19a457ec37 100644 --- a/vector/src/main/res/layout/item_room_directory.xml +++ b/vector/src/main/res/layout/item_room_directory.xml @@ -1,5 +1,4 @@ - + tools:src="@drawable/network_matrix" /> + app:layout_goneMarginEnd="@dimen/layout_horizontal_margin" + tools:text="@string/directory_server_native_rooms" + tools:visibility="visible" /> - + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_room_directory_server.xml b/vector/src/main/res/layout/item_room_directory_server.xml new file mode 100644 index 0000000000..5705e1c623 --- /dev/null +++ b/vector/src/main/res/layout/item_room_directory_server.xml @@ -0,0 +1,67 @@ + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_vertical_margin.xml b/vector/src/main/res/layout/item_vertical_margin.xml new file mode 100644 index 0000000000..fac46e47ea --- /dev/null +++ b/vector/src/main/res/layout/item_vertical_margin.xml @@ -0,0 +1,5 @@ + + diff --git a/vector/src/main/res/menu/menu_directory_server_picker.xml b/vector/src/main/res/menu/menu_directory_server_picker.xml deleted file mode 100644 index c544c80f8c..0000000000 --- a/vector/src/main/res/menu/menu_directory_server_picker.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/vector/src/main/res/values/config.xml b/vector/src/main/res/values/config.xml index 2b22b1c49b..30ca8d7f56 100755 --- a/vector/src/main/res/values/config.xml +++ b/vector/src/main/res/values/config.xml @@ -23,6 +23,7 @@ matrix.org + gitter.im diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 1e3d246e64..6b1f00287e 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1584,9 +1584,14 @@ Select a room directory The server may be unavailable or overloaded Type a homeserver to list public rooms from - Homeserver URL + Server name All rooms on %s server All native %s rooms + Your server + Add a new server + Enter the name of a new server you want to explore. + "Can't find this server or its room list" + This server is already present in the list Type hereā€¦