Space explore rooms screen alignment with design in figma (#5834)
This commit is contained in:
		
							parent
							
								
									18842b5e3d
								
							
						
					
					
						commit
						a30912f688
					
				
							
								
								
									
										1
									
								
								changelog.d/5658.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								changelog.d/5658.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Space explore screen changes: removed space card, added rooms filtering | ||||
| @ -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() | ||||
| } | ||||
| @ -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<SpaceDirectoryState>() { | ||||
| 
 | ||||
|     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) } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -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<FragmentSpaceDirectoryBinding>(), | ||||
|         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)) | ||||
|     } | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -11,35 +11,12 @@ | ||||
|         android:layout_width="match_parent" | ||||
|         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 | ||||
|                 android:id="@+id/toolbar" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 app:layout_collapseMode="pin" /> | ||||
| 
 | ||||
|         </com.google.android.material.appbar.CollapsingToolbarLayout> | ||||
|                 android:layout_height="?attr/actionBarSize" | ||||
|                 app:contentInsetStart="0dp"> | ||||
|             </com.google.android.material.appbar.MaterialToolbar> | ||||
| 
 | ||||
|     </com.google.android.material.appbar.AppBarLayout> | ||||
| 
 | ||||
| @ -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" | ||||
|  | ||||
| @ -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" /> | ||||
| 
 | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
| @ -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> | ||||
| @ -1,6 +1,16 @@ | ||||
| <?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"> | ||||
| 
 | ||||
|     <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 | ||||
|         android:id="@+id/spaceAddRoom" | ||||
|         android:title="@string/space_add_existing_rooms" | ||||
|  | ||||
| @ -2856,6 +2856,9 @@ | ||||
|     <!-- TODO delete --> | ||||
|     <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 they’re 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_only">Add existing rooms</string> | ||||
|     <string name="space_add_existing_spaces">Add existing spaces</string> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user