Skip to content

Instantly share code, notes, and snippets.

@dmitriigriazin
Last active July 19, 2017 21:51
Show Gist options
  • Save dmitriigriazin/b3b7d3ca94ad45a141e8e71ea3aee03a to your computer and use it in GitHub Desktop.
Save dmitriigriazin/b3b7d3ca94ad45a141e8e71ea3aee03a to your computer and use it in GitHub Desktop.
package ru.avito.aviyo.play_market_info
import android.os.Bundle
import com.jakewharton.rxrelay2.BehaviorRelay
import com.jakewharton.rxrelay2.PublishRelay
import io.reactivex.disposables.CompositeDisposable
import ru.avito.aviyo.SchedulersFactory
import ru.avito.aviyo.distinctTypes
import ru.avito.aviyo.play_market_info.ScreenState.*
import ru.avito.aviyo.plusAssign
interface Presenter {
fun attachView(view: View)
fun detachView()
fun attachRouter(router: Router)
fun detachRouter()
fun dispose()
fun onSaveState(): Bundle
}
class PresenterImpl(private val stateMachine: StateMachine,
private val interactor: Interactor,
private val schedulers: SchedulersFactory,
savedState: Bundle?) : Presenter {
private val state: BehaviorRelay<ScreenState> = BehaviorRelay.createDefault(Empty())
private var lastInput = savedState?.getString(KEY_LAST_INPUT)
private val toolbarUpClicks: PublishRelay<Unit> = PublishRelay.create()
private val inputEvents: PublishRelay<String> = PublishRelay.create()
private val retryClicks: PublishRelay<Unit> = PublishRelay.create()
private lateinit var routerDisposable: CompositeDisposable
private lateinit var viewDisposable: CompositeDisposable
private val lifecycleDisposable = CompositeDisposable()
init {
lifecycleDisposable += state
.distinctTypes()
.switchMap { currentState ->
when (currentState) {
is Empty -> listenToInputEvents(currentState)
is Content -> listenToInputEvents(currentState)
is FullScreenLoading -> requestData(currentState, currentState.input)
is FullScreenError -> reloadDataByRetryClicks(currentState)
}
}
.subscribe(state)
lifecycleDisposable += state
.subscribe {
if (it is Content) {
lastInput = it.input
}
}
}
private fun reloadDataByRetryClicks(currentState: ScreenState) =
retryClicks.map { stateMachine.restartLoading(currentState) }
private fun requestData(currentState: ScreenState, input: String) =
interactor.getPackageData(input).map {
stateMachine.dispatchData(
screenState = currentState,
loadingState = it)
}
private fun listenToInputEvents(currentState: ScreenState) =
inputEvents.map { stateMachine.submitQuery(currentState, it) }
override fun attachView(view: View) {
viewDisposable = CompositeDisposable()
viewDisposable += view.toolbarUpClicks.subscribe(toolbarUpClicks)
viewDisposable += view.retryClicks.subscribe(retryClicks)
viewDisposable += state
.observeOn(schedulers.mainThread())
.subscribe(view.screenBinding)
viewDisposable += state
.observeOn(schedulers.mainThread())
.subscribe {
if (it is Content) {
lastInput = it.input
}
}
}
override fun detachView() {
viewDisposable.dispose()
}
override fun attachRouter(router: Router) {
routerDisposable = CompositeDisposable()
routerDisposable += toolbarUpClicks
.subscribe { router.leave() }
}
override fun detachRouter() {
routerDisposable.dispose()
}
override fun dispose() {
lifecycleDisposable.dispose()
}
override fun onSaveState() = Bundle().apply { putString(KEY_LAST_INPUT, lastInput) }
}
private const val KEY_LAST_INPUT = "KEY_LAST_INPUT"
package ru.avito.aviyo.play_market_info
import ru.avito.api.PlayMarketInfo
sealed class ScreenState() {
class Empty() : ScreenState()
class Content(val input: String, val info: PlayMarketInfo) : ScreenState()
class FullScreenLoading(val input: String) : ScreenState()
class FullScreenError(val input: String, val errorMessage: String) : ScreenState()
//class ModalError(val input: String, val info: PlayMarketInfo, val errorMessage: String) : ScreenState()
//class ModalLoading(val input: String, val info: PlayMarketInfo) : ScreenState()
}
package ru.avito.aviyo.play_market_info
import ru.avito.api.PlayMarketInfo
import ru.avito.aviyo.LoadingState
import ru.avito.aviyo.LoadingState.Error
import ru.avito.aviyo.LoadingState.Loaded
import ru.avito.aviyo.LoadingState.Loading
import ru.avito.aviyo.play_market_info.ScreenState.*
interface StateMachine {
fun dispatchData(screenState: ScreenState, loadingState: LoadingState<PlayMarketInfo>): ScreenState
fun restartLoading(state: ScreenState): ScreenState
fun submitQuery(state: ScreenState, query: String): ScreenState
}
class StateMachineImpl : StateMachine {
override fun dispatchData(screenState: ScreenState,
loadingState: LoadingState<PlayMarketInfo>): ScreenState {
return when (screenState) {
is FullScreenLoading -> {
when (loadingState) {
is Loaded -> Content(
input = screenState.input,
info = loadingState.data)
is Error -> FullScreenError(
input = screenState.input,
errorMessage = loadingState.error.message)
is Loading -> FullScreenLoading(
input = screenState.input
)
}
}
else -> error("Data dispatching is unsupported for screenState $screenState")
}
}
override fun submitQuery(state: ScreenState, query: String) = when (state) {
is Content, is Empty, is FullScreenError -> FullScreenLoading(input = query)
else -> error("Query submiting is unsupported for state $state")
}
override fun restartLoading(state: ScreenState) = when (state) {
is FullScreenError -> FullScreenLoading(input = state.input)
else -> error("Loading restart is unsupported for state $state")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment