Skip to content

Commit c3b70ae

Browse files
Add some comments.
1 parent 159aae8 commit c3b70ae

File tree

6 files changed

+53
-22
lines changed

6 files changed

+53
-22
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,43 @@ import androidx.compose.runtime.currentCompositeKeyHash
55
import com.squareup.workflow1.ActionProcessingResult
66
import com.squareup.workflow1.TreeSnapshot
77
import com.squareup.workflow1.Workflow
8+
import com.squareup.workflow1.WorkflowIdentifier
9+
import com.squareup.workflow1.compose.ComposeWorkflow
10+
import com.squareup.workflow1.internal.AbstractWorkflowNode
811
import com.squareup.workflow1.internal.WorkflowNodeId
912
import kotlinx.coroutines.selects.SelectBuilder
1013

14+
/**
15+
* Represents a workflow inside a [ComposeWorkflowNodeAdapter], either another
16+
* [ComposeWorkflow]/[ComposeWorkflowChildNode], or a
17+
* [Workflow]/[TraditionalWorkflowAdapterChildNode].
18+
*/
1119
internal interface ComposeChildNode<PropsT, OutputT, RenderingT> {
1220

21+
/** See [AbstractWorkflowNode.id]. */
1322
val id: WorkflowNodeId
1423

24+
/**
25+
* Called during workflow render passes to produce the rendering for this workflow.
26+
*
27+
* The compose analog to [AbstractWorkflowNode.render].
28+
*/
1529
@Composable fun produceRendering(
1630
workflow: Workflow<PropsT, OutputT, RenderingT>,
1731
props: PropsT
1832
): RenderingT
1933

34+
/** See [AbstractWorkflowNode.snapshot]. */
2035
fun snapshot(): TreeSnapshot
2136

37+
/** See [AbstractWorkflowNode.onNextAction]. */
2238
fun onNextAction(selector: SelectBuilder<ActionProcessingResult>): Boolean
2339
}
2440

