Skip to content

Commit

Permalink
[Wallet] Add support for social wallet (Safeguards) import (#1414)
Browse files Browse the repository at this point in the history
* Implement the latest import wallet flow designs
* Add support for social wallet (Safeguards) import
* A bit of style cleanup
  • Loading branch information
jmrossy authored Oct 22, 2019
1 parent 85a2bfe commit 3bf4177
Show file tree
Hide file tree
Showing 41 changed files with 1,880 additions and 864 deletions.
7 changes: 4 additions & 3 deletions packages/mobile/locales/en-US/backupKeyFlow6.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@
"warning": "NEVER TELL ANYONE YOUR SAFEGUARDS’ IDENTITIES.",
"skip": "Skip For Now"
},
"socialBackupPhraseHeader": "Safeguard Phrase {{index}}",
"socialBackup": {
"body":
"Share each phrase below with a friend. Be sure to send only one phrase to each person.",
"confirmation": "I have sent each Safeguard phrase to a trusted friend.",
"phrase1": "Safeguard Phrase 1",
"phrase2": "Safeguard Phrase 2",
"yourSafeguards": "Your Safeguards"
}
},
"backupPhrasePlaceholder":
"horse leopard dog monkey shark tiger lemur whale squid wolf squirrel mouse lion elephant cat shrimp bear penguin deer turtle fox zebra goat giraffe"
}
2 changes: 1 addition & 1 deletion packages/mobile/locales/en-US/nuxNamePin1.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"InvitationCode": "Invitation Code",
"optIn": "Opt In",
"submitting": "Submitting ...",
"importIt": "Import Existing Wallet",
"importIt": "Restore Existing Wallet",
"cancel": "Cancel",
"important": "Important",
"createPin": {
Expand Down
6 changes: 5 additions & 1 deletion packages/mobile/locales/en-US/nuxRestoreWallet3.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@
"emptyWalletWarning": "This wallet is empty.",
"useEmptyAnyway": "Would you like to use it anyway?",
"useEmptyWallet": "Use Empty Wallet",
"tryAnotherKey": "Try Another Backup Key"
"tryAnotherKey": "Try Another Backup Key",
"restoreSocial": "Restore with Safeguards",
"socialImportInfo":
"If you enabled Safeguards, you sent Safeguard Phrases to two friends. Please retrieve these phrases and enter them here (in any order) to regain access to your funds.",
"socialTip": "Phrases are lists of 13 words separated by spaces."
}
7 changes: 4 additions & 3 deletions packages/mobile/locales/es-419/backupKeyFlow6.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@
"warning": "NEVER TELL ANYONE YOUR SAFEGUARDS’ IDENTITIES.",
"skip": "Skip For Now"
},
"socialBackupPhraseHeader": "Safeguard Phrase {{index}}",
"socialBackup": {
"body":
"Share each phrase below with a friend. Be sure to send only one phrase to each person.",
"confirmation": "I have sent each Safeguard phrase to a trusted friend.",
"phrase1": "Safeguard Phrase 1",
"phrase2": "Safeguard Phrase 2",
"yourSafeguards": "Your Safeguards"
}
},
"backupPhrasePlaceholder":
"horse leopard dog monkey shark tiger lemur whale squid wolf squirrel mouse lion elephant cat shrimp bear penguin deer turtle fox zebra goat giraffe"
}
2 changes: 1 addition & 1 deletion packages/mobile/locales/es-419/nuxNamePin1.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"InvitationCode": "Código de invitación",
"optIn": "Inscribirse",
"submitting": "Enviando ...",
"importIt": "Importa tu monedero existente",
"importIt": "Restaurar tu monedero existente",
"cancel": "Cancelar",
"important": "Importante",
"createPin": {
Expand Down
6 changes: 5 additions & 1 deletion packages/mobile/locales/es-419/nuxRestoreWallet3.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@
"emptyWalletWarning": "Este Monedero esta vacio",
"useEmptyAnyway": "¿Te gustaría usarlo de todos modos?",
"useEmptyWallet": "Usar Monedero Vacío",
"tryAnotherKey": "Probar con otra Clave de Respaldo"
"tryAnotherKey": "Probar con otra Clave de Respaldo",
"restoreSocial": "Restore with Safeguards",
"socialImportInfo":
"If you enabled Safeguards, you sent Safeguard Phrases to two friends. Please retrieve these phrases and enter them here (in any order) to regain access to your funds.",
"socialTip": "Phrases are lists of 13 words separated by spaces."
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,14 @@ exports[`renders the EditProfile Component 1`] = `
placeholder="yourName"
rejectResponderTermination={true}
style={
Array [
Object {
"fontFamily": "Hind-Regular",
},
Object {
"borderColor": "#D1D5D8",
"borderRadius": 3,
"padding": 8,
},
Object {
"backgroundColor": "#FFFFFF",
"flex": 1,
},
]
Object {
"backgroundColor": "#FFFFFF",
"borderColor": "#D1D5D8",
"borderRadius": 3,
"flex": 1,
"fontFamily": "Hind-Regular",
"padding": 8,
}
}
underlineColorAndroid="transparent"
value="Test"
Expand Down
44 changes: 16 additions & 28 deletions packages/mobile/src/account/__snapshots__/Invite.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,14 @@ exports[`Invite renders correctly with no recipients 1`] = `
rejectResponderTermination={true}
shouldShowClipboard={[Function]}
style={
Array [
Object {
"fontFamily": "Hind-Regular",
},
Object {
"borderColor": "#D1D5D8",
"borderRadius": 3,
"padding": 8,
},
Object {
"backgroundColor": "#FFFFFF",
"flex": 1,
},
]
Object {
"backgroundColor": "#FFFFFF",
"borderColor": "#D1D5D8",
"borderRadius": 3,
"flex": 1,
"fontFamily": "Hind-Regular",
"padding": 8,
}
}
underlineColorAndroid="transparent"
value=""
Expand Down Expand Up @@ -355,20 +349,14 @@ exports[`Invite renders correctly with recipients 1`] = `
rejectResponderTermination={true}
shouldShowClipboard={[Function]}
style={
Array [
Object {
"fontFamily": "Hind-Regular",
},
Object {
"borderColor": "#D1D5D8",
"borderRadius": 3,
"padding": 8,
},
Object {
"backgroundColor": "#FFFFFF",
"flex": 1,
},
]
Object {
"backgroundColor": "#FFFFFF",
"borderColor": "#D1D5D8",
"borderRadius": 3,
"flex": 1,
"fontFamily": "Hind-Regular",
"padding": 8,
}
}
underlineColorAndroid="transparent"
value=""
Expand Down
12 changes: 10 additions & 2 deletions packages/mobile/src/backup/BackupPhrase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import CeloAnalytics from 'src/analytics/CeloAnalytics'
import { CustomEventNames } from 'src/analytics/constants'
import componentWithAnalytics from 'src/analytics/wrapper'
import { ErrorMessages } from 'src/app/ErrorMessages'
import BackupPhraseContainer from 'src/backup/BackupPhraseContainer'
import BackupPhraseContainer, {
BackupPhraseContainerMode,
BackupPhraseType,
} from 'src/backup/BackupPhraseContainer'
import { getStoredMnemonic } from 'src/backup/utils'
import { Namespaces } from 'src/i18n'
import { headerWithBackButton } from 'src/navigator/Headers'
Expand Down Expand Up @@ -99,7 +102,12 @@ class BackupPhrase extends React.Component<Props, State> {
<View>
<Text style={fontStyles.h1}>{t('yourBackupKey')}</Text>
<Text style={styles.body}>{t('backupKeySummary')}</Text>
<BackupPhraseContainer words={mnemonic} showCopy={true} />
<BackupPhraseContainer
value={mnemonic}
showCopy={true}
mode={BackupPhraseContainerMode.READONLY}
type={BackupPhraseType.BACKUP_KEY}
/>
<Text style={styles.tipText}>
<Text style={[styles.tipText, fontStyles.bold]}>{t('global:warning')}</Text>
{t('securityTip')}
Expand Down
54 changes: 49 additions & 5 deletions packages/mobile/src/backup/BackupPhraseContainer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,54 @@
import * as React from 'react'
import 'react-native'
import * as renderer from 'react-test-renderer'
import BackupPhraseContainer from 'src/backup/BackupPhraseContainer'
import { mockMnemonic } from 'test/values'
import BackupPhraseContainer, {
BackupPhraseContainerMode,
BackupPhraseType,
} from 'src/backup/BackupPhraseContainer'
import { mockMnemonic, mockMnemonicShard1 } from 'test/values'

it('renders correctly', () => {
const tree = renderer.create(<BackupPhraseContainer words={mockMnemonic} />)
expect(tree).toMatchSnapshot()
describe(BackupPhraseContainer, () => {
it('renders correctly for readonly backup phrase', () => {
const tree = renderer.create(
<BackupPhraseContainer
value={mockMnemonic}
mode={BackupPhraseContainerMode.READONLY}
type={BackupPhraseType.BACKUP_KEY}
/>
)
expect(tree).toMatchSnapshot()
})

it('renders correctly for input backup phrase', () => {
const tree = renderer.create(
<BackupPhraseContainer
value={mockMnemonic}
mode={BackupPhraseContainerMode.INPUT}
type={BackupPhraseType.BACKUP_KEY}
/>
)
expect(tree).toMatchSnapshot()
})

it('renders correctly for readonly social backup phrase', () => {
const tree = renderer.create(
<BackupPhraseContainer
value={mockMnemonicShard1}
mode={BackupPhraseContainerMode.READONLY}
type={BackupPhraseType.SOCIAL_BACKUP}
/>
)
expect(tree).toMatchSnapshot()
})

it('renders correctly for input social backup phrase', () => {
const tree = renderer.create(
<BackupPhraseContainer
value={mockMnemonicShard1}
mode={BackupPhraseContainerMode.INPUT}
type={BackupPhraseType.SOCIAL_BACKUP}
/>
)
expect(tree).toMatchSnapshot()
})
})
97 changes: 83 additions & 14 deletions packages/mobile/src/backup/BackupPhraseContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import Touchable from '@celo/react-components/components/Touchable'
import withTextInputPasteAware from '@celo/react-components/components/WithTextInputPasteAware'
import colors from '@celo/react-components/styles/colors'
import { fontStyles } from '@celo/react-components/styles/fonts'
import * as React from 'react'
import { WithNamespaces, withNamespaces } from 'react-i18next'
import { Clipboard, Platform, StyleSheet, Text, View, ViewStyle } from 'react-native'
import { Clipboard, Platform, StyleSheet, Text, TextInput, View, ViewStyle } from 'react-native'
import FlagSecure from 'react-native-flag-secure-android'
import { isValidBackupPhrase, isValidSocialBackupPhrase } from 'src/backup/utils'
import { Namespaces } from 'src/i18n'
import Logger from 'src/utils/Logger'

const PhraseInput = withTextInputPasteAware(TextInput, { top: undefined, right: 12, bottom: 12 })

export enum BackupPhraseContainerMode {
READONLY = 'READONLY',
INPUT = 'INPUT',
}

export enum BackupPhraseType {
BACKUP_KEY = 'BACKUP_KEY',
SOCIAL_BACKUP = 'SOCIAL_BACKUP',
}

type Props = {
words: string | null
value: string | null
mode: BackupPhraseContainerMode
type: BackupPhraseType
index?: number // e.g. index of safeguard phrase
showCopy?: boolean
headerText?: string
style?: ViewStyle
onChangeText?: (value: string) => void
testID?: string
} & WithNamespaces

export class BackupPhraseContainer extends React.Component<Props> {
Expand All @@ -33,30 +51,67 @@ export class BackupPhraseContainer extends React.Component<Props> {
}

onPressCopy = () => {
const { words, t } = this.props
const { value: words, t } = this.props
if (!words) {
return
}
Clipboard.setString(words)
Logger.showMessage(t('copied'))
}

onPhraseInputChange = (value: string) => {
if (this.props.onChangeText) {
this.props.onChangeText(value)
}
}

render() {
const { t, words, showCopy, headerText, style } = this.props
const { t, value: words, showCopy, style, mode, type, index, testID } = this.props

return (
<View style={style}>
<View style={styles.headerContainer}>
<Text style={styles.headerText}>{headerText || t('backupKey')}</Text>
<Text style={styles.headerText}>
{type === BackupPhraseType.BACKUP_KEY
? t('backupKey')
: t('socialBackupPhraseHeader', { index })}
</Text>
{showCopy && (
<Touchable borderless={true} onPress={this.onPressCopy}>
<Text style={styles.headerButton}>{this.props.t('global:copy')}</Text>
</Touchable>
)}
</View>
<View style={styles.phraseContainer}>
{!!words && <Text style={styles.phraseText}>{words}</Text>}
</View>
{mode === BackupPhraseContainerMode.READONLY && (
<View style={styles.phraseContainer}>
{!!words && <Text style={styles.phraseText}>{words}</Text>}
</View>
)}
{mode === BackupPhraseContainerMode.INPUT && (
<View style={styles.phraseInputContainer}>
<PhraseInput
style={[
styles.phraseInputText,
type === BackupPhraseType.SOCIAL_BACKUP && styles.socialPhraseInputText,
]}
value={words || ''}
placeholder={t('backupPhrasePlaceholder')}
onChangeText={this.onPhraseInputChange}
shouldShowClipboard={
type === BackupPhraseType.BACKUP_KEY
? isValidBackupPhrase
: isValidSocialBackupPhrase
}
underlineColorAndroid="transparent"
placeholderTextColor={colors.inactive}
enablesReturnKeyAutomatically={true}
multiline={true}
autoCorrect={false}
autoCapitalize={'none'}
testID={testID}
/>
</View>
)}
</View>
)
}
Expand Down Expand Up @@ -84,17 +139,31 @@ const styles = StyleSheet.create({
justifyContent: 'center',
padding: 14,
},
phraseText: {
...fontStyles.body,
lineHeight: 27,
color: colors.darkSecondary,
},
phraseInputContainer: {
marginTop: 10,
},
phraseInputText: {
...fontStyles.body,
borderWidth: 1,
borderColor: colors.inputBorder,
borderRadius: 4,
minHeight: 145,
padding: 14,
},
socialPhraseInputText: {
minHeight: 100,
},
button: {
alignSelf: 'center',
flex: 1,
paddingBottom: 0,
marginBottom: 0,
},
phraseText: {
...fontStyles.body,
lineHeight: 27,
color: colors.darkSecondary,
},
})

export default withNamespaces(Namespaces.backupKeyFlow6)(BackupPhraseContainer)
Loading

0 comments on commit 3bf4177

Please sign in to comment.