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>