add conflicts resolver for default states managers

This commit is contained in:
InsanusMokrassar 2022-03-27 17:15:23 +06:00
parent a328c4425a
commit 99b09c8b28
3 changed files with 23 additions and 8 deletions

View File

@ -5,8 +5,9 @@
* `Common`: * `Common`:
* New extensions `Element#onVisibilityChanged`, `Element#onVisible` and `Element#onInvisible` * New extensions `Element#onVisibilityChanged`, `Element#onVisible` and `Element#onInvisible`
* `Coroutines`: * `Coroutines`:
* New extension `Element.visibilityFlow()` * New extension `Element.visibilityFlow()`
* `FSM`:
* Now it is possible to resolve conflicts on `startChain`
## 0.9.16 ## 0.9.16

View File

@ -39,13 +39,14 @@ interface DefaultStatesManagerRepo<T : State> {
* @param repo This repo will be used as repository for storing states. All operations with this repo will happen BEFORE * @param repo This repo will be used as repository for storing states. All operations with this repo will happen BEFORE
* any event will be sent to [onChainStateUpdated], [onStartChain] or [onEndChain]. By default, will be used * any event will be sent to [onChainStateUpdated], [onStartChain] or [onEndChain]. By default, will be used
* [InMemoryDefaultStatesManagerRepo] or you may create custom [DefaultStatesManagerRepo] and pass as [repo] parameter * [InMemoryDefaultStatesManagerRepo] or you may create custom [DefaultStatesManagerRepo] and pass as [repo] parameter
* @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context] * @param onUpdateContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context]
* key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by * key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by
* new state by using [endChain] with that state * new state by using [endChain] with that state
*/ */
open class DefaultStatesManager<T : State>( open class DefaultStatesManager<T : State>(
protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(), protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(),
protected val onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true } protected val onStartContextsConflictResolver: suspend (old: T, new: T) -> Boolean = { _, _ -> true },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
) : StatesManager<T> { ) : StatesManager<T> {
protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0) protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow() override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()
@ -56,6 +57,14 @@ open class DefaultStatesManager<T : State>(
protected val mapMutex = Mutex() protected val mapMutex = Mutex()
constructor(
repo: DefaultStatesManagerRepo<T>,
onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean
) : this (
repo,
onUpdateContextsConflictResolver = onContextsConflictResolver
)
override suspend fun update(old: T, new: T) = mapMutex.withLock { override suspend fun update(old: T, new: T) = mapMutex.withLock {
val stateByOldContext: T? = repo.getContextState(old.context) val stateByOldContext: T? = repo.getContextState(old.context)
when { when {
@ -67,7 +76,7 @@ open class DefaultStatesManager<T : State>(
} }
else -> { else -> {
val stateOnNewOneContext = repo.getContextState(new.context) val stateOnNewOneContext = repo.getContextState(new.context)
if (stateOnNewOneContext == null || onContextsConflictResolver(old, new, stateOnNewOneContext)) { if (stateOnNewOneContext == null || onUpdateContextsConflictResolver(old, new, stateOnNewOneContext)) {
stateOnNewOneContext ?.let { endChainWithoutLock(it) } stateOnNewOneContext ?.let { endChainWithoutLock(it) }
repo.removeState(old) repo.removeState(old)
repo.set(new) repo.set(new)
@ -78,7 +87,11 @@ open class DefaultStatesManager<T : State>(
} }
override suspend fun startChain(state: T) = mapMutex.withLock { override suspend fun startChain(state: T) = mapMutex.withLock {
if (!repo.contains(state.context)) { val stateOnContext = repo.getContextState(state.context)
if (stateOnContext == null || onStartContextsConflictResolver(stateOnContext, state)) {
stateOnContext ?.let {
endChainWithoutLock(it)
}
repo.set(state) repo.set(state)
_onStartChain.emit(state) _onStartChain.emit(state)
} }

View File

@ -12,5 +12,6 @@ import kotlinx.coroutines.flow.*
*/ */
@Deprecated("Use DefaultStatesManager instead", ReplaceWith("DefaultStatesManager")) @Deprecated("Use DefaultStatesManager instead", ReplaceWith("DefaultStatesManager"))
fun <T: State> InMemoryStatesManager( fun <T: State> InMemoryStatesManager(
onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true } onStartContextsConflictResolver: suspend (old: T, new: T) -> Boolean = { _, _ -> true },
) = DefaultStatesManager(onContextsConflictResolver = onContextsConflictResolver) onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
) = DefaultStatesManager(onStartContextsConflictResolver = onStartContextsConflictResolver, onUpdateContextsConflictResolver = onUpdateContextsConflictResolver)