diff --git a/src/action/index-mobile.js b/src/action/index-mobile.js
index cfc5f1678..9b9cc03fe 100644
--- a/src/action/index-mobile.js
+++ b/src/action/index-mobile.js
@@ -116,7 +116,6 @@ observe(store, 'lndReady', () => {
// STUB DURING DEVELOPMENT
sinon.stub(wallet, 'update');
-sinon.stub(wallet, 'checkSeed');
sinon.stub(wallet, 'getExchangeRate');
sinon.stub(transaction, 'update');
sinon.stub(invoice, 'generateUri');
@@ -199,6 +198,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..151df4532 100644
--- a/src/action/wallet.js
+++ b/src/action/wallet.js
@@ -274,13 +274,42 @@ 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.
+ * 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/component/header.js b/src/component/header.js
index 9293cfe95..b2513aa76 100644
--- a/src/component/header.js
+++ b/src/component/header.js
@@ -76,6 +76,7 @@ const titleStyles = StyleSheet.create({
alignItems: 'center',
},
title: {
+ textAlign: 'center',
fontFamily: 'OpenSans Regular',
fontSize: 15,
letterSpacing: 2,
@@ -86,7 +87,7 @@ const titleStyles = StyleSheet.create({
},
});
-export const Title = ({ title = '', style, children }) => (
+export const Title = ({ title = '', style, keepCase, children }) => (
{children}
(
style,
]}
>
- {title.toUpperCase()}
+ {keepCase ? title : title.toUpperCase()}
);
@@ -104,6 +105,7 @@ export const Title = ({ title = '', style, children }) => (
Title.propTypes = {
title: PropTypes.string,
style: View.propTypes.style,
+ keepCase: PropTypes.bool,
children: PropTypes.node,
};
diff --git a/src/component/input.js b/src/component/input.js
index 8c336e2f9..dfeb26cf3 100644
--- a/src/component/input.js
+++ b/src/component/input.js
@@ -1,17 +1,14 @@
import React, { Component } from 'react';
-import {
- TextInput as RNTextInput,
- Text as RNText,
- StyleSheet,
-} from 'react-native';
+import { TextInput as RNTextInput, Text as RNText } from 'react-native';
import PropTypes from 'prop-types';
-import { color, font } from './style';
+import { createStyles, maxWidth } from '../component/media-query';
+import { color, font, breakWidth } from './style';
//
// Base Text Input
//
-const baseStyles = StyleSheet.create({
+const baseStyles = {
input: {
fontFamily: 'OpenSans Regular',
fontSize: font.sizeM,
@@ -19,21 +16,43 @@ const baseStyles = StyleSheet.create({
height: font.lineHeightM + 2 * 12,
color: color.blackText,
},
-});
+};
+
+const styles = createStyles(
+ baseStyles,
-export const TextInput = ({ style, ...props }) => (
-
+ maxWidth(breakWidth, {
+ input: {
+ lineHeight: undefined,
+ },
+ })
);
+export class TextInput extends Component {
+ componentDidUpdate(prevProps) {
+ const { autoFocus } = this.props;
+ prevProps.autoFocus === false && autoFocus && this._input.focus();
+ }
+
+ render() {
+ const { style, ...props } = this.props;
+ return (
+ (this._input = component)}
+ {...props}
+ />
+ );
+ }
+}
+
TextInput.propTypes = {
style: RNText.propTypes.style,
+ autoFocus: PropTypes.bool,
};
//
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 da8c213e3..63da0b3fc 100644
--- a/src/view/main-mobile.js
+++ b/src/view/main-mobile.js
@@ -10,6 +10,8 @@ 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 SeedVerifyView from './seed-verify-mobile';
import SetPinView from './set-pin-mobile';
import SetPinConfirmView from './set-pin-confirm-mobile';
import SeedSuccessView from './seed-success-mobile';
@@ -59,12 +61,16 @@ const Welcome = () => ;
const Loader = () => ;
-const SelectSeed = () => (
-
-);
+const SelectSeed = () => ;
const SeedIntro = () => ;
+const Seed = () => ;
+
+const SeedVerify = () => (
+
+);
+
const SetPassword = () => ;
const SetPasswordConfirm = () => (
@@ -167,6 +173,8 @@ const MainStack = createStackNavigator(
Loader,
SelectSeed,
SeedIntro,
+ Seed,
+ SeedVerify,
SetPassword,
SetPasswordConfirm,
SeedSuccess,
diff --git a/src/view/seed-mobile.js b/src/view/seed-mobile.js
new file mode 100644
index 000000000..170371835
--- /dev/null
+++ b/src/view/seed-mobile.js
@@ -0,0 +1,140 @@
+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 { Background } from '../component/background';
+import { Header, Title } from '../component/header';
+import { Button, BackButton, GlasButton } from '../component/button';
+import LightningBoltIcon from '../asset/icon/lightning-bolt';
+import { color, font } from '../component/style';
+
+//
+// Seed (Mobile) View
+//
+
+const styles = StyleSheet.create({
+ header: {
+ height: 150,
+ },
+ title: {
+ width: 200,
+ },
+ background: {
+ flex: 1,
+ justifyContent: 'space-between',
+ backgroundColor: color.blackDark,
+ },
+ seed: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ pagination: {
+ paddingBottom: 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',
+ margin: 20,
+ },
+ words: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'center',
+ maxWidth: 700,
+ },
+});
+
+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: 10,
+ 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/src/view/seed-verify-mobile.js b/src/view/seed-verify-mobile.js
new file mode 100644
index 000000000..9100ca523
--- /dev/null
+++ b/src/view/seed-verify-mobile.js
@@ -0,0 +1,81 @@
+import React, { Component } from 'react';
+import { View, StyleSheet } from 'react-native';
+import { observer } from 'mobx-react';
+import PropTypes from 'prop-types';
+import SeedEntry from '../component/seed-entry';
+import { Button, BackButton, GlasButton } from '../component/button';
+import { FormSubText } from '../component/form';
+import Background from '../component/background';
+import MainContent from '../component/main-content';
+import { CopyOnboardText } from '../component/text';
+import { Header } from '../component/header';
+import Card from '../component/card';
+
+//
+// Seed Verify View
+//
+
+const styles = StyleSheet.create({
+ content: {
+ justifyContent: 'flex-end',
+ },
+ title: {
+ textAlign: 'center',
+ marginBottom: 20,
+ },
+});
+
+class SeedVerifyView extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ focusedInput: 0,
+ };
+ }
+
+ render() {
+ const { store, nav, wallet } = this.props;
+ return (
+
+
+ nav.goSeed()} />
+
+
+
+
+ {"Let's double check"}
+
+
+
+ {store.seedVerifyCopy}
+ {store.seedVerifyIndexes.map((seedIndex, i) => (
+ wallet.setSeedVerify({ word, index: i })}
+ key={i}
+ autoFocus={i === this.state.focusedInput}
+ onSubmitEditing={() =>
+ i === 2
+ ? wallet.checkSeed()
+ : this.setState({ focusedInput: i + 1 })
+ }
+ onClick={() => this.setState({ focusedInput: i })}
+ />
+ ))}
+
+ wallet.checkSeed()}>Next
+
+
+ );
+ }
+}
+
+SeedVerifyView.propTypes = {
+ store: PropTypes.object.isRequired,
+ nav: PropTypes.object.isRequired,
+ wallet: PropTypes.object.isRequired,
+};
+
+export default observer(SeedVerifyView);
diff --git a/stories/screen-story.js b/stories/screen-story.js
index 823da873a..348f060d2 100644
--- a/stories/screen-story.js
+++ b/stories/screen-story.js
@@ -61,7 +61,9 @@ 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 SeedVerifyMobile from '../src/view/seed-verify-mobile';
import SetPassword from '../src/view/set-password';
import SetPinMobile from '../src/view/set-pin-mobile';
import SetPasswordConfirm from '../src/view/set-password-confirm';
@@ -116,14 +118,16 @@ 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', () => (
))
+ .add('Seed Verify (Mobile)', () => (
+
+ ))
.add('Restore Wallet: Seed', () => (
))
diff --git a/test/unit/action/wallet.spec.js b/test/unit/action/wallet.spec.js
index 2cda55e0b..44113abd5 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', async () => {
+ 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', async () => {
+ 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;