![JAR search and dependency download from the Maven repository](/logo.png)
commonMain.de.halfbit.componental.state.StateHolder.kt Maven / Gradle / Ivy
package de.halfbit.componental.state
import de.halfbit.componental.ComponentContext
import de.halfbit.componental.coroutines.ComponentalDispatchers
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import kotlin.reflect.KClass
import kotlin.reflect.cast
public interface StateHolder {
public val state: StateFlow
}
public abstract class MutableStateHolder : StateHolder {
public abstract fun updateState(reducer: (S) -> S)
public abstract fun updateIf(stateType: KClass, reducer: (W) -> S)
public inline fun updateIf(noinline reducer: (W) -> S) {
updateIf(W::class, reducer)
}
public abstract suspend fun updateWhen(stateType: KClass, reducer: (W) -> S)
public suspend inline fun updateWhen(noinline reducer: (W) -> S) {
updateWhen(W::class, reducer)
}
public abstract fun updateUiWhen(stateType: KClass, block: (S) -> Unit)
public inline fun updateUiWhen(noinline block: (S) -> Unit) {
updateUiWhen(S::class, block)
}
public abstract fun launchWithStateWhen(stateType: KClass, block: suspend (S) -> Unit)
public inline fun launchWithStateWhen(noinline block: suspend (S) -> Unit) {
launchWithStateWhen(S::class, block)
}
public abstract fun withStateWhen(stateType: KClass, block: (S) -> Unit)
public inline fun withStateWhen(noinline block: (S) -> Unit) {
withStateWhen(S::class, block)
}
}
public fun mutableStateHolder(
initialState: S,
coroutineScope: CoroutineScope,
): MutableStateHolder =
DefaultMutableStateHolder(
initialState,
coroutineScope,
)
public fun ComponentContext.mutableStateHolder(
initialState: S,
): MutableStateHolder =
mutableStateHolder(
initialState,
coroutineScope,
)
private class DefaultMutableStateHolder(
private val initialState: S,
private val coroutineScope: CoroutineScope,
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
private val mainDispatcher: CoroutineDispatcher = ComponentalDispatchers.optimalMain,
) : MutableStateHolder() {
private val channel = Channel<(S) -> S>(capacity = 64)
override val state: StateFlow =
flow {
emit(initialState)
var state = initialState
while (true) {
val reducer = channel.receive()
state = reducer.invoke(state)
emit(state)
}
}.onCompletion {
channel.cancel()
}.stateIn(
coroutineScope,
SharingStarted.Eagerly,
initialState,
)
override fun updateState(reducer: (S) -> S) {
val result = channel.trySend(reducer)
if (result.isFailure && !result.isClosed) {
throw IllegalStateException(
"Failed to schedule reducer to a not closed channel"
)
}
}
override fun updateIf(stateType: KClass, reducer: (W) -> S) {
updateState { state ->
if (stateType.isInstance(state)) {
reducer(stateType.cast(state))
} else state
}
}
override suspend fun updateWhen(stateType: KClass, reducer: (W) -> S) {
try {
withTimeout(5_000) {
state.first { stateType.isInstance(it) }
updateIf(stateType, reducer)
}
} catch (e: TimeoutCancellationException) {
println("State $stateType not reached")
}
}
override fun updateUiWhen(stateType: KClass, block: (S) -> Unit) {
updateState { state ->
if (stateType.isInstance(state)) {
coroutineScope.launch(mainDispatcher) {
block(stateType.cast(state))
}
}
state
}
}
override fun launchWithStateWhen(stateType: KClass, block: suspend (S) -> Unit) {
updateState { state ->
if (stateType.isInstance(state)) {
coroutineScope.launch(defaultDispatcher) {
block(stateType.cast(state))
}
}
state
}
}
override fun withStateWhen(stateType: KClass, block: (S) -> Unit) {
updateState { state ->
if (stateType.isInstance(state)) {
block(stateType.cast(state))
}
state
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy