Skip to content

Commit

Permalink
feat(keyless-backup): settings item with setup and delete (#4010)
Browse files Browse the repository at this point in the history
### Description

Update settings item to match latest designs. Only includes setup and
delete. In progress state is in a different issue


[Figma](https://www.figma.com/file/gGp6SVFWMbnSltkGzNzrpC/Wallet-Recovery?node-id=3604%3A30592&mode=dev)

### Test plan

Setup:


https://github.com/valora-inc/wallet/assets/5062591/a54719aa-019d-47dd-bf79-00dc784fcc32


Delete:
<img
src="https://github.com/valora-inc/wallet/assets/5062591/6f4aad6a-749d-4a54-a427-0f3fd75f317b"
width="270" />


### Related issues

- Fixes ACT-765

### Backwards compatibility

Yes
  • Loading branch information
satish-ravi authored Jul 28, 2023
1 parent 9c0058c commit 3c00247
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 10 deletions.
3 changes: 2 additions & 1 deletion locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"dataSaver": "Data Saver",
"enableDataSaver": "Enable Data Saver",
"dataSaverDetail": "Data Saver mode allows you to communicate with the Celo Network through a trusted node. You can always change this mode in app settings.",
"keylessBackupSettingsTitle": "Keyless Backup",
"keylessBackupSettingsTitle": "Email & Phone Recovery",
"setup": "Set Up",
"restartModalSwitchOff": {
"header": "Restart To Switch Off Data Saver",
"body": "To switch Data Saver on and off repeatedly, you will need to restart the app and return to this screen.",
Expand Down
45 changes: 43 additions & 2 deletions src/account/Settings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
setNumberVerified,
} from 'src/app/actions'
import { PRIVACY_LINK, TOS_LINK } from 'src/brandingConfig'
import { getKeylessBackupGate, isBackupComplete } from 'src/keylessBackup/utils'
import { ensurePincode, navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { removeStoredPin, setPincodeWithBiometry } from 'src/pincode/authentication'
Expand All @@ -39,9 +40,12 @@ mockedKeychain.getGenericPassword.mockResolvedValue({

jest.mock('src/analytics/ValoraAnalytics')
jest.mock('src/utils/Logger')
jest.mock('src/keylessBackup/utils')

describe('Account', () => {
beforeEach(() => {
mocked(getKeylessBackupGate).mockReturnValue(false)
mocked(isBackupComplete).mockReturnValue(false)
jest.clearAllMocks()
})

Expand Down Expand Up @@ -290,9 +294,8 @@ describe('Account', () => {
expect(navigate).not.toHaveBeenCalled()
})

// TODO(ACT-771): update these tests to mock statsig instead of helper function
it('does not show keyless backup', () => {
// TODO(ACT-771): update this test to verify that keyless onboarding is shown when Statsig says it should be.
// for now it should be sealed off for everyone.
const store = createMockStore()
const { queryByTestId } = render(
<Provider store={store}>
Expand All @@ -302,6 +305,44 @@ describe('Account', () => {
expect(queryByTestId('KeylessBackup')).toBeNull()
})

it('shows keyless backup setup when flag is enabled and not already backed up', () => {
mocked(getKeylessBackupGate).mockReturnValue(true)
mocked(isBackupComplete).mockReturnValue(false)
const store = createMockStore()
const { getByTestId, getByText } = render(
<Provider store={store}>
<Settings {...getMockStackScreenProps(Screens.Settings)} />
</Provider>
)
expect(getByTestId('KeylessBackup')).toBeTruthy()
expect(getByText('setup')).toBeTruthy()
fireEvent.press(getByTestId('KeylessBackup'))
expect(navigate).toHaveBeenCalledWith(Screens.WalletSecurityPrimer)
expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1)
expect(ValoraAnalytics.track).toHaveBeenLastCalledWith(
SettingsEvents.settings_set_up_keyless_backup
)
})

it('shows keyless backup delete when flag is enabled and already backed up', () => {
mocked(getKeylessBackupGate).mockReturnValue(true)
mocked(isBackupComplete).mockReturnValue(true)
const store = createMockStore()
const { getByTestId, getByText } = render(
<Provider store={store}>
<Settings {...getMockStackScreenProps(Screens.Settings)} />
</Provider>
)
expect(getByTestId('KeylessBackup')).toBeTruthy()
expect(getByText('delete')).toBeTruthy()
fireEvent.press(getByTestId('KeylessBackup'))
expect(navigate).not.toHaveBeenCalled()
expect(ValoraAnalytics.track).toHaveBeenCalledTimes(1)
expect(ValoraAnalytics.track).toHaveBeenLastCalledWith(
SettingsEvents.settings_delete_keyless_backup
)
})

it('can revoke the phone number successfully', async () => {
mockFetch.mockResponseOnce(JSON.stringify({ message: 'OK' }), {
status: 200,
Expand Down
27 changes: 20 additions & 7 deletions src/account/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ import SectionHead from 'src/components/SectionHead'
import SessionId from 'src/components/SessionId'
import {
SettingsExpandedItem,
SettingsItemCta,
SettingsItemSwitch,
SettingsItemTextValue,
} from 'src/components/SettingsItem'
import { PRIVACY_LINK, TOS_LINK } from 'src/config'
import { currentLanguageSelector } from 'src/i18n/selectors'
import { revokeVerification } from 'src/identity/actions'
import { getKeylessBackupGate, isBackupComplete } from 'src/keylessBackup/utils'
import { getLocalCurrencyCode } from 'src/localCurrency/selectors'
import DrawerTopBar from 'src/navigator/DrawerTopBar'
import { ensurePincode, navigate } from 'src/navigator/NavigationService'
Expand Down Expand Up @@ -315,6 +317,11 @@ export const Account = ({ navigation, route }: Props) => {
navigate(Screens.WalletSecurityPrimer)
}

const onPressDeleteKeylessBackup = () => {
ValoraAnalytics.track(SettingsEvents.settings_delete_keyless_backup)
// TODO(ACT-766): invoke API to delete and update status
}

const wipeReduxStore = () => {
dispatch(clearStoredAccount(account ?? '', true))
}
Expand Down Expand Up @@ -365,10 +372,11 @@ export const Account = ({ navigation, route }: Props) => {
}
}

const showKeylessBackup = () => {
// TODO(ACT-771): get from Statsig
return false
}
// TODO(ACT-771): get from Statsig
const showKeylessBackup = getKeylessBackupGate()

// TODO(ACT-684, ACT-766, ACT-767): get from redux, and also handle in progress state
const isKeylessBackupComplete = isBackupComplete()

return (
<SafeAreaView style={styles.container}>
Expand Down Expand Up @@ -416,11 +424,16 @@ export const Account = ({ navigation, route }: Props) => {
testID="RecoveryPhrase"
/>
)}
{showKeylessBackup() && ( // TODO(ACT-765): update to match designs (red/green text buttons and name)
<SettingsItemTextValue
{showKeylessBackup && (
<SettingsItemCta
title={t('keylessBackupSettingsTitle')}
onPress={onPressSetUpKeylessBackup}
onPress={
isKeylessBackupComplete ? onPressDeleteKeylessBackup : onPressSetUpKeylessBackup
}
testID="KeylessBackup"
ctaText={isKeylessBackupComplete ? t('delete') : t('setup')}
ctaColor={isKeylessBackupComplete ? colors.warning : colors.greenUI}
showChevron={!isKeylessBackupComplete}
/>
)}
<SettingsItemTextValue
Expand Down
1 change: 1 addition & 0 deletions src/analytics/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export enum SettingsEvents {
settings_revoke_phone_number_confirm = 'settings_revoke_phone_number_confirm',

settings_set_up_keyless_backup = 'settings_set_up_keyless_backup',
settings_delete_keyless_backup = 'settings_delete_keyless_backup',
}

export enum KeylessBackupEvents {
Expand Down
1 change: 1 addition & 0 deletions src/analytics/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ interface SettingsEventsProperties {
[SettingsEvents.settings_revoke_phone_number]: undefined
[SettingsEvents.settings_revoke_phone_number_confirm]: undefined
[SettingsEvents.settings_set_up_keyless_backup]: undefined
[SettingsEvents.settings_delete_keyless_backup]: undefined
}

interface KeylessBackupEventsProperties {
Expand Down
33 changes: 33 additions & 0 deletions src/components/SettingsItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { fireEvent, render } from '@testing-library/react-native'
import * as React from 'react'
import 'react-native'
import {
SettingsItemCta,
SettingsItemInput,
SettingsItemSwitch,
SettingsItemTextValue,
Expand Down Expand Up @@ -85,3 +86,35 @@ describe('SettingsItemInput', () => {
expect(onValueChange).toHaveBeenCalledWith(newValue)
})
})

describe('SettingsItemCta', () => {
it('renders correctly', () => {
const { getByTestId, getByText, queryByTestId } = render(
<SettingsItemCta testID={testID} title={title} ctaText="cta" />
)

expect(getByText(title)).toBeTruthy()
expect(getByTestId(`${testID}/cta`)).toHaveTextContent('cta')
expect(queryByTestId('ForwardChevron')).toBeNull()
})

it('renders correctly with forward chevron', () => {
const { getByTestId, getByText } = render(
<SettingsItemCta testID={testID} title={title} ctaText="cta" showChevron={true} />
)

expect(getByText(title)).toBeTruthy()
expect(getByTestId(`${testID}/cta`)).toHaveTextContent('cta')
expect(getByTestId('ForwardChevron')).toBeTruthy()
})

it('reacts on press', () => {
const onPress = jest.fn()
const { getByTestId } = render(
<SettingsItemCta testID={testID} title={title} ctaText="cta" onPress={onPress} />
)

fireEvent.press(getByTestId(testID))
expect(onPress).toHaveBeenCalled()
})
})
32 changes: 32 additions & 0 deletions src/components/SettingsItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,38 @@ export function SettingsItemInput({
)
}

type SettingsItemCtaProps = {
ctaText: string
ctaColor?: colors
showChevron?: boolean
} & BaseProps

export function SettingsItemCta({
testID,
title,
showChevron,
onPress,
ctaText,
ctaColor,
}: SettingsItemCtaProps) {
return (
<Wrapper testID={testID} onPress={onPress}>
<View style={styles.container}>
<Title value={title} />
<View style={styles.right}>
<Text
testID={testID ? `${testID}/cta` : `${title}/cta`}
style={[styles.value, { color: ctaColor }]}
>
{ctaText}
</Text>
{showChevron && <ForwardChevron color={ctaColor} />}
</View>
</View>
</Wrapper>
)
}

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
Expand Down
7 changes: 7 additions & 0 deletions src/keylessBackup/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// helper functions that default to false, but allows testing by mocking

// TODO(ACT-771): remove when status taken from statsig
export const getKeylessBackupGate = () => false

// TODO(ACT-684, ACT-766, ACT-767): remove when status taken from redux
export const isBackupComplete = () => false

0 comments on commit 3c00247

Please sign in to comment.