diff --git a/vice-core/src/commonMain/kotlin/com/eygraber/vice/ViceContainer.kt b/vice-core/src/commonMain/kotlin/com/eygraber/vice/ViceContainer.kt new file mode 100644 index 0000000..239b120 --- /dev/null +++ b/vice-core/src/commonMain/kotlin/com/eygraber/vice/ViceContainer.kt @@ -0,0 +1,92 @@ +package com.eygraber.vice + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch + +public interface ViceContainer + where V : ViceView, C : ViceCompositor, E : ViceEffects { + public val view: V + public val intents: SharedFlow + public val compositor: C + public val effects: E + + @Composable + public fun OnBackPressedHandler(enabled: Boolean, onBackPressed: () -> Unit) + + @Composable + public fun Vice() { + RunVice( + view = view, + intents = intents as MutableSharedFlow, + compositor = compositor, + effects = effects, + onBackPressedHandler = { enabled, onBackPressed -> + OnBackPressedHandler(enabled, onBackPressed) + }, + ) + } +} + +@Composable +private fun RunVice( + view: ViceView, + intents: MutableSharedFlow, + compositor: ViceCompositor, + effects: ViceEffects, + onBackPressedHandler: @Composable (Boolean, () -> Unit) -> Unit, +) { + val scope = rememberCoroutineScope { + Dispatchers.Main.immediate + } + + onBackPressedHandler(compositor.internalIsBackHandlerEnabled()) { + compositor.internalOnBackPressed { intent -> + // this is synchronous because the dispatcher is Main.immediate + scope.launch { + compositor.internalOnIntent(intent) + } + + intents.tryEmit(intent) + } + } + + ViceUdf( + view, + intents, + compositor, + effects, + scope, + ) +} + +@Suppress("NOTHING_TO_INLINE") +@Composable +private inline fun ViceUdf( + view: ViceView, + intents: SharedFlow, + compositor: ViceCompositor, + effects: ViceEffects, + scope: CoroutineScope, +) { + effects.Launch() + + val state = compositor.internalComposite(intents) + val intentHandler: (I) -> Unit = remember(scope, compositor, intents) { + { intent: I -> + // this is synchronous because the dispatcher is Main.immediate + scope.launch { + compositor.internalOnIntent(intent) + } + + (intents as MutableSharedFlow).tryEmit(intent) + } + } + + view.Render(state, intentHandler) +} diff --git a/vice-portal/src/commonMain/kotlin/com/eygraber/vice/portal/VicePortal.kt b/vice-portal/src/commonMain/kotlin/com/eygraber/vice/portal/VicePortal.kt index 008a55c..089a32b 100644 --- a/vice-portal/src/commonMain/kotlin/com/eygraber/vice/portal/VicePortal.kt +++ b/vice-portal/src/commonMain/kotlin/com/eygraber/vice/portal/VicePortal.kt @@ -1,81 +1,16 @@ -// needed to access internal members of vice-core -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - package com.eygraber.vice.portal import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import com.eygraber.portal.compose.ComposePortal -import com.eygraber.vice.Launch import com.eygraber.vice.ViceCompositor +import com.eygraber.vice.ViceContainer import com.eygraber.vice.ViceEffects import com.eygraber.vice.ViceView -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.launch -public abstract class VicePortal : ComposePortal +public abstract class VicePortal : ComposePortal, ViceContainer where V : ViceView, C : ViceCompositor, E : ViceEffects { - protected abstract val view: V - private val intents = MutableSharedFlow(extraBufferCapacity = 64) - protected abstract val compositor: C - protected abstract val effects: E - - @Composable - protected abstract fun OnBackPressedHandler(enabled: Boolean, onBackPressed: () -> Unit) - @Composable public final override fun Render() { - val scope = rememberCoroutineScope { - Dispatchers.Main.immediate - } - - OnBackPressedHandler(enabled = compositor.internalIsBackHandlerEnabled()) { - compositor.internalOnBackPressed { intent -> - // this is synchronous because the dispatcher is Main.immediate - scope.launch { - compositor.internalOnIntent(intent) - } - - intents.tryEmit(intent) - } - } - - Render( - view as ViceView, - intents, - compositor, - effects, - scope, - ) + Vice() } } - -@Suppress("NOTHING_TO_INLINE") -@Composable -private inline fun Render( - view: ViceView, - intents: SharedFlow, - compositor: ViceCompositor, - effects: ViceEffects, - scope: CoroutineScope, -) { - effects.Launch() - - val state = compositor.internalComposite(intents) - val intentHandler: (I) -> Unit = remember(scope, compositor, intents) { - { intent: I -> - // this is synchronous because the dispatcher is Main.immediate - scope.launch { - compositor.internalOnIntent(intent) - } - - (intents as MutableSharedFlow).tryEmit(intent) - } - } - - view.Render(state, intentHandler) -}