commonMain.moe.tlaster.precompose.molecule.molecule.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of precompose-molecule Show documentation
Show all versions of precompose-molecule Show documentation
PreCompose molecule intergration
package moe.tlaster.precompose.molecule
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MonotonicFrameClock
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import app.cash.molecule.RecompositionClock
import app.cash.molecule.launchMolecule
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.consumeAsFlow
import moe.tlaster.precompose.ui.viewModel
import moe.tlaster.precompose.viewmodel.ViewModel
import kotlin.coroutines.CoroutineContext
internal expect fun providePlatformDispatcher(): CoroutineContext
private class PresenterViewModel(
body: @Composable () -> T,
) : ViewModel() {
private val dispatcher = providePlatformDispatcher()
private val clock = if (dispatcher[MonotonicFrameClock] == null) {
RecompositionClock.Immediate
} else {
RecompositionClock.ContextClock
}
private val scope = CoroutineScope(dispatcher)
val state = scope.launchMolecule(clock, body)
override fun onCleared() {
scope.cancel()
}
}
@Composable
private fun rememberPresenterState(
keys: List,
body: @Composable () -> T,
): StateFlow {
@Suppress("UNCHECKED_CAST")
val viewModel = viewModel(
modelClass = PresenterViewModel::class,
keys = keys,
creator = { PresenterViewModel(body) }
) as PresenterViewModel
return viewModel.state
}
private class ActionViewModel : ViewModel() {
val channel = Channel(Channel.UNLIMITED)
val pair = channel to channel.consumeAsFlow()
override fun onCleared() {
channel.close()
}
}
@Composable
private fun rememberAction(
keys: List,
): Pair, Flow> {
@Suppress("UNCHECKED_CAST")
val viewModel = viewModel(
modelClass = ActionViewModel::class,
keys = keys,
creator = { ActionViewModel() }
) as ActionViewModel
return viewModel.pair
}
/**
* Return pair of State and Action Channel, use it in your Compose UI
* The molecule scope and the Action Channel will be managed by the [ViewModel], so it has the same lifecycle as the [ViewModel]
*
* @param keys The keys to use to identify the Presenter
* @param body The body of the molecule presenter, the flow parameter is the flow of the action channel
* @return Pair of State and Action channel
*/
@Composable
fun rememberPresenter(
keys: List = emptyList(),
body: @Composable (flow: Flow) -> T
): Pair> {
val (channel, action) = rememberAction(keys = keys)
val presenter = rememberPresenterState(keys = keys) { body(action) }
val state by presenter.collectAsState()
return state to channel
}
/**
* Return pair of State and Action Channel, use it in your Compose UI
* The molecule scope and the Action Channel will be managed by the [ViewModel], so it has the same lifecycle as the [ViewModel]
*
* @param body The body of the molecule presenter, the flow parameter is the flow of the action channel
* @return Pair of State and Action channel
*/
// @Composable
// inline fun rememberPresenter(
// crossinline body: @Composable (flow: Flow) -> T
// ): Pair> {
// return rememberPresenter(keys = listOf(T::class, E::class)) {
// body.invoke(it)
// }
// }
/**
* Return pair of State and Action channel, use it in your Presenter, not Compose UI
*
* @param body The body of the molecule presenter, the flow parameter is the flow of the action channel
* @return Pair of State and Action channel
*/
@Composable
fun rememberNestedPresenter(
body: @Composable (flow: Flow) -> T
): Pair> {
val channel = remember { Channel(Channel.UNLIMITED) }
val flow = remember { channel.consumeAsFlow() }
val presenter = body(flow)
return presenter to channel
}
/**
* Helper function to collect the action channel in your Presenter
*
* @param body Your action handler
*/
@Composable
fun Flow.collectAction(
body: suspend T.() -> Unit,
) {
LaunchedEffect(Unit) {
collect {
body(it)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy