mirror of
https://github.com/ouchadam/small-talk.git
synced 2025-02-16 12:10:45 +01:00
wip
This commit is contained in:
parent
58730470f6
commit
de1aa00715
@ -5,4 +5,5 @@ dependencies {
|
|||||||
implementation project(":features:navigator")
|
implementation project(":features:navigator")
|
||||||
implementation project(":design-library")
|
implementation project(":design-library")
|
||||||
api project(":domains:android:core")
|
api project(":domains:android:core")
|
||||||
|
api project(":domains:state")
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package app.dapk.st.core
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.dapk.state.Action
|
||||||
|
import app.dapk.state.ReducerFactory
|
||||||
|
import app.dapk.state.Store
|
||||||
|
import app.dapk.state.createStore
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class StateViewModel<S, E>(
|
||||||
|
reducerFactory: ReducerFactory<S>,
|
||||||
|
eventSource: MutableSharedFlow<E>,
|
||||||
|
) : ViewModel(), StateStore<S, E> {
|
||||||
|
|
||||||
|
private val store: Store<S> = createStore(reducerFactory, viewModelScope)
|
||||||
|
override val events: SharedFlow<E> = eventSource
|
||||||
|
override val state
|
||||||
|
get() = _state!!
|
||||||
|
private var _state: S by mutableStateOf(store.getState())
|
||||||
|
|
||||||
|
init {
|
||||||
|
_state = store.getState()
|
||||||
|
store.subscribe {
|
||||||
|
_state = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispatch(action: Action) {
|
||||||
|
viewModelScope.launch { store.dispatch(action) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <S, E> createStateViewModel(block: (suspend (E) -> Unit) -> ReducerFactory<S>): StateViewModel<S, E> {
|
||||||
|
val eventSource = MutableSharedFlow<E>(extraBufferCapacity = 1)
|
||||||
|
val reducer = block { eventSource.emit(it) }
|
||||||
|
return StateViewModel(reducer, eventSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateStore<S, E> {
|
||||||
|
fun dispatch(action: Action)
|
||||||
|
val events: SharedFlow<E>
|
||||||
|
val state: S
|
||||||
|
}
|
@ -25,4 +25,3 @@ abstract class DapkViewModel<S, VE>(initialState: S, factory: MutableStateFactor
|
|||||||
state = reducer(state)
|
state = reducer(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
domains/state/build.gradle
Normal file
8
domains/state/build.gradle
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
plugins {
|
||||||
|
id 'kotlin'
|
||||||
|
id 'java-test-fixtures'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation Dependencies.mavenCentral.kotlinCoroutinesCore
|
||||||
|
}
|
148
domains/state/src/main/kotlin/app/dapk/state/State.kt
Normal file
148
domains/state/src/main/kotlin/app/dapk/state/State.kt
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package app.dapk.state
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
fun <S> createStore(reducerFactory: ReducerFactory<S>, coroutineScope: CoroutineScope): Store<S> {
|
||||||
|
val subscribers = mutableListOf<(S) -> Unit>()
|
||||||
|
var state: S = reducerFactory.initialState()
|
||||||
|
return object : Store<S> {
|
||||||
|
private val scope = createScope(coroutineScope, this)
|
||||||
|
private val reducer = reducerFactory.create(scope)
|
||||||
|
|
||||||
|
override suspend fun dispatch(action: Action) {
|
||||||
|
scope.coroutineScope.launch {
|
||||||
|
state = reducer.reduce(action).also { nextState ->
|
||||||
|
println("!!! next state: $nextState")
|
||||||
|
subscribers.forEach { it.invoke(nextState) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getState() = state
|
||||||
|
|
||||||
|
override fun subscribe(subscriber: (S) -> Unit) {
|
||||||
|
subscribers.add(subscriber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReducerFactory<S> {
|
||||||
|
fun create(scope: ReducerScope<S>): Reducer<S>
|
||||||
|
fun initialState(): S
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface Reducer<S> {
|
||||||
|
suspend fun reduce(action: Action): S
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = object : ReducerScope<S> {
|
||||||
|
override val coroutineScope = coroutineScope
|
||||||
|
override suspend fun dispatch(action: Action) = store.dispatch(action)
|
||||||
|
override fun getState(): S = store.getState()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Store<S> {
|
||||||
|
suspend fun dispatch(action: Action)
|
||||||
|
fun getState(): S
|
||||||
|
fun subscribe(subscriber: (S) -> Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReducerScope<S> {
|
||||||
|
val coroutineScope: CoroutineScope
|
||||||
|
suspend fun dispatch(action: Action)
|
||||||
|
fun getState(): S
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface ActionHandler<S> {
|
||||||
|
val key: KClass<Action>
|
||||||
|
|
||||||
|
class Async<S>(override val key: KClass<Action>, val handler: suspend ReducerScope<S>.(Action) -> Unit) : ActionHandler<S>
|
||||||
|
class Sync<S>(override val key: KClass<Action>, val handler: (Action, S) -> S) : ActionHandler<S>
|
||||||
|
class Delegate<S>(override val key: KClass<Action>, val handler: ReducerScope<S>.(Action) -> ActionHandler<S>) : ActionHandler<S>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <S> createReducer(
|
||||||
|
initialState: S,
|
||||||
|
vararg reducers: (ReducerScope<S>) -> ActionHandler<S>,
|
||||||
|
): ReducerFactory<S> {
|
||||||
|
return object : ReducerFactory<S> {
|
||||||
|
override fun create(scope: ReducerScope<S>): Reducer<S> {
|
||||||
|
val reducersMap = reducers
|
||||||
|
.map { it.invoke(scope) }
|
||||||
|
.groupBy { it.key }
|
||||||
|
|
||||||
|
return Reducer { action ->
|
||||||
|
val result = reducersMap.keys
|
||||||
|
.filter { it.java.isAssignableFrom(action::class.java) }
|
||||||
|
.fold(scope.getState() ?: initialState) { acc, key ->
|
||||||
|
val actionHandlers = reducersMap[key]!!
|
||||||
|
actionHandlers.fold(acc) { acc, handler ->
|
||||||
|
when (handler) {
|
||||||
|
is ActionHandler.Async -> {
|
||||||
|
handler.handler.invoke(scope, action)
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
is ActionHandler.Sync -> handler.handler.invoke(action, acc)
|
||||||
|
is ActionHandler.Delegate -> when (val next = handler.handler.invoke(scope, action)) {
|
||||||
|
is ActionHandler.Async -> {
|
||||||
|
next.handler.invoke(scope, action)
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
|
||||||
|
is ActionHandler.Sync -> next.handler.invoke(action, acc)
|
||||||
|
is ActionHandler.Delegate -> error("is not possible")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initialState(): S = initialState
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <A : Action, S> sideEffect(klass: KClass<A>, block: suspend (A, S) -> Unit): (ReducerScope<S>) -> ActionHandler<S> {
|
||||||
|
return {
|
||||||
|
ActionHandler.Async(key = klass as KClass<Action>) { action -> block(action as A, getState()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <A : Action, S> change(klass: KClass<A>, block: (A, S) -> S): (ReducerScope<S>) -> ActionHandler<S> {
|
||||||
|
return {
|
||||||
|
ActionHandler.Sync(key = klass as KClass<Action>, block as (Action, S) -> S)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <A : Action, S> async(klass: KClass<A>, block: suspend ReducerScope<S>.(A) -> Unit): (ReducerScope<S>) -> ActionHandler<S> {
|
||||||
|
return {
|
||||||
|
ActionHandler.Async(key = klass as KClass<Action>, block as suspend ReducerScope<S>.(Action) -> Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <A : Action, S> multi(klass: KClass<A>, block: Multi<A, S>.(A) -> (ReducerScope<S>) -> ActionHandler<S>): (ReducerScope<S>) -> ActionHandler<S> {
|
||||||
|
val multiScope = object : Multi<A, S> {
|
||||||
|
override fun sideEffect(block: (A, S) -> Unit): (ReducerScope<S>) -> ActionHandler<S> = sideEffect(klass, block)
|
||||||
|
override fun change(block: (A, S) -> S): (ReducerScope<S>) -> ActionHandler<S> = change(klass, block)
|
||||||
|
override fun async(block: suspend ReducerScope<S>.(A) -> Unit): (ReducerScope<S>) -> ActionHandler<S> = async(klass, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ActionHandler.Delegate(key = klass as KClass<Action>) { action ->
|
||||||
|
block(multiScope, action as A).invoke(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Multi<A : Action, S> {
|
||||||
|
fun sideEffect(block: (A, S) -> Unit): (ReducerScope<S>) -> ActionHandler<S>
|
||||||
|
fun change(block: (A, S) -> S): (ReducerScope<S>) -> ActionHandler<S>
|
||||||
|
fun async(block: suspend ReducerScope<S>.(A) -> Unit): (ReducerScope<S>) -> ActionHandler<S>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Action
|
@ -4,6 +4,7 @@ dependencies {
|
|||||||
implementation project(":chat-engine")
|
implementation project(":chat-engine")
|
||||||
implementation project(":domains:android:compose-core")
|
implementation project(":domains:android:compose-core")
|
||||||
implementation project(":domains:android:viewmodel")
|
implementation project(":domains:android:viewmodel")
|
||||||
|
implementation project(":domains:state")
|
||||||
implementation project(":features:messenger")
|
implementation project(":features:messenger")
|
||||||
implementation project(":core")
|
implementation project(":core")
|
||||||
implementation project(":design-library")
|
implementation project(":design-library")
|
||||||
|
@ -68,8 +68,8 @@ fun DirectoryScreen(directoryViewModel: DirectoryViewModel) {
|
|||||||
directoryViewModel.ObserveEvents(listState, toolbarOffsetHeightPx)
|
directoryViewModel.ObserveEvents(listState, toolbarOffsetHeightPx)
|
||||||
|
|
||||||
LifecycleEffect(
|
LifecycleEffect(
|
||||||
onStart = { directoryViewModel.start() },
|
onStart = { directoryViewModel.dispatch(ComponentLifecycle.OnVisible) },
|
||||||
onStop = { directoryViewModel.stop() }
|
onStop = { directoryViewModel.dispatch(ComponentLifecycle.OnGone) }
|
||||||
)
|
)
|
||||||
|
|
||||||
val nestedScrollConnection = remember {
|
val nestedScrollConnection = remember {
|
||||||
|
@ -2,6 +2,8 @@ package app.dapk.st.directory
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import app.dapk.st.core.ProvidableModule
|
import app.dapk.st.core.ProvidableModule
|
||||||
|
import app.dapk.st.core.StateViewModel
|
||||||
|
import app.dapk.st.core.createStateViewModel
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
|
|
||||||
class DirectoryModule(
|
class DirectoryModule(
|
||||||
@ -9,10 +11,7 @@ class DirectoryModule(
|
|||||||
private val chatEngine: ChatEngine,
|
private val chatEngine: ChatEngine,
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
fun directoryViewModel(): DirectoryViewModel {
|
fun directoryViewModel(): StateViewModel<DirectoryScreenState, DirectoryEvent> {
|
||||||
return DirectoryViewModel(
|
return createStateViewModel { directoryReducer(chatEngine, ShortcutHandler(context), it) }
|
||||||
ShortcutHandler(context),
|
|
||||||
chatEngine,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package app.dapk.st.directory
|
||||||
|
|
||||||
|
import app.dapk.st.core.StateStore
|
||||||
|
import app.dapk.st.engine.ChatEngine
|
||||||
|
import app.dapk.st.engine.DirectoryState
|
||||||
|
import app.dapk.state.*
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
typealias DirectoryViewModel = StateStore<DirectoryScreenState, DirectoryEvent>
|
||||||
|
|
||||||
|
fun directoryReducer(
|
||||||
|
chatEngine: ChatEngine,
|
||||||
|
shortcutHandler: ShortcutHandler,
|
||||||
|
eventEmitter: suspend (DirectoryEvent) -> Unit,
|
||||||
|
): ReducerFactory<DirectoryScreenState> {
|
||||||
|
var syncJob: Job? = null
|
||||||
|
return createReducer(
|
||||||
|
initialState = DirectoryScreenState.EmptyLoading,
|
||||||
|
multi(ComponentLifecycle::class) { action ->
|
||||||
|
when (action) {
|
||||||
|
ComponentLifecycle.OnVisible -> async { _ ->
|
||||||
|
syncJob = chatEngine.directory().onEach {
|
||||||
|
shortcutHandler.onDirectoryUpdate(it.map { it.overview })
|
||||||
|
when (it.isEmpty()) {
|
||||||
|
true -> dispatch(DirectoryStateChange.Empty)
|
||||||
|
false -> dispatch(DirectoryStateChange.Content(it))
|
||||||
|
}
|
||||||
|
}.launchIn(coroutineScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentLifecycle.OnGone -> sideEffect { _, _ -> syncJob?.cancel() }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
change(DirectoryStateChange::class) { action, _ ->
|
||||||
|
when (action) {
|
||||||
|
is DirectoryStateChange.Content -> DirectoryScreenState.Content(action.content)
|
||||||
|
DirectoryStateChange.Empty -> DirectoryScreenState.Empty
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sideEffect(DirectorySideEffect.ScrollToTop::class) { _, _ ->
|
||||||
|
eventEmitter(DirectoryEvent.ScrollToTop)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface ComponentLifecycle : Action {
|
||||||
|
object OnVisible : ComponentLifecycle
|
||||||
|
object OnGone : ComponentLifecycle
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface DirectorySideEffect : Action {
|
||||||
|
object ScrollToTop : DirectorySideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface DirectoryStateChange : Action {
|
||||||
|
object Empty : DirectoryStateChange
|
||||||
|
data class Content(val content: DirectoryState) : DirectoryStateChange
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package app.dapk.st.directory
|
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import app.dapk.st.directory.DirectoryScreenState.*
|
|
||||||
import app.dapk.st.engine.ChatEngine
|
|
||||||
import app.dapk.st.viewmodel.DapkViewModel
|
|
||||||
import app.dapk.st.viewmodel.MutableStateFactory
|
|
||||||
import app.dapk.st.viewmodel.defaultStateFactory
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class DirectoryViewModel(
|
|
||||||
private val shortcutHandler: ShortcutHandler,
|
|
||||||
private val chatEngine: ChatEngine,
|
|
||||||
factory: MutableStateFactory<DirectoryScreenState> = defaultStateFactory(),
|
|
||||||
) : DapkViewModel<DirectoryScreenState, DirectoryEvent>(
|
|
||||||
initialState = EmptyLoading,
|
|
||||||
factory,
|
|
||||||
) {
|
|
||||||
|
|
||||||
private var syncJob: Job? = null
|
|
||||||
|
|
||||||
fun start() {
|
|
||||||
syncJob = viewModelScope.launch {
|
|
||||||
chatEngine.directory().onEach {
|
|
||||||
shortcutHandler.onDirectoryUpdate(it.map { it.overview })
|
|
||||||
state = when (it.isEmpty()) {
|
|
||||||
true -> Empty
|
|
||||||
false -> Content(it)
|
|
||||||
}
|
|
||||||
}.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
syncJob?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun scrollToTopOfMessages() {
|
|
||||||
_events.tryEmit(DirectoryEvent.ScrollToTop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,6 +9,7 @@ dependencies {
|
|||||||
implementation project(":domains:android:compose-core")
|
implementation project(":domains:android:compose-core")
|
||||||
implementation project(":domains:android:viewmodel")
|
implementation project(":domains:android:viewmodel")
|
||||||
implementation project(':domains:store')
|
implementation project(':domains:store')
|
||||||
|
implementation project(':domains:state')
|
||||||
implementation project(":core")
|
implementation project(":core")
|
||||||
implementation project(":design-library")
|
implementation project(":design-library")
|
||||||
implementation Dependencies.mavenCentral.coil
|
implementation Dependencies.mavenCentral.coil
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app.dapk.st.home
|
package app.dapk.st.home
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.dapk.st.directory.DirectorySideEffect
|
||||||
import app.dapk.st.directory.DirectoryViewModel
|
import app.dapk.st.directory.DirectoryViewModel
|
||||||
import app.dapk.st.domain.StoreCleaner
|
import app.dapk.st.domain.StoreCleaner
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
@ -92,7 +93,7 @@ class HomeViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun scrollToTopOfMessages() {
|
fun scrollToTopOfMessages() {
|
||||||
directoryViewModel.scrollToTopOfMessages()
|
directoryViewModel.dispatch(DirectorySideEffect.ScrollToTop)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changePage(page: Page) {
|
fun changePage(page: Page) {
|
||||||
|
@ -15,6 +15,7 @@ import app.dapk.st.core.DapkActivity
|
|||||||
import app.dapk.st.core.module
|
import app.dapk.st.core.module
|
||||||
import app.dapk.st.core.viewModel
|
import app.dapk.st.core.viewModel
|
||||||
import app.dapk.st.directory.DirectoryModule
|
import app.dapk.st.directory.DirectoryModule
|
||||||
|
import app.dapk.st.directory.DirectoryViewModel
|
||||||
import app.dapk.st.login.LoginModule
|
import app.dapk.st.login.LoginModule
|
||||||
import app.dapk.st.profile.ProfileModule
|
import app.dapk.st.profile.ProfileModule
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
@ -22,7 +23,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
|
|
||||||
class MainActivity : DapkActivity() {
|
class MainActivity : DapkActivity() {
|
||||||
|
|
||||||
private val directoryViewModel by viewModel { module<DirectoryModule>().directoryViewModel() }
|
private val directoryViewModel: DirectoryViewModel by viewModel { module<DirectoryModule>().directoryViewModel() }
|
||||||
private val loginViewModel by viewModel { module<LoginModule>().loginViewModel() }
|
private val loginViewModel by viewModel { module<LoginModule>().loginViewModel() }
|
||||||
private val profileViewModel by viewModel { module<ProfileModule>().profileViewModel() }
|
private val profileViewModel by viewModel { module<ProfileModule>().profileViewModel() }
|
||||||
private val homeViewModel by viewModel { module<HomeModule>().homeViewModel(directoryViewModel, loginViewModel, profileViewModel) }
|
private val homeViewModel by viewModel { module<HomeModule>().homeViewModel(directoryViewModel, loginViewModel, profileViewModel) }
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package app.dapk.st.messenger
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ImplBarTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test bar`() {
|
||||||
|
val viewModelScope = CoroutineScope(UnconfinedTestDispatcher())
|
||||||
|
val reducer = FooBar.createReducer(
|
||||||
|
initialState = ImplBar.BankState(amount = 0),
|
||||||
|
FooBar.sideEffect(ImplBar.BankAction::class) { action, state ->
|
||||||
|
println("SE ${action::class.simpleName} $state")
|
||||||
|
},
|
||||||
|
FooBar.sideEffect(ImplBar.BankAction.Foo::class) { action, state ->
|
||||||
|
println("FOO - $action $state")
|
||||||
|
},
|
||||||
|
FooBar.change(ImplBar.BankAction.Increment::class) { _, state ->
|
||||||
|
state.copy(amount = state.amount + 1)
|
||||||
|
},
|
||||||
|
FooBar.async(ImplBar.BankAction.MultiInc::class) { _ ->
|
||||||
|
flowOf(0, 1, 2)
|
||||||
|
.onEach { dispatch(ImplBar.BankAction.Increment) }
|
||||||
|
.launchIn(coroutineScope)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
val store = FooBar.createStore(reducer, viewModelScope)
|
||||||
|
store.subscribe {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
runBlocking { store.dispatch(ImplBar.BankAction.Inc) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,7 @@ include ':domains:android:viewmodel'
|
|||||||
include ':domains:store'
|
include ':domains:store'
|
||||||
include ':domains:olm-stub'
|
include ':domains:olm-stub'
|
||||||
include ':domains:olm'
|
include ':domains:olm'
|
||||||
|
include ':domains:state'
|
||||||
|
|
||||||
include ':domains:firebase:crashlytics'
|
include ':domains:firebase:crashlytics'
|
||||||
include ':domains:firebase:crashlytics-noop'
|
include ':domains:firebase:crashlytics-noop'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user