diff --git a/src/action/index-mobile.js b/src/action/index-mobile.js index 64302027f..8e277d656 100644 --- a/src/action/index-mobile.js +++ b/src/action/index-mobile.js @@ -208,6 +208,32 @@ store.selectedTransaction = (store.computedTransactions || []).find( tx => tx.type === 'bitcoin' ); store.selectedChannel = store.computedChannels && store.computedChannels[0]; +store.seedMnemonic = [ + 'empower', + 'neglect', + 'experience', + 'elevator', + 'entropy', + 'future', + 'trust', + 'swift', + 'pluck', + 'easy', + 'kite', + 'measure', + 'engage', + 'settle', + 'dog', + 'manager', + 'tool', + 'fan', + 'neglect', + 'conduct', + 'blouse', + 'stone', + 'quit', + 'cashew', +]; store.logs = [ '[14:00:24.995] [info] Using lnd in path lnd', 'Checking for update', diff --git a/src/action/wallet.js b/src/action/wallet.js index af2617957..44ae414cf 100644 --- a/src/action/wallet.js +++ b/src/action/wallet.js @@ -274,13 +274,41 @@ class WalletAction { } /** - * Initialize the seed flow by navigating to the proper next view. + * Initialize the seed flow by navigating to the proper next view and + * resetting the current seed index. * @return {undefined} */ initSeed() { + this._store.wallet.seedIndex = 0; this._nav.goSeedIntro ? this._nav.goSeedIntro() : this._nav.goSeed(); } + /** + * Initialize the next seed view by setting a new seedIndex or, if all seed + * words have been displayed, navigating to the mobile seed verify view. + * @return {undefined} + */ + initNextSeedPage() { + if (this._store.wallet.seedIndex < 16) { + this._store.wallet.seedIndex += 8; + } else { + this.initSeedVerify(); + } + } + + /** + * Initialize the previous seed view by setting a new seedIndex or, if on the + * first seed page, navigating to the select seed view. + * @return {undefined} + */ + initPrevSeedPage() { + if (this._store.wallet.seedIndex >= 8) { + this._store.wallet.seedIndex -= 8; + } else { + this._nav.goSelectSeed(); + } + } + /** * Initialize the restore wallet view by resetting input values and then * navigating to the view. diff --git a/src/asset/icon/seed.js b/src/asset/icon/seed.js new file mode 100644 index 000000000..252f3d62b --- /dev/null +++ b/src/asset/icon/seed.js @@ -0,0 +1,34 @@ +import React from 'react'; +import Svg, { Defs, Path, G, Use } from '../../component/svg'; + +// SVGR has dropped some elements not supported by react-native-svg: filter, feOffset, feGaussianBlur, feColorMatrix +const SvgSeed = props => ( + + + + + + + + + + + + +); + +export default SvgSeed; diff --git a/src/asset/icon/seed.svg b/src/asset/icon/seed.svg new file mode 100644 index 000000000..b67503947 --- /dev/null +++ b/src/asset/icon/seed.svg @@ -0,0 +1,31 @@ + + + + Group + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/component/header.js b/src/component/header.js index 864affbda..71684a4de 100644 --- a/src/component/header.js +++ b/src/component/header.js @@ -3,7 +3,7 @@ import { View, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import { createStyles, maxWidth } from './media-query'; import Text from './text'; -import { color, breakWidth } from './style'; +import { color, font, breakWidth } from './style'; // // Header @@ -11,7 +11,7 @@ import { color, breakWidth } from './style'; const baseStyles = { header: { - height: 75, + minHeight: 75, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', @@ -107,4 +107,47 @@ Title.propTypes = { children: PropTypes.node, }; +// +// Large Title +// + +const largeTitleStyles = StyleSheet.create({ + titleWrapper: { + alignItems: 'center', + paddingTop: 15, + paddingBottom: 15, + }, + title: { + maxWidth: 200, + textAlign: 'center', + fontSize: font.sizeSub, + lineHeight: font.lineHeightSub, + letterSpacing: 1, + }, + titleIcon: { + marginTop: 10, + }, +}); + +export const LargeTitle = ({ title = '', style, children }) => ( + + {children} + + {title} + + +); + +LargeTitle.propTypes = { + title: PropTypes.string, + style: Text.propTypes.style, + children: PropTypes.node, +}; + export default Header; diff --git a/src/store.js b/src/store.js index 4a838840f..498beebbd 100644 --- a/src/store.js +++ b/src/store.js @@ -47,6 +47,7 @@ export class Store { newPassword: '', passwordVerify: '', seedVerify: ['', '', ''], + seedIndex: 0, restoring: false, restoreIndex: 0, restoreSeed: Array(24).fill(''), diff --git a/src/view/main-mobile.js b/src/view/main-mobile.js index e2a675945..1b78f355a 100644 --- a/src/view/main-mobile.js +++ b/src/view/main-mobile.js @@ -10,6 +10,7 @@ import WelcomeView from './welcome'; import LoaderView from './loader'; import SelectSeedView from './select-seed'; import SeedIntroView from './seed-intro-mobile'; +import SeedView from './seed-mobile'; import SetPinView from './set-pin-mobile'; import SetPinConfirmView from './set-pin-confirm-mobile'; import SeedSuccessView from './seed-success-mobile'; @@ -59,9 +60,9 @@ const Welcome = () => ; const Loader = () => ; -const SelectSeed = () => ( - -); +const SelectSeed = () => ; + +const Seed = () => ; const SeedIntro = () => ; @@ -167,6 +168,7 @@ const MainStack = createStackNavigator( Loader, SelectSeed, SeedIntro, + Seed, SetPassword, SetPasswordConfirm, SeedSuccess, diff --git a/src/view/seed-mobile.js b/src/view/seed-mobile.js new file mode 100644 index 000000000..856db739b --- /dev/null +++ b/src/view/seed-mobile.js @@ -0,0 +1,131 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { observer } from 'mobx-react'; +import PropTypes from 'prop-types'; +import MainContent from '../component/main-content'; +import { Text } from '../component/text'; +import { SplitBackground } from '../component/background'; +import { Header, LargeTitle } from '../component/header'; +import { Button, BackButton, SmallGlasButton } from '../component/button'; +import SeedIcon from '../asset/icon/seed'; +import { color, font } from '../component/style'; + +// +// Seed (Mobile) View +// + +const styles = StyleSheet.create({ + backBtn: { + alignSelf: 'flex-start', + marginTop: 5, + }, + content: { + justifyContent: 'center', + backgroundColor: color.blackDark, + }, + pagination: { + marginBottom: 10, + }, +}); + +const SeedView = ({ store, wallet }) => ( + +
+ wallet.initPrevSeedPage()} + /> + + + +
+ + + + {`${store.wallet.seedIndex + 8} of 24`} + + + wallet.initNextSeedPage()}> + Next + +
+); + +SeedView.propTypes = { + store: PropTypes.object.isRequired, + wallet: PropTypes.object.isRequired, +}; + +// +// Word List +// + +const listStyles = StyleSheet.create({ + wrapper: { + justifyContent: 'center', + maxWidth: 350, + margin: 20, + }, + words: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + }, +}); + +const WordList = ({ seedMnemonic, startingIndex }) => ( + + + {seedMnemonic.map((word, i) => ( + + ))} + + +); + +WordList.propTypes = { + seedMnemonic: PropTypes.array.isRequired, + startingIndex: PropTypes.number, +}; + +// +// Word +// + +const wordStyles = StyleSheet.create({ + wrapper: { + justifyContent: 'center', + height: 45, + width: 115, + margin: 8, + borderWidth: 1, + borderColor: color.seedBorder, + backgroundColor: color.seedBackground, + }, + word: { + fontSize: font.sizeS * 1.2, + paddingLeft: 10, + }, +}); + +const Word = ({ word, index }) => ( + + + {index}. {word} + + +); + +Word.propTypes = { + word: PropTypes.string.isRequired, + index: PropTypes.number.isRequired, +}; + +export default observer(SeedView); diff --git a/stories/screen-story.js b/stories/screen-story.js index 225ba3cfb..93a0f3109 100644 --- a/stories/screen-story.js +++ b/stories/screen-story.js @@ -63,6 +63,7 @@ import SeedIntro from '../src/view/seed-intro-mobile'; import SeedSuccess from '../src/view/seed-success'; import SeedSuccessMobile from '../src/view/seed-success-mobile'; import Seed from '../src/view/seed'; +import SeedMobile from '../src/view/seed-mobile'; import SeedVerify from '../src/view/seed-verify'; import SetPassword from '../src/view/set-password'; import SetPinMobile from '../src/view/set-pin-mobile'; @@ -118,11 +119,10 @@ sinon.stub(auth, '_generateWalletPassword'); storiesOf('Screens', module) .add('Welcome', () => ) .add('Loader - First Time', () => ) - .add('Select Seed', () => ( - - )) + .add('Select Seed', () => ) .add('Seed Intro (Mobile)', () => ) .add('Seed', () => ) + .add('Seed (Mobile)', () => ) .add('Seed Verify', () => ( )) diff --git a/test/unit/action/wallet.spec.js b/test/unit/action/wallet.spec.js index 2cda55e0b..a9079b836 100644 --- a/test/unit/action/wallet.spec.js +++ b/test/unit/action/wallet.spec.js @@ -310,8 +310,10 @@ describe('Action Wallet Unit Tests', () => { }); describe('initSeed()', () => { - it('should navigate to seed view', () => { + it('should clear attributes and navigate to view', () => { + store.wallet.seedIndex = 42; wallet.initSeed(); + expect(store.wallet.seedIndex, 'to equal', 0); expect(nav.goSeed, 'was called once'); }); @@ -323,6 +325,38 @@ describe('Action Wallet Unit Tests', () => { }); }); + describe('initPrevSeedPage()', () => { + it('should navigate to select seed if seedIndex < 8', () => { + store.wallet.seedIndex = 7; + wallet.initPrevSeedPage(); + expect(nav.goSelectSeed, 'was called once'); + expect(store.wallet.seedIndex, 'to equal', 7); + }); + + it('should decrement seedIndex if greater than 7', () => { + store.wallet.seedIndex = 8; + wallet.initPrevSeedPage(); + expect(nav.goSelectSeed, 'was not called'); + expect(store.wallet.seedIndex, 'to equal', 0); + }); + }); + + describe('initNextSeedPage()', () => { + it('should init seed verify if seedIndex > 16', () => { + store.wallet.seedIndex = 16; + wallet.initNextSeedPage(); + expect(nav.goSeedVerify, 'was called'); + expect(store.wallet.seedIndex, 'to equal', 16); + }); + + it('should increment seedIndex if less than 16', () => { + store.wallet.seedIndex = 8; + wallet.initNextSeedPage(); + expect(nav.goSeedVerify, 'was not called'); + expect(store.wallet.seedIndex, 'to equal', 16); + }); + }); + describe('initRestoreWallet()', () => { it('should clear attributes and navigate to view', () => { store.wallet.restoreIndex = 42;