From 7c0df91a58b4c7db7de069b2f0df1e4f0b594ca6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Sat, 29 Dec 2018 19:57:38 +0100 Subject: [PATCH] Remove HomeViewModel and dispatch in multiple view models (one for each fragment) --- app/build.gradle | 14 ++- .../core/platform/RiotViewModel.kt | 7 ++ .../riotredesign/features/home/HomeActions.kt | 17 --- .../features/home/HomeActivity.kt | 19 +--- .../riotredesign/features/home/HomeModule.kt | 5 + .../features/home/HomeViewModel.kt | 107 ------------------ .../features/home/HomeViewState.kt | 18 --- .../features/home/group/GroupListActions.kt | 9 ++ .../features/home/group/GroupListFragment.kt | 13 +-- .../features/home/group/GroupListViewModel.kt | 64 +++++++++++ .../features/home/group/GroupListViewState.kt | 11 ++ .../home/group/GroupSummaryController.kt | 5 +- .../home/group/SelectedGroupHolder.kt | 22 ++++ .../home/room/detail/RoomDetailFragment.kt | 15 +-- .../home/room/detail/RoomDetailViewModel.kt | 9 +- .../home/room/list/RoomListActions.kt | 11 ++ .../home/room/list/RoomListFragment.kt | 16 +-- .../home/room/list/RoomListViewModel.kt | 93 +++++++++++++++ .../home/room/list/RoomListViewState.kt | 20 ++++ .../home/room/list/RoomSummaryController.kt | 24 +--- matrix-sdk-android-rx/build.gradle | 2 +- .../main/java/im/vector/matrix/rx/RxRoom.kt | 3 +- 22 files changed, 282 insertions(+), 222 deletions(-) create mode 100644 app/src/main/java/im/vector/riotredesign/core/platform/RiotViewModel.kt delete mode 100644 app/src/main/java/im/vector/riotredesign/features/home/HomeActions.kt delete mode 100644 app/src/main/java/im/vector/riotredesign/features/home/HomeViewModel.kt delete mode 100644 app/src/main/java/im/vector/riotredesign/features/home/HomeViewState.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/group/GroupListActions.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewState.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt create mode 100644 app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewState.kt diff --git a/app/build.gradle b/app/build.gradle index cae6db8673..1caa40659b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,7 +47,7 @@ configurations.all { strategy -> dependencies { def epoxy_version = "2.19.0" - + def arrow_version = "0.8.0" implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(":matrix-sdk-android") @@ -58,22 +58,30 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1' - implementation 'com.jakewharton.timber:timber:4.7.1' + // rx + implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' + implementation("com.airbnb.android:epoxy:$epoxy_version") kapt "com.airbnb.android:epoxy-processor:$epoxy_version" implementation "com.airbnb.android:epoxy-paging:$epoxy_version" implementation 'com.airbnb.android:mvrx:0.6.0' + // FP + implementation "io.arrow-kt:arrow-core:$arrow_version" + + // UI implementation 'com.github.bumptech.glide:glide:4.8.0' kapt 'com.github.bumptech.glide:compiler:4.8.0' implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - + // DI implementation "org.koin:koin-android:$koin_version" implementation "org.koin:koin-android-scope:$koin_version" + // TESTS testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/java/im/vector/riotredesign/core/platform/RiotViewModel.kt b/app/src/main/java/im/vector/riotredesign/core/platform/RiotViewModel.kt new file mode 100644 index 0000000000..da438d3dfc --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/core/platform/RiotViewModel.kt @@ -0,0 +1,7 @@ +package im.vector.riotredesign.core.platform + +import com.airbnb.mvrx.BaseMvRxViewModel +import com.airbnb.mvrx.MvRxState + +abstract class RiotViewModel(initialState: S) + : BaseMvRxViewModel(initialState, debugMode = false) \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeActions.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeActions.kt deleted file mode 100644 index c959a6a20e..0000000000 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeActions.kt +++ /dev/null @@ -1,17 +0,0 @@ -package im.vector.riotredesign.features.home - -import im.vector.matrix.android.api.permalinks.PermalinkData -import im.vector.matrix.android.api.session.group.model.GroupSummary -import im.vector.matrix.android.api.session.room.model.RoomSummary - -sealed class HomeActions { - - data class SelectRoom(val roomSummary: RoomSummary) : HomeActions() - - data class SelectGroup(val groupSummary: GroupSummary) : HomeActions() - - data class PermalinkClicked(val permalinkData: PermalinkData) : HomeActions() - - object RoomDisplayed : HomeActions() - -} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt index 262f040c66..c75cc05191 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt @@ -5,12 +5,10 @@ import android.content.Intent import android.os.Bundle import android.support.v4.app.FragmentManager import android.support.v4.view.GravityCompat -import android.support.v4.widget.DrawerLayout import android.support.v7.app.ActionBarDrawerToggle import android.support.v7.widget.Toolbar import android.view.Gravity import android.view.MenuItem -import android.view.View import im.vector.riotredesign.R import im.vector.riotredesign.core.extensions.replaceFragment import im.vector.riotredesign.core.platform.OnBackPressed @@ -91,11 +89,8 @@ class HomeActivity : RiotActivity(), HomeNavigator, ToolbarConfigurable { override fun openRoomDetail(roomId: String, eventId: String?) { val args = RoomDetailArgs(roomId, eventId) val roomDetailFragment = RoomDetailFragment.newInstance(args) - if (drawerLayout.isDrawerOpen(Gravity.LEFT)) { - closeDrawerLayout(Gravity.LEFT) { replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) } - } else { - replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) - } + drawerLayout.closeDrawer(Gravity.LEFT) + replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) } override fun openGroupDetail(groupId: String) { @@ -106,16 +101,6 @@ class HomeActivity : RiotActivity(), HomeNavigator, ToolbarConfigurable { Timber.v("Open user detail $userId") } - private fun closeDrawerLayout(gravity: Int, actionOnClose: () -> Unit) { - drawerLayout.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() { - override fun onDrawerClosed(p0: View) { - drawerLayout.removeDrawerListener(this) - actionOnClose() - } - }) - drawerLayout.closeDrawer(gravity) - } - companion object { fun newIntent(context: Context): Intent { return Intent(context, HomeActivity::class.java) diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index 84e8e8bce9..75491d0a04 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -1,5 +1,6 @@ package im.vector.riotredesign.features.home +import im.vector.riotredesign.features.home.group.SelectedGroupHolder import im.vector.riotredesign.features.home.room.detail.timeline.MessageItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.TextItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.TimelineDateFormatter @@ -30,5 +31,9 @@ class HomeModule(private val homeActivity: HomeActivity) { TimelineEventController(roomId, get(), get(), get()) } + single { + SelectedGroupHolder() + } + } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeViewModel.kt deleted file mode 100644 index 9fe633a628..0000000000 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeViewModel.kt +++ /dev/null @@ -1,107 +0,0 @@ -package im.vector.riotredesign.features.home - -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.permalinks.PermalinkData -import im.vector.matrix.android.api.session.Session -import im.vector.matrix.rx.rx -import im.vector.riotredesign.features.home.room.list.RoomSelectionRepository -import org.koin.android.ext.android.get - -class HomeViewModel(initialState: HomeViewState, - private val session: Session, - private val roomSelectionRepository: RoomSelectionRepository) : BaseMvRxViewModel(initialState) { - - companion object : MvRxViewModelFactory { - - @JvmStatic - override fun create(activity: FragmentActivity, state: HomeViewState): HomeViewModel { - val currentSession = Matrix.getInstance().currentSession - val roomSelectionRepository = activity.get() - return HomeViewModel(state, currentSession, roomSelectionRepository) - } - } - - init { - observeRoomSummaries() - observeGroupSummaries() - } - - fun accept(action: HomeActions) { - when (action) { - is HomeActions.SelectRoom -> handleSelectRoom(action) - is HomeActions.SelectGroup -> handleSelectGroup(action) - is HomeActions.RoomDisplayed -> setState { copy(shouldOpenRoomDetail = false) } - is HomeActions.PermalinkClicked -> handlePermalinkClicked(action) - } - } - - // PRIVATE METHODS ***************************************************************************** - - private fun handlePermalinkClicked(action: HomeActions.PermalinkClicked) = withState { state -> - when (action.permalinkData) { - is PermalinkData.EventLink -> { - - } - is PermalinkData.RoomLink -> { - - } - is PermalinkData.GroupLink -> { - - } - is PermalinkData.UserLink -> { - - } - is PermalinkData.FallbackLink -> { - - } - } - } - - private fun handleSelectRoom(action: HomeActions.SelectRoom) = withState { state -> - if (state.selectedRoomId != action.roomSummary.roomId) { - roomSelectionRepository.saveLastSelectedRoom(action.roomSummary.roomId) - setState { copy(selectedRoomId = action.roomSummary.roomId, shouldOpenRoomDetail = true) } - } - } - - private fun handleSelectGroup(action: HomeActions.SelectGroup) = withState { state -> - if (state.selectedGroup?.groupId != action.groupSummary.groupId) { - setState { copy(selectedGroup = action.groupSummary) } - } else { - setState { copy(selectedGroup = null) } - } - } - - private fun observeRoomSummaries() { - session - .rx().liveRoomSummaries() - .execute { async -> - val summaries = async() - val directRooms = summaries?.filter { it.isDirect } ?: emptyList() - val groupRooms = summaries?.filter { !it.isDirect } ?: emptyList() - - val selectedRoomId = selectedRoomId - ?: roomSelectionRepository.lastSelectedRoom() - ?: directRooms.firstOrNull()?.roomId - ?: groupRooms.firstOrNull()?.roomId - - copy( - asyncRooms = async, - directRooms = directRooms, - groupRooms = groupRooms, - selectedRoomId = selectedRoomId - ) - } - } - - private fun observeGroupSummaries() { - session - .rx().liveGroupSummaries() - .execute { async -> - copy(asyncGroups = async) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeViewState.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeViewState.kt deleted file mode 100644 index 49211b3dd0..0000000000 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeViewState.kt +++ /dev/null @@ -1,18 +0,0 @@ -package im.vector.riotredesign.features.home - -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 HomeViewState( - val asyncRooms: Async> = Uninitialized, - val directRooms: List = emptyList(), - val groupRooms: List = emptyList(), - val selectedRoomId: String? = null, - val selectedEventId: String? = null, - val shouldOpenRoomDetail: Boolean = true, - val asyncGroups: 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/GroupListActions.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListActions.kt new file mode 100644 index 0000000000..92f758a759 --- /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 index 16e4bbd817..b4799151a1 100644 --- 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 @@ -6,14 +6,11 @@ import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success -import com.airbnb.mvrx.activityViewModel +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 im.vector.riotredesign.features.home.HomeActions -import im.vector.riotredesign.features.home.HomeViewModel -import im.vector.riotredesign.features.home.HomeViewState import kotlinx.android.synthetic.main.fragment_group_list.* class GroupListFragment : RiotFragment(), GroupSummaryController.Callback { @@ -24,7 +21,7 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback { } } - private val viewModel: HomeViewModel by activityViewModel() + private val viewModel: GroupListViewModel by fragmentViewModel() private lateinit var groupController: GroupSummaryController @@ -40,14 +37,14 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback { viewModel.subscribe { renderState(it) } } - private fun renderState(state: HomeViewState) { + private fun renderState(state: GroupListViewState) { when (state.asyncGroups) { is Incomplete -> renderLoading() is Success -> renderSuccess(state) } } - private fun renderSuccess(state: HomeViewState) { + private fun renderSuccess(state: GroupListViewState) { stateView.state = StateView.State.Content groupController.setData(state) } @@ -57,7 +54,7 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback { } override fun onGroupSelected(groupSummary: GroupSummary) { - viewModel.accept(HomeActions.SelectGroup(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 0000000000..ca88f95a2d --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt @@ -0,0 +1,64 @@ +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 im.vector.riotredesign.core.platform.RiotViewModel +import org.koin.android.ext.android.get + +class GroupListViewModel(initialState: GroupListViewState, + private val selectedGroupHolder: SelectedGroupHolder, + private val session: Session +) : RiotViewModel(initialState) { + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(activity: FragmentActivity, state: GroupListViewState): GroupListViewModel { + val currentSession = Matrix.getInstance().currentSession + val selectedGroupHolder = activity.get() + return GroupListViewModel(state, selectedGroupHolder, currentSession) + } + } + + init { + observeGroupSummaries() + observeState() + } + + private fun observeState() { + subscribe { + selectedGroupHolder.setSelectedGroup(it.selectedGroup) + } + } + + 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) } + } else { + setState { copy(selectedGroup = null) } + } + } + + + private fun observeGroupSummaries() { + session + .rx().liveGroupSummaries() + .execute { async -> + copy(asyncGroups = 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 0000000000..6594b5bd5a --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewState.kt @@ -0,0 +1,11 @@ +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 + +data class GroupListViewState( + val asyncGroups: 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 index edd7c19e9f..3d34b98df9 100644 --- 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 @@ -2,12 +2,11 @@ package im.vector.riotredesign.features.home.group import com.airbnb.epoxy.TypedEpoxyController import im.vector.matrix.android.api.session.group.model.GroupSummary -import im.vector.riotredesign.features.home.HomeViewState class GroupSummaryController(private val callback: Callback? = null -) : TypedEpoxyController() { +) : TypedEpoxyController() { - override fun buildModels(viewState: HomeViewState) { + override fun buildModels(viewState: GroupListViewState) { buildGroupModels(viewState.asyncGroups(), viewState.selectedGroup) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt b/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt new file mode 100644 index 0000000000..990e6fe8a0 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupHolder.kt @@ -0,0 +1,22 @@ +package im.vector.riotredesign.features.home.group + +import arrow.core.Option +import im.vector.matrix.android.api.session.group.model.GroupSummary +import io.reactivex.Observable +import io.reactivex.subjects.BehaviorSubject + +class SelectedGroupHolder { + + private val selectedGroupStream = BehaviorSubject.createDefault>(Option.empty()) + + fun setSelectedGroup(group: GroupSummary?) { + val optionValue = Option.fromNullable(group) + selectedGroupStream.onNext(optionValue) + } + + fun selectedGroup(): Observable> { + return selectedGroupStream.hide() + } + + +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 4ed0272ed5..3cd1258ccf 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -6,8 +6,6 @@ import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.airbnb.mvrx.Success -import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import im.vector.matrix.android.api.permalinks.PermalinkParser @@ -16,8 +14,6 @@ import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.RiotFragment import im.vector.riotredesign.core.platform.ToolbarConfigurable import im.vector.riotredesign.features.home.AvatarRenderer -import im.vector.riotredesign.features.home.HomeActions -import im.vector.riotredesign.features.home.HomeViewModel import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* @@ -41,7 +37,6 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { } } - private val homeViewModel: HomeViewModel by activityViewModel() private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel() private val roomDetailArgs: RoomDetailArgs by args() @@ -83,12 +78,8 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { } private fun renderState(state: RoomDetailViewState) { - when (state.asyncTimeline) { - is Success -> renderTimeline(state.asyncTimeline()) - } - when (state.asyncRoomSummary) { - is Success -> renderRoomSummary(state.asyncRoomSummary()) - } + renderTimeline(state.asyncTimeline()) + renderRoomSummary(state.asyncRoomSummary()) } private fun renderRoomSummary(roomSummary: RoomSummary?) { @@ -113,7 +104,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { override fun onUrlClicked(url: String) { val permalinkData = PermalinkParser.parse(url) - homeViewModel.accept(HomeActions.PermalinkClicked(permalinkData)) + } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 89c70a0cf0..de89ed16f2 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -1,17 +1,17 @@ package im.vector.riotredesign.features.home.room.detail 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.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.rx.rx +import im.vector.riotredesign.core.platform.RiotViewModel class RoomDetailViewModel(initialState: RoomDetailViewState, session: Session -) : BaseMvRxViewModel(initialState) { +) : RiotViewModel(initialState) { private val room = session.getRoom(initialState.roomId)!! private val roomId = initialState.roomId @@ -53,10 +53,9 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, private fun observeTimeline() { room.rx().timeline(eventId) - .execute { async -> - copy(asyncTimeline = async) + .execute { asyncTimeline -> + copy(asyncTimeline = asyncTimeline) } } - } \ 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 new file mode 100644 index 0000000000..17b8ec9b94 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListActions.kt @@ -0,0 +1,11 @@ +package im.vector.riotredesign.features.home.room.list + +import im.vector.matrix.android.api.session.room.model.RoomSummary + +sealed class RoomListActions { + + data class SelectRoom(val roomSummary: RoomSummary) : RoomListActions() + + object RoomDisplayed : RoomListActions() + +} \ No newline at end of file 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 eeab837493..05729d4023 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 @@ -13,10 +13,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.RiotFragment import im.vector.riotredesign.core.platform.StateView -import im.vector.riotredesign.features.home.HomeActions import im.vector.riotredesign.features.home.HomeNavigator -import im.vector.riotredesign.features.home.HomeViewModel -import im.vector.riotredesign.features.home.HomeViewState import kotlinx.android.synthetic.main.fragment_room_list.* import org.koin.android.ext.android.inject @@ -29,7 +26,7 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback { } private val homeNavigator by inject() - private val homeViewModel: HomeViewModel by activityViewModel() + private val homeViewModel: RoomListViewModel by activityViewModel() private lateinit var roomController: RoomSummaryController override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -44,19 +41,15 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback { homeViewModel.subscribe { renderState(it) } } - private fun renderState(state: HomeViewState) { + private fun renderState(state: RoomListViewState) { when (state.asyncRooms) { is Incomplete -> renderLoading() is Success -> renderSuccess(state) is Fail -> renderFailure(state.asyncRooms.error) } - if (state.shouldOpenRoomDetail && state.selectedRoomId != null) { - homeNavigator.openRoomDetail(state.selectedRoomId, null) - homeViewModel.accept(HomeActions.RoomDisplayed) - } } - private fun renderSuccess(state: HomeViewState) { + private fun renderSuccess(state: RoomListViewState) { if (state.asyncRooms().isNullOrEmpty()) { stateView.state = StateView.State.Empty(getString(R.string.room_list_empty)) } else { @@ -78,7 +71,8 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback { } override fun onRoomSelected(room: RoomSummary) { - homeViewModel.accept(HomeActions.SelectRoom(room)) + homeViewModel.accept(RoomListActions.SelectRoom(room)) + homeNavigator.openRoomDetail(room.roomId, null) } } \ No newline at end of file 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 new file mode 100644 index 0000000000..198e3fee73 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt @@ -0,0 +1,93 @@ +package im.vector.riotredesign.features.home.room.list + +import android.support.v4.app.FragmentActivity +import arrow.core.Option +import com.airbnb.mvrx.MvRxViewModelFactory +import im.vector.matrix.android.api.Matrix +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.group.model.GroupSummary +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.rx.rx +import im.vector.riotredesign.core.platform.RiotViewModel +import im.vector.riotredesign.features.home.group.SelectedGroupHolder +import io.reactivex.Observable +import io.reactivex.functions.BiFunction +import org.koin.android.ext.android.get + +class RoomListViewModel(initialState: RoomListViewState, + private val session: Session, + private val selectedGroupHolder: SelectedGroupHolder, + private val roomSelectionRepository: RoomSelectionRepository) + : RiotViewModel(initialState) { + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(activity: FragmentActivity, state: RoomListViewState): RoomListViewModel { + val currentSession = Matrix.getInstance().currentSession + val roomSelectionRepository = activity.get() + val selectedGroupHolder = activity.get() + return RoomListViewModel(state, currentSession, selectedGroupHolder, roomSelectionRepository) + } + } + + init { + observeRoomSummaries() + } + + fun accept(action: RoomListActions) { + when (action) { + is RoomListActions.SelectRoom -> handleSelectRoom(action) + } + } + + // PRIVATE METHODS ***************************************************************************** + + private fun handleSelectRoom(action: RoomListActions.SelectRoom) = withState { state -> + if (state.selectedRoomId != action.roomSummary.roomId) { + roomSelectionRepository.saveLastSelectedRoom(action.roomSummary.roomId) + setState { copy(selectedRoomId = action.roomSummary.roomId) } + } + } + + private fun observeRoomSummaries() { + Observable.combineLatest, Option, RoomSummaries>( + session.rx().liveRoomSummaries(), + selectedGroupHolder.selectedGroup(), + BiFunction { rooms, selectedGroupOption -> + val selectedGroup = selectedGroupOption.orNull() + + val filteredDirectRooms = rooms + .filter { it.isDirect } + .filter { + if (selectedGroup == null) { + true + } else { + it.otherMemberIds + .intersect(selectedGroup.userIds) + .isNotEmpty() + } + } + + val filteredGroupRooms = rooms + .filter { !it.isDirect } + .filter { + selectedGroup?.roomIds?.contains(it.roomId) ?: true + } + RoomSummaries(filteredDirectRooms, filteredGroupRooms) + } + ) + .execute { async -> + val summaries = async() + val selectedRoomId = selectedRoomId + ?: roomSelectionRepository.lastSelectedRoom() + ?: summaries?.directRooms?.firstOrNull()?.roomId + ?: summaries?.groupRooms?.firstOrNull()?.roomId + + copy( + asyncRooms = async, + selectedRoomId = selectedRoomId + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewState.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewState.kt new file mode 100644 index 0000000000..2d183a55fe --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewState.kt @@ -0,0 +1,20 @@ +package im.vector.riotredesign.features.home.room.list + +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 + +data class RoomListViewState( + val asyncRooms: Async = Uninitialized, + val selectedRoomId: String? = null +) : MvRxState + +data class RoomSummaries( + val directRooms: List, + val groupRooms: List +) + +fun RoomSummaries?.isNullOrEmpty(): Boolean { + return this == null || (directRooms.isEmpty() && groupRooms.isEmpty()) +} \ No newline at end of file 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 998e59fdd6..30cdf7f767 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,17 +2,15 @@ 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.HomeViewState class RoomSummaryController(private val callback: Callback? = null -) : TypedEpoxyController() { - +) : TypedEpoxyController() { private var isDirectRoomsExpanded = true private var isGroupRoomsExpanded = true - override fun buildModels(viewState: HomeViewState) { - + override fun buildModels(viewState: RoomListViewState) { + val roomSummaries = viewState.asyncRooms() RoomCategoryItem( title = "DIRECT MESSAGES", isExpanded = isDirectRoomsExpanded, @@ -25,16 +23,7 @@ class RoomSummaryController(private val callback: Callback? = null .addTo(this) if (isDirectRoomsExpanded) { - val filteredDirectRooms = viewState.directRooms.filter { - if (viewState.selectedGroup == null) { - true - } else { - it.otherMemberIds - .intersect(viewState.selectedGroup.userIds) - .isNotEmpty() - } - } - buildRoomModels(filteredDirectRooms, viewState.selectedRoomId) + buildRoomModels(roomSummaries?.directRooms ?: emptyList(), viewState.selectedRoomId) } RoomCategoryItem( @@ -49,10 +38,7 @@ class RoomSummaryController(private val callback: Callback? = null .addTo(this) if (isGroupRoomsExpanded) { - val filteredGroupRooms = viewState.groupRooms.filter { - viewState.selectedGroup?.roomIds?.contains(it.roomId) ?: true - } - buildRoomModels(filteredGroupRooms, viewState.selectedRoomId) + buildRoomModels(roomSummaries?.groupRooms ?: emptyList(), viewState.selectedRoomId) } } diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index c05a36e837..1b5ffeed44 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -40,7 +40,7 @@ dependencies { implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' // Paging - api "android.arch.paging:runtime:1.0.1" + implementation "android.arch.paging:runtime:1.0.1" testImplementation 'junit:junit:4.12' diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index e588d078e4..a6104cfc89 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -2,10 +2,10 @@ package im.vector.matrix.rx import android.arch.paging.PagedList import im.vector.matrix.android.api.session.events.model.EnrichedEvent -import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.model.RoomSummary import io.reactivex.Observable +import io.reactivex.schedulers.Schedulers class RxRoom(private val room: Room) { @@ -15,6 +15,7 @@ class RxRoom(private val room: Room) { fun timeline(eventId: String? = null): Observable> { return room.timeline(eventId).asObservable() + .subscribeOn(Schedulers.io()) } }