Skip to content

Commit

Permalink
Replace CodeListener with RedwoodView.Root (#2374)
Browse files Browse the repository at this point in the history
* Replace CodeListener with RedwoodView.Root

This moves loading/error/ready tracking to live beside the
content, rather than as something that side-effects the
content.

This is not compatible with the old API and there is no
shim.

* Self-review updates

* Fix some build problems

* Track some API changes

* Fix CI problems

* Call super.Render()

* Update samples/emoji-search/android-views/src/main/kotlin/com/example/redwood/emojisearch/android/views/EmojiSearchActivity.kt

Co-authored-by: Jake Wharton <[email protected]>

* Code review feedback

---------

Co-authored-by: Jake Wharton <[email protected]>
  • Loading branch information
squarejesse and JakeWharton authored Oct 16, 2024
1 parent 54f3477 commit d32766e
Show file tree
Hide file tree
Showing 56 changed files with 965 additions and 670 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Fixed:
- Fix a layout bug where children of fixed-with `Row` containers were assigned the wrong width.
- Fix inconsistencies between iOS and Android for `Column` and `Row` layouts.

Breaking:
- Replace `CodeListener` with `RedwoodView.Root`. This puts the loading/error/ready state with the UI that displays that state.


## [0.15.0] - 2024-09-30
[0.15.0]: https://github.com/cashapp/redwood/releases/tag/0.15.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public fun <W : Any> RedwoodComposition(
widgetSystem: WidgetSystem<W>,
onEndChanges: () -> Unit = {},
): RedwoodComposition {
view.reset()
view.root.children.remove(0, view.root.children.widgets.size)

val saveableStateRegistry = view.savedStateRegistry?.let { viewRegistry ->
val state = viewRegistry.consumeRestoredState()
Expand Down Expand Up @@ -95,7 +95,7 @@ public fun <W : Any> RedwoodComposition(

return RedwoodComposition(
scope,
view.children,
view.root.children,
view.onBackPressedDispatcher,
saveableStateRegistry,
view.uiConfiguration,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
public class app/cash/redwood/treehouse/composeui/ComposeUiRoot : app/cash/redwood/widget/RedwoodView$Root {
public static final field $stable I
public fun <init> ()V
public fun Render (Landroidx/compose/runtime/Composer;I)V
public fun contentState (IZLjava/lang/Throwable;)V
public synthetic fun getChildren ()Lapp/cash/redwood/widget/Widget$Children;
public fun getChildren ()Lapp/cash/redwood/widget/compose/ComposeWidgetChildren;
public fun getModifier ()Lapp/cash/redwood/Modifier;
public synthetic fun getValue ()Ljava/lang/Object;
public fun getValue ()Lkotlin/jvm/functions/Function2;
public fun restart (Lkotlin/jvm/functions/Function0;)V
public fun setModifier (Lapp/cash/redwood/Modifier;)V
}

public final class app/cash/redwood/treehouse/composeui/TreehouseContentKt {
public static final fun TreehouseContent (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Lapp/cash/redwood/treehouse/TreehouseContentSource;Landroidx/compose/ui/Modifier;Lapp/cash/redwood/treehouse/CodeListener;Landroidx/compose/runtime/Composer;II)V
public static final fun TreehouseContent (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Lapp/cash/redwood/treehouse/TreehouseContentSource;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
public class app/cash/redwood/treehouse/composeui/ComposeUiRoot : app/cash/redwood/widget/RedwoodView$Root {
public static final field $stable I
public fun <init> ()V
public fun Render (Landroidx/compose/runtime/Composer;I)V
public fun contentState (IZLjava/lang/Throwable;)V
public synthetic fun getChildren ()Lapp/cash/redwood/widget/Widget$Children;
public fun getChildren ()Lapp/cash/redwood/widget/compose/ComposeWidgetChildren;
public fun getModifier ()Lapp/cash/redwood/Modifier;
public synthetic fun getValue ()Ljava/lang/Object;
public fun getValue ()Lkotlin/jvm/functions/Function2;
public fun restart (Lkotlin/jvm/functions/Function0;)V
public fun setModifier (Lapp/cash/redwood/Modifier;)V
}

public final class app/cash/redwood/treehouse/composeui/TreehouseContentKt {
public static final fun TreehouseContent (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Lapp/cash/redwood/treehouse/TreehouseContentSource;Landroidx/compose/ui/Modifier;Lapp/cash/redwood/treehouse/CodeListener;Landroidx/compose/runtime/Composer;II)V
public static final fun TreehouseContent (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Lapp/cash/redwood/treehouse/TreehouseContentSource;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,24 @@
// - Show declarations: true

// Library unique name: <app.cash.redwood:redwood-treehouse-host-composeui>
final fun <#A: app.cash.redwood.treehouse/AppService> app.cash.redwood.treehouse.composeui/TreehouseContent(app.cash.redwood.treehouse/TreehouseApp<#A>, app.cash.redwood.treehouse/TreehouseView.WidgetSystem<kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>>, app.cash.redwood.treehouse/TreehouseContentSource<#A>, androidx.compose.ui/Modifier?, app.cash.redwood.treehouse/CodeListener?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // app.cash.redwood.treehouse.composeui/TreehouseContent|TreehouseContent(app.cash.redwood.treehouse.TreehouseApp<0:0>;app.cash.redwood.treehouse.TreehouseView.WidgetSystem<kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>>;app.cash.redwood.treehouse.TreehouseContentSource<0:0>;androidx.compose.ui.Modifier?;app.cash.redwood.treehouse.CodeListener?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<app.cash.redwood.treehouse.AppService>}[0]
open class app.cash.redwood.treehouse.composeui/ComposeUiRoot : app.cash.redwood.widget/RedwoodView.Root<kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>> { // app.cash.redwood.treehouse.composeui/ComposeUiRoot|null[0]
constructor <init>() // app.cash.redwood.treehouse.composeui/ComposeUiRoot.<init>|<init>(){}[0]

open val children // app.cash.redwood.treehouse.composeui/ComposeUiRoot.children|{}children[0]
open fun <get-children>(): app.cash.redwood.widget.compose/ComposeWidgetChildren // app.cash.redwood.treehouse.composeui/ComposeUiRoot.children.<get-children>|<get-children>(){}[0]
open val value // app.cash.redwood.treehouse.composeui/ComposeUiRoot.value|{}value[0]
open fun <get-value>(): kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit> // app.cash.redwood.treehouse.composeui/ComposeUiRoot.value.<get-value>|<get-value>(){}[0]

open var modifier // app.cash.redwood.treehouse.composeui/ComposeUiRoot.modifier|{}modifier[0]
open fun <get-modifier>(): app.cash.redwood/Modifier // app.cash.redwood.treehouse.composeui/ComposeUiRoot.modifier.<get-modifier>|<get-modifier>(){}[0]
open fun <set-modifier>(app.cash.redwood/Modifier) // app.cash.redwood.treehouse.composeui/ComposeUiRoot.modifier.<set-modifier>|<set-modifier>(app.cash.redwood.Modifier){}[0]

open fun Render(androidx.compose.runtime/Composer?, kotlin/Int) // app.cash.redwood.treehouse.composeui/ComposeUiRoot.Render|Render(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
open fun contentState(kotlin/Int, kotlin/Boolean, kotlin/Throwable?) // app.cash.redwood.treehouse.composeui/ComposeUiRoot.contentState|contentState(kotlin.Int;kotlin.Boolean;kotlin.Throwable?){}[0]
open fun restart(kotlin/Function0<kotlin/Unit>?) // app.cash.redwood.treehouse.composeui/ComposeUiRoot.restart|restart(kotlin.Function0<kotlin.Unit>?){}[0]
}

final val app.cash.redwood.treehouse.composeui/app_cash_redwood_treehouse_composeui_ComposeUiRoot$stableprop // app.cash.redwood.treehouse.composeui/app_cash_redwood_treehouse_composeui_ComposeUiRoot$stableprop|#static{}app_cash_redwood_treehouse_composeui_ComposeUiRoot$stableprop[0]

final fun <#A: app.cash.redwood.treehouse/AppService> app.cash.redwood.treehouse.composeui/TreehouseContent(app.cash.redwood.treehouse/TreehouseApp<#A>, app.cash.redwood.treehouse/TreehouseView.WidgetSystem<kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>>, app.cash.redwood.treehouse/TreehouseContentSource<#A>, androidx.compose.ui/Modifier?, kotlin/Function1<kotlinx.coroutines/CoroutineScope, app.cash.redwood.widget/RedwoodView.Root<kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>>>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // app.cash.redwood.treehouse.composeui/TreehouseContent|TreehouseContent(app.cash.redwood.treehouse.TreehouseApp<0:0>;app.cash.redwood.treehouse.TreehouseView.WidgetSystem<kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>>;app.cash.redwood.treehouse.TreehouseContentSource<0:0>;androidx.compose.ui.Modifier?;kotlin.Function1<kotlinx.coroutines.CoroutineScope,app.cash.redwood.widget.RedwoodView.Root<kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>>>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<app.cash.redwood.treehouse.AppService>}[0]
final fun app.cash.redwood.treehouse.composeui/app_cash_redwood_treehouse_composeui_ComposeUiRoot$stableprop_getter(): kotlin/Int // app.cash.redwood.treehouse.composeui/app_cash_redwood_treehouse_composeui_ComposeUiRoot$stableprop_getter|app_cash_redwood_treehouse_composeui_ComposeUiRoot$stableprop_getter(){}[0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2024 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.redwood.treehouse.composeui

import androidx.compose.runtime.Composable
import app.cash.redwood.Modifier
import app.cash.redwood.widget.RedwoodView
import app.cash.redwood.widget.compose.ComposeWidgetChildren

/**
* A default base implementation of [RedwoodView.Root].
*
* This composition contributes nothing to the view hierarchy. It delegates directly to its child
* views.
*/
public open class ComposeUiRoot : RedwoodView.Root<@Composable () -> Unit> {
override val children: ComposeWidgetChildren = ComposeWidgetChildren()

override var modifier: Modifier = Modifier

override fun contentState(
loadCount: Int,
attached: Boolean,
uncaughtException: Throwable?,
) {
}

override fun restart(restart: (() -> Unit)?) {
}

override val value: @Composable () -> Unit = {
Render()
}

@Composable
public open fun Render() {
children.Render()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
Expand All @@ -31,7 +32,6 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
import app.cash.redwood.composeui.safeAreaInsets
import app.cash.redwood.treehouse.AppService
import app.cash.redwood.treehouse.CodeListener
import app.cash.redwood.treehouse.StateSnapshot
import app.cash.redwood.treehouse.TreehouseApp
import app.cash.redwood.treehouse.TreehouseContentSource
Expand All @@ -45,8 +45,9 @@ import app.cash.redwood.ui.OnBackPressedDispatcher
import app.cash.redwood.ui.Size
import app.cash.redwood.ui.UiConfiguration
import app.cash.redwood.ui.dp as redwoodDp
import app.cash.redwood.widget.RedwoodView
import app.cash.redwood.widget.SavedStateRegistry
import app.cash.redwood.widget.compose.ComposeWidgetChildren
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow

@Composable
Expand All @@ -55,9 +56,10 @@ public fun <A : AppService> TreehouseContent(
widgetSystem: WidgetSystem<@Composable () -> Unit>,
contentSource: TreehouseContentSource<A>,
modifier: Modifier = Modifier,
codeListener: CodeListener = remember { CodeListener() },
root: ((CoroutineScope) -> RedwoodView.Root<@Composable () -> Unit>) = { _ -> ComposeUiRoot() },
) {
val onBackPressedDispatcher = platformOnBackPressedDispatcher()
val scope = rememberCoroutineScope()

var viewportSize: Size? by remember { mutableStateOf(null) }
val density = LocalDensity.current
Expand All @@ -71,10 +73,9 @@ public fun <A : AppService> TreehouseContent(
LayoutDirection.Rtl -> RedwoodLayoutDirection.Rtl
},
)

val treehouseView = remember(widgetSystem) {
object : TreehouseView<@Composable () -> Unit> {
override val children = ComposeWidgetChildren()
override val root: RedwoodView.Root<@Composable () -> Unit> = root(scope)
override val onBackPressedDispatcher = onBackPressedDispatcher
override val uiConfiguration = MutableStateFlow(uiConfiguration)

Expand All @@ -86,14 +87,13 @@ public fun <A : AppService> TreehouseContent(
override var readyForContentChangeListener: ReadyForContentChangeListener<@Composable () -> Unit>? = null
override var saveCallback: TreehouseView.SaveCallback? = null
override val stateSnapshotId = StateSnapshot.Id(null)
override fun reset() = children.remove(0, children.widgets.size)
}
}
LaunchedEffect(treehouseView, uiConfiguration) {
treehouseView.uiConfiguration.value = uiConfiguration
}
DisposableEffect(treehouseView, contentSource, codeListener) {
val closeable = contentSource.bindWhenReady(treehouseView, treehouseApp, codeListener)
DisposableEffect(treehouseView, contentSource) {
val closeable = contentSource.bindWhenReady(treehouseView, treehouseApp)
onDispose {
closeable.close()
}
Expand All @@ -106,7 +106,7 @@ public fun <A : AppService> TreehouseContent(
}
},
) {
treehouseView.children.Render()
treehouseView.root.value()
}
}

Expand Down
17 changes: 4 additions & 13 deletions redwood-treehouse-host/api/android/redwood-treehouse-host.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ public final class app/cash/redwood/treehouse/ChangeListRenderer {
public final fun render-rqC_l18 (Lapp/cash/redwood/treehouse/TreehouseView;Ljava/util/List;)V
}

public class app/cash/redwood/treehouse/CodeListener {
public fun <init> ()V
public fun onCodeDetached (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView;Ljava/lang/Throwable;)V
public fun onCodeLoaded (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView;Z)V
public fun onInitialCodeLoading (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseView;)V
}

public abstract interface class app/cash/redwood/treehouse/Content {
public abstract fun awaitContent (ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun awaitContent$default (Lapp/cash/redwood/treehouse/Content;ILkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
Expand All @@ -20,8 +13,7 @@ public abstract interface class app/cash/redwood/treehouse/Content {

public final class app/cash/redwood/treehouse/ContentBindingKt {
public static final fun bindWhenReady (Lapp/cash/redwood/treehouse/Content;Lapp/cash/redwood/treehouse/TreehouseView;)Ljava/io/Closeable;
public static final fun bindWhenReady (Lapp/cash/redwood/treehouse/TreehouseContentSource;Lapp/cash/redwood/treehouse/TreehouseView;Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/CodeListener;)Ljava/io/Closeable;
public static synthetic fun bindWhenReady$default (Lapp/cash/redwood/treehouse/TreehouseContentSource;Lapp/cash/redwood/treehouse/TreehouseView;Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/CodeListener;ILjava/lang/Object;)Ljava/io/Closeable;
public static final fun bindWhenReady (Lapp/cash/redwood/treehouse/TreehouseContentSource;Lapp/cash/redwood/treehouse/TreehouseView;Lapp/cash/redwood/treehouse/TreehouseApp;)Ljava/io/Closeable;
}

public class app/cash/redwood/treehouse/EventListener {
Expand Down Expand Up @@ -82,8 +74,7 @@ public abstract interface class app/cash/redwood/treehouse/StateStore {
public abstract class app/cash/redwood/treehouse/TreehouseApp : java/lang/AutoCloseable {
public fun <init> ()V
public abstract fun close ()V
public abstract fun createContent (Lapp/cash/redwood/treehouse/TreehouseContentSource;Lapp/cash/redwood/treehouse/CodeListener;)Lapp/cash/redwood/treehouse/Content;
public static synthetic fun createContent$default (Lapp/cash/redwood/treehouse/TreehouseApp;Lapp/cash/redwood/treehouse/TreehouseContentSource;Lapp/cash/redwood/treehouse/CodeListener;ILjava/lang/Object;)Lapp/cash/redwood/treehouse/Content;
public abstract fun createContent (Lapp/cash/redwood/treehouse/TreehouseContentSource;)Lapp/cash/redwood/treehouse/Content;
public abstract fun getDispatchers ()Lapp/cash/redwood/treehouse/TreehouseDispatchers;
public abstract fun getName ()Ljava/lang/String;
public abstract fun getZipline ()Lkotlinx/coroutines/flow/StateFlow;
Expand Down Expand Up @@ -126,8 +117,8 @@ public abstract interface class app/cash/redwood/treehouse/TreehouseDispatchers
}

public final class app/cash/redwood/treehouse/TreehouseLayout : app/cash/redwood/widget/RedwoodLayout, app/cash/redwood/treehouse/TreehouseView {
public fun <init> (Landroid/content/Context;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Landroidx/activity/OnBackPressedDispatcher;)V
public synthetic fun generateDefaultLayoutParams ()Landroid/view/ViewGroup$LayoutParams;
public fun <init> (Landroid/content/Context;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Landroidx/activity/OnBackPressedDispatcher;Lapp/cash/redwood/widget/RedwoodView$Root;)V
public synthetic fun <init> (Landroid/content/Context;Lapp/cash/redwood/treehouse/TreehouseView$WidgetSystem;Landroidx/activity/OnBackPressedDispatcher;Lapp/cash/redwood/widget/RedwoodView$Root;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getReadyForContent ()Z
public fun getReadyForContentChangeListener ()Lapp/cash/redwood/treehouse/TreehouseView$ReadyForContentChangeListener;
public fun getSaveCallback ()Lapp/cash/redwood/treehouse/TreehouseView$SaveCallback;
Expand Down
Loading

0 comments on commit d32766e

Please sign in to comment.