Skip to content

Commit

Permalink
Add ViceContainer which makes framework integration easier (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
eygraber authored Mar 19, 2024
1 parent fd410cc commit d7c04e9
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 68 deletions.
92 changes: 92 additions & 0 deletions vice-core/src/commonMain/kotlin/com/eygraber/vice/ViceContainer.kt
Original file line number Diff line number Diff line change
@@ -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<V, I, C, E, S>
where V : ViceView<I, S>, C : ViceCompositor<I, S>, E : ViceEffects {
public val view: V
public val intents: SharedFlow<I>
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<I>,
compositor = compositor,
effects = effects,
onBackPressedHandler = { enabled, onBackPressed ->
OnBackPressedHandler(enabled, onBackPressed)
},
)
}
}

@Composable
private fun <I, S> RunVice(
view: ViceView<I, S>,
intents: MutableSharedFlow<I>,
compositor: ViceCompositor<I, S>,
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 <I, S> ViceUdf(
view: ViceView<I, S>,
intents: SharedFlow<I>,
compositor: ViceCompositor<I, S>,
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<I>).tryEmit(intent)
}
}

view.Render(state, intentHandler)
}
Original file line number Diff line number Diff line change
@@ -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<K, V, I, C, E, S> : ComposePortal<K>
public abstract class VicePortal<K, V, I, C, E, S> : ComposePortal<K>, ViceContainer<V, I, C, E, S>
where V : ViceView<I, S>, C : ViceCompositor<I, S>, E : ViceEffects {
protected abstract val view: V
private val intents = MutableSharedFlow<I>(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<I, S>,
intents,
compositor,
effects,
scope,
)
Vice()
}
}

@Suppress("NOTHING_TO_INLINE")
@Composable
private inline fun <I, S> Render(
view: ViceView<I, S>,
intents: SharedFlow<I>,
compositor: ViceCompositor<I, S>,
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<I>).tryEmit(intent)
}
}

view.Render(state, intentHandler)
}

0 comments on commit d7c04e9

Please sign in to comment.