Skip to content

Commit fd73a0b

Browse files
reshape the api to infer types better
1 parent 4198e39 commit fd73a0b

File tree

3 files changed

+151
-92
lines changed

3 files changed

+151
-92
lines changed

samples/containers/thingy/src/main/java/com/squareup/sample/thingy/BackStackWorkflow.kt

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ import kotlinx.coroutines.CoroutineScope
88
import kotlinx.coroutines.flow.Flow
99
import kotlinx.coroutines.flow.StateFlow
1010
import kotlinx.coroutines.flow.flowOf
11-
import kotlin.experimental.ExperimentalTypeInference
1211

1312
/**
1413
* Creates a [BackStackWorkflow]. See the docs on [BackStackWorkflow.runBackStack] for more
1514
* information about what [block] can do.
1615
*/
1716
public inline fun <PropsT, OutputT> backStackWorkflow(
18-
crossinline block: suspend BackStackScope<OutputT>.(props: StateFlow<PropsT>) -> Unit
17+
crossinline block: suspend BackStackScope.(
18+
props: StateFlow<PropsT>,
19+
emitOutput: (OutputT) -> Unit
20+
) -> Unit
1921
): Workflow<PropsT, OutputT, BackStackScreen<Screen>> =
2022
object : BackStackWorkflow<PropsT, OutputT>() {
21-
override suspend fun BackStackScope<OutputT>.runBackStack(props: StateFlow<PropsT>) {
22-
block(props)
23+
override suspend fun BackStackScope.runBackStack(
24+
props: StateFlow<PropsT>,
25+
emitOutput: (OutputT) -> Unit
26+
) {
27+
block(props, emitOutput)
2328
}
2429
}
2530

@@ -35,7 +40,7 @@ public abstract class BackStackWorkflow<PropsT, OutputT> :
3540

3641
/**
3742
* Show renderings by calling [BackStackScope.showScreen]. Show child workflows by calling
38-
* [BackStackScope.showWorkflow]. Emit outputs by calling [BackStackScope.emitOutput].
43+
* [BackStackScope.showWorkflow]. Emit outputs by calling [emitOutput].
3944
*
4045
* # Examples
4146
*
@@ -86,7 +91,10 @@ public abstract class BackStackWorkflow<PropsT, OutputT> :
8691
* `finishWith` to replace itself with `child3`. `child3` can also call `goBack` to show `child`
8792
* again.
8893
*/
89-
abstract suspend fun BackStackScope<OutputT>.runBackStack(props: StateFlow<PropsT>)
94+
abstract suspend fun BackStackScope.runBackStack(
95+
props: StateFlow<PropsT>,
96+
emitOutput: (OutputT) -> Unit
97+
)
9098

9199
final override fun asStatefulWorkflow():
92100
StatefulWorkflow<PropsT, *, OutputT, BackStackScreen<Screen>> =
@@ -97,23 +105,19 @@ public abstract class BackStackWorkflow<PropsT, OutputT> :
97105
annotation class BackStackWorkflowDsl
98106

99107
@BackStackWorkflowDsl
100-
public sealed interface BackStackScope<OutputT> : CoroutineScope {
101-
102-
/**
103-
* Emits an output to the [backStackWorkflow]'s parent.
104-
*/
105-
fun emitOutput(output: OutputT)
108+
public sealed interface BackStackParentScope {
106109

107110
/**
108111
* Starts rendering [workflow] and pushes its rendering onto the top of the backstack.
109112
*
110113
* Whenever [workflow] emits an output, [onOutput] is launched into a new coroutine. If one call
111114
* doesn't finish before another output is emitted, multiple callbacks can run concurrently.
112115
*
113-
* When [onOutput] calls [BackStackNestedScope.finishWith], this workflow stops rendering, its
114-
* rendering is removed from the backstack, and any running output handlers are cancelled.
116+
* When [onOutput] returns a value, this workflow stops rendering, its rendering is removed from
117+
* the backstack, and any running output handlers are cancelled. The calling coroutine is resumed
118+
* with the value.
115119
*
116-
* When [onOutput] calls [BackStackNestedScope.goBack], if this [showWorkflow] call is nested in
120+
* When [onOutput] calls [BackStackWorkflowScope.goBack], if this [showWorkflow] call is nested in
117121
* another, then this workflow will stop rendering, any of its still-running output handlers will
118122
* be cancelled, and the output handler that called this [showWorkflow] will be cancelled.
119123
* If this is a top-level workflow in the [BackStackWorkflow], the whole
@@ -130,32 +134,27 @@ public sealed interface BackStackScope<OutputT> : CoroutineScope {
130134
// TODO revert this back to a single value – can use the same trick to update props as for
131135
// emitting new screens.
132136
props: Flow<ChildPropsT>,
133-
onOutput: suspend BackStackNestedScope<OutputT, R>.(output: ChildOutputT) -> Unit
137+
onOutput: suspend BackStackWorkflowScope.(output: ChildOutputT) -> R
134138
): R
135139

136140
/**
137-
* Shows the screen produced by [screenFactory]. Suspends until [BackStackNestedScope.finishWith]
138-
* or [BackStackNestedScope.goBack] is called.
141+
* Shows the screen produced by [screenFactory]. Suspends untilBackStackNestedScope.goBack] is
142+
* called.
139143
*/
140144
suspend fun <R> showScreen(
141-
screenFactory: BackStackNestedScope<OutputT, R>.() -> Screen
145+
screenFactory: BackStackScreenScope<R>.() -> Screen
142146
): R
143147
}
144148

149+
@BackStackWorkflowDsl
150+
public sealed interface BackStackScope : BackStackParentScope, CoroutineScope
151+
145152
/**
146153
* Scope receiver used for all [showWorkflow] calls. This has all the capabilities of
147-
* [BackStackScope] with the additional ability to [finish][finishWith] a nested workflow or
148-
* [go back][goBack] to its outer workflow.
154+
* [BackStackScope] with the additional ability to [go back][goBack] to its outer workflow.
149155
*/
150156
@BackStackWorkflowDsl
151-
public sealed interface BackStackNestedScope<OutputT, R> : BackStackScope<OutputT> {
152-
153-
/**
154-
* Causes the [showWorkflow] call that ran the output handler that was passed this scope to return
155-
* [value] and cancels any output handlers still running for that workflow. The workflow is
156-
* removed from the stack and will no longer be rendered.
157-
*/
158-
suspend fun finishWith(value: R): Nothing
157+
public sealed interface BackStackWorkflowScope : BackStackScope {
159158

160159
/**
161160
* Removes all workflows started by the parent workflow's handler that invoked this [showWorkflow]
@@ -165,22 +164,22 @@ public sealed interface BackStackNestedScope<OutputT, R> : BackStackScope<Output
165164
suspend fun goBack(): Nothing
166165
}
167166

168-
@OptIn(ExperimentalTypeInference::class)
169-
public suspend inline fun <OutputT, ChildOutputT, R> BackStackScope<OutputT>.showWorkflow(
167+
@BackStackWorkflowDsl
168+
public sealed interface BackStackScreenScope<R> : BackStackScope {
169+
fun continueWith(value: R)
170+
fun goBack()
171+
}
172+
173+
public suspend inline fun <ChildOutputT, R> BackStackParentScope.showWorkflow(
170174
workflow: Workflow<Unit, ChildOutputT, Screen>,
171-
@BuilderInference noinline onOutput: suspend BackStackNestedScope<OutputT, R>.(output: ChildOutputT) -> Unit
175+
noinline onOutput: suspend BackStackWorkflowScope.(output: ChildOutputT) -> R
172176
): R = showWorkflow(workflow, props = flowOf(Unit), onOutput)
173177

174-
// public suspend inline fun <OutputT, ChildOutputT> BackStackScope<OutputT>.showWorkflow(
175-
// workflow: Workflow<Unit, ChildOutputT, Screen>,
176-
// noinline onOutput: suspend BackStackNestedScope<OutputT, Unit>.(output: ChildOutputT) -> Unit
177-
// ): Unit = showWorkflow(workflow, props = flowOf(Unit), onOutput)
178-
179-
public suspend inline fun <ChildPropsT> BackStackScope<*>.showWorkflow(
178+
public suspend inline fun <ChildPropsT> BackStackParentScope.showWorkflow(
180179
workflow: Workflow<ChildPropsT, Nothing, Screen>,
181180
props: Flow<ChildPropsT>,
182181
): Nothing = showWorkflow(workflow, props = props) { error("Cannot call") }
183182

184-
public suspend inline fun BackStackScope<*>.showWorkflow(
183+
public suspend inline fun BackStackParentScope.showWorkflow(
185184
workflow: Workflow<Unit, Nothing, Screen>,
186185
): Nothing = showWorkflow(workflow, props = flowOf(Unit)) { error("Cannot call") }

0 commit comments

Comments
 (0)