diff --git a/src/keylessBackup/SignInWithEmail.test.tsx b/src/keylessBackup/SignInWithEmail.test.tsx
index c2eef53385c..4a3589de4e8 100644
--- a/src/keylessBackup/SignInWithEmail.test.tsx
+++ b/src/keylessBackup/SignInWithEmail.test.tsx
@@ -9,9 +9,18 @@ import { KeylessBackupFlow, KeylessBackupOrigin } from 'src/keylessBackup/types'
import { noHeader } from 'src/navigator/Headers'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
+import { goToNextOnboardingScreen } from 'src/onboarding/steps'
import Logger from 'src/utils/Logger'
import MockedNavigator from 'test/MockedNavigator'
import { createMockStore } from 'test/utils'
+import { mockOnboardingProps } from 'test/values'
+
+const mockOnboardingPropsSelector = jest.fn(() => mockOnboardingProps)
+jest.mock('src/onboarding/steps', () => ({
+ goToNextOnboardingScreen: jest.fn(),
+ getOnboardingStepValues: () => ({ step: 2, totalSteps: 3 }),
+ onboardingPropsSelector: () => mockOnboardingPropsSelector(),
+}))
const mockAuthorize = jest.fn()
const mockGetCredentials = jest.fn()
@@ -247,7 +256,10 @@ describe('SignInWithEmail', () => {
expect(getByTestId('KeylessBackupSignInWithEmail/BottomSheet')).toBeTruthy()
fireEvent.press(getByText('signInWithEmail.bottomSheet.skip'))
- expect(navigate).toHaveBeenCalledWith(Screens.VerificationStartScreen)
+ expect(goToNextOnboardingScreen).toHaveBeenCalledWith({
+ firstScreenInCurrentStep: Screens.SignInWithEmail,
+ onboardingProps: mockOnboardingProps,
+ })
expect(AppAnalytics.track).toHaveBeenCalledWith(
KeylessBackupEvents.cab_sign_in_with_email_screen_skip,
{
diff --git a/src/navigator/types.tsx b/src/navigator/types.tsx
index e9b6d2aa4a8..0a405b1917b 100644
--- a/src/navigator/types.tsx
+++ b/src/navigator/types.tsx
@@ -306,7 +306,7 @@ export type StackParamList = {
registrationStep?: { step: number; totalSteps: number }
e164Number: string
countryCallingCode: string
- verificationCompletionScreen: keyof StackParamList
+ hasOnboarded?: boolean
}
[Screens.OnboardingSuccessScreen]: undefined
[Screens.WalletConnectRequest]:
diff --git a/src/onboarding/steps.test.ts b/src/onboarding/steps.test.ts
index ecd18006814..37a196241d7 100644
--- a/src/onboarding/steps.test.ts
+++ b/src/onboarding/steps.test.ts
@@ -1,9 +1,14 @@
import { BIOMETRY_TYPE } from 'react-native-keychain'
import { initializeAccount } from 'src/account/actions'
+import { KeylessBackupFlow } from 'src/keylessBackup/types'
import { navigate, navigateClearingStack, popToScreen } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { StackParamList } from 'src/navigator/types'
-import { updateStatsigAndNavigate } from 'src/onboarding/actions'
+import {
+ onboardingCompleted,
+ updateLastOnboardingScreen,
+ updateStatsigAndNavigate,
+} from 'src/onboarding/actions'
import {
firstOnboardingScreen,
getOnboardingStepValues,
@@ -11,7 +16,6 @@ import {
} from 'src/onboarding/steps'
import { store } from 'src/redux/store'
import { mockOnboardingProps } from 'test/values'
-import { onboardingCompleted, updateLastOnboardingScreen } from 'src/onboarding/actions'
jest.mock('src/redux/store', () => ({ store: { dispatch: jest.fn() } }))
jest.mock('src/config', () => ({
@@ -36,7 +40,7 @@ describe('onboarding steps', () => {
Screens.VerificationStartScreen,
],
name: 'newUserFlowWithEverythingEnabled',
- finalScreen: Screens.ChooseYourAdventure,
+ finalScreen: Screens.OnboardingSuccessScreen,
}
const newUserFlowWithEverythingDisabled = {
@@ -48,7 +52,7 @@ describe('onboarding steps', () => {
},
screens: [Screens.PincodeSet, Screens.ProtectWallet],
name: 'newUserFlowWithEverythingDisabled',
- finalScreen: Screens.ChooseYourAdventure,
+ finalScreen: Screens.OnboardingSuccessScreen,
}
const importWalletFlowEverythingEnabled = {
@@ -66,7 +70,7 @@ describe('onboarding steps', () => {
Screens.VerificationStartScreen,
],
name: 'importWalletFlowEverythingEnabled',
- finalScreen: Screens.ChooseYourAdventure,
+ finalScreen: Screens.OnboardingSuccessScreen,
}
beforeEach(() => {
@@ -161,7 +165,25 @@ describe('onboarding steps', () => {
)
expect(navigate).toHaveBeenCalledWith(Screens.ImportWallet)
})
- it('should navigate to ProtectWallet screen if choseToRestoreAccount is false', () => {
+ it('should navigate to CAB screen if choseToRestoreAccount is false and cloud backup is on', () => {
+ goToNextOnboardingScreen({
+ firstScreenInCurrentStep: Screens.EnableBiometry,
+ onboardingProps: {
+ ...onboardingProps,
+ choseToRestoreAccount: false,
+ showCloudAccountBackupSetup: true,
+ },
+ })
+ expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
+ expect(mockStore.dispatch).toHaveBeenCalledWith(
+ updateLastOnboardingScreen(Screens.SignInWithEmail)
+ )
+ expect(navigate).toHaveBeenCalledWith(Screens.SignInWithEmail, {
+ keylessBackupFlow: KeylessBackupFlow.Setup,
+ origin: 'Onboarding',
+ })
+ })
+ it('should navigate to ProtectWallet screen if choseToRestoreAccount is false and cloud backup is off', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.EnableBiometry,
onboardingProps: {
@@ -175,20 +197,36 @@ describe('onboarding steps', () => {
)
expect(navigate).toHaveBeenCalledWith(Screens.ProtectWallet)
})
- it('should navigate to the CYA screen', () => {
+ it('should navigate to Verification screen if choseToRestoreAccount is false, cloud backup is off and protect wallet is off', () => {
+ goToNextOnboardingScreen({
+ firstScreenInCurrentStep: Screens.EnableBiometry,
+ onboardingProps: {
+ ...onboardingProps,
+ choseToRestoreAccount: false,
+ skipProtectWallet: true,
+ },
+ })
+ expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
+ expect(mockStore.dispatch).toHaveBeenCalledWith(
+ updateLastOnboardingScreen(Screens.VerificationStartScreen)
+ )
+ expect(navigate).toHaveBeenCalledWith(Screens.VerificationStartScreen)
+ })
+ it('should navigate to end of onboarding if everything is disabled', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.EnableBiometry,
onboardingProps: {
...onboardingProps,
skipProtectWallet: true,
+ skipVerification: true,
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
})
})
@@ -222,10 +260,31 @@ describe('onboarding steps', () => {
expect(navigate).toHaveBeenCalledWith(Screens.ImportWallet)
})
- it('should navigate to ProtectWallet', () => {
+ it('should navigate to CAB screen if choseToRestoreAccount is false and cloud backup is on', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.PincodeSet,
- onboardingProps,
+ onboardingProps: {
+ ...onboardingProps,
+ choseToRestoreAccount: false,
+ showCloudAccountBackupSetup: true,
+ },
+ })
+ expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
+ expect(mockStore.dispatch).toHaveBeenCalledWith(
+ updateLastOnboardingScreen(Screens.SignInWithEmail)
+ )
+ expect(navigate).toHaveBeenCalledWith(Screens.SignInWithEmail, {
+ keylessBackupFlow: KeylessBackupFlow.Setup,
+ origin: 'Onboarding',
+ })
+ })
+ it('should navigate to ProtectWallet screen if choseToRestoreAccount is false and cloud backup is off', () => {
+ goToNextOnboardingScreen({
+ firstScreenInCurrentStep: Screens.PincodeSet,
+ onboardingProps: {
+ ...onboardingProps,
+ choseToRestoreAccount: false,
+ },
})
expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
expect(mockStore.dispatch).toHaveBeenCalledWith(
@@ -233,25 +292,41 @@ describe('onboarding steps', () => {
)
expect(navigate).toHaveBeenCalledWith(Screens.ProtectWallet)
})
- it('should navigate to the CYA screen', () => {
+ it('should navigate to Verification screen if choseToRestoreAccount is false, cloud backup is off and protect wallet is off', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.PincodeSet,
onboardingProps: {
...onboardingProps,
+ choseToRestoreAccount: false,
skipProtectWallet: true,
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.VerificationStartScreen)
+ )
+ expect(navigate).toHaveBeenCalledWith(Screens.VerificationStartScreen)
+ })
+ it('should navigate to end of onboarding if everything is disabled', () => {
+ goToNextOnboardingScreen({
+ firstScreenInCurrentStep: Screens.PincodeSet,
+ onboardingProps: {
+ ...onboardingProps,
+ skipProtectWallet: true,
+ skipVerification: true,
+ },
+ })
+ expect(mockStore.dispatch).toHaveBeenCalledWith(initializeAccount())
+ expect(mockStore.dispatch).toHaveBeenCalledWith(
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
})
})
describe('Screens.ImportWallet', () => {
- it('should navigate to the CYA screen if skipVerification is true', () => {
+ it('should navigate to end of onboarding if skipVerification is true', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.ImportWallet,
onboardingProps: {
@@ -260,13 +335,13 @@ describe('onboarding steps', () => {
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
})
- it('should also navigate to the CYA screen if numberAlreadyVerifiedCentrally is true', () => {
+ it('should also navigate to end of onboarding if numberAlreadyVerifiedCentrally is true', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.ImportWallet,
onboardingProps: {
@@ -275,10 +350,10 @@ describe('onboarding steps', () => {
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
})
it('should otherwise navigate to VerificationStartScreen', () => {
@@ -294,40 +369,40 @@ describe('onboarding steps', () => {
)
})
})
- describe('Screens.ImportSelect', () => {
- it('should navigate to the CYA screen if skipVerification is true', () => {
+ describe.each([Screens.ImportSelect, Screens.SignInWithEmail])('Screens.%s', (screen) => {
+ it('should navigate to end of onboarding if skipVerification is true', () => {
goToNextOnboardingScreen({
- firstScreenInCurrentStep: Screens.ImportSelect,
+ firstScreenInCurrentStep: screen,
onboardingProps: {
...onboardingProps,
skipVerification: true,
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
})
- it('should also navigate to the CYA screen if numberAlreadyVerifiedCentrally is true', () => {
+ it('should also navigate to end of onboarding if numberAlreadyVerifiedCentrally is true', () => {
goToNextOnboardingScreen({
- firstScreenInCurrentStep: Screens.ImportSelect,
+ firstScreenInCurrentStep: screen,
onboardingProps: {
...onboardingProps,
numberAlreadyVerifiedCentrally: true,
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
})
it('should otherwise navigate to LinkPhoneNumber', () => {
goToNextOnboardingScreen({
- firstScreenInCurrentStep: Screens.ImportSelect,
+ firstScreenInCurrentStep: screen,
onboardingProps: {
...onboardingProps,
},
@@ -340,23 +415,23 @@ describe('onboarding steps', () => {
})
describe('Screens.VerificationStartScreen and Screens.LinkPhoneNumber', () => {
it.each([Screens.VerificationStartScreen, Screens.LinkPhoneNumber])(
- 'From %s should navigate to the Screens.ChooseYourAdventure',
+ 'From %s should navigate to the end of onboarding',
(screen) => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: screen,
onboardingProps: { ...onboardingProps },
})
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
}
)
})
describe('Screens.ProtectWallet', () => {
- it('should navigate to the CYA screen if skipVerification is true', () => {
+ it('should navigate to end of onboarding if skipVerification is true', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.ProtectWallet,
onboardingProps: {
@@ -365,13 +440,13 @@ describe('onboarding steps', () => {
},
})
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateStatsigAndNavigate(Screens.ChooseYourAdventure)
+ updateStatsigAndNavigate(Screens.OnboardingSuccessScreen)
)
expect(mockStore.dispatch).toHaveBeenCalledWith(
- updateLastOnboardingScreen(Screens.ChooseYourAdventure)
+ updateLastOnboardingScreen(Screens.OnboardingSuccessScreen)
)
})
- it('should navigate to VerficationStartScreen if skipVerification is false and choseToRestoreAccount is false', () => {
+ it('should navigate to VerificationStartScreen if skipVerification is false and choseToRestoreAccount is false', () => {
goToNextOnboardingScreen({
firstScreenInCurrentStep: Screens.ProtectWallet,
onboardingProps: {
diff --git a/src/onboarding/steps.ts b/src/onboarding/steps.ts
index 7464694131b..9226eea75b4 100644
--- a/src/onboarding/steps.ts
+++ b/src/onboarding/steps.ts
@@ -6,22 +6,25 @@ import {
recoveringFromStoreWipeSelector,
} from 'src/account/selectors'
import { phoneNumberVerifiedSelector, supportedBiometryTypeSelector } from 'src/app/selectors'
+import { ONBOARDING_FEATURES_ENABLED } from 'src/config'
import { KeylessBackupFlow, KeylessBackupOrigin } from 'src/keylessBackup/types'
import * as NavigationService from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { StackParamList } from 'src/navigator/types'
-import { updateStatsigAndNavigate } from 'src/onboarding/actions'
-import { store } from 'src/redux/store'
+import {
+ onboardingCompleted,
+ updateLastOnboardingScreen,
+ updateStatsigAndNavigate,
+} from 'src/onboarding/actions'
import { ToggleableOnboardingFeatures } from 'src/onboarding/types'
-import { ONBOARDING_FEATURES_ENABLED } from 'src/config'
-import { onboardingCompleted, updateLastOnboardingScreen } from 'src/onboarding/actions'
+import { store } from 'src/redux/store'
-const END_OF_ONBOARDING_SCREENS = [Screens.TabHome, Screens.ChooseYourAdventure]
+const END_OF_ONBOARDING_SCREEN: keyof StackParamList = Screens.OnboardingSuccessScreen
interface NavigatorFunctions {
navigate: typeof NavigationService.navigate
popToScreen: typeof NavigationService.popToScreen
- finishOnboarding: (screen: keyof StackParamList) => void
+ finishOnboarding: () => void
navigateClearingStack: typeof NavigationService.navigateClearingStack
}
@@ -127,7 +130,7 @@ export function getOnboardingStepValues(screen: Screens, onboardingProps: Onboar
const nextStepAndCount: typeof NavigationService.navigate = (...args) => {
// dummy navigation function to help determine what onboarding step the user is on, without triggering side effects like actually cycling them back through the first few onboarding screens
const [nextScreen] = args
- if (!END_OF_ONBOARDING_SCREENS.includes(nextScreen)) {
+ if (nextScreen !== END_OF_ONBOARDING_SCREEN) {
totalCounter++
if (currentScreen === screen) {
reachedStep = true
@@ -139,11 +142,11 @@ export function getOnboardingStepValues(screen: Screens, onboardingProps: Onboar
currentScreen = nextScreen
}
- const finishOnboarding = (nextScreen: Screens) => {
- currentScreen = nextScreen
+ const finishOnboarding = () => {
+ currentScreen = END_OF_ONBOARDING_SCREEN
}
- while (!END_OF_ONBOARDING_SCREENS.includes(currentScreen)) {
+ while (currentScreen !== END_OF_ONBOARDING_SCREEN) {
const stepInfo = _getStepInfo({
firstScreenInStep: currentScreen,
navigator: {
@@ -183,10 +186,10 @@ export function goToNextOnboardingScreen({
navigator: {
navigate: NavigationService.navigate,
popToScreen: NavigationService.popToScreen,
- finishOnboarding: (screen: keyof StackParamList) => {
+ finishOnboarding: () => {
store.dispatch(onboardingCompleted())
- store.dispatch(updateLastOnboardingScreen(screen))
- store.dispatch(updateStatsigAndNavigate(screen))
+ store.dispatch(updateLastOnboardingScreen(END_OF_ONBOARDING_SCREEN))
+ store.dispatch(updateStatsigAndNavigate(END_OF_ONBOARDING_SCREEN))
},
navigateClearingStack: NavigationService.navigateClearingStack,
},
@@ -225,11 +228,40 @@ function _getStepInfo({ firstScreenInStep, navigator, dispatch, props }: GetStep
dispatch(updateLastOnboardingScreen(screen))
}
- const navigateImportOrImportSelect = () => {
- if (props.showCloudAccountBackupRestore) {
- wrapNavigate(Screens.ImportSelect)
+ const navigateToPhoneVerificationOrFinish = (
+ screen:
+ | Screens.LinkPhoneNumber
+ | Screens.VerificationStartScreen = Screens.VerificationStartScreen
+ ) => {
+ if (skipVerification || numberAlreadyVerifiedCentrally) {
+ finishOnboarding()
} else {
- wrapNavigate(Screens.ImportWallet)
+ // DO NOT CLEAR NAVIGATION STACK HERE - breaks restore flow on initial app open in native-stack v6
+ wrapNavigate(screen)
+ }
+ }
+
+ const biometryNext = () => {
+ if (choseToRestoreAccount) {
+ popToScreen(Screens.Welcome)
+ if (props.showCloudAccountBackupRestore) {
+ wrapNavigate(Screens.ImportSelect)
+ } else {
+ wrapNavigate(Screens.ImportWallet)
+ }
+ } else if (showCloudAccountBackupSetup) {
+ dispatch(initializeAccount())
+ wrapNavigate(Screens.SignInWithEmail, {
+ keylessBackupFlow: KeylessBackupFlow.Setup,
+ origin: KeylessBackupOrigin.Onboarding,
+ })
+ } else {
+ dispatch(initializeAccount())
+ if (skipProtectWallet) {
+ navigateToPhoneVerificationOrFinish()
+ } else {
+ wrapNavigate(Screens.ProtectWallet)
+ }
}
}
@@ -239,97 +271,29 @@ function _getStepInfo({ firstScreenInStep, navigator, dispatch, props }: GetStep
next: () => {
if (supportedBiometryType !== null) {
wrapNavigate(Screens.EnableBiometry)
- } else if (choseToRestoreAccount) {
- popToScreen(Screens.Welcome)
- navigateImportOrImportSelect()
- } else if (showCloudAccountBackupSetup) {
- dispatch(initializeAccount())
- wrapNavigate(Screens.SignInWithEmail, {
- keylessBackupFlow: KeylessBackupFlow.Setup,
- origin: KeylessBackupOrigin.Onboarding,
- })
} else {
- dispatch(initializeAccount())
- if (skipProtectWallet) {
- finishOnboarding(Screens.ChooseYourAdventure)
- } else {
- wrapNavigate(Screens.ProtectWallet)
- }
+ biometryNext()
}
},
}
case Screens.EnableBiometry:
return {
- next: () => {
- if (choseToRestoreAccount) {
- navigateImportOrImportSelect()
- } else if (showCloudAccountBackupSetup) {
- dispatch(initializeAccount())
- wrapNavigate(Screens.SignInWithEmail, {
- keylessBackupFlow: KeylessBackupFlow.Setup,
- origin: KeylessBackupOrigin.Onboarding,
- })
- } else {
- dispatch(initializeAccount())
- if (skipProtectWallet) {
- finishOnboarding(Screens.ChooseYourAdventure)
- } else {
- wrapNavigate(Screens.ProtectWallet)
- }
- }
- },
+ next: () => biometryNext(),
}
case Screens.ImportSelect:
- return {
- next: () => {
- if (skipVerification || numberAlreadyVerifiedCentrally) {
- finishOnboarding(Screens.ChooseYourAdventure)
- } else {
- // DO NOT CLEAR NAVIGATION STACK HERE - breaks restore flow on initial app open in native-stack v6
- wrapNavigate(Screens.LinkPhoneNumber)
- }
- },
- }
case Screens.SignInWithEmail:
return {
- next: () => {
- if (skipVerification || numberAlreadyVerifiedCentrally) {
- finishOnboarding(Screens.ChooseYourAdventure)
- } else {
- // DO NOT CLEAR NAVIGATION STACK HERE - breaks restore flow on initial app open in native-stack v6
- wrapNavigate(Screens.VerificationStartScreen)
- }
- },
+ next: () => navigateToPhoneVerificationOrFinish(Screens.LinkPhoneNumber),
}
case Screens.ImportWallet:
+ case Screens.ProtectWallet:
return {
- next: () => {
- if (skipVerification || numberAlreadyVerifiedCentrally) {
- finishOnboarding(Screens.ChooseYourAdventure)
- } else {
- // DO NOT CLEAR NAVIGATION STACK HERE - breaks restore flow on initial app open in native-stack v6
- wrapNavigate(Screens.VerificationStartScreen)
- }
- },
+ next: () => navigateToPhoneVerificationOrFinish(),
}
case Screens.LinkPhoneNumber:
case Screens.VerificationStartScreen:
return {
- next: () => {
- // initializeAccount is called in the middle of
- // the verification flow, so we don't need to call it here
- finishOnboarding(Screens.ChooseYourAdventure)
- },
- }
- case Screens.ProtectWallet:
- return {
- next: () => {
- if (skipVerification) {
- finishOnboarding(Screens.ChooseYourAdventure)
- } else {
- wrapNavigate(Screens.VerificationStartScreen)
- }
- },
+ next: () => finishOnboarding(),
}
default:
throw new Error(
diff --git a/src/onboarding/success/OnboardingSuccessScreen.tsx b/src/onboarding/success/OnboardingSuccessScreen.tsx
index c1dbf562629..f9f56a33a29 100644
--- a/src/onboarding/success/OnboardingSuccessScreen.tsx
+++ b/src/onboarding/success/OnboardingSuccessScreen.tsx
@@ -4,24 +4,15 @@ import { Image, StyleSheet, Text, View } from 'react-native'
import { background } from 'src/images/Images'
import Logo from 'src/images/Logo'
import { nuxNavigationOptionsNoBackButton } from 'src/navigator/Headers'
+import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
-import { goToNextOnboardingScreen, onboardingPropsSelector } from 'src/onboarding/steps'
-import { useSelector } from 'src/redux/hooks'
import colors from 'src/styles/colors'
import { typeScale } from 'src/styles/fonts'
import { Spacing } from 'src/styles/styles'
function OnboardingSuccessScreen() {
- const onboardingProps = useSelector(onboardingPropsSelector)
useEffect(() => {
- const timeout = setTimeout(
- () =>
- goToNextOnboardingScreen({
- firstScreenInCurrentStep: Screens.VerificationStartScreen,
- onboardingProps,
- }),
- 3000
- )
+ const timeout = setTimeout(() => navigate(Screens.ChooseYourAdventure), 3000)
return () => clearTimeout(timeout)
}, [])
diff --git a/src/verify/VerificationCodeInputScreen.test.tsx b/src/verify/VerificationCodeInputScreen.test.tsx
index 03c5554bb05..f75c7cd4f1d 100644
--- a/src/verify/VerificationCodeInputScreen.test.tsx
+++ b/src/verify/VerificationCodeInputScreen.test.tsx
@@ -7,13 +7,21 @@ import SmsRetriever from 'react-native-sms-retriever'
import { Provider } from 'react-redux'
import { showError } from 'src/alert/actions'
import { ErrorMessages } from 'src/app/ErrorMessages'
-import { navigate } from 'src/navigator/NavigationService'
+import { navigate, popToScreen } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
+import { goToNextOnboardingScreen } from 'src/onboarding/steps'
import { sleep } from 'src/utils/sleep'
import VerificationCodeInputScreen from 'src/verify/VerificationCodeInputScreen'
import networkConfig from 'src/web3/networkConfig'
import MockedNavigator from 'test/MockedNavigator'
-import { createMockStore } from 'test/utils'
+import { createMockStore, getMockStackScreenProps } from 'test/utils'
+import { mockOnboardingProps } from 'test/values'
+
+const mockOnboardingPropsSelector = jest.fn(() => mockOnboardingProps)
+jest.mock('src/onboarding/steps', () => ({
+ goToNextOnboardingScreen: jest.fn(),
+ onboardingPropsSelector: () => mockOnboardingPropsSelector(),
+}))
const mockFetch = fetch as FetchMock
@@ -37,15 +45,19 @@ const store = createMockStore({
},
})
-const renderComponent = () =>
+const renderComponent = ({
+ hasOnboarded = false,
+}: {
+ hasOnboarded?: boolean
+} = {}) =>
render(
@@ -94,7 +106,7 @@ describe('VerificationCodeInputScreen', () => {
)
})
- it('verifies the sms code', async () => {
+ it('verifies the sms code and navigates to next onboarding screen', async () => {
mockFetch.mockResponseOnce(JSON.stringify({ data: { verificationId: 'someId' } }), {
status: 200,
})
@@ -122,7 +134,99 @@ describe('VerificationCodeInputScreen', () => {
await act(() => {
jest.runOnlyPendingTimers()
})
- expect(navigate).toHaveBeenCalledWith(Screens.OnboardingSuccessScreen)
+ expect(goToNextOnboardingScreen).toHaveBeenCalledWith({
+ firstScreenInCurrentStep: Screens.VerificationStartScreen,
+ onboardingProps: mockOnboardingProps,
+ })
+ expect(navigate).not.toHaveBeenCalled()
+ expect(popToScreen).not.toHaveBeenCalled()
+ })
+
+ it('verifies the sms code and navigates to home if not in onboarding and no previous routes found', async () => {
+ mockFetch.mockResponseOnce(JSON.stringify({ data: { verificationId: 'someId' } }), {
+ status: 200,
+ })
+ mockFetch.mockResponseOnce(JSON.stringify({ message: 'OK' }), {
+ status: 200,
+ })
+
+ const { getByTestId } = renderComponent({ hasOnboarded: true })
+
+ await act(() => {
+ fireEvent.changeText(getByTestId('PhoneVerificationCode'), '123456')
+ })
+
+ await waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(2))
+ expect(mockFetch).toHaveBeenNthCalledWith(2, `${networkConfig.verifySmsCodeUrl}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ authorization: `${networkConfig.authHeaderIssuer} 0xabc:someSignedMessage`,
+ },
+ body: '{"phoneNumber":"+31619123456","verificationId":"someId","smsCode":"123456","clientPlatform":"android","clientVersion":"0.0.1"}',
+ })
+ expect(getByTestId('PhoneVerificationCode/CheckIcon')).toBeTruthy()
+
+ await act(() => {
+ jest.runOnlyPendingTimers()
+ })
+ expect(navigate).toHaveBeenCalledWith(Screens.TabHome)
+ expect(popToScreen).not.toHaveBeenCalled()
+ expect(goToNextOnboardingScreen).not.toHaveBeenCalled()
+ })
+
+ it('verifies the sms code and navigates to previous route if not in onboarding', async () => {
+ mockFetch.mockResponseOnce(JSON.stringify({ data: { verificationId: 'someId' } }), {
+ status: 200,
+ })
+ mockFetch.mockResponseOnce(JSON.stringify({ message: 'OK' }), {
+ status: 200,
+ })
+
+ const screenProps = getMockStackScreenProps(Screens.VerificationCodeInputScreen, {
+ countryCallingCode: '+31',
+ e164Number,
+ hasOnboarded: true,
+ })
+
+ jest.mocked(screenProps.navigation).getState = jest.fn(
+ () =>
+ ({
+ routes: [
+ { name: Screens.ProfileSubmenu },
+ { name: Screens.VerificationStartScreen },
+ { name: Screens.VerificationCodeInputScreen },
+ ],
+ }) as any
+ )
+
+ const { getByTestId } = render(
+
+
+
+ )
+
+ await act(() => {
+ fireEvent.changeText(getByTestId('PhoneVerificationCode'), '123456')
+ })
+
+ await waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(2))
+ expect(mockFetch).toHaveBeenNthCalledWith(2, `${networkConfig.verifySmsCodeUrl}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ authorization: `${networkConfig.authHeaderIssuer} 0xabc:someSignedMessage`,
+ },
+ body: '{"phoneNumber":"+31619123456","verificationId":"someId","smsCode":"123456","clientPlatform":"android","clientVersion":"0.0.1"}',
+ })
+ expect(getByTestId('PhoneVerificationCode/CheckIcon')).toBeTruthy()
+
+ await act(() => {
+ jest.runOnlyPendingTimers()
+ })
+ expect(popToScreen).toHaveBeenCalledWith(Screens.ProfileSubmenu)
+ expect(navigate).not.toHaveBeenCalled()
+ expect(goToNextOnboardingScreen).not.toHaveBeenCalled()
})
it('waits for the verificationId to be captured before verifying sms', async () => {
@@ -195,7 +299,10 @@ describe('VerificationCodeInputScreen', () => {
await act(() => {
jest.runOnlyPendingTimers()
})
- expect(navigate).toHaveBeenCalledWith(Screens.OnboardingSuccessScreen)
+ expect(goToNextOnboardingScreen).toHaveBeenCalledWith({
+ firstScreenInCurrentStep: Screens.VerificationStartScreen,
+ onboardingProps: mockOnboardingProps,
+ })
})
it('handles when phone number already verified', async () => {
@@ -218,7 +325,12 @@ describe('VerificationCodeInputScreen', () => {
await act(() => {
jest.runOnlyPendingTimers()
})
- await waitFor(() => expect(navigate).toHaveBeenCalledWith(Screens.OnboardingSuccessScreen))
+ await waitFor(() =>
+ expect(goToNextOnboardingScreen).toHaveBeenCalledWith({
+ firstScreenInCurrentStep: Screens.VerificationStartScreen,
+ onboardingProps: mockOnboardingProps,
+ })
+ )
})
it('shows error in verifying sms code', async () => {
diff --git a/src/verify/VerificationCodeInputScreen.tsx b/src/verify/VerificationCodeInputScreen.tsx
index 0d3c561c749..4cdf8252f64 100644
--- a/src/verify/VerificationCodeInputScreen.tsx
+++ b/src/verify/VerificationCodeInputScreen.tsx
@@ -9,10 +9,12 @@ import { PhoneVerificationEvents } from 'src/analytics/Events'
import BackButton from 'src/components/BackButton'
import InfoBottomSheet from 'src/components/InfoBottomSheet'
import { HeaderTitleWithSubtitle } from 'src/navigator/Headers'
-import { navigate } from 'src/navigator/NavigationService'
+import { navigate, popToScreen } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { TopBarTextButton } from 'src/navigator/TopBarButton'
import { StackParamList } from 'src/navigator/types'
+import { goToNextOnboardingScreen, onboardingPropsSelector } from 'src/onboarding/steps'
+import { useSelector } from 'src/redux/hooks'
import colors from 'src/styles/colors'
import { useVerifyPhoneNumber } from 'src/verify/hooks'
import VerificationCodeInput from 'src/verify/VerificationCodeInput'
@@ -21,7 +23,6 @@ function VerificationCodeInputScreen({
route,
navigation,
}: NativeStackScreenProps) {
- const nextScreen = route.params.verificationCompletionScreen
const [showHelpDialog, setShowHelpDialog] = useState(false)
const { t } = useTranslation()
@@ -30,6 +31,7 @@ function VerificationCodeInputScreen({
route.params.e164Number,
route.params.countryCallingCode
)
+ const onboardingProps = useSelector(onboardingPropsSelector)
const onResendSms = () => {
AppAnalytics.track(PhoneVerificationEvents.phone_verification_resend_message)
@@ -83,7 +85,24 @@ function VerificationCodeInputScreen({
setSmsCode={setSmsCode}
onResendSms={onResendSms}
onSuccess={() => {
- navigate(nextScreen)
+ if (route.params.hasOnboarded) {
+ const routes = navigation.getState().routes
+ // if not from onboarding, go back to the screen where phone
+ // verification was started, which is 2 screens back. If no screen
+ // is found, go to home screen
+ const prevRoute = routes[routes.length - 3]
+ if (prevRoute?.name) {
+ popToScreen(prevRoute.name)
+ } else {
+ navigate(Screens.TabHome)
+ }
+ } else {
+ // if onboarding, continue with the next onboarding screen
+ goToNextOnboardingScreen({
+ firstScreenInCurrentStep: Screens.VerificationStartScreen,
+ onboardingProps,
+ })
+ }
}}
containerStyle={{ marginTop: headerHeight }}
/>
diff --git a/src/verify/VerificationStartScreen.test.tsx b/src/verify/VerificationStartScreen.test.tsx
index ccbd30713de..2d7d30625c3 100644
--- a/src/verify/VerificationStartScreen.test.tsx
+++ b/src/verify/VerificationStartScreen.test.tsx
@@ -166,7 +166,7 @@ describe('VerificationStartScreen', () => {
countryCallingCode: '+31',
e164Number: '+31619123456',
registrationStep: { step: 3, totalSteps: 3 },
- verificationCompletionScreen: 'OnboardingSuccessScreen',
+ hasOnboarded: false,
})
})
@@ -189,7 +189,7 @@ describe('VerificationStartScreen', () => {
countryCallingCode: '+31',
e164Number: '+31619123456',
registrationStep: undefined,
- verificationCompletionScreen: 'OnboardingSuccessScreen',
+ hasOnboarded: false,
})
})
@@ -212,7 +212,7 @@ describe('VerificationStartScreen', () => {
countryCallingCode: '+31',
e164Number: '+31619123456',
registrationStep: undefined,
- verificationCompletionScreen: 'TabHome',
+ hasOnboarded: true,
})
})
})
diff --git a/src/verify/VerificationStartScreen.tsx b/src/verify/VerificationStartScreen.tsx
index 3f494fb80e4..4630a608fc4 100644
--- a/src/verify/VerificationStartScreen.tsx
+++ b/src/verify/VerificationStartScreen.tsx
@@ -82,20 +82,11 @@ function VerificationStartScreen({
countryCallingCode: country?.countryCallingCode || '',
})
- const routes = navigation.getState().routes
- const prevRoute = routes[routes.length - 2] // -2 because -1 is the current route
- // Usually it makes sense to navigate the user back to where they launched
- // the verification flow after they complete it, but during onboarding we
- // want to navigate to the next step.
- const verificationCompletionScreen = !route.params?.hasOnboarded
- ? Screens.OnboardingSuccessScreen
- : (prevRoute?.name ?? Screens.TabHome)
-
navigate(Screens.VerificationCodeInputScreen, {
registrationStep: showSteps ? { step, totalSteps } : undefined,
e164Number: phoneNumberInfo.e164Number,
countryCallingCode: country?.countryCallingCode || '',
- verificationCompletionScreen,
+ hasOnboarded: route.params?.hasOnboarded,
})
}