From 051757adcabcf2912d0fcd24b089def8393291c7 Mon Sep 17 00:00:00 2001 From: y9vad9 Date: Wed, 10 Jan 2024 01:30:45 +0100 Subject: [PATCH] feat: `SmartValue` implementation for updatable multi-source values --- foundation/smart-value/build.gradle.kts | 11 ++++ .../io/timemates/remote/value/SmartValue.kt | 66 +++++++++++++++++++ .../remote/value/UpdatableSmartValue.kt | 28 ++++++++ settings.gradle.kts | 1 + 4 files changed, 106 insertions(+) create mode 100644 foundation/smart-value/build.gradle.kts create mode 100644 foundation/smart-value/src/main/kotlin/io/timemates/remote/value/SmartValue.kt create mode 100644 foundation/smart-value/src/main/kotlin/io/timemates/remote/value/UpdatableSmartValue.kt diff --git a/foundation/smart-value/build.gradle.kts b/foundation/smart-value/build.gradle.kts new file mode 100644 index 0000000..7d7bc5e --- /dev/null +++ b/foundation/smart-value/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id(libs.plugins.configurations.multiplatform.library.get().pluginId) +} + +kotlin { + explicitApi() +} + +dependencies { + commonMainImplementation(libs.kotlinx.coroutines) +} \ No newline at end of file diff --git a/foundation/smart-value/src/main/kotlin/io/timemates/remote/value/SmartValue.kt b/foundation/smart-value/src/main/kotlin/io/timemates/remote/value/SmartValue.kt new file mode 100644 index 0000000..94bd86a --- /dev/null +++ b/foundation/smart-value/src/main/kotlin/io/timemates/remote/value/SmartValue.kt @@ -0,0 +1,66 @@ +package io.timemates.remote.value + +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.flow.Flow + +/** + * A generic interface representing a remote value with local storage capabilities. + * + * This interface provides access to the current value, local value, and remote value. + * It also supports setting a listener to be notified when the value is received. + * + * @param T The type of the value. + */ +public interface SmartValue { + /** + * A [Flow] representing the local value. This flow can emit multiple values if any change + * has occurred in the local storage (if watching for updates is supported). + * + * **Note**: Some [SmartValue]s can support remote only value meaning there's no + * value will be present. + * + * @see kotlinx.coroutines.flow.Flow + */ + public val localValue: Flow + + /** + * A [Flow] representing the remote value. This flow can have multiple outputs depending on the + * implementation, as it may receive updates from external sources, such as the server. + * + * **Note**: [remoteValue] shouldn't be considered as 'last actual source' as for specific cases + * [localValue] can receive updates that made on client that appears much faster than on server + * or actual 'update watching' system could be missing. + * + * @see kotlinx.coroutines.flow.Flow + */ + public val remoteValue: Flow + + public companion object { + /** + * A factory method for creating instances of [SmartValue]. + * + * This method is used to construct a [SmartValue] instance with specified local and remote update + * logic, along with optional channels for receiving updates from local and remote sources. + * + * @param T The type of the value. + * @param local A suspending lambda representing the logic to retrieve the initial local value. + * @param remote A suspending lambda representing the logic to retrieve the initial remote value. + * @param localUpdates An optional [ReceiveChannel] to receive updates from local sources. + * @param remoteUpdates An optional [ReceiveChannel] to receive updates from remote sources. + * @return A [SmartValue] instance configured with the provided parameters. + * + * @see SmartValue + * @see kotlinx.coroutines.channels.ReceiveChannel + */ + public fun of( + local: suspend () -> T?, + remote: suspend () -> T?, + localUpdates: ReceiveChannel? = null, + remoteUpdates: ReceiveChannel? = null, + ): SmartValue { + return UpdatableSmartValue( + local, remote, localUpdates, remoteUpdates + ) + } + } +} \ No newline at end of file diff --git a/foundation/smart-value/src/main/kotlin/io/timemates/remote/value/UpdatableSmartValue.kt b/foundation/smart-value/src/main/kotlin/io/timemates/remote/value/UpdatableSmartValue.kt new file mode 100644 index 0000000..a039d6b --- /dev/null +++ b/foundation/smart-value/src/main/kotlin/io/timemates/remote/value/UpdatableSmartValue.kt @@ -0,0 +1,28 @@ +package io.timemates.remote.value + +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.receiveAsFlow + +internal class UpdatableSmartValue( + private val local: suspend () -> T?, + private val remote: suspend () -> T?, + private val localUpdates: ReceiveChannel? = null, + private val remoteUpdates: ReceiveChannel? = null, +) : SmartValue { + override val localValue: Flow = flow { + emit(local()) + + localUpdates?.receiveAsFlow()?.collect { + emit(it) + } + } + override val remoteValue: Flow = flow { + emit(remote()) + + remoteUpdates?.receiveAsFlow()?.collect { + emit(it) + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 1764d51..c710f8d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,7 @@ include( ":foundation:system-tray", ":foundation:shimmer-compose", ":foundation:time", + ":foundation:smart-value", ) include(