Groups : add UI to show groups

This commit is contained in:
ganfra 2018-11-05 18:34:18 +01:00
parent a3539153ef
commit 64759abc9e
14 changed files with 239 additions and 9 deletions

View File

@ -7,6 +7,7 @@ import android.view.ViewGroup
import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.replaceFragment
import im.vector.riotredesign.core.platform.RiotFragment
import im.vector.riotredesign.features.home.group.GroupListFragment
import im.vector.riotredesign.features.home.room.list.RoomListFragment

class HomeDrawerFragment : RiotFragment() {
@ -25,10 +26,11 @@ class HomeDrawerFragment : RiotFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) {
val groupListFragment = GroupListFragment.newInstance()
replaceFragment(groupListFragment, R.id.groupListFragmentContainer)
val roomListFragment = RoomListFragment.newInstance()
replaceFragment(roomListFragment, R.id.roomListFragmentContainer)
}
}


}

View File

@ -0,0 +1,9 @@
package im.vector.riotredesign.features.home.group

import im.vector.matrix.android.api.session.group.model.GroupSummary

sealed class GroupListActions {

data class SelectGroup(val groupSummary: GroupSummary) : GroupListActions()

}

View File

@ -0,0 +1,59 @@
package im.vector.riotredesign.features.home.group

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.RiotFragment
import im.vector.riotredesign.core.platform.StateView
import kotlinx.android.synthetic.main.fragment_room_list.*

class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {

companion object {
fun newInstance(): GroupListFragment {
return GroupListFragment()
}
}

private val viewModel: GroupListViewModel by fragmentViewModel()
private lateinit var roomController: GroupSummaryController

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_group_list, container, false)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
roomController = GroupSummaryController(this)
stateView.contentView = epoxyRecyclerView
epoxyRecyclerView.setController(roomController)
viewModel.subscribe { renderState(it) }
}

private fun renderState(state: GroupListViewState) {
when (state.async) {
is Incomplete -> renderLoading()
is Success -> renderSuccess(state)
}
}

private fun renderSuccess(state: GroupListViewState) {
stateView.state = StateView.State.Content
roomController.setData(state)
}

private fun renderLoading() {
stateView.state = StateView.State.Loading
}

override fun onGroupSelected(groupSummary: GroupSummary) {
viewModel.accept(GroupListActions.SelectGroup(groupSummary))
}

}

View File

@ -0,0 +1,53 @@
package im.vector.riotredesign.features.home.group

import android.support.v4.app.FragmentActivity
import com.airbnb.mvrx.BaseMvRxViewModel
import com.airbnb.mvrx.MvRxViewModelFactory
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx
import org.koin.android.ext.android.get

class GroupListViewModel(initialState: GroupListViewState,
private val session: Session
) : BaseMvRxViewModel<GroupListViewState>(initialState) {

companion object : MvRxViewModelFactory<GroupListViewState> {

@JvmStatic
override fun create(activity: FragmentActivity, state: GroupListViewState): GroupListViewModel {
val matrix = activity.get<Matrix>()
val currentSession = matrix.currentSession
return GroupListViewModel(state, currentSession)
}
}

init {
observeGroupSummaries()
}

fun accept(action: GroupListActions) {
when (action) {
is GroupListActions.SelectGroup -> handleSelectGroup(action)
}
}

// PRIVATE METHODS *****************************************************************************

private fun handleSelectGroup(action: GroupListActions.SelectGroup) {
withState { state ->
if (state.selectedGroup?.groupId != action.groupSummary.groupId) {
setState { copy(selectedGroup = action.groupSummary) }
}
}
}

private fun observeGroupSummaries() {
session
.rx().liveGroupSummaries()
.execute { async ->
copy(async = async)
}
}

}

View File

@ -0,0 +1,12 @@
package im.vector.riotredesign.features.home.group

import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary

data class GroupListViewState(
val async: Async<List<GroupSummary>> = Uninitialized,
val selectedGroup: GroupSummary? = null
) : MvRxState

View File

@ -0,0 +1,34 @@
package im.vector.riotredesign.features.home.group

import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.group.model.GroupSummary

