diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 2f94cf9c8f..02fed66981 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -953,8 +953,8 @@ private fun RootNavHost( app = appViewModel, wallet = walletViewModel, transfer = transferViewModel, - onContinueClick = { navController.popBackStack(inclusive = true) }, - onTransferUnavailable = { navController.popBackStack(inclusive = true) }, + onContinueClick = { navController.navigateOnSavingsTransferExit() }, + onTransferUnavailable = { navController.navigateOnSavingsTransferExit() }, ) } deepLinkableComposable { @@ -2090,6 +2090,17 @@ fun NavController.navigateToTransferSavingsIntro() = navigateTo(Routes.SavingsIn fun NavController.navigateToTransferSavingsAvailability() = navigateTo(Routes.SavingsAvailability) +/** + * Exits the savings transfer to home. The coop close retry job holds on to this callback for up to + * 30 minutes, so it is ignored unless the savings progress screen that owns it is still on screen. + * Matching the whole transfer graph would also pop a transfer to spending the user started since. + */ +fun NavController.navigateOnSavingsTransferExit() { + val isOnSavingsProgress = currentDestination?.hasRoute() == true + if (!isOnSavingsProgress) return + navigateToHome() +} + fun NavController.navigateToTransferSpendingStart(hasSeenSpendingIntro: Boolean) = navigateTo(transferSpendingStartRoute(hasSeenSpendingIntro)) diff --git a/app/src/test/java/to/bitkit/ui/ContentViewTest.kt b/app/src/test/java/to/bitkit/ui/ContentViewTest.kt index 367b06b885..270bc474b5 100644 --- a/app/src/test/java/to/bitkit/ui/ContentViewTest.kt +++ b/app/src/test/java/to/bitkit/ui/ContentViewTest.kt @@ -1,5 +1,13 @@ package to.bitkit.ui +import android.content.Context +import androidx.navigation.NavDestination.Companion.hasRoute +import androidx.navigation.NavHostController +import androidx.navigation.compose.ComposeNavigator +import androidx.navigation.compose.composable +import androidx.navigation.createGraph +import androidx.navigation.navigation +import androidx.test.core.app.ApplicationProvider import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -88,4 +96,72 @@ class ContentViewTest { assertEquals(receiveSheetPresentationKey(sheet), receiveSheetPresentationKey(samePresentation)) assertFalse(receiveSheetPresentationKey(sheet) == receiveSheetPresentationKey(nextPresentation)) } + + @Test + fun `savings transfer completion returns home and drops spending from back stack`() { + val navController = transferNavController() + navController.navigateTo(Routes.Spending) + navController.navigateToTransferSavingsAvailability() + navController.navigateTo(Routes.SavingsProgress) + + navController.navigateOnSavingsTransferExit() + + assertTrue(navController.currentDestination?.hasRoute() == true) + assertNull(navController.previousBackStackEntry) + } + + @Test + fun `savings transfer exit leaves a screen opened on top of the flow alone`() { + val navController = transferNavController() + navController.navigateToTransferSavingsAvailability() + navController.navigateTo(Routes.SavingsProgress) + navController.navigateTo(Routes.Settings) + + navController.navigateOnSavingsTransferExit() + + assertTrue(navController.currentDestination?.hasRoute() == true) + } + + @Test + fun `savings transfer exit does nothing once the transfer flow is gone`() { + val navController = transferNavController() + navController.navigateTo(Routes.Spending) + navController.navigateToTransferSavingsAvailability() + navController.navigateTo(Routes.SavingsProgress) + navController.navigateOnSavingsTransferExit() + navController.navigateTo(Routes.Settings) + + navController.navigateOnSavingsTransferExit() + + assertTrue(navController.currentDestination?.hasRoute() == true) + } + + @Test + fun `savings transfer exit leaves a later transfer to spending alone`() { + val navController = transferNavController() + navController.navigateToTransferSavingsAvailability() + navController.navigateTo(Routes.SavingsProgress) + navController.navigateOnSavingsTransferExit() + navController.navigateTo(Routes.Spending) + navController.navigateTo(Routes.SpendingConfirm) + + navController.navigateOnSavingsTransferExit() + + assertTrue(navController.currentDestination?.hasRoute() == true) + } + + private fun transferNavController(): NavHostController = + NavHostController(ApplicationProvider.getApplicationContext()).apply { + navigatorProvider.addNavigator(ComposeNavigator()) + graph = createGraph(startDestination = Routes.Home) { + composable {} + composable {} + composable {} + navigation(startDestination = Routes.SavingsAvailability) { + composable {} + composable {} + composable {} + } + } + } } diff --git a/changelog.d/next/809.fixed.md b/changelog.d/next/809.fixed.md new file mode 100644 index 0000000000..5440f616a4 --- /dev/null +++ b/changelog.d/next/809.fixed.md @@ -0,0 +1 @@ +Tapping OK after moving funds from spending to savings now returns to the home screen instead of the spending wallet. diff --git a/journeys/README.md b/journeys/README.md index aba350028c..0cf64a1c62 100644 --- a/journeys/README.md +++ b/journeys/README.md @@ -127,6 +127,7 @@ fixtures, push notifications) live in each suite's README. | [security](security) | 1 | PIN result sheet layout at a long locale and font scale; no README | | [subscriptions](subscriptions) | 4 | Paykit subscription lifecycle across two wallets, plus the Payments tab | | [tags](tags) | 1 | Tag input length cap on an activity; no backend, no README | +| [transfer](transfer) | 1 | Spending to Savings exit route; needs an open channel and closes it; no README | | [transfers](transfers) | 1 | Transfer to Spending settling after the LSP closes the channel; no README | | [widgets](widgets) | 2 | Needs no backend — the quickest way to see the loop work; no README | @@ -155,6 +156,7 @@ Known differences in the corpus, as of the iOS port (synonymdev/bitkit-ios#691): | `home/pull-to-refresh-rates.xml` | not ported — iOS does not refresh exchange rates on pull to refresh | | `security/pin-result-long-label.xml` | not ported — the toggle exists on the iOS security success screen, but the overlap check is a follow-up | | `tags/activity-tag-length-cap.xml` | not ported — iOS has no 20-character cap on tag input | +| `transfer/transfer-to-savings-returns-home.xml` | not ported — iOS already resets navigation to home on the same OK, so the journey has no iOS counterpart yet | | — | `hardware-wallet/transfer-to-spending-over-max.xml` exists only on iOS | ### Running one on iOS diff --git a/journeys/transfer/transfer-to-savings-returns-home.xml b/journeys/transfer/transfer-to-savings-returns-home.xml new file mode 100644 index 0000000000..bca5412dd2 --- /dev/null +++ b/journeys/transfer/transfer-to-savings-returns-home.xml @@ -0,0 +1,28 @@ + + + Verifies that confirming a Spending to Savings transfer and tapping OK on the success screen + lands on the wallet home screen, not on the Spending wallet screen the flow was started from, + and that system back from there does not reopen the Spending screen or the transfer flow + (synonymdev/bitkit-android#809). iOS resets navigation to home on the same OK. + + Precondition: onboarded dev wallet with an open, usable channel to the LSP and a POSITIVE + Spending balance. The transfer closes that channel, so reopen one (Blocktank order through the + lsp helper, then mine blocks) afterwards if later work needs a Spending balance. Start on the + wallet home screen. + + + Verify the Spending balance card (testTag "ActivitySpending") shows a positive amount + Tap the Spending balance card (testTag "ActivitySpending") + Tap "Transfer To Savings" (testTag "TransferToSavings") + If the transfer to savings intro appears, tap "Get Started" (testTag "SavingsIntro-button") + Tap "Continue" (testTag "AvailabilityContinue") on the funds availability screen + On the confirm screen, swipe the "Swipe To Transfer" handle (testTag "GRAB") fully to the right + Wait up to 60 seconds for the "Transfer Successful" screen (testTag "TransferSuccess") with the OK button (testTag "TransferSuccess-button") + Tap OK (testTag "TransferSuccess-button") + Verify the home screen is visible with the Savings and Spending cards (testTags "ActivitySavings" and "ActivitySpending") + Verify there is no back button (testTag "NavigationBack") and no "Transfer To Savings" button (testTag "TransferToSavings") + Verify the Spending balance card (testTag "ActivitySpending") shows 0 + Press the system back button once + Verify the app is backgrounded to the launcher, and neither the Spending screen nor any transfer screen is shown + +