Skip to content

Conversation

zach-klippenstein
Copy link
Collaborator

@zach-klippenstein zach-klippenstein commented Aug 28, 2025

Inspired by the end of @swankjesse's talk Coroutines Party Tricks. Very rough sketch, missing the auto-loading screen feature, mostly just because I ran out of time. See the kdoc on BackStackWorkflow for more.

enum class MyOutputs {
  Back,
  Done,
}

data class RetryScreen(
  val message: String,
  val onRetryClicked: () -> Unit,
  val onCancelClicked: () -> Unit,
) : Screen

data object LoadingScreen : Screen

class MyWorkflow(
  private val child1: Workflow<Unit, String, Screen>,
  private val child2: Workflow<Unit, String, Screen>,
  private val child3: Workflow<String, Nothing, Screen>,
  private val networkCall: suspend (String) -> String
) : BackStackWorkflow<String, MyOutputs>() {

  override suspend fun BackStackScope.runBackStack(
    props: StateFlow<String>,
    emitOutput: (MyOutputs) -> Unit
  ) {
    // Step 1
    showWorkflow(child1) { output ->
      when (output) {
        "back" -> emitOutput(MyOutputs.Back)
        "next" -> {
          // Step 2
          val childResult = showWorkflow(child2) { output ->
              // Removes child2 from the stack, cancels the output handler from step 1, and just
              // leaves child1 rendering.
            if (output == "back") goBack()
            output
          }

          // Step 3 – make a network call, showing a retry screen if it fails. If the user cancels
          // instead of retrying, we go back to showing child1.
          val networkResult = networkCallWithRetry(childResult)

          // Step 4: Show a workflow for 3 seconds then finish.
          launch {
            delay(3.seconds)
            emitOutput(MyOutputs.Done)
          }
          showWorkflow(child3, flowOf(networkResult))
        }

        else -> error("Unexpected output: $output")
      }
    }
  }

  override fun createIdleScreen(): Screen = LoadingScreen

  private suspend fun BackStackParentScope.networkCallWithRetry(
    request: String
  ): String {
    // TODO: Show a loading screen automatically.
    var networkResult = networkCall(request)
    while (networkResult == "failure") {
      showScreen {
        RetryScreen(
          message = networkResult,
          onRetryClicked = { continueWith(Unit) },
          // Go back to showing child1.
          onCancelClicked = { goBack() }
        )
      }
      networkResult = networkCall(request)
    }
    return networkResult
  }
}

@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch 2 times, most recently from e96cb32 to 4f7a1ba Compare August 28, 2025 04:07
@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch from ce573a2 to c027265 Compare August 28, 2025 04:20
@zach-klippenstein zach-klippenstein changed the title wip trying to implement jwilson's idea in workflow wip trying to implement "person as a function" in workflow Aug 28, 2025
@swankjesse
Copy link
Contributor

This is exciting!

In case you missed it, the toy version of this from the talk is here:
https://github.com/swankjesse/coroutines-party-tricks/blob/main/code/lib/src/main/kotlin/com/publicobject/tricks/navigation/RealDisplay.kt

@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch from 1981666 to fd73a0b Compare August 28, 2025 18:41
@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch 2 times, most recently from 2b4104a to 5fc252c Compare August 28, 2025 19:56
@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch from 5fc252c to 1a5bdeb Compare August 28, 2025 19:56
* the parent passes a new props, the `showScreen` call is cancelled and called again with the
* new props, replacing the old instance of `MyScreen` in the backstack with a new one. Since
* both instances of `MyScreen` are compatible, this is not a navigation event but just updates
* the `MyScreen` view factory.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So cool

@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch from df7d502 to d456f68 Compare August 29, 2025 19:41
@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch 5 times, most recently from ddc6b11 to 9788c79 Compare September 1, 2025 20:36
@zach-klippenstein zach-klippenstein force-pushed the zachklipp/coroutine-stepper branch from 9788c79 to 7bece0b Compare September 1, 2025 20:39
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants