Skip to content

Commit

Permalink
feat(CAB): Add landing screen for phone verification (recovery) (#4821)
Browse files Browse the repository at this point in the history
### Description

Add in link phone number landing screen. This will come after
successfully completing the recovery flow. Not hooked up yet since [that
work](https://linear.app/valora/issue/ACT-781/recovery-success-screen)
isn't done.

### Test plan

Unit tests added.


https://github.com/valora-inc/wallet/assets/140328381/1f1e14ca-bbb6-4453-871e-d85769b4f141


### Related issues

- Fixes #[ACT-768]

### Backwards compatibility

Yes, just adding new screen.
  • Loading branch information
finnian0826 authored Feb 5, 2024
1 parent 871e186 commit fe2e559
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 3 deletions.
6 changes: 6 additions & 0 deletions locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2045,5 +2045,11 @@
"getStartedHome": {
"title": "Buy crypto or transfer tokens",
"body": "Buy or transfer compatible tokens to send crypto and explore web3!"
},
"linkPhoneNumber": {
"title": "Link a phone number to your wallet address",
"description": "No more messy wallet addresses! Send crypto to other Valora users with just a phone number",
"startButtonLabel": "Link Phone Number",
"later": "I'll do this later"
}
}
3 changes: 3 additions & 0 deletions src/analytics/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ export enum OnboardingEvents {

cya_button_press = 'cya_button_press',
cya_later = 'cya_later',

link_phone_number = 'link_phone_number',
link_phone_number_later = 'link_phone_number_later',
}

// Events emitted in the CPV flow
Expand Down
2 changes: 2 additions & 0 deletions src/analytics/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ interface OnboardingEventsProperties {
[OnboardingEvents.cya_later]: {
cardOrder: AdventureCardName[]
}
[OnboardingEvents.link_phone_number]: undefined
[OnboardingEvents.link_phone_number_later]: undefined
}

interface PhoneVerificationEventsProperties {
Expand Down
2 changes: 2 additions & 0 deletions src/analytics/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
[OnboardingEvents.protect_wallet_complete]: ``,
[OnboardingEvents.cya_button_press]: `when one of the choose your adventure card is pressed. Properties include card name, position of the card (1-based index) and card order`,
[OnboardingEvents.cya_later]: `when "I'll explore later" is pressed`,
[OnboardingEvents.link_phone_number]: `User chooses to link phone number for CPV after recovery flow`,
[OnboardingEvents.link_phone_number_later]: `User chooses not to link phone number for CPV after recovery flow`,

// Events emitted in the CPV flow
[PhoneVerificationEvents.phone_verification_skip_confirm]: `when skip is confirmed from the dialog in the phone number input screen`,
Expand Down
25 changes: 22 additions & 3 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum BtnTypes {
SECONDARY_WHITE_BG = 'SecondaryWhiteBg',
ONBOARDING = 'Onboarding',
ONBOARDING_SECONDARY = 'OnboardingSecondary',
GRAY_WITH_BORDER = 'GrayWithBorder',
}

export enum BtnSizes {
Expand Down Expand Up @@ -88,7 +89,7 @@ export default React.memo(function Button(props: ButtonProps) {
[props.onPress, type, disabled]
)

const { textColor, backgroundColor, opacity } = getColors(type, disabled)
const { textColor, backgroundColor, opacity, borderColor } = getColors(type, disabled)

return (
<View style={getStyleForWrapper(size, style)}>
Expand All @@ -97,7 +98,10 @@ export default React.memo(function Button(props: ButtonProps) {
<Touchable
onPress={debouncedOnPress}
disabled={disabled}
style={[getStyle(size, backgroundColor, opacity, iconPositionLeft), touchableStyle]}
style={[
getStyle(size, backgroundColor, opacity, borderColor, iconPositionLeft),
touchableStyle,
]}
testID={testID}
>
{showLoading ? (
Expand Down Expand Up @@ -161,6 +165,7 @@ function getColors(type: BtnTypes, disabled: boolean | undefined) {
let textColor
let backgroundColor
let opacity
let borderColor
switch (type) {
case BtnTypes.PRIMARY:
textColor = colors.white
Expand All @@ -184,19 +189,33 @@ function getColors(type: BtnTypes, disabled: boolean | undefined) {
backgroundColor = colors.white
opacity = disabled ? 0.5 : 1.0
break
case BtnTypes.GRAY_WITH_BORDER:
textColor = colors.black
backgroundColor = colors.gray1
borderColor = colors.gray2
break
}

return { textColor, backgroundColor, opacity }
return { textColor, backgroundColor, opacity, borderColor }
}

function getStyle(
size: BtnSizes | undefined,
backgroundColor: Colors,
opacity: number | undefined,
borderColor: Colors | undefined,
iconPositionLeft: boolean
) {
const borderStyles = borderColor
? {
borderColor,
borderRadius: 100,
borderWidth: 1,
}
: {}
const commonStyles: ViewStyle = {
...styles.button,
...borderStyles,
backgroundColor,
opacity,
flexDirection: iconPositionLeft ? 'row' : 'row-reverse',
Expand Down
45 changes: 45 additions & 0 deletions src/keylessBackup/LinkPhoneNumber.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { fireEvent, render } from '@testing-library/react-native'
import React from 'react'
import { Provider } from 'react-redux'
import { OnboardingEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import LinkPhoneNumber from 'src/keylessBackup/LinkPhoneNumber'
import { navigate, navigateHome } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { createMockStore, getMockStackScreenProps } from 'test/utils'

jest.mock('src/navigator/NavigationService')
jest.mock('src/analytics/ValoraAnalytics')

describe('LinkPhoneNumber', () => {
beforeEach(() => {
jest.clearAllMocks()
})

it('navigates to verification start screen', async () => {
const { getByTestId } = render(
<Provider store={createMockStore()}>
<LinkPhoneNumber {...getMockStackScreenProps(Screens.LinkPhoneNumber)} />
</Provider>
)
fireEvent.press(getByTestId('LinkPhoneNumberButton'))

expect(navigate).toHaveBeenCalledTimes(1)
expect(navigate).toHaveBeenCalledWith(Screens.VerificationStartScreen, {
isOnboarding: false,
})
expect(ValoraAnalytics.track).toHaveBeenCalledWith(OnboardingEvents.link_phone_number)
})

it('navigates to home on later', async () => {
const { getByTestId } = render(
<Provider store={createMockStore()}>
<LinkPhoneNumber {...getMockStackScreenProps(Screens.LinkPhoneNumber)} />
</Provider>
)
fireEvent.press(getByTestId('LinkPhoneNumberLater'))

expect(navigateHome).toHaveBeenCalledTimes(1)
expect(ValoraAnalytics.track).toHaveBeenCalledWith(OnboardingEvents.link_phone_number_later)
})
})
103 changes: 103 additions & 0 deletions src/keylessBackup/LinkPhoneNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { useLayoutEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, Text, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { OnboardingEvents } 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 { navigate, navigateHome } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { StackParamList } from 'src/navigator/types'
import colors from 'src/styles/colors'
import { typeScale } from 'src/styles/fonts'
import { Spacing } from 'src/styles/styles'

type Props = NativeStackScreenProps<StackParamList, Screens.LinkPhoneNumber>

export default function LinkPhoneNumber({ navigation }: Props) {
const { t } = useTranslation()

useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => <BackButton />,
headerStyle: {
backgroundColor: colors.gray1,
},
})
}, [navigation])

const continueButtonOnPress = async () => {
ValoraAnalytics.track(OnboardingEvents.link_phone_number)
navigate(Screens.VerificationStartScreen, { isOnboarding: false })
}
const laterButtonOnPress = async () => {
ValoraAnalytics.track(OnboardingEvents.link_phone_number_later)
navigateHome()
}

return (
<SafeAreaView style={styles.safeArea} edges={['bottom']}>
<View>
<View style={styles.viewContainer}>
<View style={styles.screenTextContainer}>
<Text style={styles.screenTitle}>{t('linkPhoneNumber.title')}</Text>
<Text style={styles.screenDescription}>{t('linkPhoneNumber.description')}</Text>
</View>
</View>
<View style={styles.buttonView}>
<Button
text={t('linkPhoneNumber.startButtonLabel')}
onPress={continueButtonOnPress}
style={styles.button}
type={BtnTypes.PRIMARY}
size={BtnSizes.FULL}
testID="LinkPhoneNumberButton"
/>
<Button
text={t('linkPhoneNumber.later')}
onPress={laterButtonOnPress}
style={styles.button}
type={BtnTypes.GRAY_WITH_BORDER}
size={BtnSizes.FULL}
testID="LinkPhoneNumberLater"
/>
</View>
</View>
</SafeAreaView>
)
}

const styles = StyleSheet.create({
safeArea: {
alignItems: 'center',
backgroundColor: colors.gray1,
flexGrow: 1,
justifyContent: 'space-between',
},
screenDescription: {
...typeScale.bodyMedium,
textAlign: 'center',
},
screenTitle: {
...typeScale.titleSmall,
marginTop: Spacing.Thick24,
textAlign: 'center',
},
screenTextContainer: {
gap: Spacing.Regular16,
},
viewContainer: {
alignItems: 'center',
flex: 1,
gap: Spacing.Thick24,
paddingHorizontal: Spacing.Thick24,
},
buttonView: {
padding: Spacing.Thick24,
alignItems: 'center',
},
button: { marginBottom: Spacing.Thick24, width: '100%' },
})
6 changes: 6 additions & 0 deletions src/navigator/Navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import ImportWallet from 'src/import/ImportWallet'
import KeylessBackupPhoneCodeInput from 'src/keylessBackup/KeylessBackupPhoneCodeInput'
import KeylessBackupPhoneInput from 'src/keylessBackup/KeylessBackupPhoneInput'
import KeylessBackupProgress from 'src/keylessBackup/KeylessBackupProgress'
import LinkPhoneNumber from 'src/keylessBackup/LinkPhoneNumber'
import SetUpKeylessBackup from 'src/keylessBackup/SetUpKeylessBackup'
import SignInWithEmail from 'src/keylessBackup/SignInWithEmail'
import WalletSecurityPrimer from 'src/keylessBackup/WalletSecurityPrimer'
Expand Down Expand Up @@ -472,6 +473,11 @@ const settingsScreens = (Navigator: typeof Stack) => (
options={{ headerStyle: {} }}
component={KeylessBackupProgress}
/>
<Navigator.Screen
name={Screens.LinkPhoneNumber}
options={{ headerStyle: {} }}
component={LinkPhoneNumber}
/>
</>
)

Expand Down
1 change: 1 addition & 0 deletions src/navigator/Screens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export enum Screens {
Language = 'Language',
LanguageModal = 'LanguageModal',
Licenses = 'Licenses',
LinkPhoneNumber = 'LinkPhoneNumber',
Main = 'Main',
MainModal = 'MainModal',
MultichainBeta = 'MultichainBeta',
Expand Down
1 change: 1 addition & 0 deletions src/navigator/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export type StackParamList = {
}
| undefined
[Screens.Licenses]: undefined
[Screens.LinkPhoneNumber]: undefined
[Screens.Main]: undefined
[Screens.MainModal]: undefined
[Screens.MultichainBeta]: undefined
Expand Down
1 change: 1 addition & 0 deletions test/RootStateSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2623,6 +2623,7 @@
"Language",
"LanguageModal",
"Licenses",
"LinkPhoneNumber",
"Main",
"MainModal",
"MultichainBeta",
Expand Down

0 comments on commit fe2e559

Please sign in to comment.