2019-01-28 17:46:39 +01:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2019-07-12 20:38:15 +02:00
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2019-01-28 17:46:39 +01:00
|
|
|
|
|
|
|
package org.mozilla.fenix.mvi
|
|
|
|
|
2019-05-10 01:06:12 +02:00
|
|
|
import androidx.lifecycle.ViewModel
|
2019-01-28 17:46:39 +01:00
|
|
|
import io.reactivex.Observable
|
2019-01-31 07:49:41 +01:00
|
|
|
import io.reactivex.Observer
|
2019-01-28 17:46:39 +01:00
|
|
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
2019-05-15 08:16:48 +02:00
|
|
|
import io.reactivex.disposables.CompositeDisposable
|
|
|
|
import io.reactivex.rxkotlin.withLatestFrom
|
2019-01-28 17:46:39 +01:00
|
|
|
import io.reactivex.schedulers.Schedulers
|
2019-05-15 08:16:48 +02:00
|
|
|
import io.reactivex.subjects.BehaviorSubject
|
|
|
|
import io.reactivex.subjects.PublishSubject
|
|
|
|
|
|
|
|
interface UIComponentViewModel<S : ViewState, C : Change> {
|
|
|
|
val changes: Observer<C>
|
|
|
|
val state: Observable<S>
|
|
|
|
}
|
|
|
|
|
|
|
|
interface UIComponentViewModelProvider<S : ViewState, C : Change> {
|
|
|
|
fun fetchViewModel(): UIComponentViewModel<S, C>
|
|
|
|
}
|
2019-01-28 17:46:39 +01:00
|
|
|
|
2019-01-31 07:49:41 +01:00
|
|
|
abstract class UIComponent<S : ViewState, A : Action, C : Change>(
|
|
|
|
protected val actionEmitter: Observer<A>,
|
2019-05-15 08:16:48 +02:00
|
|
|
protected val changesObservable: Observable<C>,
|
|
|
|
private val viewModelProvider: UIComponentViewModelProvider<S, C>
|
2019-01-31 07:49:41 +01:00
|
|
|
) {
|
2019-02-20 03:10:17 +01:00
|
|
|
open val uiView: UIView<S, A, C> by lazy { initView() }
|
2019-01-28 17:46:39 +01:00
|
|
|
|
2019-01-31 07:49:41 +01:00
|
|
|
abstract fun initView(): UIView<S, A, C>
|
2019-01-28 17:46:39 +01:00
|
|
|
open fun getContainerId() = uiView.containerId
|
2019-05-15 08:16:48 +02:00
|
|
|
|
|
|
|
fun bind(): CompositeDisposable {
|
|
|
|
val compositeDisposable = CompositeDisposable()
|
|
|
|
val viewModel = viewModelProvider.fetchViewModel()
|
|
|
|
|
|
|
|
compositeDisposable.add(changesObservable.subscribe(viewModel.changes::onNext))
|
2019-05-15 19:17:37 +02:00
|
|
|
compositeDisposable.add(
|
|
|
|
viewModel
|
|
|
|
.state
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(uiView.updateView())
|
|
|
|
)
|
2019-05-15 08:16:48 +02:00
|
|
|
|
|
|
|
return compositeDisposable
|
|
|
|
}
|
2019-05-10 01:06:12 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 08:16:48 +02:00
|
|
|
abstract class UIComponentViewModelBase<S : ViewState, C : Change>(
|
2019-05-14 22:49:02 +02:00
|
|
|
initialState: S,
|
2019-05-15 08:16:48 +02:00
|
|
|
reducer: Reducer<S, C>
|
|
|
|
) : ViewModel(), UIComponentViewModel<S, C> {
|
2019-02-20 03:10:17 +01:00
|
|
|
|
2019-05-15 08:16:48 +02:00
|
|
|
final override val changes: Observer<C>
|
|
|
|
private var _state: BehaviorSubject<S> = BehaviorSubject.createDefault(initialState)
|
|
|
|
override val state: Observable<S>
|
|
|
|
get() = _state
|
|
|
|
|
|
|
|
init {
|
|
|
|
changes = PublishSubject.create<C>()
|
|
|
|
|
|
|
|
changes
|
|
|
|
.withLatestFrom(_state)
|
|
|
|
.map { reducer(it.second, it.first) }
|
2019-01-28 17:46:39 +01:00
|
|
|
.distinctUntilChanged()
|
2019-05-15 08:16:48 +02:00
|
|
|
.subscribe(_state)
|
2019-05-10 01:06:12 +02:00
|
|
|
}
|
2019-01-28 17:46:39 +01:00
|
|
|
}
|