From 4b4e2b2de39fc0c81949a049acc2623b74fe076f Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:11:41 -0500 Subject: [PATCH 01/17] Add compose ui dependency to common in circuit --- .../androidReleaseRuntimeClasspath.txt | 1 + .../dependencies/jvmRuntimeClasspath.txt | 22 +++++++++++++++++++ .../androidReleaseRuntimeClasspath.txt | 1 + .../dependencies/jvmRuntimeClasspath.txt | 22 +++++++++++++++++++ circuit/build.gradle.kts | 2 ++ .../androidReleaseRuntimeClasspath.txt | 1 + circuit/dependencies/jvmRuntimeClasspath.txt | 22 +++++++++++++++++++ 7 files changed, 71 insertions(+) diff --git a/circuit-codegen-annotations/dependencies/androidReleaseRuntimeClasspath.txt b/circuit-codegen-annotations/dependencies/androidReleaseRuntimeClasspath.txt index 03b734740..0b38928dd 100644 --- a/circuit-codegen-annotations/dependencies/androidReleaseRuntimeClasspath.txt +++ b/circuit-codegen-annotations/dependencies/androidReleaseRuntimeClasspath.txt @@ -44,6 +44,7 @@ com.squareup.anvil:annotations javax.inject:javax.inject org.jetbrains.compose.runtime:runtime-saveable org.jetbrains.compose.runtime:runtime +org.jetbrains.compose.ui:ui org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 diff --git a/circuit-codegen-annotations/dependencies/jvmRuntimeClasspath.txt b/circuit-codegen-annotations/dependencies/jvmRuntimeClasspath.txt index 878d0555c..40efeb674 100644 --- a/circuit-codegen-annotations/dependencies/jvmRuntimeClasspath.txt +++ b/circuit-codegen-annotations/dependencies/jvmRuntimeClasspath.txt @@ -1,10 +1,30 @@ com.google.dagger:dagger com.squareup.anvil:annotations javax.inject:javax.inject +org.jetbrains.compose.animation:animation-core-desktop +org.jetbrains.compose.animation:animation-core +org.jetbrains.compose.animation:animation-desktop +org.jetbrains.compose.animation:animation +org.jetbrains.compose.foundation:foundation-desktop +org.jetbrains.compose.foundation:foundation-layout-desktop +org.jetbrains.compose.foundation:foundation-layout +org.jetbrains.compose.foundation:foundation org.jetbrains.compose.runtime:runtime-desktop org.jetbrains.compose.runtime:runtime-saveable-desktop org.jetbrains.compose.runtime:runtime-saveable org.jetbrains.compose.runtime:runtime +org.jetbrains.compose.ui:ui-desktop +org.jetbrains.compose.ui:ui-geometry-desktop +org.jetbrains.compose.ui:ui-geometry +org.jetbrains.compose.ui:ui-graphics-desktop +org.jetbrains.compose.ui:ui-graphics +org.jetbrains.compose.ui:ui-text-desktop +org.jetbrains.compose.ui:ui-text +org.jetbrains.compose.ui:ui-unit-desktop +org.jetbrains.compose.ui:ui-unit +org.jetbrains.compose.ui:ui-util-desktop +org.jetbrains.compose.ui:ui-util +org.jetbrains.compose.ui:ui org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 @@ -14,4 +34,6 @@ org.jetbrains.kotlinx:atomicfu org.jetbrains.kotlinx:kotlinx-coroutines-bom org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm org.jetbrains.kotlinx:kotlinx-coroutines-core +org.jetbrains.skiko:skiko-awt +org.jetbrains.skiko:skiko org.jetbrains:annotations diff --git a/circuit-test/dependencies/androidReleaseRuntimeClasspath.txt b/circuit-test/dependencies/androidReleaseRuntimeClasspath.txt index fde4a58db..c4cfeec3c 100644 --- a/circuit-test/dependencies/androidReleaseRuntimeClasspath.txt +++ b/circuit-test/dependencies/androidReleaseRuntimeClasspath.txt @@ -45,6 +45,7 @@ app.cash.turbine:turbine com.google.guava:listenablefuture org.jetbrains.compose.runtime:runtime-saveable org.jetbrains.compose.runtime:runtime +org.jetbrains.compose.ui:ui org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 diff --git a/circuit-test/dependencies/jvmRuntimeClasspath.txt b/circuit-test/dependencies/jvmRuntimeClasspath.txt index 89081ba8c..804bf29c4 100644 --- a/circuit-test/dependencies/jvmRuntimeClasspath.txt +++ b/circuit-test/dependencies/jvmRuntimeClasspath.txt @@ -2,10 +2,30 @@ app.cash.molecule:molecule-runtime-jvm app.cash.molecule:molecule-runtime app.cash.turbine:turbine-jvm app.cash.turbine:turbine +org.jetbrains.compose.animation:animation-core-desktop +org.jetbrains.compose.animation:animation-core +org.jetbrains.compose.animation:animation-desktop +org.jetbrains.compose.animation:animation +org.jetbrains.compose.foundation:foundation-desktop +org.jetbrains.compose.foundation:foundation-layout-desktop +org.jetbrains.compose.foundation:foundation-layout +org.jetbrains.compose.foundation:foundation org.jetbrains.compose.runtime:runtime-desktop org.jetbrains.compose.runtime:runtime-saveable-desktop org.jetbrains.compose.runtime:runtime-saveable org.jetbrains.compose.runtime:runtime +org.jetbrains.compose.ui:ui-desktop +org.jetbrains.compose.ui:ui-geometry-desktop +org.jetbrains.compose.ui:ui-geometry +org.jetbrains.compose.ui:ui-graphics-desktop +org.jetbrains.compose.ui:ui-graphics +org.jetbrains.compose.ui:ui-text-desktop +org.jetbrains.compose.ui:ui-text +org.jetbrains.compose.ui:ui-unit-desktop +org.jetbrains.compose.ui:ui-unit +org.jetbrains.compose.ui:ui-util-desktop +org.jetbrains.compose.ui:ui-util +org.jetbrains.compose.ui:ui org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 @@ -17,4 +37,6 @@ org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm org.jetbrains.kotlinx:kotlinx-coroutines-core org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm org.jetbrains.kotlinx:kotlinx-coroutines-test +org.jetbrains.skiko:skiko-awt +org.jetbrains.skiko:skiko org.jetbrains:annotations diff --git a/circuit/build.gradle.kts b/circuit/build.gradle.kts index b6b3d43e6..b05abe1f4 100644 --- a/circuit/build.gradle.kts +++ b/circuit/build.gradle.kts @@ -20,8 +20,10 @@ kotlin { api(libs.compose.runtime) api(libs.coroutines) api(projects.backstack) + api(libs.compose.ui) } } + maybeCreate("jvmMain").apply { dependencies { api(libs.compose.foundation) } } maybeCreate("androidMain").apply { dependencies { api(libs.androidx.compose.runtime) diff --git a/circuit/dependencies/androidReleaseRuntimeClasspath.txt b/circuit/dependencies/androidReleaseRuntimeClasspath.txt index cc98e83ed..4025b5add 100644 --- a/circuit/dependencies/androidReleaseRuntimeClasspath.txt +++ b/circuit/dependencies/androidReleaseRuntimeClasspath.txt @@ -41,6 +41,7 @@ androidx.versionedparcelable:versionedparcelable com.google.guava:listenablefuture org.jetbrains.compose.runtime:runtime-saveable org.jetbrains.compose.runtime:runtime +org.jetbrains.compose.ui:ui org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 diff --git a/circuit/dependencies/jvmRuntimeClasspath.txt b/circuit/dependencies/jvmRuntimeClasspath.txt index 8028160fe..5de29d247 100644 --- a/circuit/dependencies/jvmRuntimeClasspath.txt +++ b/circuit/dependencies/jvmRuntimeClasspath.txt @@ -1,7 +1,27 @@ +org.jetbrains.compose.animation:animation-core-desktop +org.jetbrains.compose.animation:animation-core +org.jetbrains.compose.animation:animation-desktop +org.jetbrains.compose.animation:animation +org.jetbrains.compose.foundation:foundation-desktop +org.jetbrains.compose.foundation:foundation-layout-desktop +org.jetbrains.compose.foundation:foundation-layout +org.jetbrains.compose.foundation:foundation org.jetbrains.compose.runtime:runtime-desktop org.jetbrains.compose.runtime:runtime-saveable-desktop org.jetbrains.compose.runtime:runtime-saveable org.jetbrains.compose.runtime:runtime +org.jetbrains.compose.ui:ui-desktop +org.jetbrains.compose.ui:ui-geometry-desktop +org.jetbrains.compose.ui:ui-geometry +org.jetbrains.compose.ui:ui-graphics-desktop +org.jetbrains.compose.ui:ui-graphics +org.jetbrains.compose.ui:ui-text-desktop +org.jetbrains.compose.ui:ui-text +org.jetbrains.compose.ui:ui-unit-desktop +org.jetbrains.compose.ui:ui-unit +org.jetbrains.compose.ui:ui-util-desktop +org.jetbrains.compose.ui:ui-util +org.jetbrains.compose.ui:ui org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 @@ -11,4 +31,6 @@ org.jetbrains.kotlinx:atomicfu org.jetbrains.kotlinx:kotlinx-coroutines-bom org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm org.jetbrains.kotlinx:kotlinx-coroutines-core +org.jetbrains.skiko:skiko-awt +org.jetbrains.skiko:skiko org.jetbrains:annotations From e5d5b15d6c0a65aca5854e2a9e9814edd37a9b73 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:12:08 -0500 Subject: [PATCH 02/17] Make circuit codegen purely JVM --- circuit-codegen/build.gradle.kts | 2 -- circuit-codegen/dependencies/runtimeClasspath.txt | 9 --------- circuit-codegen/gradle.properties | 1 + 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/circuit-codegen/build.gradle.kts b/circuit-codegen/build.gradle.kts index 0577dedd6..1711eefa7 100644 --- a/circuit-codegen/build.gradle.kts +++ b/circuit-codegen/build.gradle.kts @@ -13,7 +13,5 @@ dependencies { implementation(libs.dagger) implementation(libs.kotlinpoet) implementation(libs.kotlinpoet.ksp) - implementation(projects.circuit) - implementation(projects.circuitCodegenAnnotations) implementation(libs.anvil.annotations) } diff --git a/circuit-codegen/dependencies/runtimeClasspath.txt b/circuit-codegen/dependencies/runtimeClasspath.txt index f59380e29..4bd83d88b 100644 --- a/circuit-codegen/dependencies/runtimeClasspath.txt +++ b/circuit-codegen/dependencies/runtimeClasspath.txt @@ -5,19 +5,10 @@ com.squareup.anvil:annotations com.squareup:kotlinpoet-ksp com.squareup:kotlinpoet javax.inject:javax.inject -org.jetbrains.compose.runtime:runtime-desktop -org.jetbrains.compose.runtime:runtime-saveable-desktop -org.jetbrains.compose.runtime:runtime-saveable -org.jetbrains.compose.runtime:runtime org.jetbrains.kotlin:kotlin-bom org.jetbrains.kotlin:kotlin-reflect org.jetbrains.kotlin:kotlin-stdlib-common org.jetbrains.kotlin:kotlin-stdlib-jdk7 org.jetbrains.kotlin:kotlin-stdlib-jdk8 org.jetbrains.kotlin:kotlin-stdlib -org.jetbrains.kotlinx:atomicfu-jvm -org.jetbrains.kotlinx:atomicfu -org.jetbrains.kotlinx:kotlinx-coroutines-bom -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm -org.jetbrains.kotlinx:kotlinx-coroutines-core org.jetbrains:annotations diff --git a/circuit-codegen/gradle.properties b/circuit-codegen/gradle.properties index 1fba72e7c..5d9e63f9f 100644 --- a/circuit-codegen/gradle.properties +++ b/circuit-codegen/gradle.properties @@ -1,3 +1,4 @@ POM_ARTIFACT_ID=circuit-codegen POM_NAME=Circuit (Codegen) POM_DESCRIPTION=Circuit (Codegen) +circuit.noCompose=true From c47f81d6f441769e66062451178c79edd3fb3382 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:12:21 -0500 Subject: [PATCH 03/17] Add toml deps --- gradle/libs.versions.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 41a5a08fd..38974450f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -143,6 +143,8 @@ coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose-jb" } compose-runtime-saveable = { module = "org.jetbrains.compose.runtime:runtime-saveable", version.ref = "compose-jb" } +compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose-jb" } +compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "compose-jb" } coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } From 2a0f0d1c754d188978217d1bd639b967e0733175 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:14:07 -0500 Subject: [PATCH 04/17] Introduce Modifier to Ui.Content() --- .../commonMain/kotlin/com/slack/circuit/Ui.kt | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt b/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt index 9bfb119fb..61d7e8b24 100644 --- a/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt +++ b/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt @@ -3,6 +3,7 @@ package com.slack.circuit import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier /** * Represents a composable UI for the given [UiState]. Conventionally, this should just be the @@ -15,11 +16,11 @@ import androidx.compose.runtime.Composable * * Usage: * ``` - * internal fun tacoUi(): Ui = ui { state -> - * Tacos(state) + * internal fun tacoUi(): Ui = ui { state, modifier -> + * Tacos(state, modifier) * } * - * @Composable private fun Tacos(state: State) {...} + * @Composable private fun Tacos(state: State, modifier: Modifier = Modifier) {...} * * @Preview * @Composable @@ -38,11 +39,10 @@ import androidx.compose.runtime.Composable * @see ui */ public interface Ui { - @Composable public fun Content(state: UiState) + @Composable public fun Content(state: UiState, modifier: Modifier) /** - * A factory that creates [ScreenUis][ScreenUi], which in turn contain the desired [Ui] for a - * given [Screen]. + * A factory that creates [Ui's][Ui] for a given [Screen]. * * Note that individual UIs should just be top-level [ui] function calls that factories simply * call into. This allows easily standing up composable preview functions. @@ -53,31 +53,24 @@ public interface Ui { * screen: Screen, * context: CircuitContext * ): ScreenUi? { - * val ui = when (screen) { - * is AddFavorites -> { - * addFavoritesUi() - * } - * else -> return null + * return when (screen) { + * is AddFavorites -> addFavoritesUi() + * else -> null * } - * return ScreenUi( - * ui = ui as Ui<*, *>, - * ) * } * } * * private fun addFavoritesUi() = - * ui { state -> Favorites(state) } + * ui { state, modifier -> Favorites(state, modifier) } * - * @Composable private fun Favorites(state: State) {...} + * @Composable private fun Favorites(state: State, modifier: Modifier = Modifier) {...} * ``` */ public fun interface Factory { - public fun create(screen: Screen, context: CircuitContext): ScreenUi? + public fun create(screen: Screen, context: CircuitContext): Ui<*>? } } -public data class ScreenUi(val ui: Ui<*>) - /** * Due to this bug in Studio, we can't write lambda impls of [Ui] directly. This works around it by * offering a shim function of the same name. Once it's fixed, we can remove this and make [Ui] a @@ -88,12 +81,12 @@ public data class ScreenUi(val ui: Ui<*>) * @see [Ui] for main docs. */ public inline fun ui( - crossinline body: @Composable (state: UiState) -> Unit + crossinline body: @Composable (state: UiState, modifier: Modifier) -> Unit ): Ui { return object : Ui { @Composable - override fun Content(state: UiState) { - body(state) + override fun Content(state: UiState, modifier: Modifier) { + body(state, modifier) } } } From e1864ff62d9e60672c4a29531a09a928e2e87d91 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:15:31 -0500 Subject: [PATCH 05/17] Update EventListener --- .../kotlin/com/slack/circuit/EventListener.kt | 9 ++++----- .../kotlin/com/slack/circuit/EventListenerTest.kt | 13 +++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/circuit/src/commonMain/kotlin/com/slack/circuit/EventListener.kt b/circuit/src/commonMain/kotlin/com/slack/circuit/EventListener.kt index b423a9e68..8e7f92b54 100644 --- a/circuit/src/commonMain/kotlin/com/slack/circuit/EventListener.kt +++ b/circuit/src/commonMain/kotlin/com/slack/circuit/EventListener.kt @@ -30,16 +30,15 @@ public interface EventListener { public fun onBeforeCreateUi(screen: Screen, context: CircuitContext) {} /** - * Called just after creating a [screenUi] for a given [screen]. The ui may be null if none was - * found. + * Called just after creating a [ui] for a given [screen]. The ui may be null if none was found. */ - public fun onAfterCreateUi(screen: Screen, screenUi: ScreenUi?, context: CircuitContext) {} + public fun onAfterCreateUi(screen: Screen, ui: Ui<*>?, context: CircuitContext) {} - /** Called when no content was found and one or both of [presenter] and [screenUi] are null. */ + /** Called when no content was found and one or both of [presenter] and [ui] are null. */ public fun onUnavailableContent( screen: Screen, presenter: Presenter<*>?, - screenUi: ScreenUi?, + ui: Ui<*>?, context: CircuitContext ) {} diff --git a/circuit/src/jvmTest/kotlin/com/slack/circuit/EventListenerTest.kt b/circuit/src/jvmTest/kotlin/com/slack/circuit/EventListenerTest.kt index 07d7d7e43..389e882da 100644 --- a/circuit/src/jvmTest/kotlin/com/slack/circuit/EventListenerTest.kt +++ b/circuit/src/jvmTest/kotlin/com/slack/circuit/EventListenerTest.kt @@ -5,6 +5,7 @@ package com.slack.circuit import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.Modifier import app.cash.molecule.RecompositionClock.Immediate import app.cash.molecule.launchMolecule import app.cash.turbine.Turbine @@ -36,7 +37,7 @@ class EventListenerTest { val circuitConfig = CircuitConfig.Builder() .addPresenterFactory { _, _, _ -> presenter } - .addUiFactory { _, _ -> ScreenUi(ui) } + .addUiFactory { _, _ -> ui } .eventListenerFactory(eventListenerFactory) .build() @@ -65,7 +66,7 @@ private class StringPresenter(val state: State) : Presenter private class StringUi : Ui { - @Composable override fun Content(state: StringState) {} + @Composable override fun Content(state: StringState, modifier: Modifier) {} } private class RecordingEventListener(private val onDispose: () -> Unit) : EventListener { @@ -97,17 +98,17 @@ private class RecordingEventListener(private val onDispose: () -> Unit) : EventL log("onBeforeCreateUi: $screen") } - override fun onAfterCreateUi(screen: Screen, screenUi: ScreenUi?, context: CircuitContext) { - log("onAfterCreateUi: $screen, $screenUi") + override fun onAfterCreateUi(screen: Screen, ui: Ui<*>?, context: CircuitContext) { + log("onAfterCreateUi: $screen, $ui") } override fun onUnavailableContent( screen: Screen, presenter: Presenter<*>?, - screenUi: ScreenUi?, + ui: Ui<*>?, context: CircuitContext, ) { - error("onUnavailableContent: $screen, $presenter, $screenUi") + error("onUnavailableContent: $screen, $presenter, $ui") } override fun onStartPresent() { From 746cc9141e8959bdbc73b736347a3c3d9f4266b4 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:16:08 -0500 Subject: [PATCH 06/17] Make unavailableContent non-null If people want the old error behavior, they can make a composable that just errors instead of drawing --- .../kotlin/com/slack/circuit/CircuitConfig.kt | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitConfig.kt b/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitConfig.kt index cae85b015..c915c5f0d 100644 --- a/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitConfig.kt +++ b/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitConfig.kt @@ -2,8 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 package com.slack.circuit +import androidx.compose.foundation.background +import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle /** * [CircuitConfig] adapts [presenter factories][Presenter.Factory] to their corresponding renderable @@ -53,7 +58,7 @@ import androidx.compose.runtime.Immutable public class CircuitConfig private constructor(builder: Builder) { private val uiFactories: List = builder.uiFactories.toList() private val presenterFactories: List = builder.presenterFactories.toList() - public val onUnavailableContent: (@Composable (screen: Screen) -> Unit)? = + public val onUnavailableContent: (@Composable (screen: Screen, modifier: Modifier) -> Unit) = builder.onUnavailableContent internal val eventListenerFactory: EventListener.Factory? = builder.eventListenerFactory @@ -82,11 +87,11 @@ public class CircuitConfig private constructor(builder: Builder) { return null } - public fun ui(screen: Screen, context: CircuitContext = CircuitContext(null, this)): ScreenUi? { + public fun ui(screen: Screen, context: CircuitContext = CircuitContext(null, this)): Ui<*>? { return nextUi(null, screen, context) } - public fun nextUi(skipPast: Ui.Factory?, screen: Screen, context: CircuitContext): ScreenUi? { + public fun nextUi(skipPast: Ui.Factory?, screen: Screen, context: CircuitContext): Ui<*>? { val start = uiFactories.indexOf(skipPast) + 1 for (i in start until uiFactories.size) { val ui = uiFactories[i].create(screen, context) @@ -103,7 +108,8 @@ public class CircuitConfig private constructor(builder: Builder) { public class Builder constructor() { public val uiFactories: MutableList = mutableListOf() public val presenterFactories: MutableList = mutableListOf() - public var onUnavailableContent: (@Composable (screen: Screen) -> Unit)? = null + public var onUnavailableContent: (@Composable (screen: Screen, modifier: Modifier) -> Unit) = + UnavailableContent private set public var eventListenerFactory: EventListener.Factory? = null private set @@ -140,10 +146,9 @@ public class CircuitConfig private constructor(builder: Builder) { presenterFactories.addAll(factories) } - public fun setOnUnavailableContent(content: @Composable (screen: Screen) -> Unit): Builder = - apply { - onUnavailableContent = content - } + public fun setOnUnavailableContent( + content: @Composable (screen: Screen, modifier: Modifier) -> Unit + ): Builder = apply { onUnavailableContent = content } public fun eventListenerFactory(factory: EventListener.Factory): Builder = apply { eventListenerFactory = factory @@ -154,3 +159,12 @@ public class CircuitConfig private constructor(builder: Builder) { } } } + +private val UnavailableContent: @Composable (screen: Screen, modifier: Modifier) -> Unit = + { screen, modifier -> + BasicText( + "Route not available: ${screen.javaClass.name}", + modifier.background(Color.Red), + style = TextStyle(color = Color.Yellow) + ) + } From 1abdbacece6a7587c15f49f851adeeb522aec8f5 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:16:21 -0500 Subject: [PATCH 07/17] Update circuit content for new APIs --- .../com/slack/circuit/CircuitContent.kt | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitContent.kt b/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitContent.kt index 0a79eeece..fd0699d67 100644 --- a/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitContent.kt +++ b/circuit/src/commonMain/kotlin/com/slack/circuit/CircuitContent.kt @@ -7,22 +7,27 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier @Composable public fun CircuitContent( screen: Screen, + modifier: Modifier = Modifier, circuitConfig: CircuitConfig = requireNotNull(LocalCircuitConfig.current), - unavailableContent: (@Composable (screen: Screen) -> Unit)? = circuitConfig.onUnavailableContent, + unavailableContent: (@Composable (screen: Screen, modifier: Modifier) -> Unit) = + circuitConfig.onUnavailableContent, ) { - CircuitContent(screen, Navigator.NoOp, circuitConfig, unavailableContent) + CircuitContent(screen, modifier, Navigator.NoOp, circuitConfig, unavailableContent) } @Composable public fun CircuitContent( screen: Screen, + modifier: Modifier = Modifier, onNavEvent: (event: NavEvent) -> Unit, circuitConfig: CircuitConfig = requireNotNull(LocalCircuitConfig.current), - unavailableContent: (@Composable (screen: Screen) -> Unit)? = circuitConfig.onUnavailableContent, + unavailableContent: (@Composable (screen: Screen, modifier: Modifier) -> Unit) = + circuitConfig.onUnavailableContent, ) { val navigator = remember(onNavEvent) { @@ -42,30 +47,32 @@ public fun CircuitContent( } } } - CircuitContent(screen, navigator, circuitConfig, unavailableContent) + CircuitContent(screen, modifier, navigator, circuitConfig, unavailableContent) } @Composable internal fun CircuitContent( screen: Screen, + modifier: Modifier, navigator: Navigator, circuitConfig: CircuitConfig, - unavailableContent: (@Composable (screen: Screen) -> Unit)?, + unavailableContent: (@Composable (screen: Screen, modifier: Modifier) -> Unit), ) { val parent = LocalCircuitContext.current val context = remember(screen, navigator, circuitConfig, parent) { CircuitContext(parent, circuitConfig) } CompositionLocalProvider(LocalCircuitContext provides context) { - CircuitContent(screen, navigator, circuitConfig, unavailableContent, context) + CircuitContent(screen, modifier, navigator, circuitConfig, unavailableContent, context) } } @Composable internal fun CircuitContent( screen: Screen, + modifier: Modifier, navigator: Navigator, circuitConfig: CircuitConfig, - unavailableContent: (@Composable (screen: Screen) -> Unit)?, + unavailableContent: (@Composable (screen: Screen, modifier: Modifier) -> Unit), context: CircuitContext, ) { val eventListener = @@ -83,23 +90,22 @@ internal fun CircuitContent( eventListener.onAfterCreatePresenter(screen, navigator, presenter, context) eventListener.onBeforeCreateUi(screen, context) - val screenUi = circuitConfig.ui(screen, context) - eventListener.onAfterCreateUi(screen, screenUi, context) + val ui = circuitConfig.ui(screen, context) + eventListener.onAfterCreateUi(screen, ui, context) - if (screenUi != null && presenter != null) { + if (ui != null && presenter != null) { @Suppress("UNCHECKED_CAST") - CircuitContent(screen, eventListener, presenter, screenUi.ui as Ui) - } else if (unavailableContent != null) { - eventListener.onUnavailableContent(screen, presenter, screenUi, context) - unavailableContent(screen) + CircuitContent(screen, modifier, eventListener, presenter, ui as Ui) } else { - error("Could not render screen $screen") + eventListener.onUnavailableContent(screen, presenter, ui, context) + unavailableContent(screen, modifier) } } @Composable private fun CircuitContent( screen: Screen, + modifier: Modifier, eventListener: EventListener, presenter: Presenter, ui: Ui, @@ -117,5 +123,5 @@ private fun CircuitContent( onDispose { eventListener.onDisposeContent() } } - ui.Content(state) + ui.Content(state, modifier) } From 8ece3c7f2aeede687e3e15b6fd2bd05c79c074f6 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:16:34 -0500 Subject: [PATCH 08/17] Update NavigableCircuitContent --- .../NavigableCircuitContent.android.kt | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/circuit/src/androidMain/kotlin/com/slack/circuit/NavigableCircuitContent.android.kt b/circuit/src/androidMain/kotlin/com/slack/circuit/NavigableCircuitContent.android.kt index da5202afb..0e671d282 100644 --- a/circuit/src/androidMain/kotlin/com/slack/circuit/NavigableCircuitContent.android.kt +++ b/circuit/src/androidMain/kotlin/com/slack/circuit/NavigableCircuitContent.android.kt @@ -12,8 +12,6 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.with -import androidx.compose.foundation.background -import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -24,8 +22,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle import com.slack.circuit.backstack.BackStack import com.slack.circuit.backstack.NavDecoration import com.slack.circuit.backstack.ProvidedValues @@ -41,7 +37,8 @@ public fun NavigableCircuitContent( enableBackHandler: Boolean = true, providedValues: Map = providedValuesForBackStack(backstack), decoration: NavDecoration = NavigatorDefaults.DefaultDecoration, - unavailableRoute: @Composable (String) -> Unit = NavigatorDefaults.UnavailableRoute, + unavailableRoute: (@Composable (screen: Screen, modifier: Modifier) -> Unit) = + circuitConfig.onUnavailableContent, ) { BackHandler(enabled = enableBackHandler && backstack.size > 1, onBack = navigator::pop) @@ -64,17 +61,17 @@ public fun BasicNavigableCircuitContent( modifier: Modifier = Modifier, circuitConfig: CircuitConfig = requireNotNull(LocalCircuitConfig.current), decoration: NavDecoration = NavigatorDefaults.EmptyDecoration, - unavailableRoute: @Composable (String) -> Unit = NavigatorDefaults.UnavailableRoute, + unavailableRoute: (@Composable (screen: Screen, modifier: Modifier) -> Unit) = + circuitConfig.onUnavailableContent, ) { val activeContentProviders = buildList { for (record in backstack) { val provider = key(record.key) { - val routeName = record.route val screen = record.screen val currentContent: (@Composable (SaveableBackStack.Record) -> Unit) = { - CircuitContent(screen, navigator, circuitConfig) { unavailableRoute(routeName) } + CircuitContent(screen, modifier, navigator, circuitConfig, unavailableRoute) } val currentRouteContent by rememberUpdatedState(currentContent) @@ -157,16 +154,4 @@ public object NavigatorDefaults { content(arg) } } - - /** - * Bright ugly error text telling a developer they didn't provide a route that a [BackStack] asked - * for. - */ - public val UnavailableRoute: @Composable (String) -> Unit = { route -> - BasicText( - "Route not available: $route", - Modifier.background(Color.Red), - style = TextStyle(color = Color.Yellow) - ) - } } From efa5c3b6dd33ea470a86f3e57c8bb462a09e2aba Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:16:54 -0500 Subject: [PATCH 09/17] Update code gen for new behavior + removed direct circuit deps --- .../codegen/CircuitSymbolProcessorProvider.kt | 180 +++++++++++------- 1 file changed, 110 insertions(+), 70 deletions(-) diff --git a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt index 89bd0b9db..839486f44 100644 --- a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt +++ b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt @@ -23,16 +23,9 @@ import com.google.devtools.ksp.symbol.KSDeclaration import com.google.devtools.ksp.symbol.KSFunctionDeclaration import com.google.devtools.ksp.symbol.KSType import com.google.devtools.ksp.symbol.Visibility -import com.slack.circuit.CircuitContext -import com.slack.circuit.CircuitUiState -import com.slack.circuit.Navigator -import com.slack.circuit.Presenter -import com.slack.circuit.Screen -import com.slack.circuit.ScreenUi -import com.slack.circuit.Ui -import com.slack.circuit.codegen.annotations.CircuitInject import com.squareup.anvil.annotations.ContributesMultibinding import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.FunSpec @@ -45,7 +38,6 @@ import com.squareup.kotlinpoet.STAR import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asClassName -import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.joinToCode import com.squareup.kotlinpoet.ksp.addOriginatingKSFile import com.squareup.kotlinpoet.ksp.toClassName @@ -55,9 +47,17 @@ import dagger.assisted.AssistedFactory import javax.inject.Inject import javax.inject.Provider -private val CIRCUIT_INJECT_ANNOTATION = CircuitInject::class.java.canonicalName -private val CIRCUIT_PRESENTER = Presenter::class.java.canonicalName -private val CIRCUIT_UI = Ui::class.java.canonicalName +private val MODIFIER = ClassName("androidx.compose.ui", "Modifier") +private val CIRCUIT_INJECT_ANNOTATION = + ClassName("com.slack.circuit.codegen.annotations", "CircuitInject") +private val CIRCUIT_PRESENTER = ClassName("com.slack.circuit", "Presenter") +private val CIRCUIT_PRESENTER_FACTORY = CIRCUIT_PRESENTER.nestedClass("Factory") +private val CIRCUIT_UI = ClassName("com.slack.circuit", "Ui") +private val CIRCUIT_UI_FACTORY = CIRCUIT_UI.nestedClass("Factory") +private val CIRCUIT_UI_STATE = ClassName("com.slack.circuit", "CircuitUiState") +private val SCREEN = ClassName("com.slack.circuit", "Screen") +private val NAVIGATOR = ClassName("com.slack.circuit", "Navigator") +private val CIRCUIT_CONTEXT = ClassName("com.slack.circuit", "CircuitContext") private const val FACTORY = "Factory" @AutoService(SymbolProcessorProvider::class) @@ -68,9 +68,11 @@ public class CircuitSymbolProcessorProvider : SymbolProcessorProvider { } private class CircuitSymbols private constructor(resolver: Resolver) { - val circuitUiState = resolver.loadKSType() - val screen = resolver.loadKSType() - val navigator = resolver.loadKSType() + val modifier = resolver.loadKSType(MODIFIER.canonicalName) + val circuitUiState = resolver.loadKSType(CIRCUIT_UI_STATE.canonicalName) + val screen = resolver.loadKSType(SCREEN.canonicalName) + val navigator = resolver.loadKSType(NAVIGATOR.canonicalName) + companion object { fun create(resolver: Resolver): CircuitSymbols? { @Suppress("SwallowedException") @@ -83,24 +85,29 @@ private class CircuitSymbols private constructor(resolver: Resolver) { } } -private inline fun Resolver.loadKSType(): KSType { - return loadOptionalKSType() - ?: error("Could not find ${T::class.java.canonicalName} in classpath") -} +private inline fun Resolver.loadKSType(): KSType = + loadKSType(T::class.java.canonicalName) + +private fun Resolver.loadKSType(name: String?): KSType = + loadOptionalKSType(name) ?: error("Could not find $name in classpath") -private inline fun Resolver.loadOptionalKSType(): KSType? { - return getClassDeclarationByName(getKSNameFromString(T::class.java.canonicalName)) - ?.asType(emptyList()) +private inline fun Resolver.loadOptionalKSType(): KSType? = + loadOptionalKSType(T::class.java.canonicalName) + +internal fun Resolver.loadOptionalKSType(name: String?): KSType? { + if (name == null) return null + return getClassDeclarationByName(getKSNameFromString(name))?.asType(emptyList()) } private class CircuitSymbolProcessor( private val logger: KSPLogger, - private val codeGenerator: CodeGenerator + private val codeGenerator: CodeGenerator, ) : SymbolProcessor { override fun process(resolver: Resolver): List { val symbols = CircuitSymbols.create(resolver) ?: return emptyList() - resolver.getSymbolsWithAnnotation(CIRCUIT_INJECT_ANNOTATION).forEach { annotatedElement -> + resolver.getSymbolsWithAnnotation(CIRCUIT_INJECT_ANNOTATION.canonicalName).forEach { + annotatedElement -> when (annotatedElement) { is KSClassDeclaration -> { generateFactory(annotatedElement, InstantiationType.CLASS, symbols) @@ -121,12 +128,12 @@ private class CircuitSymbolProcessor( private fun generateFactory( annotatedElement: KSAnnotated, instantiationType: InstantiationType, - symbols: CircuitSymbols + symbols: CircuitSymbols, ) { val circuitInjectAnnotation = annotatedElement.annotations.first { it.annotationType.resolve().declaration.qualifiedName?.asString() == - CIRCUIT_INJECT_ANNOTATION + CIRCUIT_INJECT_ANNOTATION.canonicalName } val screenKSType = circuitInjectAnnotation.arguments[0].value as KSType val screenIsObject = @@ -185,7 +192,7 @@ private class CircuitSymbolProcessor( val packageName: String, val factoryType: FactoryType, val constructorParams: List, - val codeBlock: CodeBlock + val codeBlock: CodeBlock, ) /** Computes the data needed to generate a factory. */ @@ -232,34 +239,72 @@ private class CircuitSymbolProcessor( assistedParams ) FactoryType.UI -> { + // State param is optional val stateParam = fd.parameters.singleOrNull { parameter -> symbols.circuitUiState.isAssignableFrom(parameter.type.resolve()) } - if (stateParam == null) { - CodeBlock.of( - "%M<%T>·{·%M(%L)·}", - MemberName("com.slack.circuit", "ui"), - CircuitUiState::class.java, - MemberName(packageName, name), - assistedParams - ) - } else { - val block = - if (assistedParams.isEmpty()) { - CodeBlock.of("") - } else { - CodeBlock.of(",·%L", assistedParams) + + // Modifier param is required + val modifierParam = + fd.parameters.singleOrNull { parameter -> + symbols.modifier.isAssignableFrom(parameter.type.resolve()) + } + ?: run { + logger.error("UI composable functions must have a Modifier parameter!", fd) + return null } - CodeBlock.of( - "%M<%T>·{·state·->·%M(%L·=·state%L)·}", - MemberName("com.slack.circuit", "ui"), - stateParam.type.resolve().toTypeName(), - MemberName(packageName, name), - stateParam.name!!.getShortName(), - block - ) - } + + /* + Diagram of what goes into generating a function! + - State parameter is _optional_ and can be omitted if it's static state. + - When omitted, the argument becomes _ and the param is omitted entirely. + - is either the State or CircuitUiState if no state param is used. + - Modifier parameter is required. + - Assisted parameters can be 0 or more extra supported assisted parameters. + + Optional state param + Optional state arg │ + │ │ Required modifier param + │ Req modifier arg │ │ + ┌─── ui function │ │ │ │ Any assisted params + │ │ │ Composable │ │ │ + │ State type │ │ │ │ │ │ + │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ + └──────┴───── ──┴── ───┴──── ──┴───── ───────┴───── ────────┴────────── ────────┴──────── + ui { state, modifier -> Function(state = state, modifier = modifier, ) } + ──────────────────────────────────────────────────────────────────────────────────────────────────── + + Diagram generated with asciiflow. You can make new ones or edit with this link. + https://asciiflow.com/#/share/eJzVVM1KxDAQfpVhTgr1IizLlt2CCF4F9ZhLdGclkKbd%2FMCW0rfwcXwan8SsWW27sd0qXixzmDTffN83bSY1Kp4TpspJmaDkFWlMsWa4Y5guZvOEYeWzy%2FnCZ5Z21i8Ywg%2Be29KKQnEJxnJLUHLNc8bUKIjr55jo7eXVR1R62Dplo4Xc0dYJTWvIi7XYCNIDnnpVvqjF9%2BzF2sFlsBvCCdg49bTvsVcx4vtb2u7ySlXAjRHG%2BlY%2BOjBBdYSqza6LvCwMf5Q0VS%2FqDjq%2FzVYlDfV1zPMrpWHSf6w1JeDz4HfejGCH9ybrTQT%2BUan%2FEk4s7%2Fdj%2F%2BAPUQZ1uAOSdtwuMrg5TM9ZuB9WEWb1lSawPBqL7Bwahg027%2Byjz8s%3D) + */ + + @Suppress("IfThenToElvis") // The elvis is less readable here + val stateType = + if (stateParam == null) CIRCUIT_UI_STATE else stateParam.type.resolve().toTypeName() + val stateArg = if (stateParam == null) "_" else "state" + val stateParamBlock = + if (stateParam == null) CodeBlock.of("") + else CodeBlock.of("%L·=·state,·", stateParam.name!!.getShortName()) + val modifierParamBlock = + CodeBlock.of("%L·=·modifier", modifierParam.name!!.getShortName()) + val assistedParamsBlock = + if (assistedParams.isEmpty()) { + CodeBlock.of("") + } else { + CodeBlock.of(",·%L", assistedParams) + } + CodeBlock.of( + "%M<%T>·{·%L,·modifier·->·%M(%L%L%L)·}", + MemberName("com.slack.circuit", "ui"), + stateType, + stateArg, + MemberName(packageName, name), + stateParamBlock, + modifierParamBlock, + assistedParamsBlock + ) } } } @@ -291,8 +336,8 @@ private class CircuitSymbolProcessor( .getAllSuperTypes() .mapNotNull { when (it.declaration.qualifiedName?.asString()) { - CIRCUIT_UI -> FactoryType.UI - CIRCUIT_PRESENTER -> FactoryType.PRESENTER + CIRCUIT_UI.canonicalName -> FactoryType.UI + CIRCUIT_PRESENTER.canonicalName -> FactoryType.PRESENTER else -> null } } @@ -361,7 +406,7 @@ private fun KSFunctionDeclaration.assistedParameters( symbols: CircuitSymbols, logger: KSPLogger, screenType: KSType, - allowNavigator: Boolean + allowNavigator: Boolean, ): CodeBlock { return buildSet { for (param in parameters) { @@ -410,22 +455,17 @@ private fun KSType.isInstanceOf(type: KSType): Boolean { private fun TypeSpec.Builder.buildUiFactory( originatingSymbol: KSAnnotated, screenBranch: CodeBlock, - instantiationCodeBlock: CodeBlock + instantiationCodeBlock: CodeBlock, ): TypeSpec { - return addSuperinterface(Ui.Factory::class) + return addSuperinterface(CIRCUIT_UI_FACTORY) .addFunction( FunSpec.builder("create") .addModifiers(KModifier.OVERRIDE) - .addParameter("screen", Screen::class) - .addParameter("context", CircuitContext::class) - .returns(ScreenUi::class.asClassName().copy(nullable = true)) + .addParameter("screen", SCREEN) + .addParameter("context", CIRCUIT_CONTEXT) + .returns(CIRCUIT_UI.parameterizedBy(STAR).copy(nullable = true)) .beginControlFlow("return·when·(screen)") - .addStatement( - "%L·->·%T(%L)", - screenBranch, - ScreenUi::class.asTypeName(), - instantiationCodeBlock - ) + .addStatement("%L·->·%L", screenBranch, instantiationCodeBlock) .addStatement("else·->·null") .endControlFlow() .build() @@ -437,7 +477,7 @@ private fun TypeSpec.Builder.buildUiFactory( private fun TypeSpec.Builder.buildPresenterFactory( originatingSymbol: KSAnnotated, screenBranch: CodeBlock, - instantiationCodeBlock: CodeBlock + instantiationCodeBlock: CodeBlock, ): TypeSpec { // The TypeSpec below will generate something similar to the following. // public class AboutPresenterFactory : Presenter.Factory { @@ -452,14 +492,14 @@ private fun TypeSpec.Builder.buildPresenterFactory( // } // } - return addSuperinterface(Presenter.Factory::class) + return addSuperinterface(CIRCUIT_PRESENTER_FACTORY) .addFunction( FunSpec.builder("create") .addModifiers(KModifier.OVERRIDE) - .addParameter("screen", Screen::class) - .addParameter("navigator", Navigator::class) - .addParameter("context", CircuitContext::class) - .returns(Presenter::class.asClassName().parameterizedBy(STAR).copy(nullable = true)) + .addParameter("screen", SCREEN) + .addParameter("navigator", NAVIGATOR) + .addParameter("context", CIRCUIT_CONTEXT) + .returns(CIRCUIT_PRESENTER.parameterizedBy(STAR).copy(nullable = true)) .beginControlFlow("return when (screen)") .addStatement("%L·->·%L", screenBranch, instantiationCodeBlock) .addStatement("else·->·null") From ef561e686dacc02ffa03306c329321db697fafcb Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:17:01 -0500 Subject: [PATCH 10/17] Update samples --- .../sample/counter/android/AndroidCounterCircuit.kt | 5 ++--- .../sample/counter/desktop/DesktopCounterCircuit.kt | 5 ++--- .../circuit/sample/counter/mosaic/MosaicCounterCircuit.kt | 7 +++---- .../com/slack/circuit/sample/interop/MainActivity.kt | 7 +++---- .../main/kotlin/com/slack/circuit/star/home/HomeScreen.kt | 2 +- .../kotlin/com/slack/circuit/star/petdetail/PetDetail.kt | 4 ++-- .../com/slack/circuit/star/petdetail/PetPhotoCarousel.kt | 4 ++-- .../com/slack/circuit/star/petdetail/PetDetailUiTest.kt | 8 ++++---- 8 files changed, 19 insertions(+), 23 deletions(-) diff --git a/samples/counter/android/src/main/kotlin/com/slack/circuit/sample/counter/android/AndroidCounterCircuit.kt b/samples/counter/android/src/main/kotlin/com/slack/circuit/sample/counter/android/AndroidCounterCircuit.kt index b4723b64a..136b11851 100644 --- a/samples/counter/android/src/main/kotlin/com/slack/circuit/sample/counter/android/AndroidCounterCircuit.kt +++ b/samples/counter/android/src/main/kotlin/com/slack/circuit/sample/counter/android/AndroidCounterCircuit.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.slack.circuit.CircuitContext import com.slack.circuit.Screen -import com.slack.circuit.ScreenUi import com.slack.circuit.Ui import com.slack.circuit.sample.counter.CounterEvent import com.slack.circuit.sample.counter.CounterScreen @@ -66,9 +65,9 @@ fun Counter(state: CounterState, modifier: Modifier = Modifier) { } class CounterUiFactory : Ui.Factory { - override fun create(screen: Screen, context: CircuitContext): ScreenUi? { + override fun create(screen: Screen, context: CircuitContext): Ui<*>? { return when (screen) { - is CounterScreen -> ScreenUi(ui { state -> Counter(state) }) + is CounterScreen -> ui { state, modifier -> Counter(state, modifier) } else -> null } } diff --git a/samples/counter/desktop/src/main/kotlin/com/slack/circuit/sample/counter/desktop/DesktopCounterCircuit.kt b/samples/counter/desktop/src/main/kotlin/com/slack/circuit/sample/counter/desktop/DesktopCounterCircuit.kt index 31f477530..a18598c58 100644 --- a/samples/counter/desktop/src/main/kotlin/com/slack/circuit/sample/counter/desktop/DesktopCounterCircuit.kt +++ b/samples/counter/desktop/src/main/kotlin/com/slack/circuit/sample/counter/desktop/DesktopCounterCircuit.kt @@ -27,7 +27,6 @@ import com.slack.circuit.CircuitConfig import com.slack.circuit.CircuitContent import com.slack.circuit.CircuitContext import com.slack.circuit.Screen -import com.slack.circuit.ScreenUi import com.slack.circuit.Ui import com.slack.circuit.sample.counter.CounterEvent import com.slack.circuit.sample.counter.CounterPresenterFactory @@ -67,9 +66,9 @@ fun Counter(state: CounterState, modifier: Modifier = Modifier) { } class CounterUiFactory : Ui.Factory { - override fun create(screen: Screen, context: CircuitContext): ScreenUi? { + override fun create(screen: Screen, context: CircuitContext): Ui<*>? { return when (screen) { - is CounterScreen -> ScreenUi(ui { state -> Counter(state) }) + is CounterScreen -> ui { state, modifier -> Counter(state, modifier) } else -> null } } diff --git a/samples/counter/mosaic/src/main/kotlin/com/slack/circuit/sample/counter/mosaic/MosaicCounterCircuit.kt b/samples/counter/mosaic/src/main/kotlin/com/slack/circuit/sample/counter/mosaic/MosaicCounterCircuit.kt index 18b6675d5..b59da53ee 100644 --- a/samples/counter/mosaic/src/main/kotlin/com/slack/circuit/sample/counter/mosaic/MosaicCounterCircuit.kt +++ b/samples/counter/mosaic/src/main/kotlin/com/slack/circuit/sample/counter/mosaic/MosaicCounterCircuit.kt @@ -19,7 +19,6 @@ import com.slack.circuit.CircuitConfig import com.slack.circuit.CircuitContent import com.slack.circuit.CircuitContext import com.slack.circuit.Screen -import com.slack.circuit.ScreenUi import com.slack.circuit.Ui import com.slack.circuit.sample.counter.CounterEvent import com.slack.circuit.sample.counter.CounterPresenterFactory @@ -53,15 +52,15 @@ fun main(args: Array) = MosaicCounterCommand().main(args) object MosaicCounterScreen : CounterScreen class CounterUiFactory : Ui.Factory { - override fun create(screen: Screen, context: CircuitContext): ScreenUi? { + override fun create(screen: Screen, context: CircuitContext): Ui<*>? { return when (screen) { - MosaicCounterScreen -> ScreenUi(counterUi()) + MosaicCounterScreen -> counterUi() else -> null } } } -private fun counterUi(): Ui = ui { state -> Counter(state) } +private fun counterUi(): Ui = ui { state, _ -> Counter(state) } @Composable private fun Counter(state: CounterState) { diff --git a/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt b/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt index f028dc04d..747073e11 100644 --- a/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt +++ b/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt @@ -35,7 +35,6 @@ import com.slack.circuit.CircuitContent import com.slack.circuit.CircuitContext import com.slack.circuit.Navigator import com.slack.circuit.Presenter -import com.slack.circuit.ScreenUi import com.slack.circuit.Ui import com.slack.circuit.sample.counter.CounterPresenterFactory import com.slack.circuit.sample.counter.CounterScreen @@ -58,7 +57,7 @@ class MainActivity : AppCompatActivity() { } .addUiFactory { screen, _ -> when (screen) { - is InteropCounterScreen -> ScreenUi(screen.uiSource.createUi()) + is InteropCounterScreen -> screen.uiSource.createUi() else -> null } } @@ -213,12 +212,12 @@ private enum class PresenterSource { enum class UiSource { Circuit { override fun createUi(): Ui { - return ui { state -> Counter(state) } + return ui { state, modifier -> Counter(state, modifier) } } }, View { override fun createUi(): Ui { - return ui { state -> CounterViewComposable(state) } + return ui { state, modifier -> CounterViewComposable(state, modifier) } } }; diff --git a/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt b/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt index 4a78dcc38..92856b532 100644 --- a/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt +++ b/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt @@ -75,7 +75,7 @@ fun HomeContent(state: HomeScreen.State, modifier: Modifier = Modifier) { ) { paddingValues -> Box(modifier = Modifier.padding(paddingValues)) { val screen = state.homeNavState.bottomNavItems[state.homeNavState.index].screen - CircuitContent(screen, { event -> eventSink(ChildNav(event)) }) + CircuitContent(screen, onNavEvent = { event -> eventSink(ChildNav(event)) }) } } } diff --git a/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetDetail.kt b/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetDetail.kt index 332d98bdb..d832a6784 100644 --- a/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetDetail.kt +++ b/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetDetail.kt @@ -161,12 +161,12 @@ internal object PetDetailTestConstants { @CircuitInject(PetDetailScreen::class, AppScope::class) @Composable -internal fun PetDetail(state: PetDetailScreen.State) { +internal fun PetDetail(state: PetDetailScreen.State, modifier: Modifier = Modifier) { val systemUiController = rememberSystemUiController() systemUiController.setStatusBarColor(MaterialTheme.colorScheme.background) systemUiController.setNavigationBarColor(MaterialTheme.colorScheme.background) - Scaffold(modifier = Modifier.systemBarsPadding(), topBar = { TopBar(state) }) { padding -> + Scaffold(modifier = modifier.systemBarsPadding(), topBar = { TopBar(state) }) { padding -> when (state) { is PetDetailScreen.State.Loading -> Loading(padding) is PetDetailScreen.State.UnknownAnimal -> UnknownAnimal(padding) diff --git a/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetPhotoCarousel.kt b/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetPhotoCarousel.kt index a3fb2f5a2..93c82239a 100644 --- a/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetPhotoCarousel.kt +++ b/samples/star/src/main/kotlin/com/slack/circuit/star/petdetail/PetPhotoCarousel.kt @@ -110,7 +110,7 @@ internal object PetPhotoCarouselTestConstants { @CircuitInject(PetPhotoCarouselScreen::class, AppScope::class) @OptIn(ExperimentalPagerApi::class) @Composable -internal fun PetPhotoCarousel(state: PetPhotoCarouselScreen.State) { +internal fun PetPhotoCarousel(state: PetPhotoCarouselScreen.State, modifier: Modifier = Modifier) { val (name, photoUrls, photoUrlMemoryCacheKey) = state val context = LocalContext.current val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE @@ -128,7 +128,7 @@ internal fun PetPhotoCarousel(state: PetPhotoCarouselScreen.State) { val scope = rememberCoroutineScope() val requester = remember { FocusRequester() } @Suppress("MagicNumber") - val columnModifier = if (isLandscape) Modifier.fillMaxWidth(0.5f) else Modifier.fillMaxSize() + val columnModifier = if (isLandscape) modifier.fillMaxWidth(0.5f) else modifier.fillMaxSize() Column( columnModifier .testTag(CAROUSEL_TAG) diff --git a/samples/star/src/test/kotlin/com/slack/circuit/star/petdetail/PetDetailUiTest.kt b/samples/star/src/test/kotlin/com/slack/circuit/star/petdetail/PetDetailUiTest.kt index 7e830dd26..5a171a6ba 100644 --- a/samples/star/src/test/kotlin/com/slack/circuit/star/petdetail/PetDetailUiTest.kt +++ b/samples/star/src/test/kotlin/com/slack/circuit/star/petdetail/PetDetailUiTest.kt @@ -40,10 +40,10 @@ class PetDetailUiTest { private var carouselScreen: PetPhotoCarouselScreen? = null private val circuitConfig = CircuitConfig.Builder() - .setOnUnavailableContent { screen -> + .setOnUnavailableContent { screen, modifier -> when (screen) { is PetPhotoCarouselScreen -> { - PetPhotoCarousel(PetPhotoCarouselScreen.State(screen)) + PetPhotoCarousel(PetPhotoCarouselScreen.State(screen), modifier) carouselScreen = screen } } @@ -146,8 +146,8 @@ class PetDetailUiTest { val circuitConfig = CircuitConfig.Builder() - .setOnUnavailableContent { screen -> - PetPhotoCarousel(PetPhotoCarouselScreen.State(screen as PetPhotoCarouselScreen)) + .setOnUnavailableContent { screen, modifier -> + PetPhotoCarousel(PetPhotoCarouselScreen.State(screen as PetPhotoCarouselScreen), modifier) } .build() From 09b2b9dd9fea111ed4df10b5f2cb181966526453 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:24:55 -0500 Subject: [PATCH 11/17] Plumb modifier through in places where we had to use a wrapper Box --- .../slack/circuit/sample/interop/MainActivity.kt | 13 ++++++------- .../com/slack/circuit/star/home/HomeScreen.kt | 11 ++++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt b/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt index 747073e11..42d2136d1 100644 --- a/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt +++ b/samples/interop/src/main/kotlin/com/slack/circuit/sample/interop/MainActivity.kt @@ -7,7 +7,6 @@ import android.os.Parcelable import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -88,12 +87,12 @@ class MainActivity : AppCompatActivity() { } }, ) { paddingValues -> - Box(Modifier.padding(paddingValues)) { - // TODO this is necessary because the CircuitContent caches the Ui and Presenter, which - // doesn't play well swapping out the Ui and Presenter sources. Might be nice to make - // them live enough to support this, but also sort of orthogonal to the point of this - // sample. - key(circuitScreen) { CircuitContent(screen = circuitScreen) } + // TODO this is necessary because the CircuitContent caches the Ui and Presenter, which + // doesn't play well swapping out the Ui and Presenter sources. Might be nice to make + // them live enough to support this, but also sort of orthogonal to the point of this + // sample. + key(circuitScreen) { + CircuitContent(screen = circuitScreen, modifier = Modifier.padding(paddingValues)) } } } diff --git a/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt b/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt index 92856b532..bcec55846 100644 --- a/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt +++ b/samples/star/src/main/kotlin/com/slack/circuit/star/home/HomeScreen.kt @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.slack.circuit.star.home -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding @@ -73,10 +72,12 @@ fun HomeContent(state: HomeScreen.State, modifier: Modifier = Modifier) { } } ) { paddingValues -> - Box(modifier = Modifier.padding(paddingValues)) { - val screen = state.homeNavState.bottomNavItems[state.homeNavState.index].screen - CircuitContent(screen, onNavEvent = { event -> eventSink(ChildNav(event)) }) - } + val screen = state.homeNavState.bottomNavItems[state.homeNavState.index].screen + CircuitContent( + screen, + modifier = Modifier.padding(paddingValues), + onNavEvent = { event -> eventSink(ChildNav(event)) } + ) } } From d760ff766f762ce12690ae937a6f6dddabda8392 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 16:32:02 -0500 Subject: [PATCH 12/17] Remaining doc cleanups --- .../src/commonMain/kotlin/com/slack/circuit/Ui.kt | 2 +- docs/factories.md | 14 +++++++------- docs/interop.md | 2 +- docs/navigation.md | 4 ++-- docs/ui.md | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt b/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt index 61d7e8b24..8a8d48a74 100644 --- a/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt +++ b/circuit/src/commonMain/kotlin/com/slack/circuit/Ui.kt @@ -52,7 +52,7 @@ public interface Ui { * override fun create( * screen: Screen, * context: CircuitContext - * ): ScreenUi? { + * ): Ui<*>? { * return when (screen) { * is AddFavorites -> addFavoritesUi() * else -> null diff --git a/docs/factories.md b/docs/factories.md index 863640b80..243ab9b4e 100644 --- a/docs/factories.md +++ b/docs/factories.md @@ -31,19 +31,19 @@ class FavoritesScreenPresenterFactory @Inject constructor( UI factories are similar, but generally should not aggregate other UIs unless there’s a DI-specific reason to do so (which there usually isn’t!). ```kotlin -class FavoritesScreenUiFactory : @Inject constructor() : Ui.Factory { - override fun create(screen: Screen): ScreenUi? { +class FavoritesScreenUiFactory @Inject constructor() : Ui.Factory { + override fun create(screen: Screen): Ui<*>? { return when (screen) { - is FavoritesScreen -> ScreenUi(favoritesUi()) - else null -> + is FavoritesScreen -> favoritesUi() + else -> null } } } -private fun favoritesUi() = ui { state -> Favorites(state) } +private fun favoritesUi() = ui { state, modifier -> Favorites(state, modifier) } ``` !!! info - Note how these return a `ScreenUi` class that holds the Ui instance. We are using this indirection as a toe-hold for possible other future UI metadata, such as `Modifier` instances. + Note how these include a `Modifier`. You should pass on these modifiers to your UI. [Always provide a modifier!](https://chris.banes.me/posts/always-provide-a-modifier/) -We canonically write these out as a separate function (`favoritesUi()`) that returns a `Ui`, which in turn calls through to the real (basic) Compose UI function (`Favorites()`). This ensures our basic compose functions are top-level and accessible by tests, and also discourages storing anything in class members rather than idiomatic composable state vars. +We canonically write these out as a separate function (`favoritesUi()`) that returns a `Ui`, which in turn calls through to the real (basic) Compose UI function (`Favorites()`). This ensures our basic compose functions are top-level and accessible by tests, and also discourages storing anything in class members rather than idiomatic composable state vars. If you use code gen, it handles the intermediate function for you. diff --git a/docs/interop.md b/docs/interop.md index c3af160da..9528816dc 100644 --- a/docs/interop.md +++ b/docs/interop.md @@ -21,7 +21,7 @@ You can wrap your view in an `AndroidView` in a custom `Ui` implementation. ```kotlin class ExistingCustomViewUi : Ui { @Composable - fun Content(state: State) { + fun Content(state: State, modifier: Modifier = Modifier) { AndroidView( modifier = ... factory = { context -> diff --git a/docs/navigation.md b/docs/navigation.md index 17b066256..9d512d802 100644 --- a/docs/navigation.md +++ b/docs/navigation.md @@ -48,8 +48,8 @@ setContent { Navigation carries special semantic value in `CircuitContent` as well, where it’s common for UIs to want to curry navigation events emitted by nested UIs. For this case, there’s a `CircuitContent` overload that accepts an optional onNavEvent callback that you must then forward to a Navigator instance. ```kotlin -@Composable fun ParentUi(state: ParentState) { - CircuitContent(NestedScreen, onNavEvent = { navEvent -> state.eventSink(NestedNav(navEvent)) }) +@Composable fun ParentUi(state: ParentState, modifier: Modifier = Modifier) { + CircuitContent(NestedScreen, modifier = modifier, onNavEvent = { navEvent -> state.eventSink(NestedNav(navEvent)) }) } @Composable fun ParentPresenter(navigator: Navigator): ParentState { diff --git a/docs/ui.md b/docs/ui.md index 26e7491da..e20db64a8 100644 --- a/docs/ui.md +++ b/docs/ui.md @@ -5,7 +5,7 @@ The core Ui interface is simply this: ```kotlin interface Ui { - @Composable fun Content(state: UiState) + @Composable fun Content(state: UiState, modifier: Modifier) } ``` @@ -14,7 +14,7 @@ Like presenters, simple UIs can also skip the class all together for use in othe ```kotlin @CircuitInject // Relevant DI wiring is generated @Composable -private fun Favorites(state: FavoritesState) { +private fun Favorites(state: FavoritesState, modifier: Modifier = Modifier) { // ... } ``` @@ -31,11 +31,11 @@ Let’s look a little more closely at the last bullet point about preview functi ```kotlin @Preview @Composable -private fun PreviewFavorites() = Favorites(FavoritesState(listOf("Reeses", "Lola")) +private fun PreviewFavorites() = Favorites(FavoritesState(listOf("Reeses", "Lola"))) @Preview @Composable -private fun PreviewEmptyFavorites() = Favorites(FavoritesState(listOf()) +private fun PreviewEmptyFavorites() = Favorites(FavoritesState(listOf())) ``` TODO image sample of IDE preview From 9704df1b93969edaad388c2df030936d4bfab1d2 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 17:01:28 -0500 Subject: [PATCH 13/17] Detekt isms --- .../circuit/codegen/CircuitSymbolProcessorProvider.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt index 839486f44..ccf5a4e10 100644 --- a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt +++ b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt @@ -85,15 +85,9 @@ private class CircuitSymbols private constructor(resolver: Resolver) { } } -private inline fun Resolver.loadKSType(): KSType = - loadKSType(T::class.java.canonicalName) - private fun Resolver.loadKSType(name: String?): KSType = loadOptionalKSType(name) ?: error("Could not find $name in classpath") -private inline fun Resolver.loadOptionalKSType(): KSType? = - loadOptionalKSType(T::class.java.canonicalName) - internal fun Resolver.loadOptionalKSType(name: String?): KSType? { if (name == null) return null return getClassDeclarationByName(getKSNameFromString(name))?.asType(emptyList()) @@ -197,7 +191,7 @@ private class CircuitSymbolProcessor( /** Computes the data needed to generate a factory. */ // Detekt and ktfmt don't agree on whether or not the rectangle rule makes for readable code. - @Suppress("ComplexMethod", "LongMethod") + @Suppress("ComplexMethod", "LongMethod", "ReturnCount") @OptIn(KspExperimental::class) private fun computeFactoryData( annotatedElement: KSAnnotated, From 3a2e8501c9abc48dcb28220c2ff8c85cf9558175 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 1 Feb 2023 17:03:10 -0500 Subject: [PATCH 14/17] Fix instrumentation tests --- .../com/slack/circuit/star/petdetail/PetDetailTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/star/src/androidTest/kotlin/com/slack/circuit/star/petdetail/PetDetailTest.kt b/samples/star/src/androidTest/kotlin/com/slack/circuit/star/petdetail/PetDetailTest.kt index bc545ce82..4e8ecdac2 100644 --- a/samples/star/src/androidTest/kotlin/com/slack/circuit/star/petdetail/PetDetailTest.kt +++ b/samples/star/src/androidTest/kotlin/com/slack/circuit/star/petdetail/PetDetailTest.kt @@ -91,9 +91,9 @@ class PetDetailTest { var carouselScreen: PetPhotoCarouselScreen? = null val circuitConfig = CircuitConfig.Builder() - .setOnUnavailableContent { screen -> + .setOnUnavailableContent { screen, modifier -> carouselScreen = screen as PetPhotoCarouselScreen - PetPhotoCarousel(PetPhotoCarouselScreen.State(screen)) + PetPhotoCarousel(PetPhotoCarouselScreen.State(screen), modifier) } .build() @@ -138,8 +138,8 @@ class PetDetailTest { val circuitConfig = CircuitConfig.Builder() - .setOnUnavailableContent { screen -> - PetPhotoCarousel(PetPhotoCarouselScreen.State(screen as PetPhotoCarouselScreen)) + .setOnUnavailableContent { screen, modifier -> + PetPhotoCarousel(PetPhotoCarouselScreen.State(screen as PetPhotoCarouselScreen), modifier) } .build() From b742ecac74fe5d536e1bbb286dda7eaf0ac273cf Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Thu, 2 Feb 2023 12:53:39 -0500 Subject: [PATCH 15/17] Non-null name --- .../com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt index ccf5a4e10..a09dfb1c7 100644 --- a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt +++ b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt @@ -85,7 +85,7 @@ private class CircuitSymbols private constructor(resolver: Resolver) { } } -private fun Resolver.loadKSType(name: String?): KSType = +private fun Resolver.loadKSType(name: String): KSType = loadOptionalKSType(name) ?: error("Could not find $name in classpath") internal fun Resolver.loadOptionalKSType(name: String?): KSType? { From 50a9b1c40b28d8ef05788a405e519d7d647a1dca Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Thu, 2 Feb 2023 12:53:57 -0500 Subject: [PATCH 16/17] private loadOptionalKSType --- .../com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt index a09dfb1c7..a52ce061a 100644 --- a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt +++ b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt @@ -88,7 +88,7 @@ private class CircuitSymbols private constructor(resolver: Resolver) { private fun Resolver.loadKSType(name: String): KSType = loadOptionalKSType(name) ?: error("Could not find $name in classpath") -internal fun Resolver.loadOptionalKSType(name: String?): KSType? { +private fun Resolver.loadOptionalKSType(name: String?): KSType? { if (name == null) return null return getClassDeclarationByName(getKSNameFromString(name))?.asType(emptyList()) } From a326a27f74c62e160265779d07cd3f29257f10f8 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Thu, 2 Feb 2023 12:56:16 -0500 Subject: [PATCH 17/17] Extract CIRCUIT_BASE_PACKAGE --- .../codegen/CircuitSymbolProcessorProvider.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt index a52ce061a..698bdc0ae 100644 --- a/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt +++ b/circuit-codegen/src/main/kotlin/com/slack/circuit/codegen/CircuitSymbolProcessorProvider.kt @@ -47,17 +47,18 @@ import dagger.assisted.AssistedFactory import javax.inject.Inject import javax.inject.Provider +private const val CIRCUIT_BASE_PACKAGE = "com.slack.circuit" private val MODIFIER = ClassName("androidx.compose.ui", "Modifier") private val CIRCUIT_INJECT_ANNOTATION = - ClassName("com.slack.circuit.codegen.annotations", "CircuitInject") -private val CIRCUIT_PRESENTER = ClassName("com.slack.circuit", "Presenter") + ClassName("$CIRCUIT_BASE_PACKAGE.codegen.annotations", "CircuitInject") +private val CIRCUIT_PRESENTER = ClassName(CIRCUIT_BASE_PACKAGE, "Presenter") private val CIRCUIT_PRESENTER_FACTORY = CIRCUIT_PRESENTER.nestedClass("Factory") -private val CIRCUIT_UI = ClassName("com.slack.circuit", "Ui") +private val CIRCUIT_UI = ClassName(CIRCUIT_BASE_PACKAGE, "Ui") private val CIRCUIT_UI_FACTORY = CIRCUIT_UI.nestedClass("Factory") -private val CIRCUIT_UI_STATE = ClassName("com.slack.circuit", "CircuitUiState") -private val SCREEN = ClassName("com.slack.circuit", "Screen") -private val NAVIGATOR = ClassName("com.slack.circuit", "Navigator") -private val CIRCUIT_CONTEXT = ClassName("com.slack.circuit", "CircuitContext") +private val CIRCUIT_UI_STATE = ClassName(CIRCUIT_BASE_PACKAGE, "CircuitUiState") +private val SCREEN = ClassName(CIRCUIT_BASE_PACKAGE, "Screen") +private val NAVIGATOR = ClassName(CIRCUIT_BASE_PACKAGE, "Navigator") +private val CIRCUIT_CONTEXT = ClassName(CIRCUIT_BASE_PACKAGE, "CircuitContext") private const val FACTORY = "Factory" @AutoService(SymbolProcessorProvider::class)