Skip to content

Commit

Permalink
fix(profile): register new screens for settings, help, invite (#5121)
Browse files Browse the repository at this point in the history
### Description

Registers new screens and modifies headers so can navigate back.

### Test plan

Unit tests updated.

**Manual tests:**

Feature gate false:


https://github.com/valora-inc/wallet/assets/140328381/74b676ea-334a-4c49-ae17-f868ac98b776


Feature gate true:


https://github.com/valora-inc/wallet/assets/140328381/91ded2a7-d54f-43a8-8f65-a33ac8a3fbaf


### Related issues

- Fixes ACT-1132

### Backwards compatibility

<!-- Brief explanation of why these changes are/are not backwards
compatible. -->

### Network scalability

If a new NetworkId and/or Network are added in the future, the changes
in this PR will:

- N/A

---------

Co-authored-by: Tom McGuire <Mcgtom10@gmail.com>
Co-authored-by: Satish Ravi <satish.ravi@valoraapp.com>
  • Loading branch information
3 people committed Mar 20, 2024
1 parent 0695fa9 commit dde91eb
Show file tree
Hide file tree
Showing 21 changed files with 2,143 additions and 90 deletions.
10 changes: 7 additions & 3 deletions src/account/Settings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,9 @@ describe('Account', () => {
},
body: '{"phoneNumber":"+14155550000","clientPlatform":"android","clientVersion":"0.0.1"}',
})
expect(navigate).toHaveBeenLastCalledWith(Screens.BackupPhrase, { navigatedFromSettings: true })
expect(navigate).toHaveBeenLastCalledWith(Screens.BackupPhrase, {
settingsScreen: Screens.SettingsDrawer,
})
})

