From d87a3a039f6bbdbcfaf0d1cd2cbec016b0e03f08 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 26 Mar 2022 10:57:50 +0600 Subject: [PATCH 1/6] start 0.9.17 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d8a8572ac1..5283588a79c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.9.17 + ## 0.9.16 * `Common`: diff --git a/gradle.properties b/gradle.properties index 192c37e235a..bc19ff604f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 From 86e70c0961808b477036b23aecdd5cd435141e8c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 26 Mar 2022 11:01:40 +0600 Subject: [PATCH 2/6] js visibility of objects extensions --- CHANGELOG.md | 6 +++ .../common/HTMLElementDomChanged.kt | 42 ++++++++++++++++++- .../ElementVisibilityFlow.kt | 28 +++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 coroutines/src/jsMain/kotlin/dev.inmo.micro_utils.coroutines/ElementVisibilityFlow.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 5283588a79c..5360f44cf0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## 0.9.17 +* `Common`: + * New extensions `Element#onVisibilityChanged`, `Element#onVisible` and `Element#onInvisible` +* `Coroutines`: + * New extension `Element.visibilityFlow()` + + ## 0.9.16 * `Common`: diff --git a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt index 5d803dca1fb..825414a3587 100644 --- a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt +++ b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt @@ -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 + } + } } diff --git a/coroutines/src/jsMain/kotlin/dev.inmo.micro_utils.coroutines/ElementVisibilityFlow.kt b/coroutines/src/jsMain/kotlin/dev.inmo.micro_utils.coroutines/ElementVisibilityFlow.kt new file mode 100644 index 00000000000..54b217527ee --- /dev/null +++ b/coroutines/src/jsMain/kotlin/dev.inmo.micro_utils.coroutines/ElementVisibilityFlow.kt @@ -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 = 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() + } +} From c0f61ca89678be5e2e3ca409a18823a7bfbfdaeb Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 26 Mar 2022 19:11:44 +0600 Subject: [PATCH 3/6] Update libs.versions.toml --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9490cefd914..25021e34a23 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" From a328c4425afc555dbc3c6af858cd5001bae708e6 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 27 Mar 2022 13:57:08 +0600 Subject: [PATCH 4/6] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5360f44cf0c..577c3a4328f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ## 0.9.16 +* `Versions`: + * `Klock`: `2.6.3` -> `2.7.0` * `Common`: * New extension `Node#onRemoved` * `Compose`: From 99b09c8b28a1b610118b3d666bb13c00ffbfa8ae Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 27 Mar 2022 17:15:23 +0600 Subject: [PATCH 5/6] add conflicts resolver for default states managers --- CHANGELOG.md | 5 +++-- .../common/managers/DefaultStatesManager.kt | 21 +++++++++++++++---- .../common/managers/InMemoryStatesManager.kt | 5 +++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 577c3a4328f..0d4c555406c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,9 @@ * `Common`: * New extensions `Element#onVisibilityChanged`, `Element#onVisible` and `Element#onInvisible` * `Coroutines`: - * New extension `Element.visibilityFlow()` - + * New extension `Element.visibilityFlow()` +* `FSM`: + * Now it is possible to resolve conflicts on `startChain` ## 0.9.16 diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt index 4c67a5fbd31..30b899da341 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt @@ -39,13 +39,14 @@ interface DefaultStatesManagerRepo { * @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 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( protected val repo: DefaultStatesManagerRepo = 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 { protected val _onChainStateUpdated = MutableSharedFlow>(0) override val onChainStateUpdated: Flow> = _onChainStateUpdated.asSharedFlow() @@ -56,6 +57,14 @@ open class DefaultStatesManager( protected val mapMutex = Mutex() + constructor( + repo: DefaultStatesManagerRepo, + 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 +76,7 @@ open class DefaultStatesManager( } 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 +87,11 @@ open class DefaultStatesManager( } 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) } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt index 323e895b315..3cfe75323ad 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt @@ -12,5 +12,6 @@ import kotlinx.coroutines.flow.* */ @Deprecated("Use DefaultStatesManager instead", ReplaceWith("DefaultStatesManager")) fun 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) From c7ee1c28b2f635d8c0afe9e5b09adc9e8b0cab68 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 27 Mar 2022 17:43:01 +0600 Subject: [PATCH 6/6] fix of docs for DefaultStatesManager --- .../micro_utils/fsm/common/managers/DefaultStatesManager.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt index 30b899da341..b6e9556855c 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt @@ -39,13 +39,16 @@ interface DefaultStatesManagerRepo { * @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 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( protected val repo: DefaultStatesManagerRepo = InMemoryDefaultStatesManagerRepo(), - protected val onStartContextsConflictResolver: suspend (old: T, new: 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 { protected val _onChainStateUpdated = MutableSharedFlow>(0)