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

ComposeWorkflow sketch #1266

Draft
wants to merge 3 commits into
base: main
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
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ material = "1.11.0"
mockito-core = "3.12.4"
mockito-kotlin = "3.2.0"

molecule = "2.0.0"

mockk = "1.13.9"
robolectric = "4.10.3"

Expand Down Expand Up @@ -226,6 +228,8 @@ lanterna = "com.googlecode.lanterna:lanterna:3.1.1"
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito-core" }
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito-kotlin" }

molecule = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }

mockk = { module = "io.mockk:mockk", version.ref = "mockk" }

reactivestreams = "org.reactivestreams:reactive-streams:1.0.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,19 @@ androidx.startup:startup-runtime:1.1.1
androidx.tracing:tracing:1.0.0
androidx.transition:transition:1.5.1
androidx.versionedparcelable:versionedparcelable:1.1.1
app.cash.molecule:molecule-runtime-android:2.0.0
app.cash.molecule:molecule-runtime:2.0.0
com.google.guava:listenablefuture:1.0
com.squareup.okio:okio-jvm:3.3.0
com.squareup.okio:okio:3.3.0
org.jetbrains.compose.runtime:runtime:1.6.10
org.jetbrains.kotlin:kotlin-bom:1.9.24
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.24
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.24
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24
org.jetbrains.kotlin:kotlin-stdlib:1.9.24
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3
org.jetbrains.kotlin:kotlin-stdlib:2.0.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1
org.jetbrains:annotations:23.0.0
22 changes: 22 additions & 0 deletions workflow-ui/compose/api/compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public final class com/squareup/workflow1/ui/compose/ComposeScreenKt {
public static final fun ComposeScreen (Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/compose/ComposeScreen;
}

public abstract interface class com/squareup/workflow1/ui/compose/ComposeWorkflow {
public abstract fun render (Landroidx/compose/runtime/Composer;I)Ljava/lang/Object;
}

public abstract interface class com/squareup/workflow1/ui/compose/ComposeWorkflowWithProps {
public abstract fun render (Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/compose/CompositionRootKt {
public static final fun withCompositionRoot (Lcom/squareup/workflow1/ui/compose/ScreenComposableFactoryFinder;Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow1/ui/compose/ScreenComposableFactoryFinder;
}
Expand Down Expand Up @@ -64,6 +72,20 @@ public final class com/squareup/workflow1/ui/compose/ScreenComposableFactoryKt {
public static final fun toComposableFactory (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/compose/ScreenComposableFactory;
}

public final class com/squareup/workflow1/ui/compose/State {
public static final field $stable I
public fun <init> (Lcom/squareup/workflow1/Worker;Ljava/lang/Object;)V
public final fun component1 ()Lcom/squareup/workflow1/Worker;
public final fun component2 ()Ljava/lang/Object;
public final fun copy (Lcom/squareup/workflow1/Worker;Ljava/lang/Object;)Lcom/squareup/workflow1/ui/compose/State;
public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/compose/State;Lcom/squareup/workflow1/Worker;Ljava/lang/Object;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/compose/State;
public fun equals (Ljava/lang/Object;)Z
public final fun getLatestRendering ()Ljava/lang/Object;
public final fun getRenderings ()Lcom/squareup/workflow1/Worker;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/squareup/workflow1/ui/compose/TextControllerAsMutableStateKt {
public static final fun asMutableState (Lcom/squareup/workflow1/ui/TextController;Landroidx/compose/runtime/Composer;I)Landroidx/compose/runtime/MutableState;
}
Expand Down
1 change: 1 addition & 0 deletions workflow-ui/compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ dependencies {
implementation(libs.androidx.lifecycle.compose)
implementation(libs.androidx.lifecycle.core)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.molecule)
implementation(libs.squareup.okio)

implementation(project(":workflow-core"))
Expand Down
15 changes: 9 additions & 6 deletions workflow-ui/compose/dependencies/releaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,19 @@ androidx.startup:startup-runtime:1.1.1
androidx.tracing:tracing:1.0.0
androidx.transition:transition:1.5.1
androidx.versionedparcelable:versionedparcelable:1.1.1
app.cash.molecule:molecule-runtime-android:2.0.0
app.cash.molecule:molecule-runtime:2.0.0
com.google.guava:listenablefuture:1.0
com.squareup.okio:okio-jvm:3.3.0
com.squareup.okio:okio:3.3.0
org.jetbrains.compose.runtime:runtime:1.6.10
org.jetbrains.kotlin:kotlin-bom:1.9.24
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.24
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.24
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24
org.jetbrains.kotlin:kotlin-stdlib:1.9.24
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3
org.jetbrains.kotlin:kotlin-stdlib:2.0.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1
org.jetbrains:annotations:23.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.squareup.workflow1.ui.compose

import androidx.compose.runtime.Composable
import app.cash.molecule.RecompositionMode.Immediate
import app.cash.molecule.launchMolecule
import com.squareup.workflow1.SessionWorkflow
import com.squareup.workflow1.Snapshot
import com.squareup.workflow1.Worker
import com.squareup.workflow1.Workflow
import com.squareup.workflow1.WorkflowExperimentalApi
import com.squareup.workflow1.action
import com.squareup.workflow1.asWorker
import com.squareup.workflow1.runningWorker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.drop

/**
* - Note that `RenderT` can be our old friends `Screen`, `Overlay`,
* `MarketStackScreen`, etc.
*
* - For `OutputT`, pass a `MutableStateFlow` as a prop and
* consume it as a worker. Perhaps we can provide some sugar
* for that, but I don't think it needs to be part of the
* fundamental machinery
*
* - Snapshots: TBD. [Easy enough to put a `SavedStateRegistry` in place.](https://docs.google.com/presentation/d/1JTN3PpFG27b06OqKEOdHXZSos8wHck3y9T4pRvwpir8/edit#slide=id.g149ba2e5ba1_0_204)
* Tricky bit is that Snapshot really wants you to read and
* write `ByteString`, with support provided for `Parcelable`.
* How do we get `SavedStateRegistry` to play nice with that?
* Maybe `SavedStateRegistryOwner` and `SavedStateController`
* are flexible enough. Not excited about this.
*/
public fun interface ComposeWorkflow<out RenderT> {
@Composable public fun render(): RenderT
}

public fun interface ComposeWorkflowWithProps<in PropsT, out RenderT> {
@Composable public fun render(propsT: PropsT): RenderT
}

@PublishedApi
internal data class State<RenderT>(
val renderings: Worker<RenderT>,
val latestRendering: RenderT
)

public inline fun <reified RenderT> ComposeWorkflow<RenderT>.asWorkflow(): Workflow<Unit, Nothing, RenderT> {
@OptIn(WorkflowExperimentalApi::class)
return object : SessionWorkflow<Unit, State<RenderT>, Nothing, RenderT>() {
val rootPresenter = this@asWorkflow

override fun initialState(
props: Unit,
snapshot: Snapshot?,
workflowScope: CoroutineScope
): State<RenderT> {
val stateFlow = workflowScope.launchMolecule(mode = Immediate) { rootPresenter.render() }
val worker: Worker<RenderT> = stateFlow.drop(1).asWorker()
return State(worker, stateFlow.value)
}

override fun render(
renderProps: Unit,
renderState: State<RenderT>,
context: RenderContext
): RenderT {
context.runningWorker(renderState.renderings) {
action("nextRendering") {
state = state.copy(latestRendering = it)
}
}
return renderState.latestRendering
}

override fun snapshotState(state: State<RenderT>): Snapshot? = null
}
}
Loading