Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not change Workflow API for COmpose #833

Draft
wants to merge 1 commit into
base: sedwards/compose-with-return-rendering
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -235,127 +235,127 @@ class PerformancePoemWorkflow(
}
}
}

@Composable
override fun Rendering(
renderProps: Poem,
renderState: State,
context: RenderContext,
): OverviewDetailScreen {
when (renderState) {
Initializing -> {
// Again, the entire `Initializing` state is a smell, which is most obvious from the
// use of `Worker.from { Unit }`. A Worker doing no work and only shuttling the state
// along is usually the sign you have an extraneous state that can be collapsed!
// Don't try this at home.
context.runningWorker(
Worker.from {
isLoading.value = true
},
"initializing"
) {
action {
isLoading.value = false
state = Selected(NO_SELECTED_STANZA)
}
}
return OverviewDetailScreen(overviewRendering = BackStackScreen(BlankScreen))
}
else -> {
val (stanzaIndex, currentStateIsLoading, repeat) = when (renderState) {
is ComplexCall -> Triple(renderState.payload, true, renderState.repeater)
is Selected -> Triple(renderState.stanzaIndex, false, 0)
Initializing -> throw IllegalStateException("No longer initializing.")
}

if (currentStateIsLoading) {
if (repeat > 0) {
// Running a flow that emits 'repeat' number of times
context.runningWorker(
flow {
while (true) {
// As long as this Worker is running we want to be emitting values.
delay(2)
emit(repeat)
}
}.asTraceableWorker("EventRepetition")
) {
action {
(state as? ComplexCall)?.let { currentState ->
// Still repeating the complex call
state = ComplexCall(
payload = currentState.payload,
repeater = (currentState.repeater - 1).coerceAtLeast(0)
)
}
}
}
} else {
context.runningWorker(
worker = TraceableWorker.from("PoemLoading") {
isLoading.value = true
delay(simulatedPerfConfig.complexityDelay)
// No Output for Worker is necessary because the selected index
// is already in the state.
}
) {
action {
isLoading.value = false
(state as? ComplexCall)?.let { currentState ->
state = Selected(currentState.payload)
}
}
}
}
}

val stanzaListOverview = context.ChildRendering(
StanzaListWorkflow,
StanzaListWorkflow.Props(
poem = renderProps,
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
),
key = "",
) { selected ->
HandleStanzaListOutput(simulatedPerfConfig, selected)
}
.copy(selection = stanzaIndex)

if (stanzaIndex != NO_SELECTED_STANZA) {
val stackedStanzas = renderProps.stanzas.subList(0, stanzaIndex + 1)
.mapIndexed { index, _ ->
context.ChildRendering(
StanzaWorkflow,
Props(
poem = renderProps,
index = index,
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
),
key = "$index",
) {
when (it) {
CloseStanzas -> ClearSelection(simulatedPerfConfig)
ShowPreviousStanza -> SelectPrevious(simulatedPerfConfig)
ShowNextStanza -> SelectNext(simulatedPerfConfig)
}
}
}.toBackStackScreen<Screen>()

return OverviewDetailScreen(
overviewRendering = BackStackScreen(stanzaListOverview),
detailRendering = stackedStanzas
)
}

return OverviewDetailScreen(
overviewRendering = BackStackScreen(stanzaListOverview),
selectDefault = {
context.actionSink.send(HandleStanzaListOutput(simulatedPerfConfig, 0))
}
)
}
}
}
//
// @Composable
// override fun Rendering(
// renderProps: Poem,
// renderState: State,
// context: RenderContext,
// ): OverviewDetailScreen {
// when (renderState) {
// Initializing -> {
// // Again, the entire `Initializing` state is a smell, which is most obvious from the
// // use of `Worker.from { Unit }`. A Worker doing no work and only shuttling the state
// // along is usually the sign you have an extraneous state that can be collapsed!
// // Don't try this at home.
// context.runningWorker(
// Worker.from {
// isLoading.value = true
// },
// "initializing"
// ) {
// action {
// isLoading.value = false
// state = Selected(NO_SELECTED_STANZA)
// }
// }
// return OverviewDetailScreen(overviewRendering = BackStackScreen(BlankScreen))
// }
// else -> {
// val (stanzaIndex, currentStateIsLoading, repeat) = when (renderState) {
// is ComplexCall -> Triple(renderState.payload, true, renderState.repeater)
// is Selected -> Triple(renderState.stanzaIndex, false, 0)
// Initializing -> throw IllegalStateException("No longer initializing.")
// }
//
// if (currentStateIsLoading) {
// if (repeat > 0) {
// // Running a flow that emits 'repeat' number of times
// context.runningWorker(
// flow {
// while (true) {
// // As long as this Worker is running we want to be emitting values.
// delay(2)
// emit(repeat)
// }
// }.asTraceableWorker("EventRepetition")
// ) {
// action {
// (state as? ComplexCall)?.let { currentState ->
// // Still repeating the complex call
// state = ComplexCall(
// payload = currentState.payload,
// repeater = (currentState.repeater - 1).coerceAtLeast(0)
// )
// }
// }
// }
// } else {
// context.runningWorker(
// worker = TraceableWorker.from("PoemLoading") {
// isLoading.value = true
// delay(simulatedPerfConfig.complexityDelay)
// // No Output for Worker is necessary because the selected index
// // is already in the state.
// }
// ) {
// action {
// isLoading.value = false
// (state as? ComplexCall)?.let { currentState ->
// state = Selected(currentState.payload)
// }
// }
// }
// }
// }
//
// val stanzaListOverview = context.ChildRendering(
// StanzaListWorkflow,
// StanzaListWorkflow.Props(
// poem = renderProps,
// eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
// ),
// key = "",
// ) { selected ->
// HandleStanzaListOutput(simulatedPerfConfig, selected)
// }
// .copy(selection = stanzaIndex)
//
// if (stanzaIndex != NO_SELECTED_STANZA) {
// val stackedStanzas = renderProps.stanzas.subList(0, stanzaIndex + 1)
// .mapIndexed { index, _ ->
// context.ChildRendering(
// StanzaWorkflow,
// Props(
// poem = renderProps,
// index = index,
// eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
// ),
// key = "$index",
// ) {
// when (it) {
// CloseStanzas -> ClearSelection(simulatedPerfConfig)
// ShowPreviousStanza -> SelectPrevious(simulatedPerfConfig)
// ShowNextStanza -> SelectNext(simulatedPerfConfig)
// }
// }
// }.toBackStackScreen<Screen>()
//
// return OverviewDetailScreen(
// overviewRendering = BackStackScreen(stanzaListOverview),
// detailRendering = stackedStanzas
// )
// }
//
// return OverviewDetailScreen(
// overviewRendering = BackStackScreen(stanzaListOverview),
// selectDefault = {
// context.actionSink.send(HandleStanzaListOutput(simulatedPerfConfig, 0))
// }
// )
// }
// }
// }

