Skip to content

Commit

Permalink
Merge pull request #100 from futuredapp/feature/v3-decompose-stateflow
Browse files Browse the repository at this point in the history
Refactor asStateFlow() extension
  • Loading branch information
Syntey authored Dec 10, 2024
2 parents b2123eb + ee98b45 commit 730ce55
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package app.futured.arkitekt.decompose.coroutines

import com.arkivanov.decompose.value.Value
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

/**
* A [StateFlow] implementation that wraps a Decompose Value.
*
* @param T The type of the value.
* @property decomposeValue The Decompose Value to be wrapped.
*/
internal class ValueStateFlow<out T : Any>(
private val decomposeValue: Value<T>,
) : StateFlow<T> {

override val value: T
get() = decomposeValue.value

override val replayCache: List<T> = listOf(value)

override suspend fun collect(collector: FlowCollector<T>): Nothing {
val relay = MutableStateFlow(decomposeValue.value)
val cancellation = decomposeValue.subscribe { value -> relay.value = value }

try {
relay.collect(collector)
} finally {
cancellation.cancel()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,13 @@
package app.futured.arkitekt.decompose.ext

import app.futured.arkitekt.decompose.coroutines.ValueStateFlow
import com.arkivanov.decompose.value.Value
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn

/**
* Converts this Decompose [Value] to Kotlin [Flow].
*
* @param T The type of the value.
* @return A [Flow] emitting the values of the Decompose [Value].
*/
fun <T : Any> Value<T>.asFlow(): Flow<T> = callbackFlow {
val cancellation = subscribe { value ->
trySendBlocking(value)
}

awaitClose {
cancellation.cancel()
}
}

/**
* Converts this Decompose [Value] to Kotlin [StateFlow].
*
* @param T The type of the value.
* @param coroutineScope The [CoroutineScope] in which the [StateFlow] is active.
* @return A [StateFlow] emitting the values of the [Value].
* @return A [StateFlow] emitting the values of this [Value].
*/
fun <T : Any> Value<T>.asStateFlow(
coroutineScope: CoroutineScope,
): StateFlow<T> = asFlow()
.stateIn(
scope = coroutineScope,
started = SharingStarted.Lazily,
initialValue = value,
)
fun <T : Any> Value<T>.asStateFlow(): StateFlow<T> = ValueStateFlow(decomposeValue = this)
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,5 @@ internal class HomeNavHostComponent(
)
}
},
).asStateFlow(componentCoroutineScope)
).asStateFlow()
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal class ProfileNavHostComponent(
)
}
},
).asStateFlow(componentCoroutineScope)
).asStateFlow()

override val actions: ProfileNavHost.Actions = object : ProfileNavHost.Actions {
override fun pop() = stackNavigator.pop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ internal class RootNavHostComponent(
)
}
},
).asStateFlow(coroutineScope = componentCoroutineScope)
).asStateFlow()

init {
doOnCreate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal class SignedInNavHostComponent(
)
}
},
).asStateFlow(componentCoroutineScope)
).asStateFlow()

override val viewState: StateFlow<SignedInNavHostViewState> = componentState.combine(stack) { state, stack ->
state.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ internal class SecondComponent(
@InjectedParam componentContext: AppComponentContext,
@InjectedParam override val navigation: SecondScreenNavigation,
) : ScreenComponent<SecondViewState, Nothing, SecondScreenNavigation>(
componentContext = componentContext,
defaultState = SecondViewState,
),
componentContext = componentContext,
defaultState = SecondViewState,
),
SecondScreen {

override val viewState: StateFlow<SecondViewState> = componentState
Expand Down Expand Up @@ -55,7 +55,7 @@ internal class SecondComponent(
)
}
},
).asStateFlow(componentCoroutineScope)
).asStateFlow()

override val actions: SecondScreen.Actions = object : SecondScreen.Actions {
override fun onBack() = navigation.pop()
Expand Down

0 comments on commit 730ce55

Please sign in to comment.