From b197630fe82d3ea2837584ae3100f3473861f5e7 Mon Sep 17 00:00:00 2001 From: Alex Sokol Date: Mon, 14 Jun 2021 12:45:49 +0300 Subject: [PATCH 1/5] close #30 --- README.md | 71 ++++++++----------- buildSrc/src/main/kotlin/Dependencies.kt | 4 ++ buildSrc/src/main/kotlin/Version.kt | 3 + .../kotlin/fun.kotlingang.deploy/Deploy.kt | 6 +- .../kotlingang/kds/annotation/UnsafeKType.kt | 2 +- .../kds/storage/KTypeDataStorage.kt | 2 +- extensions/README.md | 2 + extensions/build.gradle.kts | 3 + .../extensions-androidx/build.gradle.kts | 30 ++++++++ .../src/main/AndroidManifest.xml | 1 + .../mutable_state/StorageMutableState.kt | 71 +++++++++++++++++++ gradle.properties | 3 +- settings.gradle.kts | 4 +- 13 files changed, 154 insertions(+), 48 deletions(-) create mode 100644 extensions/README.md create mode 100644 extensions/build.gradle.kts create mode 100644 extensions/extensions-androidx/build.gradle.kts create mode 100644 extensions/extensions-androidx/src/main/AndroidManifest.xml create mode 100644 extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt diff --git a/README.md b/README.md index 7aac2bc..3942dcb 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,34 @@ fun main() { Note that the library is written in a way where you may **fully** customize it (add xml format for files/etc, implement java.Serializable support and so on, interfaces are common, so you may still use delegates, commits, mutations on it) +## Integrations +Library integrates with some other libraries providing convenient API for storing androidx `MutableState`. Integrations still require including storage dependency. + +
+Expand + +### Androidx MutableState +
+Expand + +```kotlin +object ComposeStorage : ... { + val username = mutableState() +} +... +@Composable +fun UserNameText() { + val username by remember { ComposeStorage.username } + Text ( + text = username + ) +} +``` + +
+ +
+ ## Implementation > When targeting JS, only IR is supported @@ -244,49 +272,6 @@ There **are** plans for other implementations (bundle, ns-user-default, etc.), b **Platforms**: Any
**Dependency**: `fun.kotlingang.kds:json:$version` -## Plans -There are a lot of possibilities to customize the library, the main goal, for now, is a stabilization of user API. - -**Ideas**:
-I think it may be cool to integrate the library with [kvision](https://github.com/rjaros/kvision), `compose`, etc. - -### KVision integration example -
-Expand -

- -```kotlin -object AppData : KLocalDataStorage() { - val clicks by kvisionState() -} - -class App : Application() { - override fun start() { - root(id = "root") { - vPanel { - h1(AppData.clicks) { clicks -> - + "Clicked $clicks times" - } - button(text = "Click!") { - onClick { - // Changes still handled by storage - clicks.value++ - } - } - } - } - } -} - -fun main() = startApplication(::App) -``` - -

-
- -**Near future**:
-I would separate the `json` module (add `refs-proxy` module to proxy references) and `files` (add `content-storage` module to add abstraction over storages that converting data to Map and then serializing it to content) - [badge-android]: http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat [badge-ios]: http://img.shields.io/badge/platform-ios-CDCDCD.svg?style=flat [badge-js]: http://img.shields.io/badge/platform-js-F8DB5D.svg?style=flat diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 741ebc1..7001e14 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -1,3 +1,7 @@ +// Used in library const val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:${Version.SERIALIZATION}" const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Version.COROUTINES}" const val nodejsExternals = "org.jetbrains.kotlinx:kotlinx-nodejs:${Version.NODEJS_EXTERNALS}" + +// Used for integrations +const val composeRuntime = "androidx.compose.runtime:runtime:${Version.COMPOSE}" diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt index 1f794c2..272bda0 100644 --- a/buildSrc/src/main/kotlin/Version.kt +++ b/buildSrc/src/main/kotlin/Version.kt @@ -4,5 +4,8 @@ object Version { const val SERIALIZATION = "1.2.1" const val COROUTINES = "1.5.0" + const val NODEJS_EXTERNALS = "0.0.7" + + const val COMPOSE = "1.0.0-beta08" } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/fun.kotlingang.deploy/Deploy.kt b/buildSrc/src/main/kotlin/fun.kotlingang.deploy/Deploy.kt index d8ae1ed..0554e35 100644 --- a/buildSrc/src/main/kotlin/fun.kotlingang.deploy/Deploy.kt +++ b/buildSrc/src/main/kotlin/fun.kotlingang.deploy/Deploy.kt @@ -76,7 +76,11 @@ class Deploy : Plugin { name.set(entity.name ?: error("shouldn't be null")) description.set(entity.description ?: error("shouldn't be null")) } - from(components[entity.componentName]) + + if(components.asMap.containsKey(entity.componentName)) + from(components[entity.componentName]) + else + System.err.println("Trying to create component from ${entity.componentName}, but this component not found in project ${project.name}") } } diff --git a/core/src/commonMain/kotlin/fun/kotlingang/kds/annotation/UnsafeKType.kt b/core/src/commonMain/kotlin/fun/kotlingang/kds/annotation/UnsafeKType.kt index e21506e..4f1c6e3 100644 --- a/core/src/commonMain/kotlin/fun/kotlingang/kds/annotation/UnsafeKType.kt +++ b/core/src/commonMain/kotlin/fun/kotlingang/kds/annotation/UnsafeKType.kt @@ -4,7 +4,7 @@ package `fun`.kotlingang.kds.annotation @RequiresOptIn ( message = """ This function uses unsafe `KType to value` association, so you can provide - KType associated with Int and value with different type, be careful! + KType associated with one type and value with different type, be careful! """, level = RequiresOptIn.Level.WARNING ) diff --git a/core/src/commonMain/kotlin/fun/kotlingang/kds/storage/KTypeDataStorage.kt b/core/src/commonMain/kotlin/fun/kotlingang/kds/storage/KTypeDataStorage.kt index 1d314b3..4d20d48 100644 --- a/core/src/commonMain/kotlin/fun/kotlingang/kds/storage/KTypeDataStorage.kt +++ b/core/src/commonMain/kotlin/fun/kotlingang/kds/storage/KTypeDataStorage.kt @@ -16,7 +16,7 @@ public interface KTypeDataStorage { public fun putWithKType(key: String, type: KType, value: Any?) /** - * May be you would use inline [getWithKType]? + * Maybe you would use inline [getWithKType]? */ @RawSetterGetter @UnsafeKType diff --git a/extensions/README.md b/extensions/README.md new file mode 100644 index 0000000..564bd0e --- /dev/null +++ b/extensions/README.md @@ -0,0 +1,2 @@ +# Extensions +> Modules in this directory contains extensions to use with other frameworks diff --git a/extensions/build.gradle.kts b/extensions/build.gradle.kts new file mode 100644 index 0000000..d909ea7 --- /dev/null +++ b/extensions/build.gradle.kts @@ -0,0 +1,3 @@ +configure<`fun`.kotlingang.deploy.DeployEntity> { + ignore = true +} diff --git a/extensions/extensions-androidx/build.gradle.kts b/extensions/extensions-androidx/build.gradle.kts new file mode 100644 index 0000000..b94709b --- /dev/null +++ b/extensions/extensions-androidx/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + id(plugin.androidLibrary) + kotlin(plugin.android) +} + +android { + // Workaround since explicitApi() does not work for android + kotlinOptions.freeCompilerArgs += "-Xexplicit-api=strict" + kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" + + compileSdk = Version.COMPILE_SDK + + defaultConfig { + targetSdk = Version.COMPILE_SDK + minSdk = Version.MIN_SDK + } + + buildFeatures { + buildConfig = false + } +} + +configure<`fun`.kotlingang.deploy.DeployEntity> { + componentName = "release" +} + +dependencies { + api(core) + implementation(composeRuntime) +} diff --git a/extensions/extensions-androidx/src/main/AndroidManifest.xml b/extensions/extensions-androidx/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1ffd87f --- /dev/null +++ b/extensions/extensions-androidx/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt b/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt new file mode 100644 index 0000000..992aa33 --- /dev/null +++ b/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt @@ -0,0 +1,71 @@ +package `fun`.kotlingang.kds.compose.mutable_state + +import `fun`.kotlingang.kds.annotation.RawSetterGetter +import `fun`.kotlingang.kds.annotation.UnsafeKType +import `fun`.kotlingang.kds.optional.fillDefault +import `fun`.kotlingang.kds.storage.KTypeDataStorage +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.SnapshotMutationPolicy +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.structuralEqualityPolicy +import kotlin.reflect.KProperty +import kotlin.reflect.KType +import kotlin.reflect.typeOf + + +public inline fun KTypeDataStorage.mutableState ( + policy: SnapshotMutationPolicy = structuralEqualityPolicy(), +): StorageMutableStateProvider = mutableState(policy) { null } + +@OptIn(ExperimentalStdlibApi::class, UnsafeKType::class) +public inline fun KTypeDataStorage.mutableState ( + policy: SnapshotMutationPolicy = structuralEqualityPolicy(), + noinline defaultValue: () -> T, +): StorageMutableStateProvider = StorageMutableStateProvider ( + storage = this, + type = typeOf(), + defaultValue = defaultValue, + policy = policy +) + + +public class StorageMutableStateProvider @UnsafeKType constructor ( + private val storage: KTypeDataStorage, + private val type: KType, + private val defaultValue: () -> T, + private val policy: SnapshotMutationPolicy +) { + @OptIn(UnsafeKType::class) + public operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): StorageMutableState = + StorageMutableState(storage, property.name, type, defaultValue, policy) +} + +@OptIn(RawSetterGetter::class, UnsafeKType::class) +public class StorageMutableState @UnsafeKType constructor ( + private val storage: KTypeDataStorage, + private val key: String, + private val type: KType, + defaultValue: () -> T, + policy: SnapshotMutationPolicy +) : MutableState { + private val state = mutableStateOf ( + storage.getWithKType(key, type).fillDefault(defaultValue).value, + policy = policy + ) + + override var value: T + get() = state.value + set(value) { + storage.putWithKType(key, type, value) + state.value = value + } + + override fun component1(): T = value + + override fun component2(): (T) -> Unit = { value = it } + + public operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value + public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + this.value = value + } +} diff --git a/gradle.properties b/gradle.properties index 541d095..5e738b5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,4 @@ kotlin.code.style=official kotlin.js.generate.executable.default=false -kotlin.mpp.stability.nowarn=true \ No newline at end of file +kotlin.mpp.stability.nowarn=true +android.useAndroidX=true \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index fdf6781..784acd2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,5 +14,7 @@ include ( "json:json-files", "json:json-local-storage", "json:json-shared-preferences", - "json:json-bundle" + "json:json-bundle", + + "extensions:extensions-androidx" ) From 2dfbbd7f1d94b3ba8fcde86e8111cc3c93f79ceb Mon Sep 17 00:00:00 2001 From: Alex Sokol Date: Mon, 14 Jun 2021 14:23:19 +0300 Subject: [PATCH 2/5] close #32 --- README.md | 54 ++++++++++++++++- buildSrc/src/main/kotlin/Dependencies.kt | 2 + buildSrc/src/main/kotlin/Version.kt | 2 + .../mutable_state/StorageMutableState.kt | 4 +- .../StorageMutableStateFlow.kt | 6 ++ .../extensions-kvision/build.gradle.kts | 22 +++++++ .../StorageObservableValue.kt | 59 +++++++++++++++++++ settings.gradle.kts | 5 +- 8 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt create mode 100644 extensions/extensions-kvision/build.gradle.kts create mode 100644 extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt diff --git a/README.md b/README.md index 3942dcb..c27ab8a 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,48 @@ fun UserNameText() { +### KVision ObservableValue +
+Expand + +```kotlin +object AppData : KLocalDataStorage() { + val clicks = observableValue { 0 } +} + +class App : Application() { + override fun start() { + root(id = "root") { + vPanel { + h1(AppData.clicks) { clicks -> + + "Clicked $clicks times" + } + button(text = "Click!") { + onClick { + // Changes still handled by storage + clicks.value++ + } + } + } + } + } +} + +fun main() = startApplication(::App) +``` + +
+ +### Coroutines MutableStateFlow +
+Expand + +```kotlin + +``` + +
+ ## Implementation @@ -257,8 +299,16 @@ All `kds` packages are located at repository [maven.kotlingang.fun](https://mave **Dependency**: `fun.kotlingang.kds:json-bundle:$version`
**Example**: GitHub [repo](https://github.com/kotlingang/kds-android-example) -### Custom -There **are** plans for other implementations (bundle, ns-user-default, etc.), but if you want to create your implementation, take a look at the following dependencies +## Integrations implementation + +### Androidx Extensions +> MutableState [implementation](extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt) + +**Platforms**: ![android][badge-android]
+**Dependency**: `fun.kotlingang.kds:extensions-androidx:$version` + +## Custom +If you want to create your own implementation, take a look at the following dependencies #### Core > The core module with delegates and main interfaces diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 7001e14..b54cdb2 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -5,3 +5,5 @@ const val nodejsExternals = "org.jetbrains.kotlinx:kotlinx-nodejs:${Version.NODE // Used for integrations const val composeRuntime = "androidx.compose.runtime:runtime:${Version.COMPOSE}" +const val kvision = "io.kvision:kvision:${Version.KVISION}" + diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt index 272bda0..cbb9cb1 100644 --- a/buildSrc/src/main/kotlin/Version.kt +++ b/buildSrc/src/main/kotlin/Version.kt @@ -5,6 +5,8 @@ object Version { const val SERIALIZATION = "1.2.1" const val COROUTINES = "1.5.0" + const val KVISION = "4.8.1" + const val NODEJS_EXTERNALS = "0.0.7" const val COMPOSE = "1.0.0-beta08" diff --git a/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt b/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt index 992aa33..94dd730 100644 --- a/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt +++ b/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt @@ -2,7 +2,7 @@ package `fun`.kotlingang.kds.compose.mutable_state import `fun`.kotlingang.kds.annotation.RawSetterGetter import `fun`.kotlingang.kds.annotation.UnsafeKType -import `fun`.kotlingang.kds.optional.fillDefault +import `fun`.kotlingang.kds.optional.getOrDefault import `fun`.kotlingang.kds.storage.KTypeDataStorage import androidx.compose.runtime.MutableState import androidx.compose.runtime.SnapshotMutationPolicy @@ -49,7 +49,7 @@ public class StorageMutableState @UnsafeKType constructor ( policy: SnapshotMutationPolicy ) : MutableState { private val state = mutableStateOf ( - storage.getWithKType(key, type).fillDefault(defaultValue).value, + storage.getWithKType(key, type).getOrDefault(defaultValue), policy = policy ) diff --git a/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt b/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt new file mode 100644 index 0000000..5e0c528 --- /dev/null +++ b/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt @@ -0,0 +1,6 @@ +package `fun`.kotlingang.kds.coroutines.mutable_state_flow + + +class StorageMutableStateFlow { + +} diff --git a/extensions/extensions-kvision/build.gradle.kts b/extensions/extensions-kvision/build.gradle.kts new file mode 100644 index 0000000..178480f --- /dev/null +++ b/extensions/extensions-kvision/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + kotlin(plugin.js) +} + +kotlin { + explicitApi() + + js(IR) { + browser() + } + + sourceSets { + all { + languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn") + } + } +} + +dependencies { + api(core) + implementation(kvision) +} diff --git a/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt b/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt new file mode 100644 index 0000000..f67f50b --- /dev/null +++ b/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt @@ -0,0 +1,59 @@ +package `fun`.kotlingang.kds.kvision.observable_value + +import `fun`.kotlingang.kds.annotation.RawSetterGetter +import `fun`.kotlingang.kds.annotation.UnsafeKType +import `fun`.kotlingang.kds.optional.getOrDefault +import `fun`.kotlingang.kds.storage.KTypeDataStorage +import io.kvision.state.MutableState +import io.kvision.state.ObservableValue +import kotlin.reflect.KProperty +import kotlin.reflect.KType +import kotlin.reflect.typeOf + + +public inline fun KTypeDataStorage.observableValue(): StorageObservableValueProvider = + observableValue { null } + +@OptIn(UnsafeKType::class, ExperimentalStdlibApi::class) +public inline fun KTypeDataStorage.observableValue ( + noinline default: () -> T +): StorageObservableValueProvider = StorageObservableValueProvider(storage = this, typeOf(), default) + +@OptIn(UnsafeKType::class) +public class StorageObservableValueProvider @UnsafeKType constructor ( + private val storage: KTypeDataStorage, + private val type: KType, + private val default: () -> T +) { + public operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): StorageObservableValue = + StorageObservableValue(storage, property.name, type, default) +} + +@OptIn(UnsafeKType::class, RawSetterGetter::class) +public class StorageObservableValue @UnsafeKType constructor ( + private val storage: KTypeDataStorage, + private val key: String, + private val type: KType, + default: () -> T +) : MutableState { + private val observable = ObservableValue ( + storage.getWithKType(key, type).getOrDefault(default) + ) + + public var value: T by observable::value + + override fun setState(state: T) { + storage.putWithKType(key, type, state) + observable.setState(state) + } + + override fun getState(): T = observable.getState() + + override fun subscribe(observer: (T) -> Unit): () -> Unit = observable.subscribe(observer) + + public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + setState(value) + } + + public operator fun getValue(thisRef: Any?, property: KProperty<*>): T = getState() +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 784acd2..1bc8c68 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,5 +16,8 @@ include ( "json:json-shared-preferences", "json:json-bundle", - "extensions:extensions-androidx" + "extensions", + "extensions:extensions-androidx", + "extensions:extensions-kvision", + "extensions:extensions-coroutines" ) From 81664041cb20ce6a4862b652698827ea8e2934a4 Mon Sep 17 00:00:00 2001 From: Alex Sokol Date: Mon, 14 Jun 2021 15:25:24 +0300 Subject: [PATCH 3/5] close #31 --- README.md | 5 +- .../extensions-coroutines/build.gradle.kts | 28 ++++++ .../StorageMutableStateFlow.kt | 92 ++++++++++++++++++- 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 extensions/extensions-coroutines/build.gradle.kts diff --git a/README.md b/README.md index c27ab8a..0494761 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,10 @@ fun main() = startApplication(::App) Expand ```kotlin - +object CoroutinesStorage : ... { + // Use it everywhere you need to save state flow values + val stateFlow by mutableStateFlow() +} ``` diff --git a/extensions/extensions-coroutines/build.gradle.kts b/extensions/extensions-coroutines/build.gradle.kts new file mode 100644 index 0000000..edfe777 --- /dev/null +++ b/extensions/extensions-coroutines/build.gradle.kts @@ -0,0 +1,28 @@ +@file:Suppress("UNUSED_VARIABLE") + +plugins { + kotlin(plugin.multiplatform) +} + +kotlin { + explicitApi() + + jvm() + js(IR) { + nodejs() + browser() + } + + sourceSets { + val commonMain by getting { + dependencies { + api(core) + implementation(coroutines) + } + } + + all { + languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn") + } + } +} diff --git a/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt b/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt index 5e0c528..ab28c0d 100644 --- a/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt +++ b/extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt @@ -1,6 +1,96 @@ package `fun`.kotlingang.kds.coroutines.mutable_state_flow +import `fun`.kotlingang.kds.annotation.RawSetterGetter +import `fun`.kotlingang.kds.annotation.UnsafeKType +import `fun`.kotlingang.kds.optional.getOrDefault +import `fun`.kotlingang.kds.storage.KTypeDataStorage +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlin.reflect.KProperty +import kotlin.reflect.KType +import kotlin.reflect.typeOf -class StorageMutableStateFlow { +public inline fun KTypeDataStorage.mutableStateFlow(): StorageMutableStateFlowProvider = + mutableStateFlow { null } + +@OptIn(ExperimentalStdlibApi::class, UnsafeKType::class) +public inline fun KTypeDataStorage.mutableStateFlow ( + noinline defaultValue: () -> T, +): StorageMutableStateFlowProvider = StorageMutableStateFlowProvider ( + storage = this, + type = typeOf(), + defaultValue = defaultValue +) + +@OptIn(UnsafeKType::class) +public class StorageMutableStateFlowProvider @UnsafeKType constructor ( + private val storage: KTypeDataStorage, + private val type: KType, + private val defaultValue: () -> T +) { + private lateinit var name: String + private val state by lazy { StorageMutableStateFlow(storage, name, type, defaultValue) } + + public operator fun getValue ( + thisRef: Any?, property: KProperty<*> + ): StorageMutableStateFlow { + name = property.name + return state + } +} + +@OptIn(RawSetterGetter::class, UnsafeKType::class) +public class StorageMutableStateFlow @UnsafeKType constructor ( + private val storage: KTypeDataStorage, + private val key: String, + private val type: KType, + defaultValue: () -> T +) : MutableStateFlow { + private val state = MutableStateFlow ( + storage.getWithKType(key, type).getOrDefault(defaultValue) + ) + + override var value: T + get() = state.value + set(value) { + storage.putWithKType(key, type, value) + state.value = value + } + + public operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value + public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + this.value = value + } + + @InternalCoroutinesApi + override suspend fun collect(collector: FlowCollector) { + state.collect(collector) + } + + override val subscriptionCount: StateFlow = state.subscriptionCount + + override suspend fun emit(value: T) { + state.emit(value) + storage.putWithKType(key, type, value) + } + + @ExperimentalCoroutinesApi + override fun resetReplayCache() { + state.resetReplayCache() + } + + override fun tryEmit(value: T): Boolean = state.tryEmit(value).also { emitted -> + if (emitted) storage.putWithKType(key, type, value) + } + + override fun compareAndSet(expect: T, update: T): Boolean = + state.compareAndSet(expect, update).also { set -> + if (set) storage.putWithKType(key, type, update) + } + + override val replayCache: List = state.replayCache } From ba106ce5b5b26b8b9559dc67f23c666e24c660ea Mon Sep 17 00:00:00 2001 From: Alex Sokol Date: Mon, 14 Jun 2021 15:55:49 +0300 Subject: [PATCH 4/5] version update; fixed some behaviour errors; readme update. --- README.md | 16 ++++++++++++++-- buildSrc/src/main/kotlin/AppInfo.kt | 2 +- .../compose/mutable_state/StorageMutableState.kt | 13 ++++++++++--- .../observable_value/StorageObservableValue.kt | 9 +++++++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0494761..5486bdf 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ Library integrates with some other libraries providing convenient API for storin ```kotlin object ComposeStorage : ... { - val username = mutableState() + val username by mutableState() } ... @Composable @@ -228,7 +228,7 @@ fun UserNameText() { ```kotlin object AppData : KLocalDataStorage() { - val clicks = observableValue { 0 } + val clicks by observableValue { 0 } } class App : Application() { @@ -310,6 +310,18 @@ All `kds` packages are located at repository [maven.kotlingang.fun](https://mave **Platforms**: ![android][badge-android]
**Dependency**: `fun.kotlingang.kds:extensions-androidx:$version` +### Coroutines Extensions +> MutableStateFlow [implementation](extensions/extensions-coroutines/src/commonMain/kotlin/fun/kotlingang/kds/coroutines/mutable_state_flow/StorageMutableStateFlow.kt) + +**Platform**: ![android][badge-android] ![jvm][badge-jvm] ![js][badge-js] ![nodejs][badge-nodejs] +**Dependency**: `fun.kotlingang.kds:extensions-coroutines:$version` + +### KVision Extensions +> ObservableState [implementation](extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt) + +**Platform**: ![js][badge-js] +**Dependency**: `fun.kotlingang.kds:extensions-kvision:$version` + ## Custom If you want to create your own implementation, take a look at the following dependencies diff --git a/buildSrc/src/main/kotlin/AppInfo.kt b/buildSrc/src/main/kotlin/AppInfo.kt index ab45dbf..7692789 100644 --- a/buildSrc/src/main/kotlin/AppInfo.kt +++ b/buildSrc/src/main/kotlin/AppInfo.kt @@ -1,6 +1,6 @@ object AppInfo { const val PACKAGE = "fun.kotlingang.kds" - const val VERSION = "1.0.1" + const val VERSION = "1.1.0" const val NAME = "Kotlin Data Storage" const val DESCRIPTION = "Multiplatform Coroutine-Based Kotlin Library for storing data via kotlinx.serialization" } diff --git a/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt b/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt index 94dd730..e3f1714 100644 --- a/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt +++ b/extensions/extensions-androidx/src/main/java/fun/kotlingang/kds/compose/mutable_state/StorageMutableState.kt @@ -17,6 +17,7 @@ public inline fun KTypeDataStorage.mutableState ( policy: SnapshotMutationPolicy = structuralEqualityPolicy(), ): StorageMutableStateProvider = mutableState(policy) { null } + @OptIn(ExperimentalStdlibApi::class, UnsafeKType::class) public inline fun KTypeDataStorage.mutableState ( policy: SnapshotMutationPolicy = structuralEqualityPolicy(), @@ -29,15 +30,21 @@ public inline fun KTypeDataStorage.mutableState ( ) +@OptIn(UnsafeKType::class) public class StorageMutableStateProvider @UnsafeKType constructor ( private val storage: KTypeDataStorage, private val type: KType, private val defaultValue: () -> T, private val policy: SnapshotMutationPolicy ) { - @OptIn(UnsafeKType::class) - public operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): StorageMutableState = - StorageMutableState(storage, property.name, type, defaultValue, policy) + private lateinit var name: String + private val state by lazy { StorageMutableState(storage, name, type, defaultValue, policy) } + + public operator fun getValue(thisRef: Any?, property: KProperty<*>): StorageMutableState { + name = property.name + return state + } + } @OptIn(RawSetterGetter::class, UnsafeKType::class) diff --git a/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt b/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt index f67f50b..d9117f7 100644 --- a/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt +++ b/extensions/extensions-kvision/src/main/kotlin/fun/kotlingang/kds/kvision/observable_value/StorageObservableValue.kt @@ -25,8 +25,13 @@ public class StorageObservableValueProvider @UnsafeKType constructor ( private val type: KType, private val default: () -> T ) { - public operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): StorageObservableValue = - StorageObservableValue(storage, property.name, type, default) + private lateinit var name: String + private val state by lazy { StorageObservableValue(storage, name, type, default) } + + public operator fun getValue(thisRef: Any?, property: KProperty<*>): StorageObservableValue { + name = property.name + return state + } } @OptIn(UnsafeKType::class, RawSetterGetter::class) From d4decb02968342b80032051722093431bf5988eb Mon Sep 17 00:00:00 2001 From: Alex Sokol Date: Mon, 14 Jun 2021 16:01:38 +0300 Subject: [PATCH 5/5] refactor .gitignore --- .gitignore | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index ed84d2d..908fb7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,9 @@ -/.idea/ - -/build/ -/buildSrc/build/ -/core/build/ -/files/build/ -/local-storage/build/ -/shared-preferences/build/ -/bundle/build/ -/json/build/ -/json/json-files/build/ -/json/json-local-storage/build/ -/json/json-shared-preferences/build/ -/json/json-bundle/ - -/.gradle/ -/buildSrc/.gradle/ +.idea +build +.gradle /data/ /json/json-files/data/ /local.properties /deploy.properties -/android-app-provider/build/