-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ViceContainer which makes framework integration easier (#59)
- Loading branch information
Showing
2 changed files
with
95 additions
and
68 deletions.
There are no files selected for viewing
92 changes: 92 additions & 0 deletions
92
vice-core/src/commonMain/kotlin/com/eygraber/vice/ViceContainer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
71 changes: 3 additions & 68 deletions
71
vice-portal/src/commonMain/kotlin/com/eygraber/vice/portal/VicePortal.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |