Unverified Commit 89b74488 authored by valentinewallace's avatar valentinewallace Committed by GitHub

Merge pull request #1106 from lightninglabs/dev/seed-recovery-mobile

Dev/seed recovery mobile
parents e110f571 87d1b607
......@@ -55,10 +55,6 @@ class NavAction {
this._navigate('RestoreSeed');
}
goRestorePin() {
this._navigate('RestorePin');
}
goSeedSuccess() {
this._navigate('SeedSuccess');
}
......
......@@ -29,10 +29,6 @@ class NavAction {
this._store.route = 'RestoreSeed';
}
goRestorePassword() {
this._store.route = 'RestorePassword';
}
goSeedSuccess() {
this._store.route = 'SeedSuccess';
}
......
......@@ -53,7 +53,15 @@ class WalletAction {
* @param {number} options.index The seed index
*/
setRestoreSeed({ word, index }) {
this._store.wallet.restoreSeed[index] = word;
this._store.seedMnemonic[index] = word;
}
/**
* Set which seed restore input is in focus.
* @param {number} options.index The index of the input.
*/
setFocusedRestoreInd({ index }) {
this._store.wallet.focusedRestoreInd = index;
}
//
......@@ -212,6 +220,7 @@ class WalletAction {
}
await this.initWallet({
walletPassword: newPassword,
recoveryWindow: this._store.settings.restoring ? RECOVERY_WINDOW : 0,
seedMnemonic: this._store.seedMnemonic.toJSON(),
});
}
......@@ -311,6 +320,7 @@ class WalletAction {
* @return {undefined}
*/
initRestoreWallet() {
this._store.seedMnemonic = Array(24).fill('');
this._store.wallet.restoreIndex = 0;
this._nav.goRestoreSeed();
}
......@@ -324,8 +334,9 @@ class WalletAction {
initNextRestorePage() {
if (this._store.wallet.restoreIndex < 21) {
this._store.wallet.restoreIndex += 3;
this._store.wallet.focusedRestoreInd = this._store.wallet.restoreIndex;
} else {
this._nav.goRestorePassword();
this.initSetPassword();
}
}
......@@ -337,6 +348,7 @@ class WalletAction {
initPrevRestorePage() {
if (this._store.wallet.restoreIndex >= 3) {
this._store.wallet.restoreIndex -= 3;
this._store.wallet.focusedRestoreInd = this._store.wallet.restoreIndex;
} else {
this._nav.goSelectSeed();
}
......@@ -375,20 +387,6 @@ class WalletAction {
await this.unlockWallet({ walletPassword: password });
}
/**
* Initialize the wallet with the password input the seed that was already
* inputted, and the default recovery window.
* @return {Promise<undefined>}
*/
async restoreWallet() {
const { password, restoreSeed } = this._store.wallet;
await this.initWallet({
walletPassword: password,
seedMnemonic: restoreSeed.toJSON(),
recoveryWindow: RECOVERY_WINDOW,
});
}
/**
* Unlock the wallet by calling the grpc api with the user chosen password.
* @param {string} options.walletPassword The password used to encrypt the wallet
......
......@@ -51,7 +51,7 @@ export class Store {
seedVerify: ['', '', ''],
seedIndex: 0,
restoreIndex: 0,
restoreSeed: Array(24).fill(''),
focusedRestoreInd: 0,
},
transactions: [],
selectedTransaction: null,
......
......@@ -16,6 +16,7 @@ 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';
import RestoreSeedView from './restore-seed-mobile';
import NewAddressView from './new-address-mobile';
import PinView from './pin-mobile';
......@@ -84,6 +85,8 @@ const SetPasswordConfirm = () => (
const SeedSuccess = () => <SeedSuccessView wallet={wallet} />;
const RestoreSeed = () => <RestoreSeedView store={store} wallet={wallet} />;
const NewAddress = () => (
<NewAddressView store={store} invoice={invoice} info={info} />
);
......@@ -190,6 +193,7 @@ const MainStack = createStackNavigator(
SetPassword,
SetPasswordConfirm,
SeedSuccess,
RestoreSeed,
NewAddress,
Password,
LoaderSyncing,
......
......@@ -11,7 +11,6 @@ import SeedSuccess from './seed-success';
import SetPassword from './set-password';
import SetPasswordConfirm from './set-password-confirm';
import RestoreSeed from './restore-seed';
import RestorePassword from './restore-password';
import Password from './password';
import ResetPasswordCurrent from './reset-password-current';
import ResetPasswordNew from './reset-password-new';
......@@ -83,9 +82,6 @@ class MainView extends Component {
{route === 'RestoreSeed' && (
<RestoreSeed store={store} wallet={wallet} />
)}
{route === 'RestorePassword' && (
<RestorePassword store={store} wallet={wallet} nav={nav} />
)}
{route === 'Password' && <Password store={store} wallet={wallet} />}
{route === 'ResetPasswordCurrent' && (
<ResetPasswordCurrent store={store} nav={nav} wallet={wallet} />
......
import React from 'react';
import { StyleSheet } from 'react-native';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';
import { SplitBackground } from '../component/background';
import MainContent from '../component/main-content';
import { H1Text } from '../component/text';
import { Header } from '../component/header';
import { Button, BackButton, GlasButton } from '../component/button';
import { PasswordCard } from '../component/password-entry';
import { color } from '../component/style';
//
// Restore Wallet Password View
//
const styles = StyleSheet.create({
content: {
justifyContent: 'flex-end',
},
title: {
textAlign: 'center',
marginBottom: 20,
},
});
const RestorePasswordView = ({ store, wallet, nav }) => (
<SplitBackground image="purple-gradient-bg" bottom={color.blackDark}>
<Header>
<BackButton onPress={() => nav.goSelectSeed()} />
<Button disabled onPress={() => {}} />
</Header>
<MainContent style={styles.content}>
<H1Text style={styles.title}>Restore wallet</H1Text>
<PasswordCard
copy="Please enter your password."
placeholder="Password"
password={store.wallet.password}
onChangeText={password => wallet.setPassword({ password })}
onSubmitEditing={() => wallet.restoreWallet()}
/>
<GlasButton onPress={() => wallet.restoreWallet()}>Restore</GlasButton>
</MainContent>
</SplitBackground>
);
RestorePasswordView.propTypes = {
store: PropTypes.object.isRequired,
wallet: PropTypes.object.isRequired,
nav: PropTypes.object.isRequired,
};
export default observer(RestorePasswordView);
import React from 'react';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';
import SeedEntry from '../component/seed-entry';
import { Button, BackButton, SmallGlasButton } 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';
import { createStyles, maxWidth } from '../component/media-query';
import { smallBreakWidth } from '../component/style';
//
// Restore Seed View (Mobile)
//
const baseStyles = {
content: {
justifyContent: 'flex-end',
},
title: {
textAlign: 'center',
marginBottom: 20,
},
subText: {
maxWidth: 235,
},
};
const styles = createStyles(
baseStyles,
maxWidth(smallBreakWidth, {
title: {
fontSize: 30,
lineHeight: 40,
},
})
);
const RestoreSeedView = ({ store, wallet }) => (
<Background image="purple-gradient-bg">
<Header>
<BackButton onPress={() => wallet.initPrevRestorePage()} />
<Button disabled onPress={() => {}} />
</Header>
<MainContent style={styles.content}>
<CopyOnboardText style={styles.title}>
Restore your wallet
</CopyOnboardText>
<Card>
<FormSubText style={styles.subText}>
{store.restoreVerifyCopy}
</FormSubText>
{store.restoreVerifyIndexes.map((seedIndex, i) => (
<SeedEntry
seedIndex={seedIndex}
value={store.seedMnemonic[seedIndex - 1]}
onChangeText={word =>
wallet.setRestoreSeed({ word, index: seedIndex - 1 })
}
key={i}
autoFocus={seedIndex - 1 === store.wallet.focusedRestoreInd}
onSubmitEditing={() =>
i === 2
? wallet.initNextRestorePage()
: wallet.setFocusedRestoreInd({ index: seedIndex })
}
onClick={() =>
wallet.setFocusedRestoreInd({ index: seedIndex - 1 })
}
/>
))}
</Card>
<SmallGlasButton onPress={() => wallet.initNextRestorePage()}>
Next
</SmallGlasButton>
</MainContent>
</Background>
);
RestoreSeedView.propTypes = {
store: PropTypes.object.isRequired,
wallet: PropTypes.object.isRequired,
};
export default observer(RestoreSeedView);
......@@ -47,13 +47,20 @@ const RestoreSeedView = ({ store, wallet }) => (
{store.restoreVerifyIndexes.map((seedIndex, i) => (
<SeedEntry
seedIndex={seedIndex}
value={store.wallet.restoreSeed[seedIndex - 1]}
value={store.seedMnemonic[seedIndex - 1]}
onChangeText={word =>
wallet.setRestoreSeed({ word, index: seedIndex - 1 })
}
key={i}
autoFocus={i === 0}
onSubmitEditing={() => wallet.initNextRestorePage()}
autoFocus={seedIndex - 1 === store.wallet.focusedRestoreInd}
onSubmitEditing={() =>
i === 2
? wallet.initNextRestorePage()
: wallet.setFocusedRestoreInd({ index: seedIndex })
}
onClick={() =>
wallet.setFocusedRestoreInd({ index: seedIndex - 1 })
}
/>
))}
</Card>
......
......@@ -73,7 +73,6 @@ import SetPasswordConfirm from '../src/view/set-password-confirm';
import SetPinConfirmMobile from '../src/view/set-pin-confirm-mobile';
import Password from '../src/view/password';
import PinMobile from '../src/view/pin-mobile';
import RestorePassword from '../src/view/restore-password';
import ResetPasswordCurrent from '../src/view/reset-password-current';
import ResetPasswordNew from '../src/view/reset-password-new';
import ResetPasswordConfirm from '../src/view/reset-password-confirm';
......@@ -83,6 +82,7 @@ import NewAddressMobile from '../src/view/new-address-mobile';
import Wait from '../src/view/wait';
import WaitMobile from '../src/view/wait-mobile';
import RestoreSeed from '../src/view/restore-seed';
import RestoreSeedMobile from '../src/view/restore-seed-mobile';
const store = new Store();
store.init();
......@@ -139,6 +139,9 @@ storiesOf('Screens', module)
.add('Restore Wallet: Seed', () => (
<RestoreSeed store={store} wallet={wallet} />
))
.add('Restore Wallet: Seed (Mobile)', () => (
<RestoreSeedMobile store={store} wallet={wallet} />
))
.add('Seed Success', () => <SeedSuccess wallet={wallet} />)
.add('Seed Success (Mobile)', () => <SeedSuccessMobile wallet={wallet} />)
.add('Set Password', () => (
......@@ -155,9 +158,6 @@ storiesOf('Screens', module)
))
.add('Password', () => <Password store={store} wallet={wallet} />)
.add('PIN (Mobile)', () => <PinMobile store={store} auth={auth} />)
.add('Restore Wallet: Password', () => (
<RestorePassword store={store} wallet={wallet} nav={nav} />
))
.add('Reset Password - Current', () => (
<ResetPasswordCurrent store={store} wallet={wallet} nav={nav} />
))
......
......@@ -81,13 +81,6 @@ describe('Action Nav Unit Tests', () => {
});
});
describe('goRestorePassword()', () => {
it('should set correct route', () => {
nav.goRestorePassword();
expect(store.route, 'to equal', 'RestorePassword');
});
});
describe('goNewAddress()', () => {
it('should set correct route', () => {
nav.goNewAddress();
......
......@@ -214,6 +214,7 @@ describe('Action Wallet Unit Tests', () => {
expect(wallet.initWallet, 'was called with', {
walletPassword: 'secret123',
seedMnemonic: ['foo', 'bar', 'baz'],
recoveryWindow: 0,
});
});
......@@ -234,6 +235,18 @@ describe('Action Wallet Unit Tests', () => {
expect(wallet.initSetPassword, 'was called once');
expect(notification.display, 'was called once');
});
it('init wallet correctly during restore', async () => {
store.settings.restoring = true;
wallet.setNewPassword({ password: 'secret123' });
wallet.setPasswordVerify({ password: 'secret123' });
await wallet.checkNewPassword();
expect(wallet.initWallet, 'was called with', {
walletPassword: 'secret123',
seedMnemonic: ['foo', 'bar', 'baz'],
recoveryWindow: RECOVERY_WINDOW,
});
});
});
describe('checkResetPassword()', () => {
......@@ -354,16 +367,27 @@ describe('Action Wallet Unit Tests', () => {
it('should clear attributes and navigate to view', () => {
store.wallet.restoreIndex = 42;
wallet.initRestoreWallet();
expect(store.wallet.restoreSeed.length, 'to equal', 24);
expect(store.seedMnemonic.length, 'to equal', 24);
expect(store.wallet.restoreIndex, 'to equal', 0);
expect(nav.goRestoreSeed, 'was called once');
});
});
describe('setRestoreSeed()', () => {
beforeEach(() => {
store.seedMnemonic = Array(24).fill('');
});
it('should clear attributes', () => {
wallet.setRestoreSeed({ word: 'foo', index: 1 });
expect(store.wallet.restoreSeed[1], 'to equal', 'foo');
expect(store.seedMnemonic[1], 'to equal', 'foo');
});
});
describe('setFocusedRestoreInd()', () => {
it('should set the currently focused restore seed index', () => {
wallet.setFocusedRestoreInd({ index: 5 });
expect(store.wallet.focusedRestoreInd, 'to equal', 5);
});
});
......@@ -377,9 +401,11 @@ describe('Action Wallet Unit Tests', () => {
it('should decrement restoreIndex if greater than 2', async () => {
store.wallet.restoreIndex = 3;
store.wallet.focusedRestoreInd = 5;
wallet.initPrevRestorePage();
expect(nav.goSelectSeed, 'was not called');
expect(store.wallet.restoreIndex, 'to equal', 0);
expect(store.wallet.focusedRestoreInd, 'to equal', 0);
});
});
......@@ -387,15 +413,17 @@ describe('Action Wallet Unit Tests', () => {
it('should navigate to password screen if restoreIndex > 20', () => {
store.wallet.restoreIndex = 21;
wallet.initNextRestorePage();
expect(nav.goRestorePassword, 'was called once');
expect(nav.goSetPassword, 'was called once');
expect(store.wallet.restoreIndex, 'to equal', 21);
});
it('should increment restoreIndex if less than 21', async () => {
store.wallet.restoreIndex = 18;
store.wallet.focusedRestoreInd = 19;
wallet.initNextRestorePage();
expect(nav.goRestorePassword, 'was not called');
expect(nav.goSetPassword, 'was not called');
expect(store.wallet.restoreIndex, 'to equal', 21);
expect(store.wallet.focusedRestoreInd, 'to equal', 21);
});
});
......@@ -469,24 +497,6 @@ describe('Action Wallet Unit Tests', () => {
});
});
describe('restoreWallet()', () => {
beforeEach(() => {
sandbox.stub(wallet, 'initWallet');
});
it('calls initWallet with password and restoreSeed', async () => {
wallet.setPassword({ password: 'secret123' });
const seed = Array(24).fill('foo');
store.wallet.restoreSeed = seed;
await wallet.restoreWallet();
expect(wallet.initWallet, 'was called with', {
walletPassword: 'secret123',
seedMnemonic: seed,
recoveryWindow: RECOVERY_WINDOW,
});
});
});
describe('unlockWallet()', () => {
it('should unlock wallet', async () => {
grpc.sendUnlockerCommand.withArgs('UnlockWallet').resolves();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment