From dc0e02700e60b35bdcc942f8593f5f65603414d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Chavarr=C3=ADa?= Date: Thu, 14 Jul 2022 06:38:12 -0300 Subject: [PATCH] refactor: update import keys screen to new design (#252) * refactor: update import keys screen to new design * fix: update test * fix: clean code * Fix trash circle. Co-authored-by: Jesse Clark --- src/components/button/NavigationFooter.tsx | 95 -------- src/lib/bip39/bip39.test.ts | 2 +- src/lib/bip39/index.ts | 4 +- src/ux/createKeys/CreateKeysNavigation.tsx | 1 + .../import/ImportMasterKeyScreen.tsx | 208 +++++++++++++----- src/ux/createKeys/import/WordInput.tsx | 87 -------- src/ux/createKeys/new/WordSelector.tsx | 7 +- 7 files changed, 159 insertions(+), 245 deletions(-) delete mode 100644 src/components/button/NavigationFooter.tsx delete mode 100644 src/ux/createKeys/import/WordInput.tsx diff --git a/src/components/button/NavigationFooter.tsx b/src/components/button/NavigationFooter.tsx deleted file mode 100644 index c7345a428..000000000 --- a/src/components/button/NavigationFooter.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { colors } from '../../styles/colors' - -import React from 'react' -import { - GestureResponderEvent, - TouchableOpacity, - View, - Text, - StyleSheet, -} from 'react-native' -import { Arrow } from '../icons' - -interface Interface { - title?: string - icon?: any - onPress?: (event: GestureResponderEvent) => any - onBackwards: (event: GestureResponderEvent) => any - disabled?: boolean - testID?: string - shadowColor?: string - backgroundColor?: string -} - -export const NavigationFooter: React.FC = ({ - title, - onPress, - onBackwards, - disabled, - shadowColor, - backgroundColor = '#fff', - children, -}) => { - const imageStyle = { - ...styles.image, - shadowColor, - backgroundColor, - } - - return ( - <> - {children} - - - - - - back - - - {title} - - - - - - - ) -} - -const styles = StyleSheet.create({ - container: { - color: colors.white, - height: 80, - flexDirection: 'row', - backgroundColor: colors.blue, - justifyContent: 'space-between', - }, - buttonLeft: { - padding: 10, - flexDirection: 'row', - }, - buttonRight: { - padding: 10, - flexDirection: 'row', - }, - image: { - width: 30, - height: 30, - borderRadius: 15, - margin: 2, - }, - text: { - paddingTop: 8, - color: colors.white, - }, - textDisabled: { - color: '#cccccc', - }, -}) diff --git a/src/lib/bip39/bip39.test.ts b/src/lib/bip39/bip39.test.ts index b7758833f..a5d211ffe 100644 --- a/src/lib/bip39/bip39.test.ts +++ b/src/lib/bip39/bip39.test.ts @@ -14,7 +14,7 @@ describe('Validate Mnemonic', () => { }) test('mnemonic is too short', async () => { const error = validateMnemonic(SEED_PHRASE_TOO_SHORT) - expect(error).toEqual('you need to enter at least twelve words') + expect(error).toEqual('you need to enter at least 24 words') }) test('mnemonic is has valid words', async () => { diff --git a/src/lib/bip39/index.ts b/src/lib/bip39/index.ts index 5e042224f..082a9c9fc 100644 --- a/src/lib/bip39/index.ts +++ b/src/lib/bip39/index.ts @@ -12,8 +12,8 @@ export const validateMnemonic = (importMnemonic: string): string | null => { const mnemonic = importMnemonic ? importMnemonic.split(' ') : [] if (!isWordlistValid(mnemonic)) { return 'worldlist is not valid' - } else if (mnemonic.length < 12) { - return 'you need to enter at least twelve words' + } else if (mnemonic.length < 24) { + return 'you need to enter at least 24 words' } else { return null } diff --git a/src/ux/createKeys/CreateKeysNavigation.tsx b/src/ux/createKeys/CreateKeysNavigation.tsx index 1493a36cb..73d799cb0 100644 --- a/src/ux/createKeys/CreateKeysNavigation.tsx +++ b/src/ux/createKeys/CreateKeysNavigation.tsx @@ -56,6 +56,7 @@ export const CreateKeysNavigation: React.FC = ({ )} diff --git a/src/ux/createKeys/import/ImportMasterKeyScreen.tsx b/src/ux/createKeys/import/ImportMasterKeyScreen.tsx index fdd096b90..dfa234239 100644 --- a/src/ux/createKeys/import/ImportMasterKeyScreen.tsx +++ b/src/ux/createKeys/import/ImportMasterKeyScreen.tsx @@ -1,30 +1,40 @@ import React, { useState } from 'react' -import { StyleSheet, View, ScrollView, Text } from 'react-native' +import { + StyleSheet, + View, + ScrollView, + Text, + TouchableOpacity, +} from 'react-native' +import Carousel from 'react-native-snap-carousel' import { CreateKeysProps, ScreenProps } from '../types' -import { grid } from '../../../styles/grid' -import { validateMnemonic } from '../../../lib/bip39' -import { SquareButton } from '../../../components/button/SquareButton' -import { Arrow } from '../../../components/icons' +import { Trans } from 'react-i18next' import { colors } from '../../../styles/colors' -import { WordInput } from './WordInput' -import { NavigationFooter } from '../../../components/button/NavigationFooter' -type ImportMasterKeyScreenProps = { + +import { Arrow } from '../../../components/icons' +import { SLIDER_WIDTH, WINDOW_WIDTH } from '../../slides/Dimensions' +import { PaginationNavigator } from '../../../components/button/PaginationNavigator' +import { WordSelector } from '../new/WordSelector' +import { sharedMnemonicStyles } from '../new/styles' +import { Paragraph } from '../../../components' +import { validateMnemonic } from '../../../lib/bip39' + +interface ImportMasterKeyScreenProps { + isKeyboardVisible: boolean createFirstWallet: CreateKeysProps['createFirstWallet'] } + export const ImportMasterKeyScreen: React.FC< ScreenProps<'ImportMasterKey'> & ImportMasterKeyScreenProps -> = ({ navigation, createFirstWallet }) => { - const [selectedWords, setSelectedWords] = useState([]) +> = ({ navigation, createFirstWallet, isKeyboardVisible }) => { + const slidesIndexes = [0, 1, 2, 3, 4, 5, 6, 7] - const rows = [1, 2, 3, 4, 5, 6, 7, 8] - const onWordSelected = (selectedWord: string, wordNumber: number) => { - const currentWordsSelections = selectedWords - currentWordsSelections.splice(wordNumber - 1, 0, selectedWord) - setSelectedWords(currentWordsSelections) - } + const [selectedSlide, setSelectedSlide] = useState(0) + const [selectedWords, setSelectedWords] = useState([]) + const [carousel, setCarousel] = useState() const [error, setError] = useState(null) - const [info] = useState(null) + const handleImportMnemonic = async () => { const mnemonicError = validateMnemonic(selectedWords.join(' ')) if (!mnemonicError) { @@ -39,58 +49,140 @@ export const ImportMasterKeyScreen: React.FC< } setError(mnemonicError) } + + const handleWordSelected = (wordSelected: string, index: number) => { + const newSelectedWords = [...selectedWords] + newSelectedWords[index] = wordSelected + setSelectedWords(newSelectedWords) + } + + const handleSlideChange = (index: number) => { + setSelectedSlide(index) + setError('') + } + + const renderItem: React.FC<{ item: number }> = ({ item }) => { + const groupIndex = 3 * item + return ( + + + + + + ) + } + return ( - <> - - Enter your master key - - {rows.map(row => ( - - - - + + + navigation.navigate('CreateKeys')} + style={styles.returnButton}> + + - ))} - {selectedWords.join(' ')} - - {info && {info}} - {error && {error}} - - } + + + Sign in with a Master Key + + + + Input the words you were given when you created your wallet in + correct order + + + + + + setCarousel(c)} + data={slidesIndexes} + renderItem={renderItem} + sliderWidth={WINDOW_WIDTH} + // sliderHeight={200} + itemWidth={SLIDER_WIDTH} + inactiveSlideShift={0} + onSnapToItem={handleSlideChange} + useScrollView={false} + keyboardShouldPersistTaps="always" + pagingEnabled={false} + /> + + + {!!error && ( + + {error} + + )} + + {!isKeyboardVisible && ( + + carousel.snapToPrev()} + onNext={() => carousel.snapToNext()} + onComplete={handleImportMnemonic} + title="confirm" + currentIndex={selectedSlide} + slidesAmount={slidesIndexes.length} + containerBackgroundColor={colors.darkBlue} /> - - navigation.navigate('CreateKeys')} - onPress={handleImportMnemonic} - title="confirm" - /> - + )} + ) } const styles = StyleSheet.create({ - defaultText: { - color: colors.white, + returnButton: { + zIndex: 1, }, - parent: { - backgroundColor: colors.darkBlue, + returnButtonView: { + width: 30, + height: 30, + borderRadius: 30, + margin: 15, + backgroundColor: colors.purple, }, header: { - color: '#ffffff', - fontSize: 22, - paddingVertical: 20, - textAlign: 'center', + color: colors.white, + fontSize: 20, + paddingVertical: 10, + marginBottom: 5, + marginLeft: 60, + textAlign: 'left', + fontWeight: 'bold', + }, + subHeader: { + color: colors.white, + fontSize: 16, + marginLeft: 60, + marginBottom: 40, + textAlign: 'left', + width: SLIDER_WIDTH, + }, + + errorContainer: { + padding: 20, + marginHorizontal: 60, + marginBottom: 10, + borderRadius: 20, + backgroundColor: colors.red, + }, + errorText: { + color: colors.white, }, }) diff --git a/src/ux/createKeys/import/WordInput.tsx b/src/ux/createKeys/import/WordInput.tsx deleted file mode 100644 index 540d6effe..000000000 --- a/src/ux/createKeys/import/WordInput.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useState } from 'react' - -import { grid } from '../../../styles/grid' -import { StyleSheet, View, Text, TextInput } from 'react-native' -import { colors } from '../../../styles/colors' - -export const WordInput: React.FC<{ - wordNumber: number - handleSelection: any -}> = ({ wordNumber, handleSelection }) => { - const [wordText, setWordText] = useState('') - const [wordLocked, setWordLocked] = useState(false) - - const handleKeyDown = () => { - if (wordText && wordText.trim() !== '') { - handleSelection(wordText, wordNumber) - setWordLocked(true) - } - } - - return ( - - {wordNumber}. - {wordLocked ? ( - - {wordText} - - ) : ( - - )} - - ) -} - -const styles = StyleSheet.create({ - wordContainer: { - alignItems: 'flex-start', - alignContent: 'flex-start', - flexDirection: 'row', - marginVertical: 4, - marginLeft: 4, - }, - wordContent: { - borderRadius: 20, - backgroundColor: colors.darkPurple2, - alignItems: 'flex-start', - justifyContent: 'flex-start', - paddingHorizontal: 10, - paddingVertical: 4, - marginLeft: 10, - }, - wordText: { - color: colors.white, - fontSize: 14, - }, - wordNumber: { - color: colors.white, - display: 'flex', - paddingVertical: 4, - }, - wordInput: { - borderColor: colors.white, - borderWidth: 1, - marginHorizontal: 10, - borderRadius: 10, - flex: 1, - textAlignVertical: 'top', - paddingTop: 0, - paddingBottom: 0, - paddingLeft: 5, - height: 25, - color: colors.white, - fontSize: 15, - }, -}) diff --git a/src/ux/createKeys/new/WordSelector.tsx b/src/ux/createKeys/new/WordSelector.tsx index b2013532c..d9301bb5e 100644 --- a/src/ux/createKeys/new/WordSelector.tsx +++ b/src/ux/createKeys/new/WordSelector.tsx @@ -16,7 +16,7 @@ import { wordlists } from 'bip39' type Props = { wordIndex: number - expectedWord: string + expectedWord?: string onWordSelected: any } export const WordSelector: React.FC = ({ @@ -51,6 +51,9 @@ export const WordSelector: React.FC = ({ // only trigger the parent if there is a match return onWordSelected(newText, wordIndex) } + if (!expectedWord) { + onWordSelected(newText, wordIndex) + } // user choose the top option but it is not correct if (newText === options[0]) { @@ -165,7 +168,7 @@ const styles = StyleSheet.create({ flex: 1, }, wordStatus: { - borderRadius: 30, + ...sharedMnemonicStyles.wordNumberBadge, backgroundColor: colors.purple, }, wordRowWithSuggestions: {