State issues : restore recyclerview state + fix DI issues
This commit is contained in:
parent
753e70775a
commit
fd3fce6deb
2
.idea/dictionaries/ganfra.xml
generated
2
.idea/dictionaries/ganfra.xml
generated
@ -6,6 +6,8 @@
|
|||||||
<w>merlins</w>
|
<w>merlins</w>
|
||||||
<w>moshi</w>
|
<w>moshi</w>
|
||||||
<w>persistor</w>
|
<w>persistor</w>
|
||||||
|
<w>restorable</w>
|
||||||
|
<w>restorables</w>
|
||||||
<w>synchronizer</w>
|
<w>synchronizer</w>
|
||||||
<w>untimelined</w>
|
<w>untimelined</w>
|
||||||
</words>
|
</words>
|
||||||
|
@ -23,6 +23,7 @@ import com.facebook.stetho.Stetho
|
|||||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.riotredesign.core.di.AppModule
|
import im.vector.riotredesign.core.di.AppModule
|
||||||
|
import im.vector.riotredesign.features.home.HomeModule
|
||||||
import org.koin.log.EmptyLogger
|
import org.koin.log.EmptyLogger
|
||||||
import org.koin.standalone.StandAloneContext.startKoin
|
import org.koin.standalone.StandAloneContext.startKoin
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -32,12 +33,15 @@ class Riot : Application() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
applicationContext.setTheme(R.style.Theme_Riot)
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
Stetho.initializeWithDefaults(this)
|
Stetho.initializeWithDefaults(this)
|
||||||
}
|
}
|
||||||
AndroidThreeTen.init(this)
|
AndroidThreeTen.init(this)
|
||||||
startKoin(listOf(AppModule(this).definition), logger = EmptyLogger())
|
val appModule = AppModule(applicationContext).definition
|
||||||
|
val homeModule = HomeModule(applicationContext).definition
|
||||||
|
startKoin(listOf(appModule, homeModule), logger = EmptyLogger())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
|
@ -18,10 +18,14 @@ package im.vector.riotredesign.core.di
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.MODE_PRIVATE
|
import android.content.Context.MODE_PRIVATE
|
||||||
|
import im.vector.matrix.android.api.Matrix
|
||||||
import im.vector.riotredesign.core.resources.ColorProvider
|
import im.vector.riotredesign.core.resources.ColorProvider
|
||||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||||
|
import im.vector.riotredesign.features.home.room.VisibleRoomStore
|
||||||
import im.vector.riotredesign.features.home.room.list.RoomSelectionRepository
|
import im.vector.riotredesign.features.home.room.list.RoomSelectionRepository
|
||||||
|
import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator
|
||||||
import org.koin.dsl.module.module
|
import org.koin.dsl.module.module
|
||||||
|
|
||||||
class AppModule(private val context: Context) {
|
class AppModule(private val context: Context) {
|
||||||
@ -48,5 +52,22 @@ class AppModule(private val context: Context) {
|
|||||||
RoomSelectionRepository(get())
|
RoomSelectionRepository(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
SelectedGroupStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
VisibleRoomStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
RoomSummaryComparator()
|
||||||
|
}
|
||||||
|
|
||||||
|
factory {
|
||||||
|
Matrix.getInstance().currentSession
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.epoxy
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import im.vector.riotredesign.core.platform.DefaultListUpdateCallback
|
||||||
|
import im.vector.riotredesign.core.platform.Restorable
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE"
|
||||||
|
|
||||||
|
class LayoutManagerStateRestorer(private val layoutManager: RecyclerView.LayoutManager) : Restorable, DefaultListUpdateCallback {
|
||||||
|
|
||||||
|
private var layoutManagerState = AtomicReference<Parcelable?>()
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
val layoutManagerState = layoutManager.onSaveInstanceState()
|
||||||
|
outState.putParcelable(LAYOUT_MANAGER_STATE, layoutManagerState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
|
||||||
|
val parcelable = savedInstanceState?.getParcelable<Parcelable>(LAYOUT_MANAGER_STATE)
|
||||||
|
layoutManagerState.set(parcelable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInserted(position: Int, count: Int) {
|
||||||
|
layoutManagerState.getAndSet(null)?.also {
|
||||||
|
layoutManager.onRestoreInstanceState(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotredesign.core.platform
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
|
||||||
|
interface Restorable {
|
||||||
|
|
||||||
|
fun onSaveInstanceState(outState: Bundle)
|
||||||
|
|
||||||
|
fun onRestoreInstanceState(savedInstanceState: Bundle?)
|
||||||
|
|
||||||
|
}
|
@ -16,13 +16,34 @@
|
|||||||
|
|
||||||
package im.vector.riotredesign.core.platform
|
package im.vector.riotredesign.core.platform
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.MainThread
|
||||||
import com.airbnb.mvrx.BaseMvRxActivity
|
import com.airbnb.mvrx.BaseMvRxActivity
|
||||||
|
import com.bumptech.glide.util.Util
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
|
|
||||||
abstract class RiotActivity : BaseMvRxActivity() {
|
abstract class RiotActivity : BaseMvRxActivity() {
|
||||||
|
|
||||||
private val uiDisposables = CompositeDisposable()
|
private val uiDisposables = CompositeDisposable()
|
||||||
|
private val restorables = ArrayList<Restorable>()
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
restorables.forEach { it.onSaveInstanceState(outState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
|
||||||
|
restorables.forEach { it.onRestoreInstanceState(savedInstanceState) }
|
||||||
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
protected fun <T : Restorable> T.register(): T {
|
||||||
|
Util.assertMainThread()
|
||||||
|
restorables.add(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
protected fun Disposable.disposeOnDestroy(): Disposable {
|
protected fun Disposable.disposeOnDestroy(): Disposable {
|
||||||
uiDisposables.add(this)
|
uiDisposables.add(this)
|
||||||
|
@ -18,8 +18,10 @@ package im.vector.riotredesign.core.platform
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.MainThread
|
||||||
import com.airbnb.mvrx.BaseMvRxFragment
|
import com.airbnb.mvrx.BaseMvRxFragment
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
|
import com.bumptech.glide.util.Util.assertMainThread
|
||||||
|
|
||||||
abstract class RiotFragment : BaseMvRxFragment(), OnBackPressed {
|
abstract class RiotFragment : BaseMvRxFragment(), OnBackPressed {
|
||||||
|
|
||||||
@ -27,6 +29,18 @@ abstract class RiotFragment : BaseMvRxFragment(), OnBackPressed {
|
|||||||
activity as RiotActivity
|
activity as RiotActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val restorables = ArrayList<Restorable>()
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
restorables.forEach { it.onSaveInstanceState(outState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
||||||
|
restorables.forEach { it.onRestoreInstanceState(savedInstanceState) }
|
||||||
|
super.onViewStateRestored(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBackPressed(): Boolean {
|
override fun onBackPressed(): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -39,4 +53,11 @@ abstract class RiotFragment : BaseMvRxFragment(), OnBackPressed {
|
|||||||
arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
|
arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
protected fun <T : Restorable> T.register(): T {
|
||||||
|
assertMainThread()
|
||||||
|
restorables.add(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -37,12 +37,12 @@ import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
|||||||
import im.vector.riotredesign.features.home.room.detail.LoadingRoomDetailFragment
|
import im.vector.riotredesign.features.home.room.detail.LoadingRoomDetailFragment
|
||||||
import kotlinx.android.synthetic.main.activity_home.*
|
import kotlinx.android.synthetic.main.activity_home.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.standalone.StandAloneContext.loadKoinModules
|
import org.koin.android.scope.ext.android.bindScope
|
||||||
|
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||||
|
|
||||||
|
|
||||||
class HomeActivity : RiotActivity(), ToolbarConfigurable {
|
class HomeActivity : RiotActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
|
|
||||||
private val homeActivityViewModel: HomeActivityViewModel by viewModel()
|
private val homeActivityViewModel: HomeActivityViewModel by viewModel()
|
||||||
private val homeNavigator by inject<HomeNavigator>()
|
private val homeNavigator by inject<HomeNavigator>()
|
||||||
|
|
||||||
@ -53,10 +53,10 @@ class HomeActivity : RiotActivity(), ToolbarConfigurable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
loadKoinModules(listOf(HomeModule(this).definition))
|
|
||||||
homeNavigator.activity = this
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_home)
|
setContentView(R.layout.activity_home)
|
||||||
|
bindScope(getOrCreateScope(HomeModule.HOME_SCOPE))
|
||||||
|
homeNavigator.activity = this
|
||||||
drawerLayout.addDrawerListener(drawerListener)
|
drawerLayout.addDrawerListener(drawerListener)
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
val homeDrawerFragment = HomeDrawerFragment.newInstance()
|
val homeDrawerFragment = HomeDrawerFragment.newInstance()
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
package im.vector.riotredesign.features.home
|
package im.vector.riotredesign.features.home
|
||||||
|
|
||||||
import im.vector.matrix.android.api.Matrix
|
import android.content.Context
|
||||||
|
import im.vector.riotredesign.features.home.group.GroupSummaryController
|
||||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||||
import im.vector.riotredesign.features.home.room.VisibleRoomStore
|
import im.vector.riotredesign.features.home.room.VisibleRoomStore
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.CallItemFactory
|
import im.vector.riotredesign.features.home.room.detail.timeline.CallItemFactory
|
||||||
@ -35,84 +36,83 @@ import im.vector.riotredesign.features.home.room.list.RoomSummaryController
|
|||||||
import im.vector.riotredesign.features.html.EventHtmlRenderer
|
import im.vector.riotredesign.features.html.EventHtmlRenderer
|
||||||
import org.koin.dsl.module.module
|
import org.koin.dsl.module.module
|
||||||
|
|
||||||
class HomeModule(homeActivity: HomeActivity) {
|
class HomeModule(context: Context) {
|
||||||
|
|
||||||
val definition = module(override = true) {
|
companion object {
|
||||||
|
const val HOME_SCOPE = "HOME_SCOPE"
|
||||||
|
const val ROOM_DETAIL_SCOPE = "ROOM_DETAIL_SCOPE"
|
||||||
|
const val ROOM_LIST_SCOPE = "ROOM_LIST_SCOPE"
|
||||||
|
const val GROUP_LIST_SCOPE = "GROUP_LIST_SCOPE"
|
||||||
|
}
|
||||||
|
|
||||||
single {
|
val definition = module {
|
||||||
Matrix.getInstance().currentSession
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
// Activity scope
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
TimelineDateFormatter(get())
|
TimelineDateFormatter(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
single {
|
scope(HOME_SCOPE) {
|
||||||
EventHtmlRenderer(homeActivity, get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
MessageItemFactory(get(), get(), get(), get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
RoomNameItemFactory(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
RoomTopicItemFactory(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
RoomMemberItemFactory(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
CallItemFactory(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
RoomHistoryVisibilityItemFactory(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
DefaultItemFactory()
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
TimelineItemFactory(get(), get(), get(), get(), get(), get(), get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
HomeNavigator()
|
HomeNavigator()
|
||||||
}
|
}
|
||||||
|
|
||||||
factory {
|
scope(HOME_SCOPE) {
|
||||||
RoomSummaryController(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
factory { (roomId: String) ->
|
|
||||||
TimelineEventController(roomId, get(), get(), get())
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
TimelineMediaSizeProvider()
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
SelectedGroupStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
VisibleRoomStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
HomePermalinkHandler(get())
|
HomePermalinkHandler(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
single {
|
scope(HOME_SCOPE) {
|
||||||
RoomSummaryComparator()
|
RoomNameItemFactory(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
RoomTopicItemFactory(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
RoomMemberItemFactory(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
CallItemFactory(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
RoomHistoryVisibilityItemFactory(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
DefaultItemFactory()
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
TimelineMediaSizeProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
EventHtmlRenderer(context, get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
MessageItemFactory(get(), get(), get(), get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(HOME_SCOPE) {
|
||||||
|
TimelineItemFactory(get(), get(), get(), get(), get(), get(), get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment scopes
|
||||||
|
|
||||||
|
scope(ROOM_DETAIL_SCOPE) {
|
||||||
|
TimelineEventController(get(), get(), get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(ROOM_LIST_SCOPE) {
|
||||||
|
RoomSummaryController(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
scope(GROUP_LIST_SCOPE) {
|
||||||
|
GroupSummaryController()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,9 +36,6 @@ class HomeNavigator {
|
|||||||
eventId: String?,
|
eventId: String?,
|
||||||
addToBackstack: Boolean = false) {
|
addToBackstack: Boolean = false) {
|
||||||
Timber.v("Open room detail $roomId - $eventId - $addToBackstack")
|
Timber.v("Open room detail $roomId - $eventId - $addToBackstack")
|
||||||
if (!addToBackstack && isRoot(roomId)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
activity?.let {
|
activity?.let {
|
||||||
val args = RoomDetailArgs(roomId, eventId)
|
val args = RoomDetailArgs(roomId, eventId)
|
||||||
val roomDetailFragment = RoomDetailFragment.newInstance(args)
|
val roomDetailFragment = RoomDetailFragment.newInstance(args)
|
||||||
|
@ -27,7 +27,11 @@ 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.HomeModule
|
||||||
import kotlinx.android.synthetic.main.fragment_group_list.*
|
import kotlinx.android.synthetic.main.fragment_group_list.*
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
import org.koin.android.scope.ext.android.bindScope
|
||||||
|
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||||
|
|
||||||
class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
||||||
|
|
||||||
@ -38,8 +42,7 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel: GroupListViewModel by fragmentViewModel()
|
private val viewModel: GroupListViewModel by fragmentViewModel()
|
||||||
|
private val groupController by inject<GroupSummaryController>()
|
||||||
private lateinit var groupController: GroupSummaryController
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_group_list, container, false)
|
return inflater.inflate(R.layout.fragment_group_list, container, false)
|
||||||
@ -47,7 +50,8 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
groupController = GroupSummaryController(this)
|
bindScope(getOrCreateScope(HomeModule.GROUP_LIST_SCOPE))
|
||||||
|
groupController.callback = this
|
||||||
stateView.contentView = epoxyRecyclerView
|
stateView.contentView = epoxyRecyclerView
|
||||||
epoxyRecyclerView.setController(groupController)
|
epoxyRecyclerView.setController(groupController)
|
||||||
viewModel.subscribe { renderState(it) }
|
viewModel.subscribe { renderState(it) }
|
||||||
@ -56,7 +60,7 @@ class GroupListFragment : RiotFragment(), GroupSummaryController.Callback {
|
|||||||
private fun renderState(state: GroupListViewState) {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ package im.vector.riotredesign.features.home.group
|
|||||||
import arrow.core.Option
|
import arrow.core.Option
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import im.vector.matrix.android.api.Matrix
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotredesign.core.platform.RiotViewModel
|
import im.vector.riotredesign.core.platform.RiotViewModel
|
||||||
@ -34,7 +33,7 @@ class GroupListViewModel(initialState: GroupListViewState,
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? {
|
||||||
val currentSession = Matrix.getInstance().currentSession
|
val currentSession = viewModelContext.activity.get<Session>()
|
||||||
val selectedGroupHolder = viewModelContext.activity.get<SelectedGroupStore>()
|
val selectedGroupHolder = viewModelContext.activity.get<SelectedGroupStore>()
|
||||||
return GroupListViewModel(state, selectedGroupHolder, currentSession)
|
return GroupListViewModel(state, selectedGroupHolder, currentSession)
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,9 @@ 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
|
||||||
|
|
||||||
class GroupSummaryController(private val callback: Callback? = null
|
class GroupSummaryController : TypedEpoxyController<GroupListViewState>() {
|
||||||
) : TypedEpoxyController<GroupListViewState>() {
|
|
||||||
|
var callback: Callback? = null
|
||||||
|
|
||||||
override fun buildModels(viewState: GroupListViewState) {
|
override fun buildModels(viewState: GroupListViewState) {
|
||||||
buildGroupModels(viewState.asyncGroups(), viewState.selectedGroup)
|
buildGroupModels(viewState.asyncGroups(), viewState.selectedGroup)
|
||||||
|
@ -25,19 +25,21 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.args
|
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
|
||||||
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.HomeModule
|
||||||
import im.vector.riotredesign.features.home.HomePermalinkHandler
|
import im.vector.riotredesign.features.home.HomePermalinkHandler
|
||||||
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.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.android.scope.ext.android.bindScope
|
||||||
|
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomDetailArgs(
|
data class RoomDetailArgs(
|
||||||
@ -45,6 +47,7 @@ data class RoomDetailArgs(
|
|||||||
val eventId: String? = null
|
val eventId: String? = null
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
|
||||||
class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -57,10 +60,9 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
||||||
private val roomDetailArgs: RoomDetailArgs by args()
|
private val timelineEventController by inject<TimelineEventController>()
|
||||||
|
|
||||||
private val timelineEventController by inject<TimelineEventController> { parametersOf(roomDetailArgs.roomId) }
|
|
||||||
private val homePermalinkHandler by inject<HomePermalinkHandler>()
|
private val homePermalinkHandler by inject<HomePermalinkHandler>()
|
||||||
|
|
||||||
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
|
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -69,6 +71,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
bindScope(getOrCreateScope(HomeModule.ROOM_DETAIL_SCOPE))
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
setupSendButton()
|
setupSendButton()
|
||||||
@ -80,6 +83,8 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
roomDetailViewModel.process(RoomDetailActions.IsDisplayed)
|
roomDetailViewModel.process(RoomDetailActions.IsDisplayed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
private fun setupToolbar() {
|
private fun setupToolbar() {
|
||||||
val parentActivity = riotActivity
|
val parentActivity = riotActivity
|
||||||
if (parentActivity is ToolbarConfigurable) {
|
if (parentActivity is ToolbarConfigurable) {
|
||||||
@ -91,10 +96,14 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
|||||||
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
||||||
epoxyVisibilityTracker.attach(recyclerView)
|
epoxyVisibilityTracker.attach(recyclerView)
|
||||||
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
|
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
|
||||||
|
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||||
scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager)
|
scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager)
|
||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
timelineEventController.addModelBuildListener { it.dispatchTo(scrollOnNewMessageCallback) }
|
timelineEventController.addModelBuildListener {
|
||||||
|
it.dispatchTo(stateRestorer)
|
||||||
|
it.dispatchTo(scrollOnNewMessageCallback)
|
||||||
|
}
|
||||||
recyclerView.setController(timelineEventController)
|
recyclerView.setController(timelineEventController)
|
||||||
timelineEventController.callback = this
|
timelineEventController.callback = this
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ package im.vector.riotredesign.features.home.room.detail
|
|||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
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
|
||||||
@ -46,7 +45,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
|
||||||
val currentSession = Matrix.getInstance().currentSession
|
val currentSession = viewModelContext.activity.get<Session>()
|
||||||
val visibleRoomHolder = viewModelContext.activity.get<VisibleRoomStore>()
|
val visibleRoomHolder = viewModelContext.activity.get<VisibleRoomStore>()
|
||||||
return RoomDetailViewModel(state, currentSession, visibleRoomHolder)
|
return RoomDetailViewModel(state, currentSession, visibleRoomHolder)
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,7 @@ import im.vector.riotredesign.core.extensions.localDateTime
|
|||||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.paging.PagedListEpoxyController
|
import im.vector.riotredesign.features.home.room.detail.timeline.paging.PagedListEpoxyController
|
||||||
|
|
||||||
class TimelineEventController(private val roomId: String,
|
class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||||
private val dateFormatter: TimelineDateFormatter,
|
|
||||||
private val timelineItemFactory: TimelineItemFactory,
|
private val timelineItemFactory: TimelineItemFactory,
|
||||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider
|
private val timelineMediaSizeProvider: TimelineMediaSizeProvider
|
||||||
) : PagedListEpoxyController<TimelineEvent>(
|
) : PagedListEpoxyController<TimelineEvent>(
|
||||||
@ -82,7 +81,7 @@ class TimelineEventController(private val roomId: String,
|
|||||||
}
|
}
|
||||||
if (addDaySeparator) {
|
if (addDaySeparator) {
|
||||||
val formattedDay = dateFormatter.formatMessageDay(date)
|
val formattedDay = dateFormatter.formatMessageDay(date)
|
||||||
val daySeparatorItem = DaySeparatorItem_().formattedDay(formattedDay).id(roomId + formattedDay)
|
val daySeparatorItem = DaySeparatorItem_().formattedDay(formattedDay).id(formattedDay)
|
||||||
epoxyModels.add(daySeparatorItem)
|
epoxyModels.add(daySeparatorItem)
|
||||||
}
|
}
|
||||||
return epoxyModels
|
return epoxyModels
|
||||||
@ -90,13 +89,13 @@ class TimelineEventController(private val roomId: String,
|
|||||||
|
|
||||||
override fun addModels(models: List<EpoxyModel<*>>) {
|
override fun addModels(models: List<EpoxyModel<*>>) {
|
||||||
LoadingItemModel_()
|
LoadingItemModel_()
|
||||||
.id(roomId + "forward_loading_item")
|
.id("forward_loading_item")
|
||||||
.addIf(isLoadingForward, this)
|
.addIf(isLoadingForward, this)
|
||||||
|
|
||||||
super.add(models)
|
super.add(models)
|
||||||
|
|
||||||
LoadingItemModel_()
|
LoadingItemModel_()
|
||||||
.id(roomId + "backward_loading_item")
|
.id("backward_loading_item")
|
||||||
.addIf(!hasReachedEnd, this)
|
.addIf(!hasReachedEnd, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ sealed class RoomListActions {
|
|||||||
|
|
||||||
data class SelectRoom(val roomSummary: RoomSummary) : RoomListActions()
|
data class SelectRoom(val roomSummary: RoomSummary) : RoomListActions()
|
||||||
|
|
||||||
object RoomDisplayed : RoomListActions()
|
|
||||||
|
|
||||||
data class FilterRooms(val roomName: CharSequence? = null) : RoomListActions()
|
data class FilterRooms(val roomName: CharSequence? = null) : RoomListActions()
|
||||||
|
|
||||||
|
data class ToggleCategory(val category: RoomCategory) : RoomListActions()
|
||||||
|
|
||||||
}
|
}
|
@ -22,19 +22,25 @@ import android.text.TextWatcher
|
|||||||
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 androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
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.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
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.epoxy.LayoutManagerStateRestorer
|
||||||
|
import im.vector.riotredesign.core.extensions.observeEvent
|
||||||
import im.vector.riotredesign.core.extensions.setupAsSearch
|
import im.vector.riotredesign.core.extensions.setupAsSearch
|
||||||
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.HomeModule
|
||||||
import im.vector.riotredesign.features.home.HomeNavigator
|
import im.vector.riotredesign.features.home.HomeNavigator
|
||||||
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
|
||||||
|
import org.koin.android.scope.ext.android.bindScope
|
||||||
|
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||||
|
|
||||||
class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
||||||
|
|
||||||
@ -44,9 +50,9 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val homeNavigator by inject<HomeNavigator>()
|
|
||||||
private val roomController by inject<RoomSummaryController>()
|
private val roomController by inject<RoomSummaryController>()
|
||||||
private val homeViewModel: RoomListViewModel by activityViewModel()
|
private val homeNavigator by inject<HomeNavigator>()
|
||||||
|
private val roomListViewModel: RoomListViewModel by fragmentViewModel()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_room_list, container, false)
|
return inflater.inflate(R.layout.fragment_room_list, container, false)
|
||||||
@ -54,11 +60,36 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
bindScope(getOrCreateScope(HomeModule.ROOM_LIST_SCOPE))
|
||||||
|
setupRecyclerView()
|
||||||
|
setupFilterView()
|
||||||
|
roomListViewModel.subscribe { renderState(it) }
|
||||||
|
roomListViewModel.openRoomLiveData.observeEvent(this) {
|
||||||
|
homeNavigator.openRoomDetail(it, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
val layoutManager = LinearLayoutManager(context)
|
||||||
|
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||||
|
epoxyRecyclerView.layoutManager = layoutManager
|
||||||
roomController.callback = this
|
roomController.callback = this
|
||||||
|
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
||||||
stateView.contentView = epoxyRecyclerView
|
stateView.contentView = epoxyRecyclerView
|
||||||
epoxyRecyclerView.setController(roomController)
|
epoxyRecyclerView.setController(roomController)
|
||||||
setupFilterView()
|
}
|
||||||
homeViewModel.subscribe { renderState(it) }
|
|
||||||
|
private fun setupFilterView() {
|
||||||
|
filterRoomView.setupAsSearch()
|
||||||
|
filterRoomView.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) = Unit
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
roomListViewModel.accept(RoomListActions.FilterRooms(s))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderState(state: RoomListViewState) {
|
private fun renderState(state: RoomListViewState) {
|
||||||
@ -90,24 +121,13 @@ class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
|
|||||||
stateView.state = StateView.State.Error(message)
|
stateView.state = StateView.State.Error(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupFilterView() {
|
|
||||||
filterRoomView.setupAsSearch()
|
|
||||||
filterRoomView.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun afterTextChanged(s: Editable?) = Unit
|
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
||||||
homeViewModel.accept(RoomListActions.FilterRooms(s))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RoomSummaryController.Callback **************************************************************
|
// RoomSummaryController.Callback **************************************************************
|
||||||
|
|
||||||
override fun onRoomSelected(room: RoomSummary) {
|
override fun onRoomSelected(room: RoomSummary) {
|
||||||
homeViewModel.accept(RoomListActions.SelectRoom(room))
|
roomListViewModel.accept(RoomListActions.SelectRoom(room))
|
||||||
homeNavigator.openRoomDetail(room.roomId, null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
||||||
|
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,22 +16,23 @@
|
|||||||
|
|
||||||
package im.vector.riotredesign.features.home.room.list
|
package im.vector.riotredesign.features.home.room.list
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import arrow.core.Option
|
import arrow.core.Option
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||||
import im.vector.matrix.android.api.Matrix
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.group.model.GroupSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotredesign.core.platform.RiotViewModel
|
import im.vector.riotredesign.core.platform.RiotViewModel
|
||||||
|
import im.vector.riotredesign.core.utils.LiveEvent
|
||||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||||
import im.vector.riotredesign.features.home.room.VisibleRoomStore
|
import im.vector.riotredesign.features.home.room.VisibleRoomStore
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.functions.Function3
|
import io.reactivex.functions.Function3
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel? {
|
||||||
val currentSession = Matrix.getInstance().currentSession
|
val currentSession = viewModelContext.activity.get<Session>()
|
||||||
val roomSelectionRepository = viewModelContext.activity.get<RoomSelectionRepository>()
|
val roomSelectionRepository = viewModelContext.activity.get<RoomSelectionRepository>()
|
||||||
val selectedGroupHolder = viewModelContext.activity.get<SelectedGroupStore>()
|
val selectedGroupHolder = viewModelContext.activity.get<SelectedGroupStore>()
|
||||||
val visibleRoomHolder = viewModelContext.activity.get<VisibleRoomStore>()
|
val visibleRoomHolder = viewModelContext.activity.get<VisibleRoomStore>()
|
||||||
@ -61,6 +62,10 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
|
|
||||||
private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty())
|
private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty())
|
||||||
|
|
||||||
|
private val _openRoomLiveData = MutableLiveData<LiveEvent<String>>()
|
||||||
|
val openRoomLiveData: LiveData<LiveEvent<String>>
|
||||||
|
get() = _openRoomLiveData
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomSummaries()
|
observeRoomSummaries()
|
||||||
observeVisibleRoom()
|
observeVisibleRoom()
|
||||||
@ -68,16 +73,18 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
|
|
||||||
fun accept(action: RoomListActions) {
|
fun accept(action: RoomListActions) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomListActions.SelectRoom -> handleSelectRoom(action)
|
is RoomListActions.SelectRoom -> handleSelectRoom(action)
|
||||||
is RoomListActions.FilterRooms -> handleFilterRooms(action)
|
is RoomListActions.FilterRooms -> handleFilterRooms(action)
|
||||||
|
is RoomListActions.ToggleCategory -> handleToggleCategory(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
private fun handleSelectRoom(action: RoomListActions.SelectRoom) = withState { state ->
|
private fun handleSelectRoom(action: RoomListActions.SelectRoom) = withState { state ->
|
||||||
if (state.selectedRoomId != action.roomSummary.roomId) {
|
if (state.visibleRoomId != action.roomSummary.roomId) {
|
||||||
roomSelectionRepository.saveLastSelectedRoom(action.roomSummary.roomId)
|
roomSelectionRepository.saveLastSelectedRoom(action.roomSummary.roomId)
|
||||||
|
_openRoomLiveData.postValue(LiveEvent(action.roomSummary.roomId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +93,14 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
roomListFilter.accept(optionalFilter)
|
roomListFilter.accept(optionalFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleToggleCategory(action: RoomListActions.ToggleCategory) = setState {
|
||||||
|
this.toggle(action.category)
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeVisibleRoom() {
|
private fun observeVisibleRoom() {
|
||||||
visibleRoomHolder.observe()
|
visibleRoomHolder.observe()
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
setState { copy(selectedRoomId = it) }
|
setState { copy(visibleRoomId = it) }
|
||||||
}
|
}
|
||||||
.subscribe()
|
.subscribe()
|
||||||
.disposeOnClear()
|
.disposeOnClear()
|
||||||
@ -159,13 +170,13 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoomSummaries(
|
return RoomSummaries().apply {
|
||||||
favourites = favourites.sortedWith(roomSummaryComparator),
|
put(RoomCategory.FAVOURITE, favourites.sortedWith(roomSummaryComparator))
|
||||||
directRooms = directChats.sortedWith(roomSummaryComparator),
|
put(RoomCategory.DIRECT, directChats.sortedWith(roomSummaryComparator))
|
||||||
groupRooms = groupRooms.sortedWith(roomSummaryComparator),
|
put(RoomCategory.GROUP, groupRooms.sortedWith(roomSummaryComparator))
|
||||||
lowPriorities = lowPriorities.sortedWith(roomSummaryComparator),
|
put(RoomCategory.LOW_PRIORITY, lowPriorities.sortedWith(roomSummaryComparator))
|
||||||
serverNotices = serverNotices.sortedWith(roomSummaryComparator)
|
put(RoomCategory.SERVER_NOTICE, serverNotices.sortedWith(roomSummaryComparator))
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,24 +16,54 @@
|
|||||||
|
|
||||||
package im.vector.riotredesign.features.home.room.list
|
package im.vector.riotredesign.features.home.room.list
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
data class RoomListViewState(
|
data class RoomListViewState(
|
||||||
val asyncRooms: Async<RoomSummaries> = Uninitialized,
|
val asyncRooms: Async<RoomSummaries> = Uninitialized,
|
||||||
val selectedRoomId: String? = null
|
val visibleRoomId: String? = null,
|
||||||
) : MvRxState
|
val isFavouriteRoomsExpanded: Boolean = true,
|
||||||
|
val isDirectRoomsExpanded: Boolean = false,
|
||||||
|
val isGroupRoomsExpanded: Boolean = false,
|
||||||
|
val isLowPriorityRoomsExpanded: Boolean = false,
|
||||||
|
val isServerNoticeRoomsExpanded: Boolean = false
|
||||||
|
) : MvRxState {
|
||||||
|
|
||||||
data class RoomSummaries(
|
fun isCategoryExpanded(roomCategory: RoomCategory): Boolean {
|
||||||
val favourites: List<RoomSummary>,
|
return when (roomCategory) {
|
||||||
val directRooms: List<RoomSummary>,
|
RoomCategory.FAVOURITE -> isFavouriteRoomsExpanded
|
||||||
val groupRooms: List<RoomSummary>,
|
RoomCategory.DIRECT -> isDirectRoomsExpanded
|
||||||
val lowPriorities: List<RoomSummary>,
|
RoomCategory.GROUP -> isGroupRoomsExpanded
|
||||||
val serverNotices: List<RoomSummary>
|
RoomCategory.LOW_PRIORITY -> isLowPriorityRoomsExpanded
|
||||||
)
|
RoomCategory.SERVER_NOTICE -> isServerNoticeRoomsExpanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggle(roomCategory: RoomCategory): RoomListViewState {
|
||||||
|
return when (roomCategory) {
|
||||||
|
RoomCategory.FAVOURITE -> copy(isFavouriteRoomsExpanded = !isFavouriteRoomsExpanded)
|
||||||
|
RoomCategory.DIRECT -> copy(isDirectRoomsExpanded = !isDirectRoomsExpanded)
|
||||||
|
RoomCategory.GROUP -> copy(isGroupRoomsExpanded = !isGroupRoomsExpanded)
|
||||||
|
RoomCategory.LOW_PRIORITY -> copy(isLowPriorityRoomsExpanded = !isLowPriorityRoomsExpanded)
|
||||||
|
RoomCategory.SERVER_NOTICE -> copy(isServerNoticeRoomsExpanded = !isServerNoticeRoomsExpanded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias RoomSummaries = LinkedHashMap<RoomCategory, List<RoomSummary>>
|
||||||
|
|
||||||
|
enum class RoomCategory(@StringRes val titleRes: Int) {
|
||||||
|
FAVOURITE(R.string.room_list_favourites),
|
||||||
|
DIRECT(R.string.room_list_direct),
|
||||||
|
GROUP(R.string.room_list_group),
|
||||||
|
LOW_PRIORITY(R.string.room_list_low_priority),
|
||||||
|
SERVER_NOTICE(R.string.room_list_system_alert)
|
||||||
|
}
|
||||||
|
|
||||||
fun RoomSummaries?.isNullOrEmpty(): Boolean {
|
fun RoomSummaries?.isNullOrEmpty(): Boolean {
|
||||||
return this == null || (directRooms.isEmpty() && groupRooms.isEmpty() && favourites.isEmpty() && lowPriorities.isEmpty() && serverNotices.isEmpty())
|
return this == null || isEmpty()
|
||||||
}
|
}
|
@ -19,65 +19,34 @@ package im.vector.riotredesign.features.home.room.list
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
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.R
|
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
|
||||||
class RoomSummaryController(private val stringProvider: StringProvider
|
class RoomSummaryController(private val stringProvider: StringProvider
|
||||||
) : TypedEpoxyController<RoomListViewState>() {
|
) : TypedEpoxyController<RoomListViewState>() {
|
||||||
|
|
||||||
private var isFavoriteRoomsExpanded = true
|
|
||||||
private var isDirectRoomsExpanded = false
|
|
||||||
private var isGroupRoomsExpanded = false
|
|
||||||
private var isLowPriorityRoomsExpanded = false
|
|
||||||
private var isServerNoticeRoomsExpanded = false
|
|
||||||
|
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
|
|
||||||
override fun buildModels(viewState: RoomListViewState) {
|
override fun buildModels(viewState: RoomListViewState) {
|
||||||
val roomSummaries = viewState.asyncRooms()
|
val roomSummaries = viewState.asyncRooms()
|
||||||
val favourites = roomSummaries?.favourites ?: emptyList()
|
roomSummaries?.forEach { (category, summaries) ->
|
||||||
buildRoomCategory(viewState, favourites, R.string.room_list_favourites, isFavoriteRoomsExpanded) {
|
if (summaries.isEmpty()) {
|
||||||
isFavoriteRoomsExpanded = !isFavoriteRoomsExpanded
|
return@forEach
|
||||||
|
} else {
|
||||||
|
val isExpanded = viewState.isCategoryExpanded(category)
|
||||||
|
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
||||||
|
callback?.onToggleRoomCategory(category)
|
||||||
|
}
|
||||||
|
if (isExpanded) {
|
||||||
|
buildRoomModels(summaries, viewState.visibleRoomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isFavoriteRoomsExpanded) {
|
|
||||||
buildRoomModels(favourites, viewState.selectedRoomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
val directRooms = roomSummaries?.directRooms ?: emptyList()
|
|
||||||
buildRoomCategory(viewState, directRooms, R.string.room_list_direct, isDirectRoomsExpanded) {
|
|
||||||
isDirectRoomsExpanded = !isDirectRoomsExpanded
|
|
||||||
}
|
|
||||||
if (isDirectRoomsExpanded) {
|
|
||||||
buildRoomModels(directRooms, viewState.selectedRoomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
val groupRooms = roomSummaries?.groupRooms ?: emptyList()
|
|
||||||
buildRoomCategory(viewState, groupRooms, R.string.room_list_group, isGroupRoomsExpanded) {
|
|
||||||
isGroupRoomsExpanded = !isGroupRoomsExpanded
|
|
||||||
}
|
|
||||||
if (isGroupRoomsExpanded) {
|
|
||||||
buildRoomModels(groupRooms, viewState.selectedRoomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
val lowPriorities = roomSummaries?.lowPriorities ?: emptyList()
|
|
||||||
buildRoomCategory(viewState, lowPriorities, R.string.room_list_low_priority, isLowPriorityRoomsExpanded) {
|
|
||||||
isLowPriorityRoomsExpanded = !isLowPriorityRoomsExpanded
|
|
||||||
}
|
|
||||||
if (isLowPriorityRoomsExpanded) {
|
|
||||||
buildRoomModels(lowPriorities, viewState.selectedRoomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
val serverNotices = roomSummaries?.serverNotices ?: emptyList()
|
|
||||||
buildRoomCategory(viewState, serverNotices, R.string.room_list_system_alert, isServerNoticeRoomsExpanded) {
|
|
||||||
isServerNoticeRoomsExpanded = !isServerNoticeRoomsExpanded
|
|
||||||
}
|
|
||||||
if (isServerNoticeRoomsExpanded) {
|
|
||||||
buildRoomModels(serverNotices, viewState.selectedRoomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildRoomCategory(viewState: RoomListViewState, summaries: List<RoomSummary>, @StringRes titleRes: Int, isExpanded: Boolean, mutateExpandedState: () -> Unit) {
|
private fun buildRoomCategory(viewState: RoomListViewState, summaries: List<RoomSummary>, @StringRes titleRes: Int, isExpanded: Boolean, mutateExpandedState: () -> Unit) {
|
||||||
|
if (summaries.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
//TODO should add some business logic later
|
//TODO should add some business logic later
|
||||||
val unreadCount = if (summaries.isEmpty()) {
|
val unreadCount = if (summaries.isEmpty()) {
|
||||||
0
|
0
|
||||||
@ -117,6 +86,7 @@ class RoomSummaryController(private val stringProvider: StringProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
fun onToggleRoomCategory(roomCategory: RoomCategory)
|
||||||
fun onRoomSelected(room: RoomSummary)
|
fun onRoomSelected(room: RoomSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user