Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ViceContainer which makes framework integration easier #59

Merged
merged 1 commit into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}