class GroupSummaryController(private val callback: Callback? = null
) : TypedEpoxyController<GroupListViewState>() {

override fun buildModels(viewState: GroupListViewState) {
buildGroupModels(viewState.async(), viewState.selectedGroup)
}

private fun buildGroupModels(summaries: List<GroupSummary>?, selected: GroupSummary?) {
if (summaries.isNullOrEmpty()) {
return
}
summaries.forEach { groupSummary ->
val isSelected = groupSummary.groupId == selected?.groupId
GroupSummaryItem(
groupName = groupSummary.displayName,
avatarUrl = groupSummary.avatarUrl,
isSelected = isSelected,
listener = { callback?.onGroupSelected(groupSummary) }
)
.id(groupSummary.groupId)
.addTo(this)
}
}

interface Callback {
fun onGroupSelected(groupSummary: GroupSummary)
}

}

View File

@ -0,0 +1,21 @@
package im.vector.riotredesign.features.home.group

import android.widget.ImageView
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.KotlinModel
import im.vector.riotredesign.features.home.AvatarRenderer


data class GroupSummaryItem(
val groupName: CharSequence,
val avatarUrl: String?,
val isSelected: Boolean,
val listener: (() -> Unit)? = null
) : KotlinModel(R.layout.item_group) {

private val avatarImageView by bind<ImageView>(R.id.groupAvatarImageView)

override fun bind() {
AvatarRenderer.render(avatarUrl, groupName.toString(), avatarImageView)
}
}

View File

@ -1,6 +1,7 @@
package im.vector.riotredesign.features.home.room.list

import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotredesign.features.home.group.GroupListActions

sealed class RoomListActions {


View File

@ -44,8 +44,8 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
private fun renderState(state: RoomListViewState) {
when (state.async) {
is Incomplete -> renderLoading()
is Success -> renderSuccess(state)
is Fail -> renderFailure(state.async.error)
is Success -> renderSuccess(state)
is Fail -> renderFailure(state.async.error)
}
if (state.shouldOpenRoomDetail && state.selectedRoom != null) {
homeNavigator.openRoomDetail(state.selectedRoom.roomId)
@ -69,7 +69,7 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
private fun renderFailure(error: Throwable) {
val message = when (error) {
is Failure.NetworkConnection -> getString(R.string.error_no_network)
else -> getString(R.string.error_common)
else -> getString(R.string.error_common)
}
stateView.state = StateView.State.Error(message)
}

View File

@ -6,6 +6,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx
import im.vector.riotredesign.features.home.group.GroupListActions
import im.vector.riotredesign.features.home.group.GroupListViewModel
import im.vector.riotredesign.features.home.group.GroupListViewState
import org.koin.android.ext.android.get

class RoomListViewModel(initialState: RoomListViewState,
@ -28,7 +31,7 @@ class RoomListViewModel(initialState: RoomListViewState,

fun accept(action: RoomListActions) {
when (action) {
is RoomListActions.SelectRoom -> handleSelectRoom(action)
is RoomListActions.SelectRoom -> handleSelectRoom(action)
is RoomListActions.RoomDisplayed -> setState { copy(shouldOpenRoomDetail = false) }
}
}

View File

@ -2,6 +2,8 @@ package im.vector.riotredesign.features.home.room.list

import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotredesign.features.home.group.GroupListViewState
import im.vector.riotredesign.features.home.group.GroupSummaryItem

class RoomSummaryController(private val callback: Callback? = null
) : TypedEpoxyController<RoomListViewState>() {

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>


<im.vector.riotredesign.core.platform.StateView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stateView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dark">

<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/epoxyRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</im.vector.riotredesign.core.platform.StateView>

View File

@ -6,10 +6,9 @@
android:layout_height="match_parent">

<FrameLayout
android:id="@+id/communitiesFragmentContainer"
android:id="@+id/groupListFragmentContainer"
android:layout_width="56dp"
android:layout_height="match_parent"
android:background="@color/dark" />
android:layout_height="match_parent" />

<FrameLayout
android:id="@+id/roomListFragmentContainer"
@ -17,7 +16,7 @@
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/communitiesFragmentContainer"
app:layout_constraintStart_toEndOf="@+id/groupListFragmentContainer"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>

<im.vector.riotredesign.core.platform.CheckableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/itemGroupLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="true"
android:padding="8dp">

<ImageView
android:id="@+id/groupAvatarImageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
tools:src="@tools:sample/avatars" />

</im.vector.riotredesign.core.platform.CheckableFrameLayout>