override fun snapshotState(state: State): Snapshot? = null

Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ include(
":workflow-config:config-android",
":workflow-config:config-jvm",
":workflow-core",
":workflow-core-compose",
":workflow-runtime",
":workflow-rx2",
":workflow-testing",
Expand Down
35 changes: 35 additions & 0 deletions workflow-core-compose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
plugins {
`kotlin-multiplatform`
published
id("app.cash.molecule")
}

kotlin {
jvm { withJava() }
// TODO: No native targets yet for Molecule until Compose 1.2.0 available in JB KMP runtime.
// ios()

sourceSets {
all {
languageSettings.apply {
optIn("kotlin.RequiresOptIn")
}
}
val commonMain by getting {
dependencies {
api(project(":workflow-core"))
api(libs.kotlin.jdk6)
api(libs.compose.runtime)
api(libs.kotlinx.coroutines.core)
implementation(libs.molecule.runtime)
}
}
val commonTest by getting {
dependencies {
implementation(libs.kotlinx.atomicfu)
implementation(libs.kotlinx.coroutines.test.common)
implementation(libs.kotlin.test.jdk)
}
}
}
}
3 changes: 3 additions & 0 deletions workflow-core-compose/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_ARTIFACT_ID=workflow-core-compose
POM_NAME=Workflow Core Compose
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.squareup.workflow1.compose

import androidx.compose.runtime.Composable
import com.squareup.workflow1.Snapshot
import com.squareup.workflow1.StatefulWorkflow

interface StatefulComposeWorkflow<in PropsT, StateT, out OutputT, out RenderingT> {

@Composable
public fun Rendering(
renderProps: PropsT,
renderState: StateT,
context: StatefulWorkflow<PropsT, StateT, OutputT, RenderingT>.RenderContext,
): RenderingT
}

fun <PropsT, StateT, OutputT, RenderingT>
StatefulWorkflow<PropsT, StateT, OutputT, RenderingT>.asComposeWorkflow():
StatefulComposeWorkflow<PropsT, StateT, OutputT, RenderingT> {
val originalWorkflow = this
return object : StatefulComposeWorkflow<PropsT, StateT, OutputT, RenderingT>,
StatefulWorkflow<PropsT, StateT, OutputT, RenderingT>() {

@Composable
override fun Rendering(
renderProps: PropsT,
renderState: StateT,
context: RenderContext
): RenderingT = render(renderProps, renderState, context)

override fun initialState(
props: PropsT,
snapshot: Snapshot?
): StateT = originalWorkflow.initialState(props, snapshot)

override fun snapshotState(state: StateT): Snapshot? = originalWorkflow.snapshotState(state)

override fun render(
renderProps: PropsT,
renderState: StateT,
context: RenderContext
): RenderingT = originalWorkflow.render(renderProps, renderState, context)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ public abstract class StatelessWorkflow<in PropsT, out OutputT, out RenderingT>
@Suppress("UNCHECKED_CAST")
private val statefulWorkflow: StatefulWorkflow<PropsT, Unit, OutputT, RenderingT>
get() {
@Suppress("LocalVariableName")
val Rendering: @Composable BaseRenderContext<PropsT, Unit, OutputT>
.(props: PropsT, _: Unit) -> RenderingT =
@Composable { props, _ ->
val context = remember(this, this@StatelessWorkflow) {
RenderContext(this, this@StatelessWorkflow)
}
(this@StatelessWorkflow).Rendering(props, context)
}
// @Suppress("LocalVariableName")
// val Rendering: @Composable BaseRenderContext<PropsT, Unit, OutputT>
// .(props: PropsT, _: Unit) -> RenderingT =
// @Composable { props, _ ->
// val context = remember(this, this@StatelessWorkflow) {
// RenderContext(this, this@StatelessWorkflow)
// }
// (this@StatelessWorkflow).Rendering(props, context)
// }
return Workflow.stateful<PropsT, Unit, OutputT, RenderingT>(
initialState = { Unit },
render = { props, _ -> render(props, RenderContext(this, this@StatelessWorkflow)) },
Rendering = Rendering
// Rendering = Rendering
)
}

Expand Down Expand Up @@ -128,11 +128,11 @@ public inline fun <PropsT, OutputT, RenderingT> Workflow.Companion.stateless(
context: RenderContext
): RenderingT = render(context, renderProps)

@Composable
override fun Rendering(
renderProps: PropsT,
context: RenderContext
): RenderingT = Rendering(context, renderProps)
// @Composable
// override fun Rendering(
// renderProps: PropsT,
// context: RenderContext
// ): RenderingT = Rendering(context, renderProps)
}

/**
Expand Down
Loading