diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt index 5d990881..1ef0bcb8 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt @@ -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) } } - } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListActions.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListActions.kt new file mode 100644 index 00000000..92f758a7 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListActions.kt @@ -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() + +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt new file mode 100644 index 00000000..131a82e5 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt @@ -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)) + } + +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt new file mode 100644 index 00000000..1cfb98c4 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt @@ -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(initialState) { + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(activity: FragmentActivity, state: GroupListViewState): GroupListViewModel { + val matrix = activity.get() + 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) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewState.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewState.kt new file mode 100644 index 00000000..5922f073 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewState.kt @@ -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> = Uninitialized, + val selectedGroup: GroupSummary? = null +) : MvRxState \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt new file mode 100644 index 00000000..d8e114ce --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryController.kt @@ -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() { + + override fun buildModels(viewState: GroupListViewState) { + buildGroupModels(viewState.async(), viewState.selectedGroup) + } + + private fun buildGroupModels(summaries: List?, 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) + } + +} diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt new file mode 100644 index 00000000..3eff4173 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt @@ -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(R.id.groupAvatarImageView) + + override fun bind() { + AvatarRenderer.render(avatarUrl, groupName.toString(), avatarImageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt index 17b8ec9b..5556034b 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt @@ -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 { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt index 6e98c269..eb51c532 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt @@ -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) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt index 32304f08..61c1dc17 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt @@ -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) } } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt index 2ed08869..fc9e4250 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomSummaryController.kt @@ -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() { diff --git a/app/src/main/res/layout/fragment_group_list.xml b/app/src/main/res/layout/fragment_group_list.xml new file mode 100644 index 00000000..a075d511 --- /dev/null +++ b/app/src/main/res/layout/fragment_group_list.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_home_drawer.xml b/app/src/main/res/layout/fragment_home_drawer.xml index 0f9895b7..b43f68f2 100644 --- a/app/src/main/res/layout/fragment_home_drawer.xml +++ b/app/src/main/res/layout/fragment_home_drawer.xml @@ -6,10 +6,9 @@ android:layout_height="match_parent"> + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_group.xml b/app/src/main/res/layout/item_group.xml new file mode 100644 index 00000000..fbeacc90 --- /dev/null +++ b/app/src/main/res/layout/item_group.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file