diff --git a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt index dd505bfd9..f45a07f54 100644 --- a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt +++ b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt @@ -227,6 +227,28 @@ internal fun App( resultStateRegistry = resultStateRegistry, barManager = barManager, deepLink = { deepLink }, + onPendingAction = { action -> + deeplinkHandled = true + when (action) { + is DeeplinkAction.OpenCashLink -> + session.openCashLink(action.entropy) + is DeeplinkAction.Login -> + viewModel.handleLoginEntropy( + action.entropy, + onSwitchAccount = { + codeNavigator.replaceAll( + AppRoute.OnboardingFlow( + seed = action.entropy, + fromDeeplink = true + ) + ) + }, + onDismissed = { } + ) + else -> {} + } + deepLink = null + }, ), ) diff --git a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt index 7c903c145..cbc4ff514 100644 --- a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt +++ b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt @@ -27,6 +27,7 @@ import com.flipcash.app.cash.CashScreen import com.flipcash.app.contact.verification.VerificationFlowScreen import com.flipcash.app.currencycreator.CurrencyCreatorFlowScreen import com.flipcash.app.core.AppRoute +import com.flipcash.app.core.navigation.DeeplinkAction import com.flipcash.app.currency.RegionSelectionScreen import com.flipcash.app.deposit.DepositFlowScreen import com.flipcash.app.directsend.SendFlowScreen @@ -67,10 +68,11 @@ fun appEntryProvider( resultStateRegistry: NavResultStateRegistry, barManager: BarManager, deepLink: () -> DeepLink?, + onPendingAction: (DeeplinkAction) -> Unit = {}, ): (NavKey) -> NavEntry = entryProvider { // Loading / splash - annotatedEntry { MainRoot(deepLink) } + annotatedEntry { MainRoot(deepLink, onPendingAction) } // Onboarding flow annotatedEntry { key -> diff --git a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/MainRoot.kt b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/MainRoot.kt index 4392ab73d..846746ca4 100644 --- a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/MainRoot.kt +++ b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/MainRoot.kt @@ -44,7 +44,10 @@ import kotlinx.coroutines.flow.onEach import kotlin.time.Duration.Companion.seconds @Composable -internal fun MainRoot(deepLink: () -> DeepLink?) { +internal fun MainRoot( + deepLink: () -> DeepLink?, + onPendingAction: (DeeplinkAction) -> Unit = {}, +) { val navigator = LocalCodeNavigator.current val userManager = LocalUserManager.current!! var showLoading by remember { mutableStateOf(false) } @@ -129,6 +132,13 @@ internal fun MainRoot(deepLink: () -> DeepLink?) { navigator.navigateAll(launch.deeplinkRoutes) } } + + // Fire eagerly so the claim/login starts in parallel + // with the nav transition instead of waiting for + // App.kt's LaunchedEffect to see a non-Loading route. + if (launch.pendingAction != null) { + onPendingAction(launch.pendingAction) + } } }.launchIn(this) } @@ -144,6 +154,7 @@ internal fun MainRoot(deepLink: () -> DeepLink?) { internal data class LaunchNavGraph( val baseRoutes: List, val deeplinkRoutes: List = emptyList(), + val pendingAction: DeeplinkAction? = null, ) { /** * Predict the final backstack that [baseRoutes] + [navigateTo(deeplinkRoutes)] will produce. @@ -192,8 +203,13 @@ internal fun buildNavGraphForLaunch( baseRoutes = listOf(AppRoute.Main.Scanner), deeplinkRoutes = action.routes, ) - // OpenCashLink/Login/ExternalWallet are handled by App.kt's - // LaunchedEffect(deepLink, currentRoute) once we leave Loading. + + is DeeplinkAction.OpenCashLink, + is DeeplinkAction.Login -> LaunchNavGraph( + baseRoutes = listOf(AppRoute.Main.Scanner), + pendingAction = action, + ) + else -> LaunchNavGraph(listOf(AppRoute.Main.Scanner)) } } else { diff --git a/apps/flipcash/app/src/test/kotlin/com/flipcash/app/internal/ui/navigation/BuildNavGraphForLaunchTest.kt b/apps/flipcash/app/src/test/kotlin/com/flipcash/app/internal/ui/navigation/BuildNavGraphForLaunchTest.kt index dc99089af..57baf9f9c 100644 --- a/apps/flipcash/app/src/test/kotlin/com/flipcash/app/internal/ui/navigation/BuildNavGraphForLaunchTest.kt +++ b/apps/flipcash/app/src/test/kotlin/com/flipcash/app/internal/ui/navigation/BuildNavGraphForLaunchTest.kt @@ -56,25 +56,29 @@ class BuildNavGraphForLaunchTest { } @Test - fun `logged in with OpenCashLink defers to App for dispatch`() { + fun `logged in with OpenCashLink fires eagerly via pendingAction`() { + val action = DeeplinkAction.OpenCashLink("testEntropy") val result = build( state = AuthState.Ready, - action = DeeplinkAction.OpenCashLink("testEntropy"), + action = action, deepLink = dummyLink, )!! assertEquals(listOf(AppRoute.Main.Scanner), result.baseRoutes) - assertTrue(result.deeplinkRoutes.isEmpty(), "OpenCashLink must not be consumed by MainRoot") + assertTrue(result.deeplinkRoutes.isEmpty()) + assertEquals(action, result.pendingAction) } @Test - fun `logged in with Login action defers to App for dispatch`() { + fun `logged in with Login action fires eagerly via pendingAction`() { + val action = DeeplinkAction.Login("seed") val result = build( state = AuthState.Ready, - action = DeeplinkAction.Login("seed"), + action = action, deepLink = dummyLink, )!! assertEquals(listOf(AppRoute.Main.Scanner), result.baseRoutes) - assertTrue(result.deeplinkRoutes.isEmpty(), "Login must not be consumed by MainRoot") + assertTrue(result.deeplinkRoutes.isEmpty()) + assertEquals(action, result.pendingAction) } @Test