diff --git a/locales/base/translation.json b/locales/base/translation.json index d53786f861..7f8de91caa 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -129,7 +129,8 @@ "title": "Backup unavailable", "body": "You can set up wallet backup later or protect your account now by saving your recovery phrase.", "later": "I'll set up backup later", - "manual": "Save recovery phrase" + "manual": "Save recovery phrase", + "skip": "Skip (not recommended)" } }, "restore": { diff --git a/src/analytics/Events.tsx b/src/analytics/Events.tsx index 82e710a80c..e1ce5e963b 100644 --- a/src/analytics/Events.tsx +++ b/src/analytics/Events.tsx @@ -112,6 +112,7 @@ export enum KeylessBackupEvents { cab_progress_completed_continue = 'cab_progress_completed_continue', cab_progress_failed_later = 'cab_progress_failed_later', cab_progress_failed_manual = 'cab_progress_failed_manual', + cab_progress_failed_skip_onboarding = 'cab_progress_failed_skip_onboarding', cab_post_encrypted_mnemonic_failed = 'cab_post_encrypted_mnemonic_failed', cab_torus_keyshare_timeout = 'cab_torus_keyshare_timeout', cab_handle_keyless_backup_failed = 'cab_handle_keyless_backup_failed', diff --git a/src/analytics/Properties.tsx b/src/analytics/Properties.tsx index 7b22f7d793..a125e92037 100644 --- a/src/analytics/Properties.tsx +++ b/src/analytics/Properties.tsx @@ -64,6 +64,7 @@ import { SerializableRewardsInfo } from 'src/earn/types' import { ProviderSelectionAnalyticsData } from 'src/fiatExchanges/types' import { CICOFlow, FiatExchangeFlow, PaymentMethod } from 'src/fiatExchanges/utils' import { HomeActionName, NotificationBannerCTATypes, NotificationType } from 'src/home/types' +import { KeylessBackupOrigin } from 'src/keylessBackup/KeylessBackupProgress' import { KeylessBackupFlow, KeylessBackupStatus } from 'src/keylessBackup/types' import { LocalCurrencyCode } from 'src/localCurrency/consts' import { NftOrigin } from 'src/nfts/types' @@ -264,7 +265,8 @@ interface KeylessBackupEventsProperties { [KeylessBackupEvents.cab_issue_valora_keyshare_error]: CommonKeylessBackupProps [KeylessBackupEvents.cab_progress_completed_continue]: undefined [KeylessBackupEvents.cab_progress_failed_later]: undefined - [KeylessBackupEvents.cab_progress_failed_manual]: undefined + [KeylessBackupEvents.cab_progress_failed_manual]: { origin: KeylessBackupOrigin } + [KeylessBackupEvents.cab_progress_failed_skip_onboarding]: undefined [KeylessBackupEvents.cab_post_encrypted_mnemonic_failed]: { backupAlreadyExists: boolean } diff --git a/src/analytics/docs.ts b/src/analytics/docs.ts index 2e3039957c..f66107d86e 100644 --- a/src/analytics/docs.ts +++ b/src/analytics/docs.ts @@ -144,6 +144,7 @@ export const eventDocs: Record = { [KeylessBackupEvents.cab_progress_completed_continue]: ``, [KeylessBackupEvents.cab_progress_failed_later]: ``, [KeylessBackupEvents.cab_progress_failed_manual]: ``, + [KeylessBackupEvents.cab_progress_failed_skip_onboarding]: ``, [KeylessBackupEvents.cab_post_encrypted_mnemonic_failed]: ``, [KeylessBackupEvents.cab_torus_keyshare_timeout]: ``, [KeylessBackupEvents.cab_handle_keyless_backup_failed]: `When keyless backup fails to generate store encrypted mnemonic for setup or fails to retrieve and decrypt mnemonic for restore`, diff --git a/src/keylessBackup/KeylessBackupIntro.test.tsx b/src/keylessBackup/KeylessBackupIntro.test.tsx index 0246dc86cc..4e0f168dfd 100644 --- a/src/keylessBackup/KeylessBackupIntro.test.tsx +++ b/src/keylessBackup/KeylessBackupIntro.test.tsx @@ -3,6 +3,7 @@ import React from 'react' import { KeylessBackupEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' import KeylessBackupIntro from 'src/keylessBackup/KeylessBackupIntro' +import { KeylessBackupOrigin } from 'src/keylessBackup/KeylessBackupProgress' import { KeylessBackupFlow } from 'src/keylessBackup/types' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' @@ -30,6 +31,7 @@ describe('KeylessBackupIntro', () => { }) expect(navigate).toHaveBeenCalledWith(Screens.SignInWithEmail, { keylessBackupFlow: KeylessBackupFlow.Setup, + origin: KeylessBackupOrigin.Settings, }) }) @@ -68,6 +70,7 @@ describe('KeylessBackupIntro', () => { }) expect(navigate).toHaveBeenCalledWith(Screens.SignInWithEmail, { keylessBackupFlow: KeylessBackupFlow.Restore, + origin: KeylessBackupOrigin.Settings, }) }) }) diff --git a/src/keylessBackup/KeylessBackupIntro.tsx b/src/keylessBackup/KeylessBackupIntro.tsx index 41c04c3cf8..3bc80756b8 100644 --- a/src/keylessBackup/KeylessBackupIntro.tsx +++ b/src/keylessBackup/KeylessBackupIntro.tsx @@ -10,6 +10,7 @@ import CancelButton from 'src/components/CancelButton' import Card from 'src/components/Card' import TextButton from 'src/components/TextButton' import EnvelopeIcon from 'src/keylessBackup/EnvelopeIcon' +import { KeylessBackupOrigin } from 'src/keylessBackup/KeylessBackupProgress' import SmartphoneIcon from 'src/keylessBackup/SmartphoneIcon' import { KeylessBackupFlow } from 'src/keylessBackup/types' import { emptyHeader } from 'src/navigator/Headers' @@ -84,7 +85,10 @@ function KeylessBackupIntro({ route }: Props) { testID="keylessBackupIntro/Continue" onPress={() => { ValoraAnalytics.track(KeylessBackupEvents.cab_intro_continue, { keylessBackupFlow }) - navigate(Screens.SignInWithEmail, { keylessBackupFlow }) + navigate(Screens.SignInWithEmail, { + keylessBackupFlow, + origin: KeylessBackupOrigin.Settings, + }) }} text={isSetup ? t('continue') : t('next')} size={BtnSizes.FULL} diff --git a/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx b/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx index 941685c4ee..51e43969d7 100644 --- a/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx +++ b/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx @@ -116,6 +116,7 @@ function KeylessBackupPhoneCodeInput({ onSuccess={() => { navigate(Screens.KeylessBackupProgress, { keylessBackupFlow: route.params.keylessBackupFlow, + origin: route.params.origin, }) }} title={{t('phoneVerificationInput.title')}} diff --git a/src/keylessBackup/KeylessBackupPhoneInput.tsx b/src/keylessBackup/KeylessBackupPhoneInput.tsx index f30f11ceec..a721262545 100644 --- a/src/keylessBackup/KeylessBackupPhoneInput.tsx +++ b/src/keylessBackup/KeylessBackupPhoneInput.tsx @@ -28,7 +28,7 @@ type Props = NativeStackScreenProps new Countries(i18n.language), [i18n.language]) @@ -78,6 +78,7 @@ function KeylessBackupPhoneInput({ route }: Props) { navigate(Screens.KeylessBackupPhoneInput, { keylessBackupFlow, selectedCountryCodeAlpha2: countryCodeAlpha2, + origin, }) }, }) @@ -90,6 +91,7 @@ function KeylessBackupPhoneInput({ route }: Props) { navigate(Screens.KeylessBackupPhoneCodeInput, { keylessBackupFlow, e164Number: phoneNumberInfo.e164Number, + origin, }) } diff --git a/src/keylessBackup/KeylessBackupProgress.test.tsx b/src/keylessBackup/KeylessBackupProgress.test.tsx index 652fe1ff84..5a62c90d31 100644 --- a/src/keylessBackup/KeylessBackupProgress.test.tsx +++ b/src/keylessBackup/KeylessBackupProgress.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { Provider } from 'react-redux' import { KeylessBackupEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' -import KeylessBackupProgress from 'src/keylessBackup/KeylessBackupProgress' +import KeylessBackupProgress, { KeylessBackupOrigin } from 'src/keylessBackup/KeylessBackupProgress' import { keylessBackupAcceptZeroBalance, keylessBackupBail } from 'src/keylessBackup/slice' import { KeylessBackupFlow, KeylessBackupStatus } from 'src/keylessBackup/types' import { ensurePincode, navigate, navigateHome } from 'src/navigator/NavigationService' @@ -40,9 +40,13 @@ function createStore(keylessBackupStatus: KeylessBackupStatus, zeroBalance = fal }) } -function getProps(flow: KeylessBackupFlow = KeylessBackupFlow.Setup) { +function getProps( + flow: KeylessBackupFlow = KeylessBackupFlow.Setup, + origin: KeylessBackupOrigin = KeylessBackupOrigin.Settings +) { return getMockStackScreenProps(Screens.KeylessBackupProgress, { keylessBackupFlow: flow, + origin, }) } @@ -115,7 +119,49 @@ describe('KeylessBackupProgress', () => { expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) expect(ValoraAnalytics.track).toHaveBeenCalledWith( - KeylessBackupEvents.cab_progress_failed_manual + KeylessBackupEvents.cab_progress_failed_manual, + { origin: KeylessBackupOrigin.Settings } + ) + }) + it('navigates to recovery phrase on failure when coming from onboarding', async () => { + jest.mocked(ensurePincode).mockResolvedValueOnce(true) + const { getByTestId } = render( + + + + ) + expect(getByTestId('KeylessBackupProgress/ManualOnboarding')).toBeTruthy() + fireEvent.press(getByTestId('KeylessBackupProgress/ManualOnboarding')) + + await waitFor(() => expect(navigate).toHaveBeenCalledTimes(1)) + expect(navigate).toHaveBeenCalledWith(Screens.AccountKeyEducation) + + expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) + expect(ValoraAnalytics.track).toHaveBeenCalledWith( + KeylessBackupEvents.cab_progress_failed_manual, + { origin: KeylessBackupOrigin.Onboarding } + ) + }) + it('navigates to CYA on failure when coming from onboarding', async () => { + jest.mocked(ensurePincode).mockResolvedValueOnce(true) + const { getByTestId } = render( + + + + ) + expect(getByTestId('KeylessBackupProgress/Skip')).toBeTruthy() + fireEvent.press(getByTestId('KeylessBackupProgress/Skip')) + + await waitFor(() => expect(navigate).toHaveBeenCalledTimes(1)) + expect(navigate).toHaveBeenCalledWith(Screens.ChooseYourAdventure) + + expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) + expect(ValoraAnalytics.track).toHaveBeenCalledWith( + KeylessBackupEvents.cab_progress_failed_skip_onboarding ) }) }) diff --git a/src/keylessBackup/KeylessBackupProgress.tsx b/src/keylessBackup/KeylessBackupProgress.tsx index 3bb2083b04..32a0100da2 100644 --- a/src/keylessBackup/KeylessBackupProgress.tsx +++ b/src/keylessBackup/KeylessBackupProgress.tsx @@ -32,12 +32,18 @@ import Logger from 'src/utils/Logger' const TAG = 'keylessBackup/KeylessBackupProgress' +export enum KeylessBackupOrigin { + Onboarding = 'Onboarding', + Settings = 'Settings', +} + function KeylessBackupProgress({ route, navigation, }: NativeStackScreenProps) { const keylessBackupStatus = useSelector(keylessBackupStatusSelector) const { t } = useTranslation() + const { keylessBackupFlow, origin } = route.params const onPressHelp = () => { ValoraAnalytics.track(KeylessBackupEvents.cab_restore_failed_help) @@ -55,7 +61,7 @@ function KeylessBackupProgress({ navigation.setOptions({ headerRight: () => keylessBackupStatus === KeylessBackupStatus.Failed && - route.params.keylessBackupFlow === KeylessBackupFlow.Restore && ( + keylessBackupFlow === KeylessBackupFlow.Restore && ( } else { - return + return } } @@ -250,17 +256,19 @@ function Restore() { } } -function Setup() { +function Setup({ origin }: { origin: KeylessBackupOrigin }) { const keylessBackupStatus = useSelector(keylessBackupStatusSelector) const { t } = useTranslation() + const navigatedFromSettings = origin === KeylessBackupOrigin.Settings + const onPressContinue = () => { ValoraAnalytics.track(KeylessBackupEvents.cab_progress_completed_continue) navigateHome() } const onPressManual = async () => { - ValoraAnalytics.track(KeylessBackupEvents.cab_progress_failed_manual) + ValoraAnalytics.track(KeylessBackupEvents.cab_progress_failed_manual, { origin }) try { const pinIsCorrect = await ensurePincode() if (pinIsCorrect) { @@ -276,6 +284,16 @@ function Setup() { navigate(Screens.Settings) } + const onPressManualOnboarding = () => { + ValoraAnalytics.track(KeylessBackupEvents.cab_progress_failed_manual, { origin }) + navigate(Screens.AccountKeyEducation) + } + + const onPressSkip = () => { + ValoraAnalytics.track(KeylessBackupEvents.cab_progress_failed_skip_onboarding) + navigate(Screens.ChooseYourAdventure) + } + switch (keylessBackupStatus) { case KeylessBackupStatus.InProgress: { return renderInProgressState(t('keylessBackupStatus.setup.inProgress.title')) @@ -311,18 +329,32 @@ function Setup() { {t('keylessBackupStatus.setup.failed.body')}