Skip to content

Commit d24e37a

Browse files
sketch out CompositionLocal propagation
1 parent 409fd96 commit d24e37a

File tree

3 files changed

+31
-13
lines changed

3 files changed

+31
-13
lines changed

workflow-core/src/commonMain/kotlin/com/squareup/workflow1/compose/WorkflowComposables.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ fun <ChildPropsT, ChildOutputT, ChildRenderingT> renderWorkflow(
4646
}
4747
}
4848

49+
// region Convenience overloads for specific type arguments
50+
4951
/**
5052
* Renders a child [Workflow] that has no output (`OutputT` is [Nothing]).
5153
* For more documentation see [renderWorkflow].
@@ -81,3 +83,5 @@ inline fun <ChildOutputT, ChildRenderingT> renderWorkflow(
8183
inline fun <ChildRenderingT> renderWorkflow(
8284
workflow: Workflow<Unit, Nothing, ChildRenderingT>,
8385
): ChildRenderingT = renderWorkflow(workflow, Unit, onOutput = null)
86+
87+
// endregion

workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/internal/ComposedWorkflowChild.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.squareup.workflow1.internal
22

3-
import androidx.compose.runtime.CompositionContext
3+
import androidx.compose.runtime.CompositionLocalContext
44
import androidx.compose.runtime.RecomposeScope
55
import androidx.compose.runtime.RememberObserver
66
import com.squareup.workflow1.WorkflowAction
@@ -10,8 +10,8 @@ import kotlinx.coroutines.CoroutineScope
1010
internal class ComposedWorkflowChild<ChildOutputT, ParentPropsT, ParentOutputT, ParentRenderingT>(
1111
compositeHashKey: Int,
1212
private val coroutineScope: CoroutineScope,
13-
private val compositionContext: CompositionContext,
14-
private val recomposeScope: RecomposeScope
13+
private val recomposeScope: RecomposeScope,
14+
private val localsContext: CompositionLocalContext,
1515
) : RememberObserver {
1616
val workflowKey: String = "composed-workflow:${compositeHashKey.toString(radix = 16)}"
1717
private var disposed = false

workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/internal/SubtreeManager.kt

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ package com.squareup.workflow1.internal
44

55
import androidx.compose.runtime.Composable
66
import androidx.compose.runtime.Composition
7+
import androidx.compose.runtime.CompositionLocalContext
78
import androidx.compose.runtime.CompositionLocalProvider
89
import androidx.compose.runtime.MonotonicFrameClock
910
import androidx.compose.runtime.Recomposer
1011
import androidx.compose.runtime.currentCompositeKeyHash
12+
import androidx.compose.runtime.currentCompositionLocalContext
1113
import androidx.compose.runtime.currentRecomposeScope
12-
import androidx.compose.runtime.key
1314
import androidx.compose.runtime.mutableStateOf
1415
import androidx.compose.runtime.remember
15-
import androidx.compose.runtime.rememberCompositionContext
1616
import androidx.compose.runtime.rememberCoroutineScope
1717
import androidx.compose.runtime.saveable.LocalSaveableStateRegistry
1818
import androidx.compose.runtime.saveable.SaveableStateRegistry
@@ -176,6 +176,7 @@ internal class SubtreeManager<PropsT, StateT, OutputT>(
176176
val recomposer = Recomposer(coroutineContext)
177177
val composition = Composition(UnitApplier, recomposer)
178178
val saveableStateRegistry: SaveableStateRegistry // TODO
179+
val localsContext: CompositionLocalContext? // TODO
179180

180181
// TODO I think we need more than a simple UNDISPATCHED start to make this work – we have to
181182
// pump the dispatcher until the composition is finished.
@@ -188,7 +189,7 @@ internal class SubtreeManager<PropsT, StateT, OutputT>(
188189
}
189190

190191
val rendering = mutableStateOf<ChildRenderingT?>(null)
191-
composition.setContent {
192+
val wrappedContent = @Composable {
192193
CompositionLocalProvider(
193194
LocalWorkflowCompositionHost provides this,
194195
LocalSaveableStateRegistry provides saveableStateRegistry,
@@ -197,6 +198,19 @@ internal class SubtreeManager<PropsT, StateT, OutputT>(
197198
}
198199
}
199200

201+
composition.setContent {
202+
// Must provide the locals from the parent composition first so we can override the ones we
203+
// need. If it's null then there's no parent, but the CompositionLocalProvider API has no nice
204+
// way to pass nothing in this overload. I believe it's safe to actually call content through
205+
// two different code paths because whether there's a parent composition cannot change for an
206+
// existing workflow session – they can't move.
207+
if (localsContext == null) {
208+
wrappedContent()
209+
} else {
210+
CompositionLocalProvider(localsContext, wrappedContent)
211+
}
212+
}
213+
200214
// TODO prime the first frame to generate the initial rendering
201215

202216
@Suppress("UNCHECKED_CAST")
@@ -218,14 +232,14 @@ internal class SubtreeManager<PropsT, StateT, OutputT>(
218232

219233
val key = currentCompositeKeyHash
220234
val coroutineScope = rememberCoroutineScope()
221-
val compositionContext = rememberCompositionContext()
235+
val localsContext = currentCompositionLocalContext
222236
val recomposeScope = currentRecomposeScope
223237
val child = remember {
224-
ComposedWorkflowChild(
225-
key,
226-
coroutineScope,
227-
compositionContext,
228-
recomposeScope
238+
ComposedWorkflowChild<ChildOutputT, PropsT, OutputT, Unit /* TODO */>(
239+
compositeHashKey = key,
240+
coroutineScope = coroutineScope,
241+
recomposeScope = recomposeScope,
242+
localsContext = localsContext,
229243
)
230244
}
231245
child.onOutput = onOutput
@@ -236,7 +250,7 @@ internal class SubtreeManager<PropsT, StateT, OutputT>(
236250
// tracking. After the composition frame is finished, we can update the WorkflowNode state as
237251
// required.
238252
// TODO don't call render, it's not powerful enough for what we need.
239-
render(
253+
return render(
240254
child = workflow,
241255
props = props,
242256
key = child.workflowKey,

0 commit comments

Comments
 (0)