mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 18:05:36 +03:00
Groups : add UI to show groups
This commit is contained in:
parent
a3539153ef
commit
64759abc9e
14 changed files with 239 additions and 9 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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()
|
||||
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>() {
|
||||
|
|
15
app/src/main/res/layout/fragment_group_list.xml
Normal file
15
app/src/main/res/layout/fragment_group_list.xml
Normal 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>
|
|
@ -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>
|
20
app/src/main/res/layout/item_group.xml
Normal file
20
app/src/main/res/layout/item_group.xml
Normal 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>
|
Loading…
Reference in a new issue