Skip to content

Instantly share code, notes, and snippets.

@hoc081098
Created June 11, 2024 08:07
Show Gist options
  • Save hoc081098/d2e982052e9be27473fad238f75860b4 to your computer and use it in GitHub Desktop.
Save hoc081098/d2e982052e9be27473fad238f75860b4 to your computer and use it in GitHub Desktop.
buildStore.kt
// FlowRedux is from
https://github.com/hoc081098/GithubSearchKMM-Compose-SwiftUI/tree/master/flowredux/src/commonMain/kotlin/com/hoc081098/flowredux
package com.hoc081098.flowredux.dsl
import com.hoc081098.flowredux.FlowReduxStore
import com.hoc081098.flowredux.SideEffect
import com.hoc081098.flowredux.createFlowReduxStore
import com.hoc081098.flowredux.liftAction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
@DslMarker
public annotation class FlowReduxDsl
public fun <Action, State, Change> buildStore(block: FlowReduxStoreBuilder<Action, State, Change>.() -> Unit): FlowReduxStore<Action, State> {
val builder = FlowReduxStoreBuilder<Action, State, Change>()
block(builder)
return builder.build()
}
private object UnInitialized
public typealias DslReducer<State, Change> = State.(change: Change) -> State
public typealias DslSideEffect<Action, Change> = (actionFlow: Flow<Action>) -> Flow<Change>
public typealias DslSideEffectWithReceiver<Action, Change> = Flow<Action>.() -> Flow<Change>
@FlowReduxDsl
public class FlowReduxStoreBuilder<Action, State, Change> internal constructor() {
private var _scope: CoroutineScope? = null
private var _initialState: Any? = UnInitialized
private var _reducer: DslReducer<State, Change>? = null
private val _sideEffects = mutableListOf<DslSideEffect<Action, Change>>()
private lateinit var stateFlow: StateFlow<State>
@FlowReduxDsl
public fun coroutineScope(coroutineScope: CoroutineScope) {
_scope = coroutineScope
}
@FlowReduxDsl
public fun initialState(initialState: State) {
_initialState = initialState
}
@FlowReduxDsl
public fun reduce(block: DslReducer<State, Change>) {
_reducer = block
}
@FlowReduxDsl
public fun actions(block: ActionsBuilder<Action, State, Change>.() -> Unit) {
val actionsBuilder = ActionsBuilder<Action, State, Change> { stateFlow }
block(actionsBuilder)
_sideEffects.addAll(actionsBuilder._sideEffects)
}
internal fun build(): FlowReduxStore<Action, State> {
val reducer = requireNotNull(_reducer) { "Reducer must be set" }
require(_initialState !== UnInitialized) { "Initial state must be set" }
@Suppress("UNCHECKED_CAST")
return requireNotNull(_scope) { "CoroutineScope must be set" }
.createFlowReduxStore<DlsAction<Action, Change>, State>(
initialState = _initialState as State,
sideEffects = _sideEffects.map { dlsSideEffect ->
SideEffect { actionFlow, stateFlow, coroutineScope ->
actionFlow
.filterIsInstance<DlsAction.External<Action>>()
.map { it.action }
.let(dlsSideEffect)
.map { DlsAction.Internal(it) }
}
},
reducer = { state, action ->
when (action) {
is DlsAction.External -> state
is DlsAction.Internal -> reducer(state, action.change)
}
},
)
.liftAction<DlsAction<Action, Change>, State, Action> { DlsAction.External(it) }
.also { stateFlow = it.stateFlow }
}
}
internal sealed interface DlsAction<out Action, out Change> {
data class External<Action>(val action: Action) : DlsAction<Action, Nothing>
data class Internal<Change>(val change: Change) : DlsAction<Nothing, Change>
}
@FlowReduxDsl
public class ActionsBuilder<Action, State, Change> internal constructor(
private val getStateFlow: () -> StateFlow<State>,
) {
public val stateFlow: StateFlow<State> get() = getStateFlow()
@PublishedApi
internal val _sideEffects: MutableList<DslSideEffect<Action, Change>> = mutableListOf()
@FlowReduxDsl
public inline fun <reified T : Action> perform(noinline block: DslSideEffectWithReceiver<Action, Change>) {
_sideEffects.add(block)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment