From d12783c4e0e5dcdce0fe77b214f6e3ff209c2bcb Mon Sep 17 00:00:00 2001 From: Veyndan Stuart Date: Thu, 30 Nov 2023 15:19:10 +0100 Subject: [PATCH] De-duplicate loading logic in `TreehouseAppContent#preload` and `TreehouseAppContent#bind` (#1714) --- .../app/cash/redwood/treehouse/Content.kt | 3 +- .../redwood/treehouse/TreehouseAppContent.kt | 40 ++++++------------- .../cash/redwood/treehouse/CodeHostTest.kt | 2 +- .../redwood/treehouse/FakeTreehouseView.kt | 6 +-- .../treehouse/TreehouseAppContentTest.kt | 5 ++- 5 files changed, 21 insertions(+), 35 deletions(-) diff --git a/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/Content.kt b/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/Content.kt index 585d3b1e07..1399d12e1f 100644 --- a/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/Content.kt +++ b/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/Content.kt @@ -19,6 +19,7 @@ import app.cash.redwood.ui.OnBackPressedDispatcher import app.cash.redwood.ui.UiConfiguration import kotlin.native.ObjCName import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.flow.StateFlow /** * A UI built as an interactive widget tree, that may or may not be actively running, or bound to an @@ -43,7 +44,7 @@ public interface Content { */ public fun preload( onBackPressedDispatcher: OnBackPressedDispatcher, - uiConfiguration: UiConfiguration, + uiConfiguration: StateFlow, ) /** diff --git a/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt b/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt index c2d082c712..3d0126cd73 100644 --- a/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt +++ b/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt @@ -51,10 +51,10 @@ private sealed interface ViewState { data class Preloading( val onBackPressedDispatcher: OnBackPressedDispatcher, - val uiConfiguration: UiConfiguration, + val uiConfiguration: StateFlow, ) : ViewState - class Bound( + data class Bound( val view: TreehouseView<*>, ) : ViewState } @@ -82,7 +82,7 @@ internal class TreehouseAppContent( override fun preload( onBackPressedDispatcher: OnBackPressedDispatcher, - uiConfiguration: UiConfiguration, + uiConfiguration: StateFlow, ) { dispatchers.checkUi() val previousState = stateFlow.value @@ -101,7 +101,7 @@ internal class TreehouseAppContent( codeSession = codeSession, isInitialLaunch = true, onBackPressedDispatcher = onBackPressedDispatcher, - firstUiConfiguration = MutableStateFlow(nextViewState.uiConfiguration), + firstUiConfiguration = uiConfiguration, ), ) } @@ -116,34 +116,18 @@ internal class TreehouseAppContent( override fun bind(view: TreehouseView<*>) { dispatchers.checkUi() + + if (stateFlow.value.viewState == ViewState.Bound(view)) return // Idempotent. + + preload(view.onBackPressedDispatcher, view.uiConfiguration) + val previousState = stateFlow.value val previousViewState = previousState.viewState - if (previousViewState is ViewState.Bound && previousViewState.view == view) return // Idempotent. - check(previousViewState is ViewState.None || previousViewState is ViewState.Preloading) + check(previousViewState is ViewState.Preloading) val nextViewState = ViewState.Bound(view) - - // Start the code if necessary. - val codeSession = codeHost.codeSession - val nextCodeState = when { - previousState.codeState is CodeState.Idle && codeSession != null -> { - CodeState.Running( - startViewCodeContentBinding( - codeSession = codeSession, - isInitialLaunch = true, - onBackPressedDispatcher = nextViewState.view.onBackPressedDispatcher, - firstUiConfiguration = nextViewState.view.uiConfiguration, - ), - ) - } - else -> previousState.codeState - } - - // Ask to get notified when code is ready. - if (previousViewState is ViewState.None) { - codeHost.addListener(this) - } + val nextCodeState = previousState.codeState // Make sure we're showing something in the view; either loaded code or a spinner to show that // code is coming. @@ -201,7 +185,7 @@ internal class TreehouseAppContent( } val uiConfiguration = when (viewState) { - is ViewState.Preloading -> MutableStateFlow(viewState.uiConfiguration) + is ViewState.Preloading -> viewState.uiConfiguration is ViewState.Bound -> viewState.view.uiConfiguration else -> error("unexpected receiveCodeSession with no view bound and no preload") } diff --git a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/CodeHostTest.kt b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/CodeHostTest.kt index e16aca16b4..95fbd18576 100644 --- a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/CodeHostTest.kt +++ b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/CodeHostTest.kt @@ -294,6 +294,6 @@ class CodeHostTest { } private fun treehouseView(name: String): FakeTreehouseView { - return FakeTreehouseView(onBackPressedDispatcher, name) + return FakeTreehouseView(name, onBackPressedDispatcher) } } diff --git a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeTreehouseView.kt b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeTreehouseView.kt index 920aeaa6ff..f58a05fe6a 100644 --- a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeTreehouseView.kt +++ b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeTreehouseView.kt @@ -20,10 +20,12 @@ import app.cash.redwood.ui.UiConfiguration import app.cash.redwood.widget.MutableListChildren import app.cash.redwood.widget.SavedStateRegistry import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow internal class FakeTreehouseView( - override val onBackPressedDispatcher: FakeOnBackPressedDispatcher, private val name: String, + override val onBackPressedDispatcher: FakeOnBackPressedDispatcher, + override val uiConfiguration: StateFlow = MutableStateFlow(UiConfiguration()), ) : TreehouseView { override val widgetSystem = FakeWidgetSystem() @@ -41,8 +43,6 @@ internal class FakeTreehouseView( override val children = MutableListChildren() - override val uiConfiguration = MutableStateFlow(UiConfiguration()) - override val savedStateRegistry: SavedStateRegistry? = null override fun reset() { diff --git a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/TreehouseAppContentTest.kt b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/TreehouseAppContentTest.kt index 3c6f0e7b0c..52efee93b6 100644 --- a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/TreehouseAppContentTest.kt +++ b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/TreehouseAppContentTest.kt @@ -27,6 +27,7 @@ import kotlin.test.Test import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest @@ -52,7 +53,7 @@ class TreehouseAppContentTest { frameClockFactory = FakeFrameClock.Factory, ) private val codeEventPublisher = FakeCodeEventPublisher(eventLog) - private val uiConfiguration = UiConfiguration() + private val uiConfiguration = MutableStateFlow(UiConfiguration()) @BeforeTest fun setUp() { @@ -487,6 +488,6 @@ class TreehouseAppContentTest { } private fun treehouseView(name: String): FakeTreehouseView { - return FakeTreehouseView(onBackPressedDispatcher, name) + return FakeTreehouseView(name, onBackPressedDispatcher, uiConfiguration) } }