diff --git a/src/analytics/Events.tsx b/src/analytics/Events.tsx index d5a1ea0874b..bafb00e9604 100644 --- a/src/analytics/Events.tsx +++ b/src/analytics/Events.tsx @@ -98,15 +98,18 @@ export enum KeylessBackupEvents { cab_sign_in_another_way = 'cab_sign_in_another_way', cab_sign_in_with_google = 'cab_sign_in_with_google', cab_sign_in_with_google_success = 'cab_sign_in_with_google_success', + cab_sign_in_with_email_screen_back = 'cab_sign_in_with_email_screen_back', cab_sign_in_with_email_screen_cancel = 'cab_sign_in_with_email_screen_cancel', cab_sign_in_with_email_screen_skip = 'cab_sign_in_with_email_screen_skip', cab_get_torus_keyshare_failed = 'cab_get_torus_keyshare_failed', + cab_enter_phone_number_back = 'cab_enter_phone_number_back', cab_enter_phone_number_continue = 'cab_enter_phone_number_continue', cab_enter_phone_number_cancel = 'cab_enter_phone_number_cancel', cab_intro_continue = 'cab_intro_continue', cab_issue_sms_code_start = 'cab_issue_sms_code_start', cab_issue_sms_code_success = 'cab_issue_sms_code_success', cab_issue_sms_code_error = 'cab_issue_sms_code_error', + cab_enter_phone_code_back = 'cab_enter_phone_code_back', cab_enter_phone_code_cancel = 'cab_enter_phone_code_cancel', cab_issue_valora_keyshare_start = 'cab_issue_valora_keyshare_start', cab_issue_valora_keyshare_success = 'cab_issue_valora_keyshare_success', diff --git a/src/analytics/Properties.tsx b/src/analytics/Properties.tsx index 73b399f57c9..56cd84e1b79 100644 --- a/src/analytics/Properties.tsx +++ b/src/analytics/Properties.tsx @@ -256,19 +256,22 @@ interface KeylessBackupEventsProperties { [KeylessBackupEvents.cab_sign_in_another_way]: CommonKeylessBackupProps [KeylessBackupEvents.cab_sign_in_with_google]: CommonKeylessBackupProps [KeylessBackupEvents.cab_sign_in_with_google_success]: CommonKeylessBackupProps + [KeylessBackupEvents.cab_sign_in_with_email_screen_back]: CommonKeylessBackupProps [KeylessBackupEvents.cab_sign_in_with_email_screen_cancel]: CommonKeylessBackupProps [KeylessBackupEvents.cab_sign_in_with_email_screen_skip]: CommonKeylessBackupProps - [KeylessBackupEvents.cab_enter_phone_number_continue]: CommonKeylessBackupProps + [KeylessBackupEvents.cab_enter_phone_number_back]: CommonKeylessBackupProps [KeylessBackupEvents.cab_enter_phone_number_cancel]: CommonKeylessBackupProps + [KeylessBackupEvents.cab_enter_phone_number_continue]: CommonKeylessBackupProps [KeylessBackupEvents.cab_intro_continue]: CommonKeylessBackupProps [KeylessBackupEvents.cab_issue_sms_code_start]: CommonKeylessBackupProps [KeylessBackupEvents.cab_issue_sms_code_success]: CommonKeylessBackupProps [KeylessBackupEvents.cab_issue_sms_code_error]: CommonKeylessBackupProps + [KeylessBackupEvents.cab_enter_phone_code_back]: CommonKeylessBackupProps [KeylessBackupEvents.cab_enter_phone_code_cancel]: CommonKeylessBackupProps [KeylessBackupEvents.cab_issue_valora_keyshare_start]: CommonKeylessBackupProps [KeylessBackupEvents.cab_issue_valora_keyshare_success]: CommonKeylessBackupProps [KeylessBackupEvents.cab_issue_valora_keyshare_error]: CommonKeylessBackupProps - [KeylessBackupEvents.cab_progress_completed_continue]: undefined + [KeylessBackupEvents.cab_progress_completed_continue]: { origin: KeylessBackupOrigin } [KeylessBackupEvents.cab_progress_failed_later]: undefined [KeylessBackupEvents.cab_progress_failed_manual]: { origin: KeylessBackupOrigin } [KeylessBackupEvents.cab_progress_failed_skip_onboarding]: undefined diff --git a/src/analytics/docs.ts b/src/analytics/docs.ts index 71b2ace0d17..d63ef9d4ccc 100644 --- a/src/analytics/docs.ts +++ b/src/analytics/docs.ts @@ -129,15 +129,18 @@ export const eventDocs: Record = { [KeylessBackupEvents.cab_sign_in_another_way]: `When the sign in another way button is pressed in the setup wallet backup screen`, [KeylessBackupEvents.cab_sign_in_with_google]: ``, [KeylessBackupEvents.cab_sign_in_with_google_success]: ``, + [KeylessBackupEvents.cab_sign_in_with_email_screen_back]: `When the back button is pressed on the sign in with email screen`, [KeylessBackupEvents.cab_sign_in_with_email_screen_cancel]: ``, [KeylessBackupEvents.cab_sign_in_with_email_screen_skip]: `When the skip button is pressed on the sign in with email bottom sheet`, [KeylessBackupEvents.cab_get_torus_keyshare_failed]: ``, - [KeylessBackupEvents.cab_enter_phone_number_continue]: ``, + [KeylessBackupEvents.cab_enter_phone_number_back]: `When the back button is pressed on the phone input screen`, [KeylessBackupEvents.cab_enter_phone_number_cancel]: `When the cancel button is pressed on the phone input screen`, + [KeylessBackupEvents.cab_enter_phone_number_continue]: ``, [KeylessBackupEvents.cab_intro_continue]: `When the continue button is pressed on the keyless backup intro screen`, [KeylessBackupEvents.cab_issue_sms_code_start]: ``, [KeylessBackupEvents.cab_issue_sms_code_success]: ``, [KeylessBackupEvents.cab_issue_sms_code_error]: ``, + [KeylessBackupEvents.cab_enter_phone_code_back]: `When the back button is pressed on the phone verification code input screen`, [KeylessBackupEvents.cab_enter_phone_code_cancel]: `When the cancel button is pressed on the phone verification code input screen`, [KeylessBackupEvents.cab_issue_valora_keyshare_start]: ``, [KeylessBackupEvents.cab_issue_valora_keyshare_success]: ``, diff --git a/src/components/header/CustomHeader.tsx b/src/components/header/CustomHeader.tsx index 372c7e77b64..051905fe9d9 100644 --- a/src/components/header/CustomHeader.tsx +++ b/src/components/header/CustomHeader.tsx @@ -15,7 +15,8 @@ function CustomHeader({ left, right, title, style }: Props) { return ( {!!title && {titleComponent}} - {!!left && {left}} + {/* Need left element to be an empty view if not provided for right alignment to work */} + {left ? {left} : } {!!right && {right}} ) diff --git a/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx b/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx index a0f098e3c4b..921d02d1cb7 100644 --- a/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx +++ b/src/keylessBackup/KeylessBackupPhoneCodeInput.tsx @@ -12,10 +12,13 @@ import CustomHeader from 'src/components/header/CustomHeader' import KeylessBackupCancelButton from 'src/keylessBackup/KeylessBackupCancelButton' import { useVerifyPhoneNumber } from 'src/keylessBackup/hooks' import { KeylessBackupFlow, KeylessBackupOrigin } from 'src/keylessBackup/types' +import { HeaderTitleWithSubtitle } from 'src/navigator/Headers' import { navigate, navigateHome } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { TopBarTextButton } from 'src/navigator/TopBarButton' import { StackParamList } from 'src/navigator/types' +import { getOnboardingStepValues, 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' @@ -116,6 +119,8 @@ function KeylessBackupPhoneCodeInput({ keylessBackupFlow, origin ) + const onboardingProps = useSelector(onboardingPropsSelector) + const { step, totalSteps } = getOnboardingStepValues(Screens.SignInWithEmail, onboardingProps) const bottomSheetRef = useRef(null) @@ -131,7 +136,14 @@ function KeylessBackupPhoneCodeInput({ keylessBackupFlow === KeylessBackupFlow.Setup && origin === KeylessBackupOrigin.Onboarding const headerLeft = isSetupInOnboarding ? ( - + ) : ( {t('phoneVerificationInput.title')} + ) } /> diff --git a/src/keylessBackup/KeylessBackupPhoneInput.test.tsx b/src/keylessBackup/KeylessBackupPhoneInput.test.tsx index fdc856e0e10..abebc529951 100644 --- a/src/keylessBackup/KeylessBackupPhoneInput.test.tsx +++ b/src/keylessBackup/KeylessBackupPhoneInput.test.tsx @@ -3,6 +3,7 @@ import React from 'react' import { Provider } from 'react-redux' import KeylessBackupPhoneInput from 'src/keylessBackup/KeylessBackupPhoneInput' import { KeylessBackupFlow } from 'src/keylessBackup/types' +import { noHeader } from 'src/navigator/Headers' import MockedNavigator from 'test/MockedNavigator' import { createMockStore } from 'test/utils' @@ -25,7 +26,7 @@ describe('KeylessBackupPhoneInput', () => { keylessBackupFlow: KeylessBackupFlow.Setup, selectedCountryCodeAlpha2: 'US', }} - options={KeylessBackupPhoneInput.navigationOptions} + options={noHeader} /> ) diff --git a/src/keylessBackup/KeylessBackupPhoneInput.tsx b/src/keylessBackup/KeylessBackupPhoneInput.tsx index 2f6c93ee8f0..d2654782360 100644 --- a/src/keylessBackup/KeylessBackupPhoneInput.tsx +++ b/src/keylessBackup/KeylessBackupPhoneInput.tsx @@ -2,27 +2,32 @@ import { Countries } from '@celo/phone-utils' import { NativeStackScreenProps } from '@react-navigation/native-stack' import React, { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { SafeAreaView, StyleSheet, Text } from 'react-native' +import { StyleSheet, Text } from 'react-native' import * as RNLocalize from 'react-native-localize' +import { SafeAreaView } from 'react-native-safe-area-context' import { defaultCountryCodeSelector, e164NumberSelector } from 'src/account/selectors' import { getPhoneNumberDetails } from 'src/account/utils' import { KeylessBackupEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' +import BackButton from 'src/components/BackButton' import Button, { BtnSizes, BtnTypes } from 'src/components/Button' import KeyboardAwareScrollView from 'src/components/KeyboardAwareScrollView' import KeyboardSpacer from 'src/components/KeyboardSpacer' import PhoneNumberInput from 'src/components/PhoneNumberInput' +import CustomHeader from 'src/components/header/CustomHeader' import i18n from 'src/i18n' import KeylessBackupCancelButton from 'src/keylessBackup/KeylessBackupCancelButton' -import { KeylessBackupFlow } from 'src/keylessBackup/types' -import { emptyHeader } from 'src/navigator/Headers' +import { KeylessBackupFlow, KeylessBackupOrigin } from 'src/keylessBackup/types' +import { HeaderTitleWithSubtitle } from 'src/navigator/Headers' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { StackParamList } from 'src/navigator/types' +import { getOnboardingStepValues, 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' +import variables from 'src/styles/variables' type Props = NativeStackScreenProps @@ -31,6 +36,10 @@ function KeylessBackupPhoneInput({ route }: Props) { const { selectedCountryCodeAlpha2, keylessBackupFlow, origin } = route.params const cachedNumber = useSelector(e164NumberSelector) const cachedCountryCallingCode = useSelector(defaultCountryCodeSelector) + const onboardingProps = useSelector(onboardingPropsSelector) + const { step, totalSteps } = getOnboardingStepValues(Screens.SignInWithEmail, onboardingProps) + const isSetupInOnboarding = + keylessBackupFlow === KeylessBackupFlow.Setup && origin === KeylessBackupOrigin.Onboarding const countries = useMemo(() => new Countries(i18n.language), [i18n.language]) const [phoneNumberInfo, setPhoneNumberInfo] = useState(() => getPhoneNumberDetails( @@ -98,6 +107,34 @@ function KeylessBackupPhoneInput({ route }: Props) { return ( + + ) : ( + + ) + } + title={ + isSetupInOnboarding && ( + + ) + } + /> {keylessBackupFlow === KeylessBackupFlow.Setup @@ -132,17 +169,6 @@ function KeylessBackupPhoneInput({ route }: Props) { ) } -KeylessBackupPhoneInput.navigationOptions = ({ route }: Props) => ({ - ...emptyHeader, - headerLeft: () => ( - - ), -}) - export default KeylessBackupPhoneInput const styles = StyleSheet.create({ @@ -153,6 +179,9 @@ const styles = StyleSheet.create({ scrollContainer: { padding: Spacing.Thick24, }, + header: { + paddingHorizontal: variables.contentPadding, + }, title: { ...typeScale.labelSemiBoldLarge, textAlign: 'center', diff --git a/src/keylessBackup/KeylessBackupProgress.test.tsx b/src/keylessBackup/KeylessBackupProgress.test.tsx index b7e4c33ce32..8b1a5e7f45e 100644 --- a/src/keylessBackup/KeylessBackupProgress.test.tsx +++ b/src/keylessBackup/KeylessBackupProgress.test.tsx @@ -14,7 +14,6 @@ import { ensurePincode, navigate, navigateHome } from 'src/navigator/NavigationS 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, getMockStackScreenProps } from 'test/utils' import { mockOnboardingProps } from 'test/values' @@ -26,6 +25,7 @@ jest.mock('src/utils/Logger') jest.mock('src/onboarding/steps', () => ({ goToNextOnboardingScreen: jest.fn(), onboardingPropsSelector: () => mockOnboardingPropsSelector(), + getOnboardingStepValues: () => ({ step: 2, totalSteps: 3 }), })) function createStore(keylessBackupStatus: KeylessBackupStatus, zeroBalance = false) { @@ -75,7 +75,7 @@ describe('KeylessBackupProgress', () => { ) expect(getByTestId('GreenLoadingSpinner')).toBeTruthy() }) - it('navigates to home on success', async () => { + it('navigates to home on success of the non onboarding flow', async () => { const { getByTestId } = render( @@ -88,9 +88,36 @@ describe('KeylessBackupProgress', () => { expect(navigateHome).toHaveBeenCalledTimes(1) expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) expect(ValoraAnalytics.track).toHaveBeenCalledWith( - KeylessBackupEvents.cab_progress_completed_continue + KeylessBackupEvents.cab_progress_completed_continue, + { origin: KeylessBackupOrigin.Settings } + ) + }) + + it('navigates to next onboarding screen on success of the onboarding flow', async () => { + const { getByTestId } = render( + + + + ) + expect(getByTestId('GreenLoadingSpinnerToCheck')).toBeTruthy() + expect(getByTestId('KeylessBackupProgress/Continue')).toBeTruthy() + fireEvent.press(getByTestId('KeylessBackupProgress/Continue')) + + expect(goToNextOnboardingScreen).toHaveBeenCalledWith({ + onboardingProps: mockOnboardingProps, + firstScreenInCurrentStep: Screens.SignInWithEmail, + }) + expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) + expect(ValoraAnalytics.track).toHaveBeenCalledWith( + KeylessBackupEvents.cab_progress_completed_continue, + { + origin: KeylessBackupOrigin.Onboarding, + } ) }) + it('navigates to settings on failure', async () => { const { getByTestId } = render( @@ -139,8 +166,7 @@ describe('KeylessBackupProgress', () => { expect(getByTestId('KeylessBackupProgress/ManualOnboarding')).toBeTruthy() fireEvent.press(getByTestId('KeylessBackupProgress/ManualOnboarding')) - await waitFor(() => expect(navigate).toHaveBeenCalledTimes(1)) - expect(navigate).toHaveBeenCalledWith(Screens.AccountKeyEducation) + expect(navigate).toBeCalledWith(Screens.AccountKeyEducation, { origin: 'cabOnboarding' }) expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) expect(ValoraAnalytics.track).toHaveBeenCalledWith( @@ -160,8 +186,7 @@ describe('KeylessBackupProgress', () => { expect(getByTestId('KeylessBackupProgress/Skip')).toBeTruthy() fireEvent.press(getByTestId('KeylessBackupProgress/Skip')) - await waitFor(() => expect(navigate).toHaveBeenCalledTimes(1)) - expect(navigate).toHaveBeenCalledWith(Screens.ChooseYourAdventure) + expect(navigate).toBeCalledWith(Screens.ChooseYourAdventure) expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1) expect(ValoraAnalytics.track).toHaveBeenCalledWith( @@ -325,20 +350,15 @@ describe('KeylessBackupProgress', () => { } ) }) + it('navigates to SupportContact screen on failure', async () => { const { getByTestId } = render( - ) - expect(getByTestId('KeylessBackupRestoreHelp')).toBeTruthy() - fireEvent.press(getByTestId('KeylessBackupRestoreHelp')) + expect(getByTestId('Header/KeylessBackupRestoreHelp')).toBeTruthy() + fireEvent.press(getByTestId('Header/KeylessBackupRestoreHelp')) expect(navigate).toHaveBeenCalledTimes(1) expect(navigate).toHaveBeenCalledWith(Screens.SupportContact) diff --git a/src/keylessBackup/KeylessBackupProgress.tsx b/src/keylessBackup/KeylessBackupProgress.tsx index 540b3c711a8..a9cc35667a5 100644 --- a/src/keylessBackup/KeylessBackupProgress.tsx +++ b/src/keylessBackup/KeylessBackupProgress.tsx @@ -1,14 +1,15 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' import BigNumber from 'bignumber.js' -import React, { useEffect, useLayoutEffect } from 'react' +import React, { useEffect } from 'react' import { Trans, useTranslation } from 'react-i18next' import { BackHandler, ScrollView, StyleSheet, Text, View } from 'react-native' -import { SafeAreaView } from 'react-native-safe-area-context' +import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context' import { KeylessBackupEvents } from 'src/analytics/Events' import ValoraAnalytics from 'src/analytics/ValoraAnalytics' import Button, { BtnSizes, BtnTypes } from 'src/components/Button' import Dialog from 'src/components/Dialog' import TokenDisplay from 'src/components/TokenDisplay' +import CustomHeader from 'src/components/header/CustomHeader' import GreenLoadingSpinner from 'src/icons/GreenLoadingSpinner' import GreenLoadingSpinnerToCheck from 'src/icons/GreenLoadingSpinnerToCheck' import { Help } from 'src/icons/Help' @@ -21,16 +22,22 @@ import { KeylessBackupStatus, } from 'src/keylessBackup/types' import { useDollarsToLocalAmount, useLocalCurrencyCode } from 'src/localCurrency/hooks' +import { HeaderTitleWithSubtitle } from 'src/navigator/Headers' import { ensurePincode, navigate, navigateHome } 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 { + getOnboardingStepValues, + goToNextOnboardingScreen, + onboardingPropsSelector, +} from 'src/onboarding/steps' import { totalPositionsBalanceUsdSelector } from 'src/positions/selectors' import { useDispatch, useSelector } from 'src/redux/hooks' import colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' +import variables from 'src/styles/variables' import { useTotalTokenBalance } from 'src/tokens/hooks' import Logger from 'src/utils/Logger' @@ -38,17 +45,9 @@ const TAG = 'keylessBackup/KeylessBackupProgress' 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) - navigate(Screens.SupportContact) - } - // Disable back button on Android useEffect(() => { const backPressListener = () => true @@ -56,21 +55,6 @@ function KeylessBackupProgress({ return () => BackHandler.removeEventListener('hardwareBackPress', backPressListener) }, []) - useLayoutEffect(() => { - navigation.setOptions({ - headerRight: () => - keylessBackupStatus === KeylessBackupStatus.Failed && - keylessBackupFlow === KeylessBackupFlow.Restore && ( - - ), - }) - }) - if (keylessBackupFlow === KeylessBackupFlow.Restore) { return } else { @@ -89,9 +73,16 @@ function Restore() { const totalPositionsBalanceLocal = useDollarsToLocalAmount(totalPositionsBalanceUsd) const totalBalanceLocal = totalTokenBalanceLocal?.plus(totalPositionsBalanceLocal ?? 0) + const iconMarginTop = { marginTop: variables.height / 4 } + const { t } = useTranslation() const dispatch = useDispatch() + const onPressHelp = () => { + ValoraAnalytics.track(KeylessBackupEvents.cab_restore_failed_help) + navigate(Screens.SupportContact) + } + const onPressTryAgain = () => { dispatch(keylessBackupBail()) ValoraAnalytics.track(KeylessBackupEvents.cab_restore_failed_try_again, { keylessBackupStatus }) @@ -108,11 +99,20 @@ function Restore() { switch (keylessBackupStatus) { case KeylessBackupStatus.InProgress: { - return renderInProgressState(t('keylessBackupStatus.restore.inProgress.title')) + return ( + + + + + + {t('keylessBackupStatus.setup.inProgress.title')} + + + ) } case KeylessBackupStatus.RestoreZeroBalance: { return ( - + @@ -147,9 +147,9 @@ function Restore() { } case KeylessBackupStatus.Completed: { return ( - - - + + + {t('keylessBackupStatus.restore.completed.title')} @@ -169,8 +169,7 @@ function Restore() { t('keylessBackupStatus.restore.completed.bodyZeroBalance') )} - - +