/* This Source Code Form is subject to the terms of the Mozilla Public 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/. */ package org.mozilla.fenix.mvi import androidx.lifecycle.ViewModel import io.reactivex.Observable import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.withLatestFrom import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject interface UIComponentViewModel { val changes: Observer val state: Observable } interface UIComponentViewModelProvider { fun fetchViewModel(): UIComponentViewModel } abstract class UIComponent( protected val actionEmitter: Observer, protected val changesObservable: Observable, private val viewModelProvider: UIComponentViewModelProvider ) { open val uiView: UIView by lazy { initView() } abstract fun initView(): UIView open fun getContainerId() = uiView.containerId fun bind(): CompositeDisposable { val compositeDisposable = CompositeDisposable() val viewModel = viewModelProvider.fetchViewModel() compositeDisposable.add(changesObservable.subscribe(viewModel.changes::onNext)) compositeDisposable.add( viewModel .state .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(uiView.updateView()) ) return compositeDisposable } } abstract class UIComponentViewModelBase( initialState: S, reducer: Reducer ) : ViewModel(), UIComponentViewModel { final override val changes: Observer private var _state: BehaviorSubject = BehaviorSubject.createDefault(initialState) override val state: Observable get() = _state init { changes = PublishSubject.create() changes .withLatestFrom(_state) .map { reducer(it.second, it.first) } .distinctUntilChanged() .subscribe(_state) } }