forked from GitHub-Mirror/riotX-android
Home: continue room list rework.
This commit is contained in:
@ -17,9 +17,9 @@
|
||||
package im.vector.riotredesign.core.platform
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import im.vector.riotredesign.R
|
||||
import kotlinx.android.synthetic.main.view_state.view.*
|
||||
@ -30,7 +30,7 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
sealed class State {
|
||||
object Content : State()
|
||||
object Loading : State()
|
||||
data class Empty(val message: CharSequence? = null) : State()
|
||||
data class Empty(val title: CharSequence? = null, val image: Drawable? = null, val message: CharSequence? = null) : State()
|
||||
data class Error(val message: CharSequence? = null) : State()
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.view_state, this)
|
||||
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
layoutParams = LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
|
||||
errorRetryView.setOnClickListener {
|
||||
eventCallback?.onRetryClicked()
|
||||
}
|
||||
@ -74,16 +74,18 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
emptyView.visibility = View.INVISIBLE
|
||||
contentView?.visibility = View.INVISIBLE
|
||||
}
|
||||
is StateView.State.Empty -> {
|
||||
is StateView.State.Empty -> {
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
errorView.visibility = View.INVISIBLE
|
||||
emptyView.visibility = View.VISIBLE
|
||||
emptyImageView.setImageDrawable(newState.image)
|
||||
emptyMessageView.text = newState.message
|
||||
emptyTitleView.text = newState.title
|
||||
if (contentView != null) {
|
||||
contentView!!.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
is StateView.State.Error -> {
|
||||
is StateView.State.Error -> {
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
errorView.visibility = View.VISIBLE
|
||||
emptyView.visibility = View.INVISIBLE
|
||||
|
@ -85,12 +85,16 @@ class GroupListViewModel(initialState: GroupListViewState,
|
||||
session
|
||||
.rx().liveGroupSummaries()
|
||||
.map {
|
||||
val myUser = session.getUser(session.sessionParams.credentials.userId)
|
||||
val allCommunityGroup = GroupSummary(
|
||||
groupId = ALL_COMMUNITIES_GROUP_ID,
|
||||
displayName = "All Communities",
|
||||
avatarUrl = myUser?.avatarUrl ?: "")
|
||||
listOf(allCommunityGroup) + it
|
||||
if (it.isEmpty()) {
|
||||
it
|
||||
} else {
|
||||
val myUser = session.getUser(session.sessionParams.credentials.userId)
|
||||
val allCommunityGroup = GroupSummary(
|
||||
groupId = ALL_COMMUNITIES_GROUP_ID,
|
||||
displayName = "All Communities",
|
||||
avatarUrl = myUser?.avatarUrl ?: "")
|
||||
listOf(allCommunityGroup) + it
|
||||
}
|
||||
}
|
||||
.execute { async ->
|
||||
val newSelectedGroup = selectedGroup ?: async()?.firstOrNull()
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.riotredesign.features.home.room.list
|
||||
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
|
||||
private const val ANIM_DURATION_IN_MILLIS = 100L
|
||||
|
||||
class RoomListAnimator : DefaultItemAnimator() {
|
||||
|
||||
init {
|
||||
addDuration = ANIM_DURATION_IN_MILLIS
|
||||
removeDuration = ANIM_DURATION_IN_MILLIS
|
||||
moveDuration = 0
|
||||
changeDuration = 0
|
||||
}
|
||||
|
||||
}
|
@ -91,6 +91,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||
epoxyRecyclerView.layoutManager = layoutManager
|
||||
epoxyRecyclerView.itemAnimator = RoomListAnimator()
|
||||
roomController.callback = this
|
||||
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
||||
stateView.contentView = epoxyRecyclerView
|
||||
@ -98,22 +99,40 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
|
||||
}
|
||||
|
||||
private fun renderState(state: RoomListViewState) {
|
||||
when (state.asyncRooms) {
|
||||
when (state.asyncFilteredRooms) {
|
||||
is Incomplete -> renderLoading()
|
||||
is Success -> renderSuccess(state)
|
||||
is Fail -> renderFailure(state.asyncRooms.error)
|
||||
is Fail -> renderFailure(state.asyncFilteredRooms.error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderSuccess(state: RoomListViewState) {
|
||||
if (state.asyncRooms().isNullOrEmpty()) {
|
||||
stateView.state = StateView.State.Empty(getString(R.string.room_list_empty))
|
||||
val allRooms = state.asyncRooms()
|
||||
val filteredRooms = state.asyncFilteredRooms()
|
||||
if (filteredRooms.isNullOrEmpty()) {
|
||||
renderEmptyState(allRooms)
|
||||
} else {
|
||||
stateView.state = StateView.State.Content
|
||||
}
|
||||
roomController.setData(state)
|
||||
}
|
||||
|
||||
private fun renderEmptyState(allRooms: List<RoomSummary>?) {
|
||||
val hasNoRoom = allRooms.isNullOrEmpty()
|
||||
val emptyState = when (roomListParams.displayMode) {
|
||||
DisplayMode.HOME -> {
|
||||
if (hasNoRoom) {
|
||||
StateView.State.Empty(getString(R.string.room_list_catchup_welcome_title), null, getString(R.string.room_list_catchup_welcome_body))
|
||||
} else {
|
||||
StateView.State.Empty(getString(R.string.room_list_catchup_empty_title), null, getString(R.string.room_list_catchup_empty_body))
|
||||
}
|
||||
}
|
||||
DisplayMode.PEOPLE -> StateView.State.Empty()
|
||||
DisplayMode.ROOMS -> StateView.State.Empty()
|
||||
}
|
||||
stateView.state = emptyState
|
||||
}
|
||||
|
||||
private fun renderLoading() {
|
||||
stateView.state = StateView.State.Loading
|
||||
}
|
||||
|
@ -87,6 +87,12 @@ class RoomListViewModel(initialState: RoomListViewState,
|
||||
|
||||
|
||||
private fun observeRoomSummaries() {
|
||||
homeRoomListObservableSource
|
||||
.observe()
|
||||
.execute { asyncRooms ->
|
||||
copy(asyncRooms = asyncRooms)
|
||||
}
|
||||
|
||||
homeRoomListObservableSource
|
||||
.observe()
|
||||
.flatMapSingle {
|
||||
@ -96,7 +102,7 @@ class RoomListViewModel(initialState: RoomListViewState,
|
||||
}
|
||||
.map { buildRoomSummaries(it) }
|
||||
.execute { async ->
|
||||
copy(asyncRooms = async)
|
||||
copy(asyncFilteredRooms = async)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,15 +17,12 @@
|
||||
package im.vector.riotredesign.features.home.room.list
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
data class RoomListViewState(
|
||||
val displayMode: RoomListFragment.DisplayMode,
|
||||
val asyncRooms: Async<RoomSummaries> = Uninitialized,
|
||||
val isInviteExpanded: Boolean = true,
|
||||
val isFavouriteRoomsExpanded: Boolean = true,
|
||||
val isDirectRoomsExpanded: Boolean = true,
|
||||
@ -71,5 +68,5 @@ enum class RoomCategory(@StringRes val titleRes: Int) {
|
||||
}
|
||||
|
||||
fun RoomSummaries?.isNullOrEmpty(): Boolean {
|
||||
return this == null || isEmpty()
|
||||
return this == null || this.values.flatten().isEmpty()
|
||||
}
|
@ -31,7 +31,7 @@ class RoomSummaryController(private val stringProvider: StringProvider,
|
||||
var callback: Callback? = null
|
||||
|
||||
override fun buildModels(viewState: RoomListViewState) {
|
||||
val roomSummaries = viewState.asyncRooms()
|
||||
val roomSummaries = viewState.asyncFilteredRooms()
|
||||
roomSummaries?.forEach { (category, summaries) ->
|
||||
if (summaries.isEmpty()) {
|
||||
return@forEach
|
||||
|
@ -46,11 +46,13 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||
holder.titleView.text = roomName
|
||||
holder.lastEventTimeView.text = lastEventTime
|
||||
holder.lastEventView.text = lastFormattedEvent
|
||||
holder.unreadCounterBadgeView.render(unreadCount, showHighlighted)
|
||||
AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val titleView by bind<TextView>(R.id.roomNameView)
|
||||
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.roomUnreadCounterBadgeView)
|
||||
val lastEventView by bind<TextView>(R.id.roomLastEventView)
|
||||
val lastEventTimeView by bind<TextView>(R.id.roomLastEventTimeView)
|
||||
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
|
||||
|
Reference in New Issue
Block a user