Merge pull request #140 from InsanusMokrassar/0.9.17

0.9.17
This commit is contained in:
InsanusMokrassar 2022-03-27 17:47:11 +06:00 committed by GitHub
commit 8d31c25bf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 10 deletions

View File

@ -1,7 +1,18 @@
# Changelog
## 0.9.17
* `Common`:
* New extensions `Element#onVisibilityChanged`, `Element#onVisible` and `Element#onInvisible`
* `Coroutines`:
* New extension `Element.visibilityFlow()`
* `FSM`:
* Now it is possible to resolve conflicts on `startChain`
## 0.9.16
* `Versions`:
* `Klock`: `2.6.3` -> `2.7.0`
* `Common`:
* New extension `Node#onRemoved`
* `Compose`:

View File

@ -3,7 +3,7 @@ package dev.inmo.micro_utils.common
import kotlinx.browser.document
import org.w3c.dom.*
fun Node.onRemoved(block: () -> Unit) {
fun Node.onRemoved(block: () -> Unit): MutationObserver {
lateinit var observer: MutationObserver
observer = MutationObserver { _, _ ->
@ -18,4 +18,44 @@ fun Node.onRemoved(block: () -> Unit) {
}
observer.observe(document, MutationObserverInit(childList = true, subtree = true))
return observer
}
fun Element.onVisibilityChanged(block: IntersectionObserverEntry.(Float, IntersectionObserver) -> Unit): IntersectionObserver {
var previousIntersectionRatio = -1f
val observer = IntersectionObserver { entries, observer ->
entries.forEach {
if (previousIntersectionRatio != it.intersectionRatio) {
previousIntersectionRatio = it.intersectionRatio.toFloat()
it.block(previousIntersectionRatio, observer)
}
}
}
observer.observe(this)
return observer
}
fun Element.onVisible(block: Element.(IntersectionObserver) -> Unit) {
var previous = -1f
onVisibilityChanged { intersectionRatio, observer ->
if (previous != intersectionRatio) {
if (intersectionRatio > 0 && previous == 0f) {
block(observer)
}
previous = intersectionRatio
}
}
}
fun Element.onInvisible(block: Element.(IntersectionObserver) -> Unit): IntersectionObserver {
var previous = -1f
return onVisibilityChanged { intersectionRatio, observer ->
if (previous != intersectionRatio) {
if (intersectionRatio == 0f && previous != 0f) {
block(observer)
}
previous = intersectionRatio
}
}
}

View File

@ -0,0 +1,28 @@
package dev.inmo.micro_utils.coroutines
import dev.inmo.micro_utils.common.onRemoved
import dev.inmo.micro_utils.common.onVisibilityChanged
import kotlinx.coroutines.flow.*
import org.w3c.dom.Element
fun Element.visibilityFlow(): Flow<Boolean> = channelFlow {
var previousData: Boolean? = null
val observer = onVisibilityChanged { intersectionRatio, _ ->
val currentData = intersectionRatio > 0
if (currentData != previousData) {
trySend(currentData)
}
previousData = currentData
}
val removeObserver = onRemoved {
observer.disconnect()
close()
}
invokeOnClose {
observer.disconnect()
removeObserver.disconnect()
}
}

View File

@ -39,13 +39,17 @@ interface DefaultStatesManagerRepo<T : State> {
* @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
* [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 onStartContextsConflictResolver Receive current [State] and the state passed with [startChain]. In case when
* this callback will return true, currently placed on the [State.context] [State] will be replaced by new state
* with [endChain] with current state
* @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
* new state by using [endChain] with that state
*/
open class DefaultStatesManager<T : State>(
protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(),
protected val onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> true },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
) : StatesManager<T> {
protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()
@ -56,6 +60,14 @@ open class DefaultStatesManager<T : State>(
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 {
val stateByOldContext: T? = repo.getContextState(old.context)
when {
@ -67,7 +79,7 @@ open class DefaultStatesManager<T : State>(
}
else -> {
val stateOnNewOneContext = repo.getContextState(new.context)
if (stateOnNewOneContext == null || onContextsConflictResolver(old, new, stateOnNewOneContext)) {
if (stateOnNewOneContext == null || onUpdateContextsConflictResolver(old, new, stateOnNewOneContext)) {
stateOnNewOneContext ?.let { endChainWithoutLock(it) }
repo.removeState(old)
repo.set(new)
@ -78,7 +90,11 @@ open class DefaultStatesManager<T : State>(
}
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)
_onStartChain.emit(state)
}

View File

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

View File

@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.9.16
android_code_version=106
version=0.9.17
android_code_version=107

View File

@ -8,7 +8,7 @@ jb-compose = "1.1.1"
jb-exposed = "0.37.3"
jb-dokka = "1.6.10"
klock = "2.6.3"
klock = "2.7.0"
uuid = "0.4.0"
ktor = "1.6.8"