From a8544ae134ddcd6d98862219affed51408f1f8cb Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 31 Jan 2024 18:26:52 -0500 Subject: [PATCH] WIP persist tab state This is just some tinkering to resolve #116 and after talking with @BryanStern. Wanna iterate on this more and see if we can make this simpler, and also possibly explore implementing an analogous `RetainedStateHolder`. Another thing this highlights is the fact that we always initially show a progress indicator even when restoring from a loaded state, wonder what we can do to improve that. --- .../circuit/foundation/CircuitContent.kt | 20 +---------- .../com/slack/circuit/foundation/NavEvent.kt | 25 ++++++++++++++ .../com/slack/circuit/runtime/Navigator.kt | 2 ++ .../com/slack/circuit/star/home/HomeScreen.kt | 33 +++++++++++++++---- 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/CircuitContent.kt b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/CircuitContent.kt index b5611cb9f..9306904b8 100644 --- a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/CircuitContent.kt +++ b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/CircuitContent.kt @@ -39,25 +39,7 @@ public fun CircuitContent( ) { val navigator = remember(onNavEvent) { - object : Navigator { - override fun goTo(screen: Screen) { - onNavEvent(NavEvent.GoTo(screen)) - } - - override fun resetRoot(newRoot: Screen): List { - onNavEvent(NavEvent.ResetRoot(newRoot)) - return emptyList() - } - - override fun pop(): Screen? { - onNavEvent(NavEvent.Pop) - return null - } - - override fun peek(): Screen { - return screen - } - } + Navigator.navEventNavigator(screen, onNavEvent) } CircuitContent(screen, navigator, modifier, circuit, unavailableContent) } diff --git a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavEvent.kt b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavEvent.kt index bc373bd36..9f66eed2b 100644 --- a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavEvent.kt +++ b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavEvent.kt @@ -19,6 +19,31 @@ public fun Navigator.onNavEvent(event: NavEvent) { } } +public fun Navigator.Companion.navEventNavigator( + screen: Screen, + onNavEvent: (event: NavEvent) -> Unit, +): Navigator { + return object : Navigator { + override fun goTo(screen: Screen) { + onNavEvent(NavEvent.GoTo(screen)) + } + + override fun resetRoot(newRoot: Screen): List { + onNavEvent(NavEvent.ResetRoot(newRoot)) + return emptyList() + } + + override fun pop(): Screen? { + onNavEvent(NavEvent.Pop) + return null + } + + override fun peek(): Screen { + return screen + } + } +} + /** A sealed navigation interface intended to be used when making a navigation callback. */ public sealed interface NavEvent : CircuitUiEvent { /** Corresponds to [Navigator.pop]. */ diff --git a/circuit-runtime/src/commonMain/kotlin/com/slack/circuit/runtime/Navigator.kt b/circuit-runtime/src/commonMain/kotlin/com/slack/circuit/runtime/Navigator.kt index b4ab03287..c7aac015b 100644 --- a/circuit-runtime/src/commonMain/kotlin/com/slack/circuit/runtime/Navigator.kt +++ b/circuit-runtime/src/commonMain/kotlin/com/slack/circuit/runtime/Navigator.kt @@ -43,6 +43,8 @@ public interface Navigator { override fun resetRoot(newRoot: Screen): List = emptyList() } + + public companion object } /** Calls [Navigator.pop] until the given [predicate] is matched or it pops the root. */ diff --git a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt index e1c9d37c1..1d2243120 100644 --- a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt +++ b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt @@ -19,11 +19,14 @@ import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.slack.circuit.backstack.rememberSaveableBackStack import com.slack.circuit.codegen.annotations.CircuitInject import com.slack.circuit.foundation.CircuitContent +import com.slack.circuit.foundation.LocalCircuit import com.slack.circuit.foundation.NavEvent +import com.slack.circuit.foundation.navEventNavigator import com.slack.circuit.foundation.onNavEvent +import com.slack.circuit.foundation.rememberPresenter +import com.slack.circuit.foundation.rememberUi import com.slack.circuit.retained.rememberRetained import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState @@ -85,12 +88,28 @@ fun HomeContent(state: HomeScreen.State, modifier: Modifier = Modifier) { } }, ) { paddingValues -> - val screen = state.navItems[state.selectedIndex].screen - CircuitContent( - screen, - modifier = Modifier.padding(paddingValues), - onNavEvent = { event -> state.eventSink(ChildNav(event)) }, - ) + val saveableStateHolder = rememberSaveableStateHolder() + val currentScreen = state.navItems[state.selectedIndex].screen + saveableStateHolder.SaveableStateProvider(currentScreen) { + val circuit = requireNotNull(LocalCircuit.current) + val ui = rememberUi(currentScreen, factory = circuit::ui) + val presenter = + rememberPresenter( + currentScreen, + navigator = + Navigator.navEventNavigator(currentScreen) { event -> + state.eventSink(ChildNav(event)) + }, + factory = circuit::presenter, + ) + + CircuitContent( + screen = currentScreen, + modifier = Modifier.padding(paddingValues), + presenter = presenter!!, + ui = ui!!, + ) + } contentComposed = true } Platform.ReportDrawnWhen { contentComposed }