Skip to content

Commit

Permalink
feat: rework cab for onboarding flow (#5611)
Browse files Browse the repository at this point in the history
### Description

Works to adjust the cab flow for similar styles to onboarding. 

#### Brief Summary of Changes:
- Styled bottom buttons to match other onboarding buttons - previously
they displayed lower than other onboarding buttons and buttons
throughout the app.
- Replaced usages of nav option headers with customer headers to match
onboarding headers and allow onboarding step display.
- Placed Icons on Keyless backup a set distance from the top as to avoid
icon content jumps during the flow.
- Replaced top-level views`View` with `ScrollView` throughout the
`KeylessBackupProgress.tsx`.

### Test plan

- [x] Tested locally on iOS
- [x] Tested locally on Android
- [x] Unit tests updated

### Related issues

- Fixes ACT-1233

### Backwards compatibility

Yes

### Network scalability

N/A
  • Loading branch information
MuckT authored Jul 12, 2024
1 parent 1a137fe commit 29245f3
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 189 deletions.
3 changes: 3 additions & 0 deletions src/analytics/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 5 additions & 2 deletions src/analytics/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/analytics/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,18 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
[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]: ``,
Expand Down
3 changes: 2 additions & 1 deletion src/components/header/CustomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ function CustomHeader({ left, right, title, style }: Props) {
return (
<View style={style ? [styles.container, style] : styles.container}>
{!!title && <View style={styles.titleContainer}>{titleComponent}</View>}
{!!left && <View style={styles.buttonContainer}>{left}</View>}
{/* Need left element to be an empty view if not provided for right alignment to work */}
{left ? <View style={styles.buttonContainer}>{left}</View> : <View />}
{!!right && <View style={styles.buttonContainer}>{right}</View>}
</View>
)
Expand Down
19 changes: 17 additions & 2 deletions src/keylessBackup/KeylessBackupPhoneCodeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -116,6 +119,8 @@ function KeylessBackupPhoneCodeInput({
keylessBackupFlow,
origin
)
const onboardingProps = useSelector(onboardingPropsSelector)
const { step, totalSteps } = getOnboardingStepValues(Screens.SignInWithEmail, onboardingProps)

const bottomSheetRef = useRef<BottomSheetRefType>(null)

Expand All @@ -131,7 +136,14 @@ function KeylessBackupPhoneCodeInput({
keylessBackupFlow === KeylessBackupFlow.Setup && origin === KeylessBackupOrigin.Onboarding

const headerLeft = isSetupInOnboarding ? (
<BackButton testID="BackButton" />
<BackButton
testID="BackButton"
eventName={KeylessBackupEvents.cab_enter_phone_code_back}
eventProperties={{
keylessBackupFlow,
origin,
}}
/>
) : (
<KeylessBackupCancelButton
flow={keylessBackupFlow}
Expand All @@ -155,7 +167,10 @@ function KeylessBackupPhoneCodeInput({
}
title={
isSetupInOnboarding && (
<Text style={styles.title}>{t('phoneVerificationInput.title')}</Text>
<HeaderTitleWithSubtitle
title={t('phoneVerificationInput.title')}
subTitle={t('registrationSteps', { step, totalSteps })}
/>
)
}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/keylessBackup/KeylessBackupPhoneInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -25,7 +26,7 @@ describe('KeylessBackupPhoneInput', () => {
keylessBackupFlow: KeylessBackupFlow.Setup,
selectedCountryCodeAlpha2: 'US',
}}
options={KeylessBackupPhoneInput.navigationOptions}
options={noHeader}
/>
</Provider>
)
Expand Down
57 changes: 43 additions & 14 deletions src/keylessBackup/KeylessBackupPhoneInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<StackParamList, Screens.KeylessBackupPhoneInput>

Expand All @@ -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(
Expand Down Expand Up @@ -98,6 +107,34 @@ function KeylessBackupPhoneInput({ route }: Props) {

return (
<SafeAreaView style={styles.container}>
<CustomHeader
style={styles.header}
left={
isSetupInOnboarding ? (
<BackButton
eventName={KeylessBackupEvents.cab_enter_phone_number_back}
eventProperties={{
keylessBackupFlow,
origin,
}}
/>
) : (
<KeylessBackupCancelButton
flow={keylessBackupFlow}
origin={origin}
eventName={KeylessBackupEvents.cab_enter_phone_number_cancel}
/>
)
}
title={
isSetupInOnboarding && (
<HeaderTitleWithSubtitle
title={t('phoneVerificationScreen.screenTitle')}
subTitle={t('registrationSteps', { step, totalSteps })}
/>
)
}
/>
<KeyboardAwareScrollView style={styles.scrollContainer}>
<Text style={styles.title}>
{keylessBackupFlow === KeylessBackupFlow.Setup
Expand Down Expand Up @@ -132,17 +169,6 @@ function KeylessBackupPhoneInput({ route }: Props) {
)
}

KeylessBackupPhoneInput.navigationOptions = ({ route }: Props) => ({
...emptyHeader,
headerLeft: () => (
<KeylessBackupCancelButton
flow={route.params.keylessBackupFlow}
origin={route.params.origin}
eventName={KeylessBackupEvents.cab_enter_phone_number_cancel}
/>
),
})

export default KeylessBackupPhoneInput

const styles = StyleSheet.create({
Expand All @@ -153,6 +179,9 @@ const styles = StyleSheet.create({
scrollContainer: {
padding: Spacing.Thick24,
},
header: {
paddingHorizontal: variables.contentPadding,
},
title: {
...typeScale.labelSemiBoldLarge,
textAlign: 'center',
Expand Down
50 changes: 35 additions & 15 deletions src/keylessBackup/KeylessBackupProgress.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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) {
Expand Down Expand Up @@ -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(
<Provider store={createStore(KeylessBackupStatus.Completed)}>
<KeylessBackupProgress {...getProps()} />
Expand All @@ -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(
<Provider store={createStore(KeylessBackupStatus.Completed)}>
<KeylessBackupProgress
{...getProps(KeylessBackupFlow.Setup, KeylessBackupOrigin.Onboarding)}
/>
</Provider>
)
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(
<Provider store={createStore(KeylessBackupStatus.Failed)}>
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -325,20 +350,15 @@ describe('KeylessBackupProgress', () => {
}
)
})

it('navigates to SupportContact screen on failure', async () => {
const { getByTestId } = render(
<Provider store={createStore(KeylessBackupStatus.Failed)}>
<KeylessBackupProgress {...getProps(KeylessBackupFlow.Restore)} />
<MockedNavigator
component={KeylessBackupProgress}
params={{
keylessBackupFlow: KeylessBackupFlow.Restore,
}}
/>
</Provider>
)
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)
Expand Down
Loading

0 comments on commit 29245f3

Please sign in to comment.