diff --git a/CHANGELOG.md b/CHANGELOG.md index 89adae44a07..2841ce36292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.20.16 + +* `Versions`: + * `Exposed`: `0.44.1` -> `0.45.0` +* `Coroutines`: + * Add `SpecialMutableStateFlow` + * `Compose`: + * Add `FlowState` + ## 0.20.15 * `Versions`: diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt new file mode 100644 index 00000000000..487f1065564 --- /dev/null +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt @@ -0,0 +1,48 @@ +package dev.inmo.micro_utils.coroutines.compose + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow +import dev.inmo.micro_utils.coroutines.doInUI +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +/** + * This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow]. + * Based on [SpecialMutableStateFlow] + */ +class FlowState( + initial: T, + internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default) +) : MutableState, + SpecialMutableStateFlow(initial, internalScope) { + private var internalValue: T = initial + override var value: T + get() = internalValue + set(value) { + internalValue = value + tryEmit(value) + } + + override suspend fun onChange(value: T) { + internalValue = value + super.onChange(value) + } + + override fun component1(): T = value + + override fun component2(): (T) -> Unit = { tryEmit(it) } + + override fun tryEmit(value: T): Boolean { + internalValue = value + return super.tryEmit(value) + } + + override suspend fun emit(value: T) { + internalValue = value + super.emit(value) + } +} + +//fun MutableState.asFlowState(scope: CoroutineScope = CoroutineScope(Dispatchers.Main)) = FlowState(this, scope) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt new file mode 100644 index 00000000000..e39d5cdaa48 --- /dev/null +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt @@ -0,0 +1,60 @@ +package dev.inmo.micro_utils.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.StateFlow + +/** + * Works like [StateFlow], but guarantee that latest value update will always be delivered to + * each active subscriber + */ +open class SpecialMutableStateFlow( + initialValue: T, + internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default) +) : StateFlow, FlowCollector, MutableSharedFlow { + protected val internalSharedFlow: MutableSharedFlow = MutableSharedFlow( + replay = 0, + extraBufferCapacity = 2, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + protected val publicSharedFlow: MutableSharedFlow = MutableSharedFlow( + replay = 1, + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + + protected var _value: T = initialValue + override val value: T + get() = _value + protected open suspend fun onChange(value: T) { + _value = value + publicSharedFlow.emit(value) + } + protected val job = internalSharedFlow.subscribe(internalScope) { + if (_value != it) { + onChange(it) + } + } + + override val replayCache: List + get() = publicSharedFlow.replayCache + override val subscriptionCount: StateFlow + get() = publicSharedFlow.subscriptionCount + + @ExperimentalCoroutinesApi + override fun resetReplayCache() = publicSharedFlow.resetReplayCache() + + override fun tryEmit(value: T): Boolean { + return internalSharedFlow.tryEmit(value) + } + + override suspend fun emit(value: T) { + internalSharedFlow.emit(value) + } + + override suspend fun collect(collector: FlowCollector) = publicSharedFlow.collect(collector) +} diff --git a/gradle.properties b/gradle.properties index 9633593bfe2..b7806dd69fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.20.15 -android_code_version=221 +version=0.20.16 +android_code_version=222 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9702c5454ba..7b5779f4107 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ kt-coroutines = "1.7.3" kslog = "1.3.1" jb-compose = "1.5.11" -jb-exposed = "0.44.1" +jb-exposed = "0.45.0" jb-dokka = "1.9.10" korlibs = "4.0.10" @@ -21,7 +21,7 @@ koin = "3.5.0" okio = "3.6.0" -ksp = "1.9.20-1.0.14" +ksp = "1.9.21-1.0.15" kotlin-poet = "1.15.1" versions = "0.50.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e411586a54a..a5952066425 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixBuilder.kt b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixBuilder.kt index 6c5292f47ee..a218cefa6eb 100644 --- a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixBuilder.kt +++ b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixBuilder.kt @@ -1,12 +1,15 @@ package dev.inmo.micro_utils.matrix -class MatrixBuilder { +open class MatrixBuilder { private val mutMatrix: MutableList> = ArrayList() val matrix: Matrix get() = mutMatrix fun row(t: List) = mutMatrix.add(t) + fun add(t: List) = mutMatrix.add(t) operator fun List.unaryPlus() = row(this) + operator fun plus(t: List) = add(t) + operator fun T.unaryPlus() = add(listOf(this)) } fun MatrixBuilder.row(block: RowBuilder.() -> Unit) = +RowBuilder().also(block).row diff --git a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/RowBuilder.kt b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/RowBuilder.kt index f79a71ea2c5..f1b4fced24b 100644 --- a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/RowBuilder.kt +++ b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/RowBuilder.kt @@ -1,12 +1,13 @@ package dev.inmo.micro_utils.matrix -class RowBuilder { +open class RowBuilder { private val mutRow: MutableList = ArrayList() val row: Row get() = mutRow - fun column(t: T) = mutRow.add(t) + fun add(t: T) = mutRow.add(t) operator fun T.unaryPlus() = column(this) + fun column(t: T) = mutRow.add(t) } fun row(block: RowBuilder.() -> Unit): List = RowBuilder().also(block).row