Space explore rooms screen alignment with design in figma (#5834)

This commit is contained in:
fedrunov 2022-05-19 11:49:20 +02:00 committed by GitHub
parent 18842b5e3d
commit a30912f688
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 166 additions and 72 deletions

1
changelog.d/5658.feature Normal file
View file

@ -0,0 +1 @@
Space explore screen changes: removed space card, added rooms filtering

View file

@ -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<SpaceDirectoryFilterNoResults.Holder>() {
class Holder : VectorEpoxyHolder()
}

View file

@ -34,6 +34,8 @@ import im.vector.app.core.ui.list.genericEmptyWithActionItem
import im.vector.app.core.ui.list.genericPillItem import im.vector.app.core.ui.list.genericPillItem
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.list.spaceChildInfoItem 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 im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import me.gujun.android.span.span import me.gujun.android.span.span
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
@ -53,6 +55,7 @@ class SpaceDirectoryController @Inject constructor(
) : TypedEpoxyController<SpaceDirectoryState>() { ) : TypedEpoxyController<SpaceDirectoryState>() {
interface InteractionListener { interface InteractionListener {
fun onFilterQueryChanged(query: String?)
fun onButtonClick(spaceChildInfo: SpaceChildInfo) fun onButtonClick(spaceChildInfo: SpaceChildInfo)
fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo) fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo)
fun onRoomClick(spaceChildInfo: SpaceChildInfo) fun onRoomClick(spaceChildInfo: SpaceChildInfo)
@ -62,6 +65,7 @@ class SpaceDirectoryController @Inject constructor(
} }
var listener: InteractionListener? = null var listener: InteractionListener? = null
private val matchFilter = SpaceChildInfoMatchFilter()
override fun buildModels(data: SpaceDirectoryState?) { override fun buildModels(data: SpaceDirectoryState?) {
val host = this val host = this
@ -76,7 +80,7 @@ class SpaceDirectoryController @Inject constructor(
val failure = results.error val failure = results.error
if (failure is Failure.ServerError && failure.error.code == M_UNRECOGNIZED) { if (failure is Failure.ServerError && failure.error.code == M_UNRECOGNIZED) {
genericPillItem { genericPillItem {
id("HS no Support") id("hs_no_support")
imageRes(R.drawable.error) imageRes(R.drawable.error)
tintIcon(false) tintIcon(false)
text( text(
@ -132,43 +136,52 @@ class SpaceDirectoryController @Inject constructor(
} }
} }
} else { } else {
flattenChildInfo.forEach { info -> matchFilter.filter = data?.currentFilter ?: ""
val isSpace = info.roomType == RoomType.SPACE val filteredChildInfo = flattenChildInfo.filter { matchFilter.test(it) }
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 { if (filteredChildInfo.isEmpty()) {
id(info.childRoomId) spaceDirectoryFilterNoResults {
matrixItem(matrixItem) id("no_results")
avatarRenderer(host.avatarRenderer) }
topic(info.topic) } else {
suggested(info.suggested.orFalse()) filteredChildInfo.forEach { info ->
errorLabel( val isSpace = info.roomType == RoomType.SPACE
error?.let { val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
host.stringProvider.getString(R.string.error_failed_to_join_room, host.errorFormatter.toHumanReadable(it)) 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) }
} }
} }
} }

View file

@ -23,6 +23,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope 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.databinding.FragmentSpaceDirectoryBinding
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.home.room.detail.timeline.TimelineEventController 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.permalink.PermalinkHandler
import im.vector.app.features.spaces.manage.ManageType import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet
@ -63,7 +63,6 @@ data class SpaceDirectoryArgs(
class SpaceDirectoryFragment @Inject constructor( class SpaceDirectoryFragment @Inject constructor(
private val epoxyController: SpaceDirectoryController, private val epoxyController: SpaceDirectoryController,
private val permalinkHandler: PermalinkHandler, private val permalinkHandler: PermalinkHandler,
private val spaceCardRenderer: SpaceCardRenderer,
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment<FragmentSpaceDirectoryBinding>(), ) : VectorBaseFragment<FragmentSpaceDirectoryBinding>(),
SpaceDirectoryController.InteractionListener, 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 // Hide FAB when list is scrolling
views.spaceDirectoryList.addOnScrollListener( views.spaceDirectoryList.addOnScrollListener(
object : RecyclerView.OnScrollListener() { object : RecyclerView.OnScrollListener() {
@ -167,18 +163,37 @@ class SpaceDirectoryFragment @Inject constructor(
// it's the root // it's the root
toolbar?.setTitle(R.string.space_explore_activity_title) toolbar?.setTitle(R.string.space_explore_activity_title)
} else { } else {
toolbar?.title = state.currentRootSummary?.name val spaceName = state.currentRootSummary?.name
?: state.currentRootSummary?.canonicalAlias ?: 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 views.addOrCreateChatRoomButton.isVisible = state.canAddRooms
} }
override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state -> override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented 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) super.onPrepareOptionsMenu(menu)
} }
@ -198,6 +213,10 @@ class SpaceDirectoryFragment @Inject constructor(
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onFilterQueryChanged(query: String?) {
viewModel.handle(SpaceDirectoryViewAction.FilterRooms(query))
}
override fun onButtonClick(spaceChildInfo: SpaceChildInfo) { override fun onButtonClick(spaceChildInfo: SpaceChildInfo) {
viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo)) viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo))
} }

View file

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
sealed class SpaceDirectoryViewAction : VectorViewModelAction { sealed class SpaceDirectoryViewAction : VectorViewModelAction {
data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction() data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
data class JoinOrOpen(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 ShowDetails(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction() data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction()
object CreateNewRoom : SpaceDirectoryViewAction() object CreateNewRoom : SpaceDirectoryViewAction()

View file

@ -225,9 +225,16 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
_viewEvents.post(SpaceDirectoryViewEvents.NavigateToCreateNewRoom(state.currentRootSummary?.roomId ?: initialState.spaceId)) _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 -> private fun handleBack() = withState { state ->
if (state.hierarchyStack.isEmpty()) { if (state.hierarchyStack.isEmpty()) {
_viewEvents.post(SpaceDirectoryViewEvents.Dismiss) _viewEvents.post(SpaceDirectoryViewEvents.Dismiss)

View file

@ -11,35 +11,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/spaceExploreCollapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?android:colorBackground"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:scrimAnimationDuration="250"
app:scrimVisibleHeightTrigger="120dp"
app:titleEnabled="false"
app:toolbarId="@id/toolbar">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp">
<include
android:id="@+id/spaceCard"
layout="@layout/fragment_matrix_to_room_space_card" />
</FrameLayout>
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" /> app:contentInsetStart="0dp">
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -57,7 +34,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp " android:layout_marginBottom="16dp"
android:contentDescription="@string/a11y_create_room" android:contentDescription="@string/a11y_create_room"
android:scaleType="center" android:scaleType="center"
android:src="@drawable/ic_fab_add" android:src="@drawable/ic_fab_add"

View file

@ -136,8 +136,8 @@
android:layout_height="1dp" android:layout_height="1dp"
android:background="?vctr_list_separator_system" android:background="?vctr_list_separator_system"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="@id/joinSuggestedRoomButton"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="@id/roomNameView"
app:layout_constraintTop_toBottomOf="@id/inlineErrorText" /> app:layout_constraintTop_toBottomOf="@id/inlineErrorText" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/space_explore_rooms_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:foreground="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:paddingTop="16dp"
tools:viewBindingIgnore="true">
<TextView
style="@style/Widget.Vector.TextView.Body.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/space_explore_filter_no_result_title" />
<TextView
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/space_explore_filter_no_result_description"
android:textColor="?vctr_content_secondary" />
<TextView
style="@style/Widget.Vector.TextView.Body.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/create_new_room"
android:textColor="?colorSecondary" />
</androidx.appcompat.widget.LinearLayoutCompat>

View file

@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/spaceSearch"
android:icon="@drawable/ic_filter"
android:title="@string/search"
app:searchIcon="@drawable/ic_filter"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
<item <item
android:id="@+id/spaceAddRoom" android:id="@+id/spaceAddRoom"
android:title="@string/space_add_existing_rooms" android:title="@string/space_add_existing_rooms"

View file

@ -2856,6 +2856,9 @@
<!-- TODO delete --> <!-- TODO delete -->
<string name="pick_tings_to_leave" tools:ignore="UnusedResources">Pick things to leave</string> <string name="pick_tings_to_leave" tools:ignore="UnusedResources">Pick things to leave</string>
<string name="space_explore_filter_no_result_title">No results found</string>
<string name="space_explore_filter_no_result_description">Some results may be hidden because theyre private and you need an invite to them.</string>
<string name="space_add_existing_rooms">Add existing rooms and space</string> <string name="space_add_existing_rooms">Add existing rooms and space</string>
<string name="space_add_existing_rooms_only">Add existing rooms</string> <string name="space_add_existing_rooms_only">Add existing rooms</string>
<string name="space_add_existing_spaces">Add existing spaces</string> <string name="space_add_existing_spaces">Add existing spaces</string>