it('deletes the account for an unverified account successfully', async () => {
Expand All @@ -480,15 +482,17 @@ describe('Account', () => {

const tree = render(
<Provider store={store}>
<Settings {...getMockStackScreenProps(Screens.Settings)} />
<Settings {...getMockStackScreenProps(Screens.Settings, { isTabNav: true })} />
</Provider>
)

fireEvent.press(tree.getByText('deleteAccountTitle'))
fireEvent.press(tree.getByText('deleteAccountWarning.buttonLabel'))

await waitFor(() =>
expect(navigate).toHaveBeenCalledWith(Screens.BackupPhrase, { navigatedFromSettings: true })
expect(navigate).toHaveBeenCalledWith(Screens.BackupPhrase, {
settingsScreen: Screens.Settings,
})
)
expect(mockFetch).not.toHaveBeenCalled()
})
Expand Down
14 changes: 10 additions & 4 deletions src/account/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,13 @@ import { useRevokeCurrentPhoneNumber } from 'src/verify/hooks'
import { selectSessions } from 'src/walletConnect/selectors'
import { walletAddressSelector } from 'src/web3/selectors'

type Props = NativeStackScreenProps<StackParamList, Screens.Settings>
type Props = NativeStackScreenProps<StackParamList, Screens.Settings | Screens.SettingsDrawer>

export const Account = ({ navigation, route }: Props) => {
const dispatch = useDispatch()
const { t } = useTranslation()
const promptConfirmRemovalModal = route.params?.promptConfirmRemovalModal
const isTabNav = route.params?.isTabNav

const revokeBottomSheetRef = useRef<BottomSheetRefType>(null)
const deleteAccountBottomSheetRef = useRef<BottomSheetRefType>(null)
Expand Down Expand Up @@ -306,7 +307,9 @@ export const Account = ({ navigation, route }: Props) => {
const pinIsCorrect = await ensurePincode()
if (pinIsCorrect) {
ValoraAnalytics.track(SettingsEvents.start_account_removal)
navigate(Screens.BackupPhrase, { navigatedFromSettings: true })
navigate(Screens.BackupPhrase, {
settingsScreen: isTabNav ? Screens.Settings : Screens.SettingsDrawer,
})
}
} catch (error) {
Logger.error('SettingsItem@onPress', 'PIN ensure error', error)
Expand Down Expand Up @@ -435,8 +438,11 @@ export const Account = ({ navigation, route }: Props) => {
}

return (
<SafeAreaView style={styles.container}>
<DrawerTopBar />
<SafeAreaView
style={styles.container}
edges={isTabNav ? ['bottom', 'left', 'right'] : undefined}
>
{!isTabNav && <DrawerTopBar />}
<ScrollView testID="SettingsScrollView">
<TouchableWithoutFeedback onPress={onDevSettingsTriggerPress}>
<Text style={styles.title} testID={'SettingsTitle'}>
Expand Down
4 changes: 2 additions & 2 deletions src/account/Support.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { FAQ_LINK, FORUM_LINK } from 'src/config'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { navigateToURI } from 'src/utils/linking'
import { createMockStore } from 'test/utils'
import { createMockStore, getMockStackScreenProps } from 'test/utils'

const renderSupport = () =>
render(
<Provider store={createMockStore()}>
<Support />
<Support {...getMockStackScreenProps(Screens.Support)} />
</Provider>
)

Expand Down
14 changes: 11 additions & 3 deletions src/account/Support.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { ScrollView, StyleSheet, Text, View } from 'react-native'
Expand All @@ -7,21 +8,28 @@ import { FAQ_LINK, FORUM_LINK } from 'src/config'
import DrawerTopBar from 'src/navigator/DrawerTopBar'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { StackParamList } from 'src/navigator/types'
import fontStyles from 'src/styles/fonts'
import { navigateToURI } from 'src/utils/linking'

type Props = NativeStackScreenProps<StackParamList, Screens.Support | Screens.SupportDrawer>

const openExternalLink = (link: string) => () => navigateToURI(link)

const onPressContact = () => {
navigate(Screens.SupportContact)
}

const Support = () => {
const Support = ({ navigation, route }: Props) => {
const { t } = useTranslation()
const isTabNav = route.params?.isTabNav

return (
<SafeAreaView style={styles.container}>
<DrawerTopBar />
<SafeAreaView
style={styles.container}
edges={isTabNav ? ['bottom', 'left', 'right'] : undefined}
>
{!isTabNav && <DrawerTopBar />}
<ScrollView>
<Text style={styles.title} testID={'SettingsTitle'}>
{t('help')}
Expand Down
10 changes: 7 additions & 3 deletions src/backup/BackupComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ import fontStyles from 'src/styles/fonts'
type Props = NativeStackScreenProps<StackParamList, Screens.BackupComplete>

function BackupComplete({ route }: Props) {
const navigatedFromSettings = route.params?.navigatedFromSettings ?? false
const settingsScreen = route.params?.settingsScreen ?? undefined
const backupCompleted = useSelector(backupCompletedSelector)
const { t } = useTranslation()

useEffect(() => {
const timer = setTimeout(() => {
if (navigatedFromSettings) {
navigate(Screens.Settings, { promptConfirmRemovalModal: true })
if (settingsScreen) {
// TODO(ACT-1133): change settingsScreen props to isAccountRemoval boolean to know if we need to go back to settings to show promptConfirmRemovalModal
navigate(settingsScreen === Screens.Settings ? Screens.Settings : Screens.SettingsDrawer, {
promptConfirmRemovalModal: true,
isTabNav: settingsScreen === Screens.Settings,
})
} else if (backupCompleted) {
ValoraAnalytics.track(OnboardingEvents.backup_complete)
navigateHome()
Expand Down
56 changes: 31 additions & 25 deletions src/backup/BackupPhrase.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,38 @@ import BackupPhrase from 'src/backup/BackupPhrase'
import { Screens } from 'src/navigator/Screens'
import { createMockStore, getMockStackScreenProps } from 'test/utils'

it('renders correctly with backup not completed', () => {
const tree = render(
<Provider store={createMockStore()}>
<BackupPhrase {...getMockStackScreenProps(Screens.BackupPhrase)} />
</Provider>
)
expect(tree).toMatchSnapshot()
})
describe.each([
{ settingsScreen: Screens.SettingsDrawer },
{ settingsScreen: Screens.Settings },
{},
])('BackupPhrase (settingsScreen: $settingsScreen)', (routeParams) => {
it('renders correctly with backup not completed', () => {
const tree = render(
<Provider store={createMockStore()}>
<BackupPhrase {...getMockStackScreenProps(Screens.BackupPhrase)} />
</Provider>
)
expect(tree).toMatchSnapshot()
})

it('renders correctly with backup completed', () => {
const tree = render(
<Provider store={createMockStore({ account: { backupCompleted: true } })}>
<BackupPhrase {...getMockStackScreenProps(Screens.BackupPhrase)} />
</Provider>
)
expect(tree).toMatchSnapshot()
})
it('renders correctly with backup completed', () => {
const tree = render(
<Provider store={createMockStore({ account: { backupCompleted: true } })}>
<BackupPhrase {...getMockStackScreenProps(Screens.BackupPhrase)} />
</Provider>
)
expect(tree).toMatchSnapshot()
})

it('still renders when mnemonic doesnt show up', () => {
const mockGetGenericPassword = Keychain.getGenericPassword as jest.Mock
mockGetGenericPassword.mockResolvedValue(null)
it('still renders when mnemonic doesnt show up', () => {
const mockGetGenericPassword = Keychain.getGenericPassword as jest.Mock
mockGetGenericPassword.mockResolvedValue(null)

const tree = render(
<Provider store={createMockStore({ account: { backupCompleted: true } })}>
<BackupPhrase {...getMockStackScreenProps(Screens.BackupPhrase)} />
</Provider>
)
expect(tree).toMatchSnapshot()
const tree = render(
<Provider store={createMockStore({ account: { backupCompleted: true } })}>
<BackupPhrase {...getMockStackScreenProps(Screens.BackupPhrase)} />
</Provider>
)
expect(tree).toMatchSnapshot()
})
})
11 changes: 5 additions & 6 deletions src/backup/BackupPhrase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,23 +97,22 @@ class BackupPhrase extends React.Component<Props, State> {

onPressContinue = () => {
ValoraAnalytics.track(OnboardingEvents.backup_continue)
navigate(Screens.BackupQuiz, { navigatedFromSettings: this.navigatedFromSettings() })
navigate(Screens.BackupQuiz, { settingsScreen: this.settingsScreen() })
}

navigatedFromSettings = () => {
return this.props.route.params?.navigatedFromSettings ?? false
settingsScreen = () => {
return this.props.route.params?.settingsScreen ?? undefined
}

render() {
const { t, backupCompleted } = this.props
const { mnemonic, isConfirmChecked } = this.state
const navigatedFromSettings = this.navigatedFromSettings()
return (
<SafeAreaView style={styles.container}>
<CustomHeader
style={{ paddingHorizontal: variables.contentPadding }}
left={
navigatedFromSettings ? (
this.settingsScreen() ? (
<CancelButton style={styles.cancelButton} />
) : (
<CancelConfirm screen={TAG} />
Expand All @@ -129,7 +128,7 @@ class BackupPhrase extends React.Component<Props, State> {
/>
<Text style={styles.body}>{t('backupKeyWarning')}</Text>
</ScrollView>
{(!backupCompleted || navigatedFromSettings) && (
{(!backupCompleted || this.settingsScreen()) && (
<>
<View style={styles.confirmationSwitchContainer}>
<Switch
Expand Down
54 changes: 39 additions & 15 deletions src/backup/BackupQuiz.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { fireEvent, render, waitFor } from '@testing-library/react-native'
import * as React from 'react'
import { Provider } from 'react-redux'
import BackupQuiz, { BackupQuiz as BackupQuizRaw } from 'src/backup/BackupQuiz'
import BackupQuiz, { BackupQuiz as BackupQuizRaw, navOptionsForQuiz } from 'src/backup/BackupQuiz'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import MockedNavigator from 'test/MockedNavigator'
import { createMockStore, getMockI18nProps, getMockStackScreenProps } from 'test/utils'
import { mockAccount, mockMnemonic } from 'test/values'

Expand All @@ -21,12 +23,6 @@ const mockScreenProps = getMockStackScreenProps(Screens.BackupQuiz)

describe('BackupQuiz', () => {
const store = createMockStore()
beforeEach(() => {
// According to the react-native-testing-library docs, if we're using
// fake timers, tests that use async/await will stall.
jest.useRealTimers()
})

it('renders correctly', async () => {
const { getByTestId, toJSON } = render(
<Provider store={store}>
Expand All @@ -37,6 +33,38 @@ describe('BackupQuiz', () => {
expect(toJSON()).toMatchSnapshot()
})

it('Cancel navigates correctly when no settingScreen passed', () => {
const { getByText, getByTestId } = render(
<Provider store={store}>
<MockedNavigator component={BackupQuiz} options={navOptionsForQuiz} />
</Provider>
)

fireEvent.press(getByTestId('CancelButton'))
expect(getByText('cancelDialog.title')).toBeTruthy()
expect(getByText('cancelDialog.body')).toBeTruthy()
})

it.each([{ settingsScreen: Screens.SettingsDrawer }, { settingsScreen: Screens.Settings }])(
'Cancel navigates correctly (settingsScreen: $settingsScreen)',
(routeParams) => {
const { getByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={BackupQuiz}
params={routeParams}
options={navOptionsForQuiz}
/>
</Provider>
)

fireEvent.press(getByTestId('CancelButton'))
expect(navigate).toBeCalledWith(routeParams.settingsScreen, {
isTabNav: routeParams.settingsScreen === Screens.Settings,
})
}
)

describe('when word is pressed', () => {
it('removes it from the options adds it to chosen words', async () => {
const mockSetBackupCompleted = jest.fn()
Expand Down Expand Up @@ -72,14 +100,8 @@ describe('BackupQuiz', () => {
})
})

/**
* Note(Rossy): Unfortunately I have to skip this test for now.
* The test must wait for buttons to be ready, and which takes
* in total over 10 seconds for all 24 mnemonic words. Maybe the
* test renderer perf will improve at some point and we can enable this.
*/
// eslint-disable-next-line jest/no-disabled-tests
it.skip('can complete the quiz correctly', async () => {
it('can complete the quiz correctly', async () => {
jest.useFakeTimers()
const mockSetBackupCompleted = jest.fn()
const { getByText, getByTestId } = render(
<Provider store={store}>
Expand All @@ -96,7 +118,9 @@ describe('BackupQuiz', () => {
await waitFor(() => getByText(word))
fireEvent.press(getByText(word))
}

fireEvent.press(getByTestId('QuizSubmit'))
jest.advanceTimersByTime(2000)
expect(mockSetBackupCompleted).toHaveBeenCalled()
})
})
23 changes: 15 additions & 8 deletions src/backup/BackupQuiz.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,22 @@ const mapStateToProps = (state: RootState): StateProps => {
}

export const navOptionsForQuiz = ({ route }: OwnProps) => {
const navigatedFromSettings = route.params?.navigatedFromSettings
const onCancel = () => {
navigate(Screens.Settings)
}
const settingsScreen = route.params?.settingsScreen ?? undefined
return {
...emptyHeader,
headerLeft: () => {
return navigatedFromSettings ? (
<CancelButton onCancel={onCancel} style={styles.cancelButton} />
return settingsScreen ? (
<CancelButton
onCancel={() =>
navigate(
settingsScreen === Screens.Settings ? Screens.Settings : Screens.SettingsDrawer,
{
isTabNav: settingsScreen === Screens.Settings,
}
)
}
style={styles.cancelButton}
/>
) : (
<CancelConfirm screen={TAG} />
)
Expand Down Expand Up @@ -188,8 +195,8 @@ export class BackupQuiz extends React.Component<Props, State> {
if (lengthsMatch && contentMatches(userChosenWords, mnemonic)) {
Logger.debug(TAG, 'Backup quiz passed')
this.props.setBackupCompleted()
const navigatedFromSettings = this.props.route.params?.navigatedFromSettings ?? false
navigate(Screens.BackupComplete, { navigatedFromSettings })
const settingsScreen = this.props.route.params?.settingsScreen ?? undefined
navigate(Screens.BackupComplete, { settingsScreen })
ValoraAnalytics.track(OnboardingEvents.backup_quiz_complete)
} else {
Logger.debug(TAG, 'Backup quiz failed, reseting words')
Expand Down

0 comments on commit dde91eb

Please sign in to comment.