mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-08 08:08:45 +01:00
Remove HomeViewModel and dispatch in multiple view models (one for each fragment)
This commit is contained in:
parent
7f11c141c7
commit
7c0df91a58
@ -47,7 +47,7 @@ configurations.all { strategy ->
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
def epoxy_version = "2.19.0"
|
def epoxy_version = "2.19.0"
|
||||||
|
def arrow_version = "0.8.0"
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation project(":matrix-sdk-android")
|
implementation project(":matrix-sdk-android")
|
||||||
@ -58,22 +58,30 @@ dependencies {
|
|||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||||
|
|
||||||
implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'
|
implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'
|
||||||
|
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.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")
|
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
||||||
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||||
implementation "com.airbnb.android:epoxy-paging:$epoxy_version"
|
implementation "com.airbnb.android:epoxy-paging:$epoxy_version"
|
||||||
implementation 'com.airbnb.android:mvrx:0.6.0'
|
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'
|
implementation 'com.github.bumptech.glide:glide:4.8.0'
|
||||||
kapt 'com.github.bumptech.glide:compiler:4.8.0'
|
kapt 'com.github.bumptech.glide:compiler:4.8.0'
|
||||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||||
|
|
||||||
|
// DI
|
||||||
implementation "org.koin:koin-android:$koin_version"
|
implementation "org.koin:koin-android:$koin_version"
|
||||||
implementation "org.koin:koin-android-scope:$koin_version"
|
implementation "org.koin:koin-android-scope:$koin_version"
|
||||||
|
|
||||||
|
// TESTS
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package im.vector.riotredesign.core.platform
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.BaseMvRxViewModel
|
||||||
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
|
||||||
|
abstract class RiotViewModel<S : MvRxState>(initialState: S)
|
||||||
|
: BaseMvRxViewModel<S>(initialState, debugMode = false)
|
@ -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()
|
|
||||||
|
|
||||||
}
|
|
@ -5,12 +5,10 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.FragmentManager
|
import android.support.v4.app.FragmentManager
|
||||||
import android.support.v4.view.GravityCompat
|
import android.support.v4.view.GravityCompat
|
||||||
import android.support.v4.widget.DrawerLayout
|
|
||||||
import android.support.v7.app.ActionBarDrawerToggle
|
import android.support.v7.app.ActionBarDrawerToggle
|
||||||
import android.support.v7.widget.Toolbar
|
import android.support.v7.widget.Toolbar
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.extensions.replaceFragment
|
import im.vector.riotredesign.core.extensions.replaceFragment
|
||||||
import im.vector.riotredesign.core.platform.OnBackPressed
|
import im.vector.riotredesign.core.platform.OnBackPressed
|
||||||
@ -91,11 +89,8 @@ class HomeActivity : RiotActivity(), HomeNavigator, ToolbarConfigurable {
|
|||||||
override fun openRoomDetail(roomId: String, eventId: String?) {
|
override fun openRoomDetail(roomId: String, eventId: String?) {
|
||||||
val args = RoomDetailArgs(roomId, eventId)
|
val args = RoomDetailArgs(roomId, eventId)
|
||||||
val roomDetailFragment = RoomDetailFragment.newInstance(args)
|
val roomDetailFragment = RoomDetailFragment.newInstance(args)
|
||||||
if (drawerLayout.isDrawerOpen(Gravity.LEFT)) {
|
drawerLayout.closeDrawer(Gravity.LEFT)
|
||||||
closeDrawerLayout(Gravity.LEFT) { replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) }
|
replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer)
|
||||||
} else {
|
|
||||||
replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openGroupDetail(groupId: String) {
|
override fun openGroupDetail(groupId: String) {
|
||||||
@ -106,16 +101,6 @@ class HomeActivity : RiotActivity(), HomeNavigator, ToolbarConfigurable {
|
|||||||
Timber.v("Open user detail $userId")
|
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 {
|
companion object {
|
||||||
fun newIntent(context: Context): Intent {
|
fun newIntent(context: Context): Intent {
|
||||||
return Intent(context, HomeActivity::class.java)
|
return Intent(context, HomeActivity::class.java)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package im.vector.riotredesign.features.home
|
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.MessageItemFactory
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.TextItemFactory
|
import im.vector.riotredesign.features.home.room.detail.timeline.TextItemFactory
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineDateFormatter
|
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())
|
TimelineEventController(roomId, get(), get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
SelectedGroupHolder()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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<HomeViewState>(initialState) {
|
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<HomeViewState> {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
override fun create(activity: FragmentActivity, state: HomeViewState): HomeViewModel {
|
|
||||||
val currentSession = Matrix.getInstance().currentSession
|
|
||||||
val roomSelectionRepository = activity.get<RoomSelectionRepository>()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<List<RoomSummary>> = Uninitialized,
|
|
||||||
val directRooms: List<RoomSummary> = emptyList(),
|
|
||||||
val groupRooms: List<RoomSummary> = emptyList(),
|
|
||||||
val selectedRoomId: String? = null,
|
|
||||||
val selectedEventId: String? = null,
|
|
||||||
val shouldOpenRoomDetail: Boolean = true,
|
|
||||||
val asyncGroups: Async<List<GroupSummary>> = Uninitialized,
|
|
||||||
val selectedGroup: GroupSummary? = null
|
|
||||||
) : MvRxState
|
|
@ -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()
|
||||||
|
|
||||||
|
}
|
@ -6,14 +6,11 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.airbnb.mvrx.Incomplete
|
import com.airbnb.mvrx.Incomplete
|
||||||
import com.airbnb.mvrx.Success
|
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.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.RiotFragment
|
import im.vector.riotredesign.core.platform.RiotFragment
|
||||||
import im.vector.riotredesign.core.platform.StateView
|
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.*
|
import kotlinx.android.synthetic.main.fragment_group_list.*
|
||||||
|
|
||||||
class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
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
|
private lateinit var groupController: GroupSummaryController
|
||||||
|
|
||||||
@ -40,14 +37,14 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
|||||||
viewModel.subscribe { renderState(it) }
|
viewModel.subscribe { renderState(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderState(state: HomeViewState) {
|
private fun renderState(state: GroupListViewState) {
|
||||||
when (state.asyncGroups) {
|
when (state.asyncGroups) {
|
||||||
is Incomplete -> renderLoading()
|
is Incomplete -> renderLoading()
|
||||||
is Success -> renderSuccess(state)
|
is Success -> renderSuccess(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSuccess(state: HomeViewState) {
|
private fun renderSuccess(state: GroupListViewState) {
|
||||||
stateView.state = StateView.State.Content
|
stateView.state = StateView.State.Content
|
||||||
groupController.setData(state)
|
groupController.setData(state)
|
||||||
}
|
}
|
||||||
@ -57,7 +54,7 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onGroupSelected(groupSummary: GroupSummary) {
|
override fun onGroupSelected(groupSummary: GroupSummary) {
|
||||||
viewModel.accept(HomeActions.SelectGroup(groupSummary))
|
viewModel.accept(GroupListActions.SelectGroup(groupSummary))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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<GroupListViewState>(initialState) {
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<GroupListViewState> {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
override fun create(activity: FragmentActivity, state: GroupListViewState): GroupListViewModel {
|
||||||
|
val currentSession = Matrix.getInstance().currentSession
|
||||||
|
val selectedGroupHolder = activity.get<SelectedGroupHolder>()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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<List<GroupSummary>> = Uninitialized,
|
||||||
|
val selectedGroup: GroupSummary? = null
|
||||||
|
) : MvRxState
|
@ -2,12 +2,11 @@ package im.vector.riotredesign.features.home.group
|
|||||||
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.riotredesign.features.home.HomeViewState
|
|
||||||
|
|
||||||
class GroupSummaryController(private val callback: Callback? = null
|
class GroupSummaryController(private val callback: Callback? = null
|
||||||
) : TypedEpoxyController<HomeViewState>() {
|
) : TypedEpoxyController<GroupListViewState>() {
|
||||||
|
|
||||||
override fun buildModels(viewState: HomeViewState) {
|
override fun buildModels(viewState: GroupListViewState) {
|
||||||
buildGroupModels(viewState.asyncGroups(), viewState.selectedGroup)
|
buildGroupModels(viewState.asyncGroups(), viewState.selectedGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<GroupSummary>>(Option.empty())
|
||||||
|
|
||||||
|
fun setSelectedGroup(group: GroupSummary?) {
|
||||||
|
val optionValue = Option.fromNullable(group)
|
||||||
|
selectedGroupStream.onNext(optionValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectedGroup(): Observable<Option<GroupSummary>> {
|
||||||
|
return selectedGroupStream.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -6,8 +6,6 @@ import android.support.v7.widget.LinearLayoutManager
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.airbnb.mvrx.Success
|
|
||||||
import com.airbnb.mvrx.activityViewModel
|
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkParser
|
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.RiotFragment
|
||||||
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
||||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
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 im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
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 roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
||||||
private val roomDetailArgs: RoomDetailArgs by args()
|
private val roomDetailArgs: RoomDetailArgs by args()
|
||||||
|
|
||||||
@ -83,12 +78,8 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderState(state: RoomDetailViewState) {
|
private fun renderState(state: RoomDetailViewState) {
|
||||||
when (state.asyncTimeline) {
|
renderTimeline(state.asyncTimeline())
|
||||||
is Success -> renderTimeline(state.asyncTimeline())
|
renderRoomSummary(state.asyncRoomSummary())
|
||||||
}
|
|
||||||
when (state.asyncRoomSummary) {
|
|
||||||
is Success -> renderRoomSummary(state.asyncRoomSummary())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderRoomSummary(roomSummary: RoomSummary?) {
|
private fun renderRoomSummary(roomSummary: RoomSummary?) {
|
||||||
@ -113,7 +104,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
|
|
||||||
override fun onUrlClicked(url: String) {
|
override fun onUrlClicked(url: String) {
|
||||||
val permalinkData = PermalinkParser.parse(url)
|
val permalinkData = PermalinkParser.parse(url)
|
||||||
homeViewModel.accept(HomeActions.PermalinkClicked(permalinkData))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package im.vector.riotredesign.features.home.room.detail
|
package im.vector.riotredesign.features.home.room.detail
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import android.support.v4.app.FragmentActivity
|
||||||
import com.airbnb.mvrx.BaseMvRxViewModel
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import im.vector.matrix.android.api.Matrix
|
import im.vector.matrix.android.api.Matrix
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
|
import im.vector.riotredesign.core.platform.RiotViewModel
|
||||||
|
|
||||||
class RoomDetailViewModel(initialState: RoomDetailViewState,
|
class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||||
session: Session
|
session: Session
|
||||||
) : BaseMvRxViewModel<RoomDetailViewState>(initialState) {
|
) : RiotViewModel<RoomDetailViewState>(initialState) {
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
private val roomId = initialState.roomId
|
private val roomId = initialState.roomId
|
||||||
@ -53,10 +53,9 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
|||||||
|
|
||||||
private fun observeTimeline() {
|
private fun observeTimeline() {
|
||||||
room.rx().timeline(eventId)
|
room.rx().timeline(eventId)
|
||||||
.execute { async ->
|
.execute { asyncTimeline ->
|
||||||
copy(asyncTimeline = async)
|
copy(asyncTimeline = asyncTimeline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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()
|
||||||
|
|
||||||
|
}
|
@ -13,10 +13,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
|
|||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.RiotFragment
|
import im.vector.riotredesign.core.platform.RiotFragment
|
||||||
import im.vector.riotredesign.core.platform.StateView
|
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.HomeNavigator
|
||||||
import im.vector.riotredesign.features.home.HomeViewModel
|
|
||||||
import im.vector.riotredesign.features.home.HomeViewState
|
|
||||||
import kotlinx.android.synthetic.main.fragment_room_list.*
|
import kotlinx.android.synthetic.main.fragment_room_list.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
@ -29,7 +26,7 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val homeNavigator by inject<HomeNavigator>()
|
private val homeNavigator by inject<HomeNavigator>()
|
||||||
private val homeViewModel: HomeViewModel by activityViewModel()
|
private val homeViewModel: RoomListViewModel by activityViewModel()
|
||||||
private lateinit var roomController: RoomSummaryController
|
private lateinit var roomController: RoomSummaryController
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -44,19 +41,15 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
homeViewModel.subscribe { renderState(it) }
|
homeViewModel.subscribe { renderState(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderState(state: HomeViewState) {
|
private fun renderState(state: RoomListViewState) {
|
||||||
when (state.asyncRooms) {
|
when (state.asyncRooms) {
|
||||||
is Incomplete -> renderLoading()
|
is Incomplete -> renderLoading()
|
||||||
is Success -> renderSuccess(state)
|
is Success -> renderSuccess(state)
|
||||||
is Fail -> renderFailure(state.asyncRooms.error)
|
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()) {
|
if (state.asyncRooms().isNullOrEmpty()) {
|
||||||
stateView.state = StateView.State.Empty(getString(R.string.room_list_empty))
|
stateView.state = StateView.State.Empty(getString(R.string.room_list_empty))
|
||||||
} else {
|
} else {
|
||||||
@ -78,7 +71,8 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomSelected(room: RoomSummary) {
|
override fun onRoomSelected(room: RoomSummary) {
|
||||||
homeViewModel.accept(HomeActions.SelectRoom(room))
|
homeViewModel.accept(RoomListActions.SelectRoom(room))
|
||||||
|
homeNavigator.openRoomDetail(room.roomId, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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<RoomListViewState>(initialState) {
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<RoomListViewState> {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
override fun create(activity: FragmentActivity, state: RoomListViewState): RoomListViewModel {
|
||||||
|
val currentSession = Matrix.getInstance().currentSession
|
||||||
|
val roomSelectionRepository = activity.get<RoomSelectionRepository>()
|
||||||
|
val selectedGroupHolder = activity.get<SelectedGroupHolder>()
|
||||||
|
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<List<RoomSummary>, Option<GroupSummary>, 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<RoomSummaries> = Uninitialized,
|
||||||
|
val selectedRoomId: String? = null
|
||||||
|
) : MvRxState
|
||||||
|
|
||||||
|
data class RoomSummaries(
|
||||||
|
val directRooms: List<RoomSummary>,
|
||||||
|
val groupRooms: List<RoomSummary>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun RoomSummaries?.isNullOrEmpty(): Boolean {
|
||||||
|
return this == null || (directRooms.isEmpty() && groupRooms.isEmpty())
|
||||||
|
}
|
@ -2,17 +2,15 @@ package im.vector.riotredesign.features.home.room.list
|
|||||||
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.riotredesign.features.home.HomeViewState
|
|
||||||
|
|
||||||
class RoomSummaryController(private val callback: Callback? = null
|
class RoomSummaryController(private val callback: Callback? = null
|
||||||
) : TypedEpoxyController<HomeViewState>() {
|
) : TypedEpoxyController<RoomListViewState>() {
|
||||||
|
|
||||||
|
|
||||||
private var isDirectRoomsExpanded = true
|
private var isDirectRoomsExpanded = true
|
||||||
private var isGroupRoomsExpanded = true
|
private var isGroupRoomsExpanded = true
|
||||||
|
|
||||||
override fun buildModels(viewState: HomeViewState) {
|
override fun buildModels(viewState: RoomListViewState) {
|
||||||
|
val roomSummaries = viewState.asyncRooms()
|
||||||
RoomCategoryItem(
|
RoomCategoryItem(
|
||||||
title = "DIRECT MESSAGES",
|
title = "DIRECT MESSAGES",
|
||||||
isExpanded = isDirectRoomsExpanded,
|
isExpanded = isDirectRoomsExpanded,
|
||||||
@ -25,16 +23,7 @@ class RoomSummaryController(private val callback: Callback? = null
|
|||||||
.addTo(this)
|
.addTo(this)
|
||||||
|
|
||||||
if (isDirectRoomsExpanded) {
|
if (isDirectRoomsExpanded) {
|
||||||
val filteredDirectRooms = viewState.directRooms.filter {
|
buildRoomModels(roomSummaries?.directRooms ?: emptyList(), viewState.selectedRoomId)
|
||||||
if (viewState.selectedGroup == null) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
it.otherMemberIds
|
|
||||||
.intersect(viewState.selectedGroup.userIds)
|
|
||||||
.isNotEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildRoomModels(filteredDirectRooms, viewState.selectedRoomId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomCategoryItem(
|
RoomCategoryItem(
|
||||||
@ -49,10 +38,7 @@ class RoomSummaryController(private val callback: Callback? = null
|
|||||||
.addTo(this)
|
.addTo(this)
|
||||||
|
|
||||||
if (isGroupRoomsExpanded) {
|
if (isGroupRoomsExpanded) {
|
||||||
val filteredGroupRooms = viewState.groupRooms.filter {
|
buildRoomModels(roomSummaries?.groupRooms ?: emptyList(), viewState.selectedRoomId)
|
||||||
viewState.selectedGroup?.roomIds?.contains(it.roomId) ?: true
|
|
||||||
}
|
|
||||||
buildRoomModels(filteredGroupRooms, viewState.selectedRoomId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ dependencies {
|
|||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
|
||||||
|
|
||||||
// Paging
|
// Paging
|
||||||
api "android.arch.paging:runtime:1.0.1"
|
implementation "android.arch.paging:runtime:1.0.1"
|
||||||
|
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
@ -2,10 +2,10 @@ package im.vector.matrix.rx
|
|||||||
|
|
||||||
import android.arch.paging.PagedList
|
import android.arch.paging.PagedList
|
||||||
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
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.Room
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
|
||||||
class RxRoom(private val room: Room) {
|
class RxRoom(private val room: Room) {
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ class RxRoom(private val room: Room) {
|
|||||||
|
|
||||||
fun timeline(eventId: String? = null): Observable<PagedList<EnrichedEvent>> {
|
fun timeline(eventId: String? = null): Observable<PagedList<EnrichedEvent>> {
|
||||||
return room.timeline(eventId).asObservable()
|
return room.timeline(eventId).asObservable()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user