41+
/**
42+
* Returns a stable key identifying a call to [ComposeWorkflowChildNode.renderChild] in the
43+
* composition, suitable for use with [WorkflowIdentifier].
44+
*/
2545
@OptIn(ExperimentalStdlibApi::class)
2646
@Composable
2747
internal fun rememberChildRenderKey(): String {

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

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ import kotlin.coroutines.CoroutineContext
4545
private const val OUTPUT_QUEUE_LIMIT = 1_000
4646

4747
/**
48-
* Representation and implementation of a single [ComposeWorkflow].
48+
* Representation and implementation of a single [ComposeWorkflow] inside a
49+
* [ComposeWorkflowNodeAdapter].
4950
*/
5051
@OptIn(WorkflowExperimentalApi::class)
5152
internal class ComposeWorkflowChildNode<PropsT, OutputT, RenderingT>(
@@ -77,8 +78,10 @@ internal class ComposeWorkflowChildNode<PropsT, OutputT, RenderingT>(
7778
override val sessionId: Long = idCounter.createId()
7879

7980
private var lastProps by mutableStateOf(initialProps)
80-
private val saveableStateRegistry: SaveableStateRegistry
81+
82+
/** This does not need to be a snapshot state object, it's only set again by [snapshot]. */
8183
private var snapshotCache = snapshot?.childTreeSnapshots
84+
private val saveableStateRegistry: SaveableStateRegistry
8285
private val childNodes = mutableVectorOf<ComposeChildNode<*, *, *>>()
8386

8487
private val outputsChannel = Channel<OutputT>(capacity = OUTPUT_QUEUE_LIMIT)
@@ -121,7 +124,7 @@ internal class ComposeWorkflowChildNode<PropsT, OutputT, RenderingT>(
121124

122125
val workflowSnapshot = snapshot?.workflowSnapshot
123126
var restoredRegistry: SaveableStateRegistry? = null
124-
// Don't care about this return value, our state is separate.
127+
// Don't care if the interceptor returns a state value, our state is stored in the composition.
125128
interceptor.onInitialState(
126129
props = initialProps,
127130
snapshot = workflowSnapshot,
@@ -273,7 +276,7 @@ internal class ComposeWorkflowChildNode<PropsT, OutputT, RenderingT>(
273276
} else {
274277
// We need to be able to explicitly request recomposition of this composable when an action
275278
// cascade results in this child changing state because traditional workflows don't know
276-
// about Compose.
279+
// about Compose. See comment in acceptChildActionResult for more info.
277280
val recomposeScope = currentRecomposeScope
278281
remember {
279282
TraditionalWorkflowAdapterChildNode(
@@ -288,6 +291,11 @@ internal class ComposeWorkflowChildNode<PropsT, OutputT, RenderingT>(
288291
interceptor = interceptor,
289292
idCounter = idCounter,
290293
acceptChildActionResult = { actionApplied ->
294+
// If this child needs to be re-rendered on the next render pass and there are no other
295+
// state changes in the compose runtime during this action cascade, if we don't
296+
// explicitly invalidate the recompose scope then the recomposer will think it doesn't
297+
// have anything to do and not recompose us, which means we wouldn't have a chance to
298+
// re-render the traditional workflow.
291299
if (actionApplied.stateChanged) {
292300
recomposeScope.invalidate()
293301
}
@@ -334,20 +342,7 @@ internal class ComposeWorkflowChildNode<PropsT, OutputT, RenderingT>(
334342
): ActionProcessingResult {
335343
log("handling child output: $appliedActionFromChild")
336344
val outputFromChild = appliedActionFromChild.output
337-
// if (outputFromChild == null || onOutput == null) {
338-
// // The child didn't actually emit anything or we don't care, so we don't need to
339-
// // propagate anything to the parent. We halt the action cascade by simply returning
340-
// // here without calling emitAppliedActionToParent.
341-
// //
342-
// // NOTE: SubtreeManager has an additional case for PARTIAL_TREE_RENDERING, but we
343-
// // can just assume that using ComposeWorkflow at all implies that optimization.
344-
// //
345-
// // If our child state changed, we need to report that ours did too, as per the
346-
// // comment in StatefulWorkflowNode.applyAction.
347-
// return appliedActionFromChild.withOutput(null)
348-
// }
349-
350-
// The child DID emit an output, so we need to call our handler, which will zero or
345+
// The child emitted an output, so we need to call our handler, which will zero or
351346
// more of two things: (1) change our state, (2) emit an output.
352347

353348
// If this workflow calls emitOutput while running child output handler, we don't want
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package com.squareup.workflow1.internal.compose
22

3+
import com.squareup.workflow1.WorkflowInterceptor
4+
import com.squareup.workflow1.compose.ComposeWorkflow
5+
36
/**
4-
* Fake state object passed to WorkflowInterceptors.
7+
* Fake state object passed to [WorkflowInterceptor]s as the state for [ComposeWorkflow]s.
8+
*
9+
* If we need interceptors to be able to identify compose workflows, we can just make this public.
510
*/
611
internal object ComposeWorkflowState

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ internal class RecomposerDriver(
100100
}
101101

102102
suspend fun runRecomposeAndApplyChanges() {
103+
// Note: This context is _only_ used for the actual recompose loop. Everything inside the
104+
// composition (rememberCoroutineScope, LaunchedEffects, etc) will NOT see these, and will see
105+
// only whatever context was passed into the Recomposer's constructor (plus the stuff it adds
106+
// to that context itself, like the BroadcastFrameClock).
103107
withContext(dispatcher + frameClock) {
104108
recomposer.runRecomposeAndApplyChanges()
105109
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import com.squareup.workflow1.internal.createWorkflowNode
1717
import kotlinx.coroutines.selects.SelectBuilder
1818
import kotlin.coroutines.CoroutineContext
1919

20+
/**
21+
* Entry point back into the Workflow runtime from a Compose runtime (i.e. a
22+
* [ComposeWorkflowNodeAdapter]).
23+
*/
2024
internal class TraditionalWorkflowAdapterChildNode<PropsT, OutputT, RenderingT>(
2125
id: WorkflowNodeId,
2226
workflow: Workflow<PropsT, OutputT, RenderingT>,

workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/internal/compose/coroutines/Synchronization.kt

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

3-
import kotlinx.coroutines.channels.Channel
3+
import kotlinx.coroutines.channels.SendChannel
44

55
internal expect class Lock()
66

77
internal expect inline fun <R> Lock.withLock(block: () -> R): R
88

9-
// TODO pull into separate file
10-
internal fun <T> Channel<T>.requireSend(element: T) {
9+
/**
10+
* Tries to send [element] to this channel and throws an [IllegalStateException] if the channel is
11+
* full or closed.
12+
*/
13+
internal fun <T> SendChannel<T>.requireSend(element: T) {
1114
val result = trySend(element)
1215
if (result.isClosed) {
1316
throw IllegalStateException(

0 commit comments

Comments
 (0)