added mediators for logging

This commit is contained in:
Максим Бачинский 2020-09-22 11:38:10 +03:00
parent 468b1ef820
commit eb32192b34
6 changed files with 138 additions and 1 deletions

View File

@ -5,6 +5,7 @@ dependencies {
implementation project(":navigation-base")
implementation project(":lifecycle")
implementation project(":kotlin-extensions")
implementation project(":logging")
implementation("androidx.core:core-ktx")
implementation("androidx.appcompat:appcompat")
@ -23,6 +24,8 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android")
implementation("com.tylerthrailkill.helpers:pretty-print:2.0.2")
def fragmentVersion = "1.2.1"
def lifecycleVersion = "2.2.0"
def coroutinesVersion = "1.3.7"

View File

@ -1,12 +1,19 @@
package ru.touchin.roboswag.mvi_arch.core
import android.os.Parcelable
import androidx.annotation.CallSuper
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import ru.touchin.mvi_arch.BuildConfig
import ru.touchin.roboswag.mvi_arch.marker.ViewAction
import ru.touchin.roboswag.mvi_arch.marker.ViewState
import ru.touchin.roboswag.mvi_arch.mediator.LoggingMediator
import ru.touchin.roboswag.mvi_arch.mediator.MediatorStore
/**
* Base [ViewModel] to use in MVI architecture.
@ -33,6 +40,12 @@ abstract class MviViewModel<NavArgs : Parcelable, Action : ViewAction, State : V
protected val handle: SavedStateHandle
) : ViewModel() {
private val mediatorStore = MediatorStore(
listOfNotNull(
LoggingMediator(this::class.simpleName!!).takeIf { BuildConfig.DEBUG }
)
)
protected val navArgs: NavArgs = handle.get(MviFragment.INIT_ARGS_KEY) ?: throw IllegalStateException("Nav args mustn't be null")
protected val _state = MutableLiveData(initialState)
@ -41,6 +54,22 @@ abstract class MviViewModel<NavArgs : Parcelable, Action : ViewAction, State : V
protected val currentState: State
get() = _state.value ?: initialState
abstract fun dispatchAction(action: Action)
private val stateMediatorObserver = Observer<State>(mediatorStore::onNewState)
init {
viewModelScope.launch {
state.observeForever(stateMediatorObserver)
}
}
@CallSuper
open fun dispatchAction(action: Action) {
mediatorStore.onAction(action)
}
override fun onCleared() {
super.onCleared()
state.removeObserver(stateMediatorObserver)
}
}

View File

@ -14,9 +14,12 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import ru.touchin.mvi_arch.BuildConfig
import ru.touchin.roboswag.mvi_arch.marker.SideEffect
import ru.touchin.roboswag.mvi_arch.marker.StateChange
import ru.touchin.roboswag.mvi_arch.marker.ViewState
import ru.touchin.roboswag.mvi_arch.mediator.LoggingMediator
import ru.touchin.roboswag.mvi_arch.mediator.MediatorStore
abstract class Store<Change : StateChange, Effect : SideEffect, State : ViewState>(
initialState: State
@ -32,6 +35,12 @@ abstract class Store<Change : StateChange, Effect : SideEffect, State : ViewStat
private val childStores: MutableList<ChildStore<*, *, *>> = mutableListOf()
private val mediatorStore = MediatorStore(
listOfNotNull(
LoggingMediator(this::class.simpleName!!).takeIf { BuildConfig.DEBUG }
)
)
init {
storeScope.launch {
effects
@ -43,10 +52,13 @@ abstract class Store<Change : StateChange, Effect : SideEffect, State : ViewStat
}
fun changeState(change: Change) {
mediatorStore.onStateChange(change)
val (newState, newEffect) = reduce(currentState, change)
if (currentState != newState) {
state.value = newState
mediatorStore.onNewState(newState)
}
childStores.forEach { childStore ->
@ -55,6 +67,7 @@ abstract class Store<Change : StateChange, Effect : SideEffect, State : ViewStat
newEffect?.let {
effects.offer(it)
mediatorStore.onEffect(it)
}
}

View File

@ -0,0 +1,49 @@
package ru.touchin.roboswag.mvi_arch.mediator
import com.tylerthrailkill.helpers.prettyprint.pp
import ru.touchin.roboswag.core.log.Lc
import ru.touchin.roboswag.mvi_arch.marker.SideEffect
import ru.touchin.roboswag.mvi_arch.marker.StateChange
import ru.touchin.roboswag.mvi_arch.marker.ViewAction
import ru.touchin.roboswag.mvi_arch.marker.ViewState
class LoggingMediator(private val objectName: String) : Mediator {
override fun onEffect(effect: SideEffect) {
logObject(
prefix = "New Effect:\n",
obj = effect
)
}
override fun onAction(action: ViewAction) {
logObject(
prefix = "New Action:\n",
obj = action
)
}
override fun onNewState(state: ViewState) {
logObject(
prefix = "New State:\n",
obj = state
)
}
override fun onStateChange(change: StateChange) {
logObject(
prefix = "New State change:\n",
obj = change
)
}
private fun <T> logObject(
prefix: String,
obj: T
) {
val builder = StringBuilder()
pp(obj = obj, writeTo = builder)
val prettyOutput = builder.toString()
Lc.d("$objectName: $prefix$prettyOutput\n")
}
}

View File

@ -0,0 +1,18 @@
package ru.touchin.roboswag.mvi_arch.mediator
import ru.touchin.roboswag.mvi_arch.marker.SideEffect
import ru.touchin.roboswag.mvi_arch.marker.StateChange
import ru.touchin.roboswag.mvi_arch.marker.ViewAction
import ru.touchin.roboswag.mvi_arch.marker.ViewState
interface Mediator {
fun onEffect(effect: SideEffect)
fun onAction(action: ViewAction)
fun onNewState(state: ViewState)
fun onStateChange(change: StateChange)
}

View File

@ -0,0 +1,25 @@
package ru.touchin.roboswag.mvi_arch.mediator
import ru.touchin.roboswag.mvi_arch.marker.SideEffect
import ru.touchin.roboswag.mvi_arch.marker.StateChange
import ru.touchin.roboswag.mvi_arch.marker.ViewAction
import ru.touchin.roboswag.mvi_arch.marker.ViewState
class MediatorStore(private val mediators: List<Mediator>) : Mediator {
override fun onAction(action: ViewAction) {
mediators.forEach { it.onAction(action) }
}
override fun onEffect(effect: SideEffect) {
mediators.forEach { it.onEffect(effect) }
}
override fun onNewState(state: ViewState) {
mediators.forEach { it.onNewState(state) }
}
override fun onStateChange(change: StateChange) {
mediators.forEach { it.onStateChange(change) }
}
}