-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c007b73
commit 2abe340
Showing
11 changed files
with
303 additions
and
107 deletions.
There are no files selected for viewing
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
68 changes: 0 additions & 68 deletions
68
workflow-core/src/commonMain/kotlin/com/squareup/workflow1/WorkflowComposables.kt
This file was deleted.
Oops, something went wrong.
6 changes: 5 additions & 1 deletion
6
...com/squareup/workflow1/ComposeWorkflow.kt → ...reup/workflow1/compose/ComposeWorkflow.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
2 changes: 1 addition & 1 deletion
2
.../squareup/workflow1/WorkflowComposable.kt → ...p/workflow1/compose/WorkflowComposable.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
98 changes: 98 additions & 0 deletions
98
workflow-core/src/commonMain/kotlin/com/squareup/workflow1/compose/WorkflowComposables.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,98 @@ | ||
package com.squareup.workflow1.compose | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.MutableState | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import com.squareup.workflow1.BaseRenderContext | ||
import com.squareup.workflow1.Workflow | ||
|
||
/** | ||
* Renders a child [Workflow] from any [WorkflowComposable] (e.g. a [ComposeWorkflow.Rendering] or | ||
* [BaseRenderContext.renderComposable]) and returns its rendering. | ||
* | ||
* @param onOutput An optional function that, if non-null, will be called when the child emits an | ||
* output. If null, the child's outputs will be ignored. | ||
*/ | ||
@WorkflowComposable | ||
@Composable | ||
fun <ChildPropsT, ChildOutputT, ChildRenderingT> renderChild( | ||
workflow: Workflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
onOutput: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT { | ||
val host = LocalWorkflowCompositionHost.current | ||
return host.renderChild(workflow, props, onOutput) | ||
} | ||
|
||
/** | ||
* Renders a child [Workflow] from any [WorkflowComposable] (e.g. a [ComposeWorkflow.Rendering] or | ||
* [BaseRenderContext.renderComposable]) and returns its rendering. | ||
* | ||
* @param onOutput An optional function that, if non-null, will be called when the child emits an | ||
* output. If null, the child's outputs will be ignored. | ||
*/ | ||
@WorkflowComposable | ||
@Composable | ||
inline fun <ChildPropsT, ChildRenderingT> renderChild( | ||
workflow: Workflow<ChildPropsT, Nothing, ChildRenderingT>, | ||
props: ChildPropsT, | ||
): ChildRenderingT = renderChild(workflow, props, onOutput = null) | ||
|
||
/** | ||
* Renders a child [Workflow] from any [WorkflowComposable] (e.g. a [ComposeWorkflow.Rendering] or | ||
* [BaseRenderContext.renderComposable]) and returns its rendering. | ||
* | ||
* @param onOutput An optional function that, if non-null, will be called when the child emits an | ||
* output. If null, the child's outputs will be ignored. | ||
*/ | ||
@WorkflowComposable | ||
@Composable | ||
inline fun <ChildOutputT, ChildRenderingT> renderChild( | ||
workflow: Workflow<Unit, ChildOutputT, ChildRenderingT>, | ||
noinline onOutput: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT = renderChild(workflow, props = Unit, onOutput) | ||
|
||
/** | ||
* Renders a child [Workflow] from any [WorkflowComposable] (e.g. a [ComposeWorkflow.Rendering] or | ||
* [BaseRenderContext.renderComposable]) and returns its rendering. | ||
* | ||
* @param onOutput An optional function that, if non-null, will be called when the child emits an | ||
* output. If null, the child's outputs will be ignored. | ||
*/ | ||
@WorkflowComposable | ||
@Composable | ||
inline fun <ChildRenderingT> renderChild( | ||
workflow: Workflow<Unit, Nothing, ChildRenderingT>, | ||
): ChildRenderingT = renderChild(workflow, Unit, onOutput = null) | ||
|
||
@WorkflowComposable | ||
@Composable | ||
fun <ChildPropsT, ChildOutputT, ChildRenderingT> renderChild( | ||
workflow: ComposeWorkflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
handler: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT { | ||
val childRendering = remember { mutableStateOf<ChildRenderingT?>(null) } | ||
// Since this function returns a value, it can't restart without also restarting its parent. | ||
// IsolateRecomposeScope allows the subtree to restart and only restarts us if the rendering value | ||
// actually changed. | ||
RecomposeScopeIsolator( | ||
child = workflow, | ||
props = props, | ||
handler = handler, | ||
result = childRendering | ||
) | ||
@Suppress("UNCHECKED_CAST") | ||
return childRendering.value as ChildRenderingT | ||
} | ||
|
||
@Composable | ||
private fun <PropsT, OutputT, RenderingT> RecomposeScopeIsolator( | ||
child: ComposeWorkflow<PropsT, OutputT, RenderingT>, | ||
props: PropsT, | ||
handler: ((OutputT) -> Unit)?, | ||
result: MutableState<RenderingT>, | ||
) { | ||
result.value = child.Rendering(props, handler ?: {}) | ||
} |
32 changes: 32 additions & 0 deletions
32
...flow-core/src/commonMain/kotlin/com/squareup/workflow1/compose/WorkflowCompositionHost.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,32 @@ | ||
package com.squareup.workflow1.compose | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.ProvidableCompositionLocal | ||
import androidx.compose.runtime.Stable | ||
import androidx.compose.runtime.staticCompositionLocalOf | ||
import com.squareup.workflow1.Workflow | ||
|
||
// TODO @InternalWorkflowApi | ||
public val LocalWorkflowCompositionHost: ProvidableCompositionLocal<WorkflowCompositionHost> = | ||
staticCompositionLocalOf { error("No WorkflowCompositionHost provided.") } | ||
|
||
/** | ||
* Represents the owner of this [WorkflowComposable] composition. | ||
*/ | ||
// TODO move these into a separate, internal-only, implementation-depended-on module to hide from | ||
// consumers by default? | ||
// TODO @InternalWorkflowApi | ||
@Stable | ||
public interface WorkflowCompositionHost { | ||
|
||
/** | ||
* Renders a child [Workflow] and returns its rendering. See the top-level composable | ||
* [com.squareup.workflow1.compose.renderChild] for main documentation. | ||
*/ | ||
@Composable | ||
public fun <ChildPropsT, ChildOutputT, ChildRenderingT> renderChild( | ||
workflow: Workflow<ChildPropsT, ChildOutputT, ChildRenderingT>, | ||
props: ChildPropsT, | ||
onOutput: ((ChildOutputT) -> Unit)? | ||
): ChildRenderingT | ||
} |
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
52 changes: 52 additions & 0 deletions
52
...ow-runtime/src/commonMain/kotlin/com/squareup/workflow1/internal/ComposedWorkflowChild.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,52 @@ | ||
package com.squareup.workflow1.internal | ||
|
||
import androidx.compose.runtime.CompositionContext | ||
import androidx.compose.runtime.RecomposeScope | ||
import androidx.compose.runtime.RememberObserver | ||
import com.squareup.workflow1.WorkflowAction | ||
import com.squareup.workflow1.action | ||
import kotlinx.coroutines.CoroutineScope | ||
|
||
internal class ComposedWorkflowChild<ChildOutputT, ParentPropsT, ParentOutputT, ParentRenderingT>( | ||
compositeHashKey: Int, | ||
private val coroutineScope: CoroutineScope, | ||
private val compositionContext: CompositionContext, | ||
private val recomposeScope: RecomposeScope | ||
) : RememberObserver { | ||
val workflowKey: String = "composed-workflow:${compositeHashKey.toString(radix = 16)}" | ||
private var disposed = false | ||
|
||
var onOutput: ((ChildOutputT) -> Unit)? = null | ||
val handler: (ChildOutputT) -> WorkflowAction<ParentPropsT, ParentOutputT, ParentRenderingT> = | ||
{ output -> | ||
action(workflowKey) { | ||
// This action is being applied to the composition host workflow, which we don't want to | ||
// update at all. | ||
// The onOutput callback instead will update any compose snapshot state required. | ||
// Technically we could probably invoke it directly from the handler, not wait until the | ||
// queued action is processed, but this ensures consistency with the rest of the workflow | ||
// runtime: the callback won't fire before other callbacks ahead in the queue. | ||
// We check disposed since a previous update may have caused a recomposition that removed | ||
// this child from composition and since it doesn't have its own channel, we have to no-op. | ||
if (!disposed) { | ||
onOutput?.invoke(output) | ||
} | ||
|
||
// TODO After invoking callback, send apply notifications and check if composition has any | ||
// invalidations. Iff it does, then mark the current workflow node as needing re-render | ||
// regardless of state change. | ||
} | ||
} | ||
|
||
override fun onAbandoned() { | ||
onForgotten() | ||
} | ||
|
||
override fun onRemembered() { | ||
} | ||
|
||
override fun onForgotten() { | ||
disposed = true | ||
TODO("notify parent that we're gone") | ||
} | ||
} |
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
Oops, something went wrong.