diff --git a/changelog.d/5658.feature b/changelog.d/5658.feature new file mode 100644 index 0000000000..ba41a03207 --- /dev/null +++ b/changelog.d/5658.feature @@ -0,0 +1 @@ +Space explore screen changes: removed space card, added rooms filtering diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceDirectoryFilterNoResults.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceDirectoryFilterNoResults.kt new file mode 100644 index 0000000000..1ae017c98c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceDirectoryFilterNoResults.kt @@ -0,0 +1,27 @@ +/* + * 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.home.room.list + +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel + +@EpoxyModelClass(layout = R.layout.item_space_directory_filter_no_results) +abstract class SpaceDirectoryFilterNoResults : VectorEpoxyModel() { + class Holder : VectorEpoxyHolder() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt index 9249240d5c..3f3b66cbcc 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt @@ -34,6 +34,8 @@ import im.vector.app.core.ui.list.genericEmptyWithActionItem import im.vector.app.core.ui.list.genericPillItem import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.list.spaceChildInfoItem +import im.vector.app.features.home.room.list.spaceDirectoryFilterNoResults +import im.vector.app.features.spaces.manage.SpaceChildInfoMatchFilter import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import me.gujun.android.span.span import org.matrix.android.sdk.api.extensions.orFalse @@ -53,6 +55,7 @@ class SpaceDirectoryController @Inject constructor( ) : TypedEpoxyController() { interface InteractionListener { + fun onFilterQueryChanged(query: String?) fun onButtonClick(spaceChildInfo: SpaceChildInfo) fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo) fun onRoomClick(spaceChildInfo: SpaceChildInfo) @@ -62,6 +65,7 @@ class SpaceDirectoryController @Inject constructor( } var listener: InteractionListener? = null + private val matchFilter = SpaceChildInfoMatchFilter() override fun buildModels(data: SpaceDirectoryState?) { val host = this @@ -76,7 +80,7 @@ class SpaceDirectoryController @Inject constructor( val failure = results.error if (failure is Failure.ServerError && failure.error.code == M_UNRECOGNIZED) { genericPillItem { - id("HS no Support") + id("hs_no_support") imageRes(R.drawable.error) tintIcon(false) text( @@ -132,43 +136,52 @@ class SpaceDirectoryController @Inject constructor( } } } else { - flattenChildInfo.forEach { info -> - val isSpace = info.roomType == RoomType.SPACE - val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true - val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false - val error = (data?.changeMembershipStates?.get(info.childRoomId) as? ChangeMembershipState.FailedJoining)?.throwable - // if it's known use that matrixItem because it would have a better computed name - val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem() - ?: info.toMatrixItem() + matchFilter.filter = data?.currentFilter ?: "" + val filteredChildInfo = flattenChildInfo.filter { matchFilter.test(it) } - spaceChildInfoItem { - id(info.childRoomId) - matrixItem(matrixItem) - avatarRenderer(host.avatarRenderer) - topic(info.topic) - suggested(info.suggested.orFalse()) - errorLabel( - error?.let { - host.stringProvider.getString(R.string.error_failed_to_join_room, host.errorFormatter.toHumanReadable(it)) + if (filteredChildInfo.isEmpty()) { + spaceDirectoryFilterNoResults { + id("no_results") + } + } else { + filteredChildInfo.forEach { info -> + val isSpace = info.roomType == RoomType.SPACE + val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true + val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false + val error = (data?.changeMembershipStates?.get(info.childRoomId) as? ChangeMembershipState.FailedJoining)?.throwable + // if it's known use that matrixItem because it would have a better computed name + val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem() + ?: info.toMatrixItem() + + spaceChildInfoItem { + id(info.childRoomId) + matrixItem(matrixItem) + avatarRenderer(host.avatarRenderer) + topic(info.topic) + suggested(info.suggested.orFalse()) + errorLabel( + error?.let { + host.stringProvider.getString(R.string.error_failed_to_join_room, host.errorFormatter.toHumanReadable(it)) + } + ) + memberCount(info.activeMemberCount ?: 0) + loading(isLoading) + buttonLabel( + when { + error != null -> host.stringProvider.getString(R.string.global_retry) + isJoined -> host.stringProvider.getString(R.string.action_open) + else -> host.stringProvider.getString(R.string.action_join) + } + ) + apply { + if (isSpace) { + itemClickListener { host.listener?.onSpaceChildClick(info) } + } else { + itemClickListener { host.listener?.onRoomClick(info) } } - ) - memberCount(info.activeMemberCount ?: 0) - loading(isLoading) - buttonLabel( - when { - error != null -> host.stringProvider.getString(R.string.global_retry) - isJoined -> host.stringProvider.getString(R.string.action_open) - else -> host.stringProvider.getString(R.string.action_join) - } - ) - apply { - if (isSpace) { - itemClickListener { host.listener?.onSpaceChildClick(info) } - } else { - itemClickListener { host.listener?.onRoomClick(info) } } + buttonClickListener { host.listener?.onButtonClick(info) } } - buttonClickListener { host.listener?.onButtonClick(info) } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index e59087778f..ed0bbdd911 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -23,6 +23,7 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.SearchView import androidx.core.text.toSpannable import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope @@ -44,7 +45,6 @@ import im.vector.app.core.utils.openUrlInExternalBrowser import im.vector.app.databinding.FragmentSpaceDirectoryBinding import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.home.room.detail.timeline.TimelineEventController -import im.vector.app.features.matrixto.SpaceCardRenderer import im.vector.app.features.permalink.PermalinkHandler import im.vector.app.features.spaces.manage.ManageType import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet @@ -63,7 +63,6 @@ data class SpaceDirectoryArgs( class SpaceDirectoryFragment @Inject constructor( private val epoxyController: SpaceDirectoryController, private val permalinkHandler: PermalinkHandler, - private val spaceCardRenderer: SpaceCardRenderer, private val colorProvider: ColorProvider ) : VectorBaseFragment(), SpaceDirectoryController.InteractionListener, @@ -123,9 +122,6 @@ class SpaceDirectoryFragment @Inject constructor( } } - views.spaceCard.matrixToCardMainButton.isVisible = false - views.spaceCard.matrixToCardSecondaryButton.isVisible = false - // Hide FAB when list is scrolling views.spaceDirectoryList.addOnScrollListener( object : RecyclerView.OnScrollListener() { @@ -167,18 +163,37 @@ class SpaceDirectoryFragment @Inject constructor( // it's the root toolbar?.setTitle(R.string.space_explore_activity_title) } else { - toolbar?.title = state.currentRootSummary?.name + val spaceName = state.currentRootSummary?.name ?: state.currentRootSummary?.canonicalAlias - ?: getString(R.string.space_explore_activity_title) + + if (spaceName != null) { + toolbar?.title = spaceName + toolbar?.subtitle = getString(R.string.space_explore_activity_title) + } else { + toolbar?.title = getString(R.string.space_explore_activity_title) + } } - spaceCardRenderer.render(state.currentRootSummary, emptyList(), this, views.spaceCard, showDescription = false) views.addOrCreateChatRoomButton.isVisible = state.canAddRooms } override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state -> menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented + + menu.findItem(R.id.spaceSearch)?.let { searchItem -> + val searchView = searchItem.actionView as SearchView + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + return true + } + + override fun onQueryTextChange(newText: String?): Boolean { + onFilterQueryChanged(newText) + return true + } + }) + } super.onPrepareOptionsMenu(menu) } @@ -198,6 +213,10 @@ class SpaceDirectoryFragment @Inject constructor( return super.onOptionsItemSelected(item) } + override fun onFilterQueryChanged(query: String?) { + viewModel.handle(SpaceDirectoryViewAction.FilterRooms(query)) + } + override fun onButtonClick(spaceChildInfo: SpaceChildInfo) { viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo)) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt index 2166a7e306..1d180eea4f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo sealed class SpaceDirectoryViewAction : VectorViewModelAction { data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction() data class JoinOrOpen(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction() + data class FilterRooms(val query: String?) : SpaceDirectoryViewAction() data class ShowDetails(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction() data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction() object CreateNewRoom : SpaceDirectoryViewAction() diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 2ddcb42e2a..7ae2feebcf 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -225,9 +225,16 @@ class SpaceDirectoryViewModel @AssistedInject constructor( _viewEvents.post(SpaceDirectoryViewEvents.NavigateToCreateNewRoom(state.currentRootSummary?.roomId ?: initialState.spaceId)) } } + is SpaceDirectoryViewAction.FilterRooms -> { + filter(action.query) + } } } + private fun filter(query: String?) { + setState { copy(currentFilter = query.orEmpty()) } + } + private fun handleBack() = withState { state -> if (state.hierarchyStack.isEmpty()) { _viewEvents.post(SpaceDirectoryViewEvents.Dismiss) diff --git a/vector/src/main/res/layout/fragment_space_directory.xml b/vector/src/main/res/layout/fragment_space_directory.xml index bc77bb1474..8bc53b5243 100644 --- a/vector/src/main/res/layout/fragment_space_directory.xml +++ b/vector/src/main/res/layout/fragment_space_directory.xml @@ -11,35 +11,12 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - - - - - - - - - + android:layout_height="?attr/actionBarSize" + app:contentInsetStart="0dp"> + @@ -57,7 +34,7 @@ android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginEnd="16dp" - android:layout_marginBottom="16dp " + android:layout_marginBottom="16dp" android:contentDescription="@string/a11y_create_room" android:scaleType="center" android:src="@drawable/ic_fab_add" diff --git a/vector/src/main/res/layout/item_explore_space_child.xml b/vector/src/main/res/layout/item_explore_space_child.xml index 8f984e1b92..eef664664d 100644 --- a/vector/src/main/res/layout/item_explore_space_child.xml +++ b/vector/src/main/res/layout/item_explore_space_child.xml @@ -136,8 +136,8 @@ android:layout_height="1dp" android:background="?vctr_list_separator_system" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="@id/joinSuggestedRoomButton" + app:layout_constraintStart_toStartOf="@id/roomNameView" app:layout_constraintTop_toBottomOf="@id/inlineErrorText" /> diff --git a/vector/src/main/res/layout/item_space_directory_filter_no_results.xml b/vector/src/main/res/layout/item_space_directory_filter_no_results.xml new file mode 100644 index 0000000000..6a8ae89c9b --- /dev/null +++ b/vector/src/main/res/layout/item_space_directory_filter_no_results.xml @@ -0,0 +1,36 @@ + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/menu/menu_space_directory.xml b/vector/src/main/res/menu/menu_space_directory.xml index c95fb846af..395b271f1a 100644 --- a/vector/src/main/res/menu/menu_space_directory.xml +++ b/vector/src/main/res/menu/menu_space_directory.xml @@ -1,6 +1,16 @@ - + + + Pick things to leave + No results found + Some results may be hidden because they’re private and you need an invite to them. + Add existing rooms and space Add existing rooms Add existing spaces