From 53b89f3a1896f39bdea7d60f594d7ff194679bb4 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 19 Jan 2023 20:19:21 +0600 Subject: [PATCH 01/14] start 0.16.7 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24e11781c26..acef26034a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.16.7 + ## 0.16.6 * `Startup`: diff --git a/gradle.properties b/gradle.properties index 878a2c04bba..bba345e0759 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.16.6 -android_code_version=174 +version=0.16.7 +android_code_version=175 From da692ccfc337ab27a458e4ce0b4a2b819cd49de7 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 19 Jan 2023 20:21:35 +0600 Subject: [PATCH 02/14] add ifTrue/ifFalse --- .../dev/inmo/micro_utils/common/IfBoolean.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt new file mode 100644 index 00000000000..b953f423eb6 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt @@ -0,0 +1,17 @@ +package dev.inmo.micro_utils.common + +inline fun Boolean.ifTrue(block: () -> T): T? { + return if (this) { + block() + } else { + null + } +} + +inline fun Boolean.ifFalse(block: () -> T): T? { + return if (this) { + null + } else { + block() + } +} From ec3afc615cc23ad15c7bd7f1e1590346ae6ecfce Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 20 Jan 2023 13:29:45 +0600 Subject: [PATCH 03/14] add serializable diff --- .../dev/inmo/micro_utils/common/DiffUtils.kt | 9 ++++-- .../common/IndexedValueSerializer.kt | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IndexedValueSerializer.kt diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt index 596e413fa21..367c267ac77 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt @@ -2,6 +2,8 @@ package dev.inmo.micro_utils.common +import kotlinx.serialization.Serializable + private inline fun getObject( additional: MutableList, iterator: Iterator @@ -24,13 +26,14 @@ private inline fun getObject( * * @see calculateDiff */ +@Serializable data class Diff internal constructor( - val removed: List>, + val removed: List<@Serializable(IndexedValueSerializer::class) IndexedValue>, /** * Old-New values pairs */ - val replaced: List, IndexedValue>>, - val added: List> + val replaced: List, @Serializable(IndexedValueSerializer::class) IndexedValue>>, + val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue> ) private inline fun performChanges( diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IndexedValueSerializer.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IndexedValueSerializer.kt new file mode 100644 index 00000000000..780972f4756 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IndexedValueSerializer.kt @@ -0,0 +1,30 @@ +package dev.inmo.micro_utils.common + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.PairSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +class IndexedValueSerializer(private val subSerializer: KSerializer) : KSerializer> { + private val originalSerializer = PairSerializer(Int.serializer(), subSerializer) + override val descriptor: SerialDescriptor + get() = originalSerializer.descriptor + + override fun deserialize(decoder: Decoder): IndexedValue { + val pair = originalSerializer.deserialize(decoder) + return IndexedValue( + pair.first, + pair.second + ) + } + + override fun serialize(encoder: Encoder, value: IndexedValue) { + originalSerializer.serialize( + encoder, + Pair(value.index, value.value) + ) + } +} From 20799b9a3e2e28f49003c27476dca24a53e878d6 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 22 Jan 2023 22:43:26 +0600 Subject: [PATCH 04/14] add repeat on failure with callback --- .../micro_utils/common/RepeatOnFailure.kt | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RepeatOnFailure.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RepeatOnFailure.kt index 409df8d2e98..4382ba40206 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RepeatOnFailure.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RepeatOnFailure.kt @@ -1,5 +1,27 @@ package dev.inmo.micro_utils.common +/** + * Executes the given [action] until getting of successful result specified number of [times]. + * + * A zero-based index of current iteration is passed as a parameter to [action]. + */ +inline fun repeatOnFailure( + onFailure: (Throwable) -> Boolean, + action: () -> R +): Result { + do { + runCatching { + action() + }.onFailure { + if (!onFailure(it)) { + return Result.failure(it) + } + }.onSuccess { + return Result.success(it) + } + } while (true) +} + /** * Executes the given [action] until getting of successful result specified number of [times]. * @@ -10,12 +32,23 @@ inline fun repeatOnFailure( onEachFailure: (Throwable) -> Unit = {}, action: (Int) -> R ): Optional { - repeat(times) { - runCatching { - action(it) - }.onFailure(onEachFailure).onSuccess { - return Optional.presented(it) + var i = 0 + val result = repeatOnFailure( + { + onEachFailure(it) + if (i < times) { + i++ + true + } else { + false + } } + ) { + action(i) + } + return if (result.isSuccess) { + Optional.presented(result.getOrThrow()) + } else { + Optional.absent() } - return Optional.absent() } From 8a6b4bb49ebb16446f8328310070a1a5f06bce74 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 22 Jan 2023 23:01:06 +0600 Subject: [PATCH 05/14] add alsoIfTrue/alsoIfFalse/letIfTrue/letIfFalse --- .../dev/inmo/micro_utils/common/IfBoolean.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt index b953f423eb6..e6401389c4e 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt @@ -1,5 +1,31 @@ package dev.inmo.micro_utils.common +inline fun Boolean.letIfTrue(block: () -> T): T? { + return if (this) { + block() + } else { + null + } +} + +inline fun Boolean.letIfFalse(block: () -> T): T? { + return if (this) { + null + } else { + block() + } +} + +inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean { + letIfTrue(block) + return this +} + +inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean { + letIfFalse(block) + return this +} + inline fun Boolean.ifTrue(block: () -> T): T? { return if (this) { block() From 6ebc5aa0c2e238b1a1da96cb42080e2d66875dd2 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 23 Jan 2023 15:21:47 +0600 Subject: [PATCH 06/14] potential fix of state value change in asMutableComposeState --- .../dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt index 334c5dcb6ea..b6d6fb12833 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt @@ -1,6 +1,7 @@ package dev.inmo.micro_utils.coroutines.compose import androidx.compose.runtime.* +import dev.inmo.micro_utils.coroutines.doInUI import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -11,7 +12,7 @@ fun Flow.asMutableComposeState( scope: CoroutineScope ): MutableState { val state = mutableStateOf(initial) - subscribeSafelyWithoutExceptions(scope) { state.value = it } + subscribeSafelyWithoutExceptions(scope) { doInUI { state.value = it } } return state } From d0a00031a18a5e2cc74da4ab81d44adc252a9292 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 26 Jan 2023 22:54:17 +0600 Subject: [PATCH 07/14] Update CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index acef26034a9..98da26f5385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## 0.16.7 +* `Common`: + * New extensions `ifTrue`/`ifFalse`/`alsoIfTrue`/`alsoIfFalse`/`letIfTrue`/`letIfFalse` + * `Diff` now is serializable + * Add `IndexedValue` serializer + * `repeatOnFailure` extending: now you may pass any lambda to check if continue to try/do something +* `Coroutines`: + * `Compose`: + * `asMutableComposeState` and all depending functions now use `doInUI` to guarantee state changin in Main Dispatcher + ## 0.16.6 * `Startup`: From 7fc93817c1a67c5f77761bd4073b960d840689b3 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 27 Jan 2023 14:45:31 +0600 Subject: [PATCH 08/14] start to add fallback repos --- .../inmo/micro_utils/repos/cache/CRUDCacheRepo.kt | 8 ++++++-- .../dev/inmo/micro_utils/repos/cache/CacheRepo.kt | 4 +++- .../inmo/micro_utils/repos/cache/CommonCacheRepo.kt | 7 +++++++ .../micro_utils/repos/cache/FallbackCacheRepo.kt | 8 ++++++++ .../micro_utils/repos/cache/KeyValueCacheRepo.kt | 8 ++++++-- .../micro_utils/repos/cache/KeyValuesCacheRepo.kt | 8 ++++++-- .../repos/cache/full/FullCRUDCacheRepo.kt | 10 +++++++++- .../repos/cache/full/FullKeyValueCacheRepo.kt | 12 ++++++++++++ .../repos/cache/full/FullKeyValuesCacheRepo.kt | 11 +++++++++++ .../dev/inmo/micro_utils/repos/KeyValueRepo.kt | 6 +++++- 10 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CommonCacheRepo.kt create mode 100644 repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt index 413e116cd92..264e727ed1c 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt @@ -10,12 +10,14 @@ open class ReadCRUDCacheRepo( protected open val parentRepo: ReadCRUDRepo, protected open val kvCache: KVCache, protected open val idGetter: (ObjectType) -> IdType -) : ReadCRUDRepo by parentRepo, CacheRepo { +) : ReadCRUDRepo by parentRepo, CommonCacheRepo { override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also { kvCache.set(id, it) }) override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id) + + override suspend fun invalidate() = kvCache.clear() } fun ReadCRUDRepo.cached( @@ -28,7 +30,7 @@ open class WriteCRUDCacheRepo( protected open val kvCache: KVCache, protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default), protected open val idGetter: (ObjectType) -> IdType -) : WriteCRUDRepo, CacheRepo { +) : WriteCRUDRepo, CommonCacheRepo { override val newObjectsFlow: Flow by parentRepo::newObjectsFlow override val updatedObjectsFlow: Flow by parentRepo::updatedObjectsFlow override val deletedObjectsIdsFlow: Flow by parentRepo::deletedObjectsIdsFlow @@ -72,6 +74,8 @@ open class WriteCRUDCacheRepo( return created } + + override suspend fun invalidate() = kvCache.clear() } fun WriteCRUDRepo.caching( diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CacheRepo.kt index 73dbbcc11a9..55abb179fd6 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CacheRepo.kt @@ -1,3 +1,5 @@ package dev.inmo.micro_utils.repos.cache -interface CacheRepo +interface CacheRepo { + suspend fun invalidate() +} diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CommonCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CommonCacheRepo.kt new file mode 100644 index 00000000000..4fcfbc3dd89 --- /dev/null +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CommonCacheRepo.kt @@ -0,0 +1,7 @@ +package dev.inmo.micro_utils.repos.cache + +/** + * Any inheritor of this should work with next logic: try to take data from some [dev.inmo.micro_utils.repos.cache.cache.KVCache] and, + * if not exists, take from origin and save to the cache for future reuse + */ +interface CommonCacheRepo : CacheRepo diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt new file mode 100644 index 00000000000..0be4cc9eb3e --- /dev/null +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt @@ -0,0 +1,8 @@ +package dev.inmo.micro_utils.repos.cache + +/** + * Any inheritor of this should work with next logic: try to take data from original repo and, + * if not exists, try to take from the cache some. In case if original repo have returned result, it should be saved to the internal + * [dev.inmo.micro_utils.repos.cache.cache.KVCache] + */ +interface FallbackCacheRepo : CacheRepo diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt index fb4a46e194e..8b9c36196d1 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.* open class ReadKeyValueCacheRepo( protected open val parentRepo: ReadKeyValueRepo, protected open val kvCache: KVCache, -) : ReadKeyValueRepo by parentRepo, CacheRepo { +) : ReadKeyValueRepo by parentRepo, CommonCacheRepo { override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) } override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key) @@ -23,6 +23,8 @@ open class ReadKeyValueCacheRepo( ) } } + + override suspend fun invalidate() = kvCache.clear() } fun ReadKeyValueRepo.cached( @@ -33,9 +35,11 @@ open class KeyValueCacheRepo( parentRepo: KeyValueRepo, kvCache: KVCache, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) -) : ReadKeyValueCacheRepo(parentRepo, kvCache), KeyValueRepo, WriteKeyValueRepo by parentRepo, CacheRepo { +) : ReadKeyValueCacheRepo(parentRepo, kvCache), KeyValueRepo, WriteKeyValueRepo by parentRepo, CommonCacheRepo { protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope) protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope) + + override suspend fun invalidate() = kvCache.clear() } fun KeyValueRepo.cached( diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt index c251b852746..d4db6d676ed 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.* open class ReadKeyValuesCacheRepo( protected open val parentRepo: ReadKeyValuesRepo, protected open val kvCache: KVCache> -) : ReadKeyValuesRepo by parentRepo, CacheRepo { +) : ReadKeyValuesRepo by parentRepo, CommonCacheRepo { override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult { return getAll(k, reversed).paginate( pagination @@ -30,6 +30,8 @@ open class ReadKeyValuesCacheRepo( } }) override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k) + + override suspend fun invalidate() = kvCache.clear() } fun ReadKeyValuesRepo.cached( @@ -40,7 +42,7 @@ open class KeyValuesCacheRepo( parentRepo: KeyValuesRepo, kvCache: KVCache>, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) -) : ReadKeyValuesCacheRepo(parentRepo, kvCache), KeyValuesRepo, WriteKeyValuesRepo by parentRepo, CacheRepo { +) : ReadKeyValuesCacheRepo(parentRepo, kvCache), KeyValuesRepo, WriteKeyValuesRepo by parentRepo, CommonCacheRepo { protected val onNewJob = parentRepo.onNewValue.onEach { (k, v) -> kvCache.set( k, @@ -56,6 +58,8 @@ open class KeyValuesCacheRepo( protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope) + + override suspend fun invalidate() = kvCache.clear() } fun KeyValuesRepo.cached( diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt index 242b07e12ed..1864180ebab 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt @@ -69,6 +69,10 @@ open class FullReadCRUDCacheRepo( { getById(id) }, { it ?.let { set(idGetter(it), it) } } ) + + override suspend fun invalidate() { + actualizeAll() + } } fun ReadCRUDRepo.cached( @@ -92,7 +96,11 @@ open class FullCRUDCacheRepo( scope, idGetter ), - CRUDRepo + CRUDRepo { + override suspend fun invalidate() { + actualizeAll() + } +} fun CRUDRepo.cached( kvCache: FullKVCache, diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt index 802cddb2925..a730b01a5d3 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt @@ -68,6 +68,10 @@ open class FullReadKeyValueCacheRepo( { parentRepo.keys(v, pagination, reversed) }, { if (it.results.isNotEmpty()) actualizeAll() } ) + + override suspend fun invalidate() { + actualizeAll() + } } fun ReadKeyValueRepo.cached( @@ -81,6 +85,10 @@ open class FullWriteKeyValueCacheRepo( ) : WriteKeyValueRepo by parentRepo, FullCacheRepo { protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope) protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope) + + override suspend fun invalidate() { + kvCache.clear() + } } fun WriteKeyValueRepo.caching( @@ -96,6 +104,10 @@ open class FullKeyValueCacheRepo( KeyValueRepo, ReadKeyValueRepo by FullReadKeyValueCacheRepo(parentRepo, kvCache) { override suspend fun unsetWithValues(toUnset: List) = parentRepo.unsetWithValues(toUnset) + + override suspend fun invalidate() { + super.invalidate() + } } fun KeyValueRepo.cached( diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt index bc92579b77a..7934a73d65a 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt @@ -102,6 +102,9 @@ open class FullReadKeyValuesCacheRepo( { if (it.results.isNotEmpty()) actualizeAll() } ) + override suspend fun invalidate() { + actualizeAll() + } } fun ReadKeyValuesRepo.cached( @@ -125,6 +128,10 @@ open class FullWriteKeyValuesCacheRepo( kvCache.get(it.first) ?.minus(it.second) ?: return@onEach ) }.launchIn(scope) + + override suspend fun invalidate() { + kvCache.clear() + } } fun WriteKeyValuesRepo.caching( @@ -146,6 +153,10 @@ open class FullKeyValuesCacheRepo( } } } + + override suspend fun invalidate() { + super.invalidate() + } } fun KeyValuesRepo.caching( diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt index 893453e9a90..307638088d2 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt @@ -125,7 +125,11 @@ interface KeyValueRepo : ReadKeyValueRepo, WriteKeyValue * By default, will remove all the data of current repo using [doAllWithCurrentPaging], [keys] and [unset] */ suspend fun clear() { - doAllWithCurrentPaging { keys(it).also { unset(it.results) } } + var count: Int + do { + count = count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE + keys(FirstPagePagination(count)).also { unset(it.results) } + } while(count > 0) } } typealias StandardKeyValueRepo = KeyValueRepo From 0c8bec4c89bf830e3b34492f76c0c95f825ed2fc Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 27 Jan 2023 15:15:27 +0600 Subject: [PATCH 09/14] add extension actualizeAll --- .../repos/cache/full/FullCRUDCacheRepo.kt | 8 +-- .../repos/cache/full/FullKeyValueCacheRepo.kt | 5 +- .../cache/full/FullKeyValuesCacheRepo.kt | 8 +-- .../repos/cache/util/ActualizeAll.kt | 61 +++++++++++++++++++ .../inmo/micro_utils/repos/KeyValueRepo.kt | 4 ++ 5 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/util/ActualizeAll.kt diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt index 1864180ebab..7878d897668 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt @@ -8,6 +8,7 @@ import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.cache.* import dev.inmo.micro_utils.repos.cache.cache.FullKVCache import dev.inmo.micro_utils.repos.cache.cache.KVCache +import dev.inmo.micro_utils.repos.cache.util.actualizeAll import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -32,12 +33,7 @@ open class FullReadCRUDCacheRepo( } protected open suspend fun actualizeAll() { - kvCache.clear() - doForAllWithNextPaging { - parentRepo.getByPagination(it).also { - kvCache.set(it.results.associateBy { idGetter(it) }) - } - } + kvCache.actualizeAll(parentRepo) } override suspend fun getByPagination(pagination: Pagination): PaginationResult = doOrTakeAndActualize( diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt index a730b01a5d3..635e5754724 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.pagination.Pagination import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.cache.cache.FullKVCache +import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.pagination.getAll import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -97,7 +98,7 @@ fun WriteKeyValueRepo.caching( ) = FullWriteKeyValueCacheRepo(this, kvCache, scope) open class FullKeyValueCacheRepo( - parentRepo: KeyValueRepo, + override val parentRepo: KeyValueRepo, kvCache: FullKVCache, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ) : FullWriteKeyValueCacheRepo(parentRepo, kvCache, scope), @@ -106,7 +107,7 @@ open class FullKeyValueCacheRepo( override suspend fun unsetWithValues(toUnset: List) = parentRepo.unsetWithValues(toUnset) override suspend fun invalidate() { - super.invalidate() + kvCache.actualizeAll(parentRepo) } } diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt index 7934a73d65a..e91ef6dbcb1 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.utils.* import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.cache.cache.FullKVCache +import dev.inmo.micro_utils.repos.cache.util.actualizeAll import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* @@ -33,8 +34,7 @@ open class FullReadKeyValuesCacheRepo( } protected open suspend fun actualizeAll() { - doAllWithCurrentPaging { kvCache.keys(it).also { kvCache.unset(it.results) } } - kvCache.set(parentRepo.getAll()) + kvCache.actualizeAll(parentRepo) } override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult { @@ -140,7 +140,7 @@ fun WriteKeyValuesRepo.caching( ) = FullWriteKeyValuesCacheRepo(this, kvCache, scope) open class FullKeyValuesCacheRepo( - parentRepo: KeyValuesRepo, + override val parentRepo: KeyValuesRepo, kvCache: FullKVCache>, scope: CoroutineScope = CoroutineScope(Dispatchers.Default) ) : FullWriteKeyValuesCacheRepo(parentRepo, kvCache, scope), @@ -155,7 +155,7 @@ open class FullKeyValuesCacheRepo( } override suspend fun invalidate() { - super.invalidate() + kvCache.actualizeAll(parentRepo) } } diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/util/ActualizeAll.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/util/ActualizeAll.kt new file mode 100644 index 00000000000..a657d6b8b45 --- /dev/null +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/util/ActualizeAll.kt @@ -0,0 +1,61 @@ +package dev.inmo.micro_utils.repos.cache.util + +import dev.inmo.micro_utils.pagination.FirstPagePagination +import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging +import dev.inmo.micro_utils.repos.ReadCRUDRepo +import dev.inmo.micro_utils.repos.ReadKeyValueRepo +import dev.inmo.micro_utils.repos.ReadKeyValuesRepo +import dev.inmo.micro_utils.repos.cache.cache.KVCache +import dev.inmo.micro_utils.repos.set + +suspend inline fun KVCache.actualizeAll( + getAll: () -> Map +) { + clear() + set(getAll()) +} + +suspend inline fun KVCache.actualizeAll( + repo: ReadKeyValueRepo +) { + clear() + val count = repo.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE + val initPagination = FirstPagePagination(count) + doForAllWithNextPaging(initPagination) { + keys(it).also { + set( + it.results.mapNotNull { k -> repo.get(k) ?.let { k to it } } + ) + } + } +} + +suspend inline fun KVCache>.actualizeAll( + repo: ReadKeyValuesRepo +) { + clear() + val count = repo.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE + val initPagination = FirstPagePagination(count) + doForAllWithNextPaging(initPagination) { + keys(it).also { + set( + it.results.associateWith { k -> repo.getAll(k) } + ) + } + } +} + +suspend inline fun KVCache.actualizeAll( + repo: ReadCRUDRepo +) { + clear() + val count = repo.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE + val initPagination = FirstPagePagination(count) + doForAllWithNextPaging(initPagination) { + keys(it).also { + set( + it.results.mapNotNull { k -> repo.getById(k) ?.let { k to it } } + ) + } + } +} diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt index 307638088d2..1faa5c4a546 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValueRepo.kt @@ -93,6 +93,10 @@ suspend inline fun WriteKeyValueRepo.set( vararg toSet: Pair ) = set(toSet.toMap()) +suspend inline fun WriteKeyValueRepo.set( + toSet: List> +) = set(toSet.toMap()) + suspend inline fun WriteKeyValueRepo.set( k: Key, v: Value ) = set(k to v) From 83d5d3faf4e15087495d7892b0642c0b5aa3cc73 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 29 Jan 2023 12:28:22 +0600 Subject: [PATCH 10/14] improve flow as state functionality --- .../common/compose/MutableStateAsState.kt | 6 +++ .../coroutines/compose/FlowAsListState.kt | 24 ++++++++++-- .../coroutines/compose/FlowAsState.kt | 38 +++++++++++++++---- .../coroutines/compose/FlowToMutableState.kt | 10 ++--- 4 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt diff --git a/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt b/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt new file mode 100644 index 00000000000..b551a9e6b74 --- /dev/null +++ b/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt @@ -0,0 +1,6 @@ +package dev.inmo.micro_utils.common.compose + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.derivedStateOf + +fun MutableState.asState() = derivedStateOf { this.value } diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt index 3ce3e860eac..bed242faa49 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt @@ -3,24 +3,40 @@ package dev.inmo.micro_utils.coroutines.compose import androidx.compose.runtime.* import androidx.compose.runtime.snapshots.SnapshotStateList import dev.inmo.micro_utils.common.applyDiff +import dev.inmo.micro_utils.coroutines.ExceptionHandler +import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.withContext +import kotlin.coroutines.CoroutineContext @Suppress("NOTHING_TO_INLINE") inline fun Flow>.asMutableComposeListState( - scope: CoroutineScope + scope: CoroutineScope, + useContextOnChange: CoroutineContext? = Dispatchers.Main, + noinline onException: ExceptionHandler?> = defaultSafelyWithoutExceptionHandlerWithNull, ): SnapshotStateList { val state = mutableStateListOf() - subscribeSafelyWithoutExceptions(scope) { + val changeBlock: suspend (List) -> Unit = useContextOnChange ?.let { + { + withContext(useContextOnChange) { + state.applyDiff(it) + } + } + } ?: { state.applyDiff(it) } + subscribeSafelyWithoutExceptions(scope, onException, changeBlock) return state } @Suppress("NOTHING_TO_INLINE") inline fun Flow>.asComposeList( - scope: CoroutineScope -): List = asMutableComposeListState(scope) + scope: CoroutineScope, + useContextOnChange: CoroutineContext? = Dispatchers.Main, + noinline onException: ExceptionHandler?> = defaultSafelyWithoutExceptionHandlerWithNull, +): List = asMutableComposeListState(scope, useContextOnChange, onException) diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt index b6d6fb12833..9bbee0e172c 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt @@ -1,36 +1,58 @@ package dev.inmo.micro_utils.coroutines.compose import androidx.compose.runtime.* +import dev.inmo.micro_utils.coroutines.ExceptionHandler +import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull import dev.inmo.micro_utils.coroutines.doInUI import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.withContext +import kotlin.coroutines.CoroutineContext fun Flow.asMutableComposeState( initial: T, - scope: CoroutineScope + scope: CoroutineScope, + useContextOnChange: CoroutineContext? = Dispatchers.Main, + onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, ): MutableState { val state = mutableStateOf(initial) - subscribeSafelyWithoutExceptions(scope) { doInUI { state.value = it } } + val changeBlock: suspend (T) -> Unit = useContextOnChange ?.let { + { + withContext(useContextOnChange) { + state.value = it + } + } + } ?: { + state.value = it + } + subscribeSafelyWithoutExceptions(scope, onException, block = changeBlock) return state } @Suppress("NOTHING_TO_INLINE") inline fun StateFlow.asMutableComposeState( - scope: CoroutineScope -): MutableState = asMutableComposeState(value, scope) + scope: CoroutineScope, + useContextOnChange: CoroutineContext? = Dispatchers.Main, + noinline onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, +): MutableState = asMutableComposeState(value, scope, useContextOnChange, onException) fun Flow.asComposeState( initial: T, - scope: CoroutineScope + scope: CoroutineScope, + useContextOnChange: CoroutineContext? = Dispatchers.Main, + onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, ): State { - val state = asMutableComposeState(initial, scope) + val state = asMutableComposeState(initial, scope, useContextOnChange, onException) return derivedStateOf { state.value } } @Suppress("NOTHING_TO_INLINE") inline fun StateFlow.asComposeState( - scope: CoroutineScope -): State = asComposeState(value, scope) + scope: CoroutineScope, + useContextOnChange: CoroutineContext? = Dispatchers.Main, + noinline onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, +): State = asComposeState(value, scope, useContextOnChange, onException) diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowToMutableState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowToMutableState.kt index 7c6d856b22d..92fba8f2176 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowToMutableState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowToMutableState.kt @@ -7,17 +7,15 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +@Deprecated("Duplicated functionality", ReplaceWith("asMutableComposeState(initial, scope)", "dev.inmo.micro_utils.coroutines.compose.asMutableComposeState")) fun Flow.toMutableState( initial: T, scope: CoroutineScope -): MutableState { - val state = mutableStateOf(initial) - subscribeSafelyWithoutExceptions(scope) { state.value = it } - return state -} +): MutableState = asMutableComposeState(initial, scope) +@Deprecated("Duplicated functionality", ReplaceWith("asMutableComposeState(scope)", "dev.inmo.micro_utils.coroutines.compose.asMutableComposeState")) @Suppress("NOTHING_TO_INLINE") inline fun StateFlow.toMutableState( scope: CoroutineScope -): MutableState = toMutableState(value, scope) +): MutableState = asMutableComposeState(scope) From 03de71df2e018d64d1157ae60d9ab6e4f78260fa Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 29 Jan 2023 12:43:51 +0600 Subject: [PATCH 11/14] add docs to as compose state --- .../coroutines/compose/FlowAsListState.kt | 18 +++++++++ .../coroutines/compose/FlowAsState.kt | 38 ++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt index bed242faa49..0f0f5309de6 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsListState.kt @@ -13,6 +13,14 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import kotlin.coroutines.CoroutineContext +/** + * Each value of [this] [Flow] will trigger [applyDiff] to the result [SnapshotStateList] + * + * @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [SnapshotStateList] + * @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that + * change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default + * @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler + */ @Suppress("NOTHING_TO_INLINE") inline fun Flow>.asMutableComposeListState( scope: CoroutineScope, @@ -33,6 +41,16 @@ inline fun Flow>.asMutableComposeListState( return state } +/** + * In fact, it is just classcast of [asMutableComposeListState] to [List] + * + * @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [List] + * @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that + * change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default + * @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler + * + * @return Changing in time [List] which follow [Flow] values + */ @Suppress("NOTHING_TO_INLINE") inline fun Flow>.asComposeList( scope: CoroutineScope, diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt index 9bbee0e172c..3bf1fecc7a7 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowAsState.kt @@ -1,6 +1,7 @@ package dev.inmo.micro_utils.coroutines.compose import androidx.compose.runtime.* +import dev.inmo.micro_utils.common.compose.asState import dev.inmo.micro_utils.coroutines.ExceptionHandler import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull import dev.inmo.micro_utils.coroutines.doInUI @@ -12,6 +13,15 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import kotlin.coroutines.CoroutineContext +/** + * Will map [this] [Flow] as [MutableState]. Returned [MutableState] WILL NOT change source [Flow] + * + * @param initial First value which will be passed to the result [MutableState] + * @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [MutableState] + * @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that + * change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default + * @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler + */ fun Flow.asMutableComposeState( initial: T, scope: CoroutineScope, @@ -32,6 +42,15 @@ fun Flow.asMutableComposeState( return state } +/** + * Will map [this] [StateFlow] as [MutableState]. Returned [MutableState] WILL NOT change source [StateFlow]. + * This conversation will pass its [StateFlow.value] as the first value + * + * @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [MutableState] + * @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that + * change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default + * @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler + */ @Suppress("NOTHING_TO_INLINE") inline fun StateFlow.asMutableComposeState( scope: CoroutineScope, @@ -39,6 +58,15 @@ inline fun StateFlow.asMutableComposeState( noinline onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, ): MutableState = asMutableComposeState(value, scope, useContextOnChange, onException) +/** + * Will create [MutableState] using [asMutableComposeState] and use [asState] to convert it as immutable state + * + * @param initial First value which will be passed to the result [State] + * @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [State] + * @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that + * change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default + * @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler + */ fun Flow.asComposeState( initial: T, scope: CoroutineScope, @@ -46,9 +74,17 @@ fun Flow.asComposeState( onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, ): State { val state = asMutableComposeState(initial, scope, useContextOnChange, onException) - return derivedStateOf { state.value } + return state.asState() } +/** + * Will map [this] [StateFlow] as [State]. This conversation will pass its [StateFlow.value] as the first value + * + * @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [State] + * @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that + * change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default + * @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler + */ @Suppress("NOTHING_TO_INLINE") inline fun StateFlow.asComposeState( scope: CoroutineScope, From cd73791b6fcbb7f8271b971fe54f1bc7e23dbf39 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 29 Jan 2023 12:46:27 +0600 Subject: [PATCH 12/14] add docs to the MutableState.asState --- CHANGELOG.md | 2 ++ .../inmo/micro_utils/common/compose/MutableStateAsState.kt | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98da26f5385..2736ba38b79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * `Diff` now is serializable * Add `IndexedValue` serializer * `repeatOnFailure` extending: now you may pass any lambda to check if continue to try/do something + * `Compose`: + * New extension `MutableState.asState` * `Coroutines`: * `Compose`: * `asMutableComposeState` and all depending functions now use `doInUI` to guarantee state changin in Main Dispatcher diff --git a/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt b/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt index b551a9e6b74..11be8cd5669 100644 --- a/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt +++ b/common/compose/src/commonMain/kotlin/dev/inmo/micro_utils/common/compose/MutableStateAsState.kt @@ -1,6 +1,10 @@ package dev.inmo.micro_utils.common.compose import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf -fun MutableState.asState() = derivedStateOf { this.value } +/** + * Converts current [MutableState] to immutable [State] using [derivedStateOf] + */ +fun MutableState.asState(): State = derivedStateOf { this.value } From 3644b83ac6b8c425df490297c521d691c86fec4a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 29 Jan 2023 13:08:41 +0600 Subject: [PATCH 13/14] fill changelog --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2736ba38b79..6418daf82f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,16 @@ * New extension `MutableState.asState` * `Coroutines`: * `Compose`: - * `asMutableComposeState` and all depending functions now use `doInUI` to guarantee state changin in Main Dispatcher + * All the `Flow` conversations to compose `State`/`MutableState`/`SnapshotStateList`/`List` got several new + parameters + * `Flow.toMutableState` now is deprecated in favor to `asMutableComposeState` +* `Repos`: + * `Cache`: + * New type `FullCacheRepo` + * New type `CommonCacheRepo` + * New type `FallbackCacheRepo` + * `CacheRepo` got `invalidate` method. It will fully reload `FullCacheRepo` and just clear `CommonCacheRepo` + * New extensions `KVCache.actualizeAll` ## 0.16.6 From fc48446ec4caf0e285f704caf187f714d9910ef4 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 29 Jan 2023 13:15:26 +0600 Subject: [PATCH 14/14] temporarily remove fallback repo --- CHANGELOG.md | 1 - .../dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt | 8 -------- 2 files changed, 9 deletions(-) delete mode 100644 repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 6418daf82f3..18b8bd0ba82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,6 @@ * `Cache`: * New type `FullCacheRepo` * New type `CommonCacheRepo` - * New type `FallbackCacheRepo` * `CacheRepo` got `invalidate` method. It will fully reload `FullCacheRepo` and just clear `CommonCacheRepo` * New extensions `KVCache.actualizeAll` diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt deleted file mode 100644 index 0be4cc9eb3e..00000000000 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.inmo.micro_utils.repos.cache - -/** - * Any inheritor of this should work with next logic: try to take data from original repo and, - * if not exists, try to take from the cache some. In case if original repo have returned result, it should be saved to the internal - * [dev.inmo.micro_utils.repos.cache.cache.KVCache] - */ -interface FallbackCacheRepo : CacheRepo