commonMain.com.squareup.workflow1.internal.RealRenderContext.kt Maven / Gradle / Ivy
The newest version!
package com.squareup.workflow1.internal
import com.squareup.workflow1.BaseRenderContext
import com.squareup.workflow1.Sink
import com.squareup.workflow1.Workflow
import com.squareup.workflow1.WorkflowAction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.SendChannel
internal class RealRenderContext(
private val renderer: Renderer,
private val sideEffectRunner: SideEffectRunner,
private val eventActionsChannel: SendChannel>
) : BaseRenderContext, Sink> {
interface Renderer {
fun render(
child: Workflow,
props: ChildPropsT,
key: String,
handler: (ChildOutputT) -> WorkflowAction
): ChildRenderingT
}
interface SideEffectRunner {
fun runningSideEffect(
key: String,
sideEffect: suspend CoroutineScope.() -> Unit
)
}
/**
* False during the current render call, set to true once this node is finished rendering.
*
* Used to:
* - prevent modifications to this object after [freeze] is called.
* - prevent sending to sinks before render returns.
*/
private var frozen = false
override val actionSink: Sink> get() = this
override fun send(value: WorkflowAction) {
if (!frozen) {
throw UnsupportedOperationException(
"Expected sink to not be sent to until after the render pass. " +
"Received action: ${value.debuggingName}"
)
}
eventActionsChannel.trySend(value)
}
override fun renderChild(
child: Workflow,
props: ChildPropsT,
key: String,
handler: (ChildOutputT) -> WorkflowAction
): ChildRenderingT {
checkNotFrozen()
return renderer.render(child, props, key, handler)
}
override fun runningSideEffect(
key: String,
sideEffect: suspend CoroutineScope.() -> Unit
) {
checkNotFrozen()
sideEffectRunner.runningSideEffect(key, sideEffect)
}
/**
* Freezes this context so that any further calls to this context will throw.
*/
fun freeze() {
checkNotFrozen()
frozen = true
}
/**
* Unfreezes when the node is about to render() again.
*/
fun unfreeze() {
frozen = false
}
private fun checkNotFrozen() = check(!frozen) {
"RenderContext cannot be used after render method returns."
}
}