Newer
Older
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: BSD-3-Clause
import { addressShort, Card, Form as FetherForm } from 'fether-ui';
Luke Schoen
committed
import RequireHealthOverlay from '../../../RequireHealthOverlay';
import Scanner from '../../../Scanner';
import withAccountsInfo from '../../../utils/withAccountsInfo';
import getBip39Wordlist from '../../../stores/utils/getBip39Wordlist';
import getParityWordlist from '../../../stores/utils/getParityWordlist';
const BIP39_WORDLIST = getBip39Wordlist();
const PARITY_WORDLIST = getParityWordlist();
@inject('createAccountStore')
@observer
class AccountImportOptions extends Component {
state = {
phrase: '',
importingFromSigner: false
};
handleNextStep = async () => {
const {
history,
location: { pathname }
handlePhraseChange = ({ target: { value: phrase } }) => {
const { invalidWords } = this.state;
const words = phrase.split(' ');
const lastVal = words.slice(-1);
const isWordEnded = lastVal.join() === '';
if (isWordEnded) {
for (let i = 0; i < words.length; i++) {
let word = words[i];
if (
word &&
!BIP39_WORDLIST.has(word.toLowerCase()) &&
!PARITY_WORDLIST.has(word.toLowerCase())
) {
invalidWords.add(word);
this.setState({
invalidWords
});
}
}
invalidWords.forEach(invalidWord => {
if (!words.includes(invalidWord)) {
invalidWords.delete(invalidWord);
}
});
if (invalidWords && invalidWords.size) {
// Guide them to contact Fether Riot
<React.Fragment>
{`${Array.from(invalidWords).join(
', '
)} is not a valid BIP39 or Parity word. If you wish to recover your account with a self-generated phrase, please`}
<button className='button -utility'>
<a
className='contact'
href='https://riot.im/app/#/room/#fether:matrix.parity.io'
rel='noopener noreferrer'
target='_blank'
>
Contact Us
</a>
</button>
});
} else {
this.setState({
error: null
});
}
this.setState({ phrase: phrase.toLowerCase() });
handleSubmitPhrase = async () => {
const phrase = this.state.phrase.trim();
createAccountStore,
createAccountStore: { setPhrase }
this.setState({ isLoading: true, phrase });
Luke Schoen
committed
await setPhrase(phrase);
Luke Schoen
committed
if (this.hasExistingAddressForImport(createAccountStore.address)) {
Luke Schoen
committed
return;
}
this.handleNextStep();
Luke Schoen
committed
} catch (error) {
this.setState({
isLoading: false,
error:
'The passphrase was not recognized. Please verify that you entered your passphrase correctly.'
});
Luke Schoen
committed
console.error(error);
}
};
handleChangeFile = async jsonString => {
const {
createAccountStore,
createAccountStore: { setJsonString }
} = this.props;
this.setState({ isLoading: true });
Luke Schoen
committed
try {
await setJsonString(jsonString);
Luke Schoen
committed
if (this.hasExistingAddressForImport(createAccountStore.address)) {
Luke Schoen
committed
return;
}
this.handleNextStep();
isLoading: false,
'Invalid file. Please check this is your actual Parity backup JSON keyfile and try again.'
console.error(error);
handleSignerImported = async ({ address, chainId: chainIdString }) => {
createAccountStore: { importFromSigner }
if (!address || !chainIdString) {
this.setState({ error: 'Invalid QR code.' });
return;
}
const chainId = parseInt(chainIdString);
if (this.hasExistingAddressForImport(address, chainId)) {
this.handleNextStep();
};
handleSignerImport = () => {
this.setState({
importingFromSigner: true
});
hasExistingAddressForImport = (addressForImport, chainId) => {
const isExistingAddress = Object.keys(accountsInfo).some(
key =>
key.toLowerCase() === addressForImport.toLowerCase() &&
(!accountsInfo[key].chainId ||
!chainId ||
accountsInfo[key].chainId === chainId)
Luke Schoen
committed
if (isExistingAddress) {
this.setState({
isLoading: false,
error: `Account ${addressShort(addressForImport)} already listed`
Luke Schoen
committed
});
}
return isExistingAddress;
render () {
const {
history,
location: { pathname }
} = this.props;
const { error, importingFromSigner, phrase } = this.state;
<Card>
<div key='createAccount'>
<div className='text -centered'>
<p>Recover from JSON Keyfile</p>
<FetherForm.InputFile
label='JSON Backup Keyfile'
onChangeFile={this.handleChangeFile}
required
/>
</div>
</Card>
);
const signerCard = (
<Card>
<div key='createAccount'>
<div className='text -centered'>
<p>Recover from Parity Signer</p>
{importingFromSigner ? (
<Scanner
onScan={this.handleSignerImported}
/>
) : (
<button
className='button -footer'
onClick={this.handleSignerImport}
>
Scan QR code
</button>
)}
</div>
</div>
</Card>
<Card>
<div key='importBackup'>
<div className='text -centered'>
<p>Recover from Seed Phrase</p>
<FetherForm.Field
as='textarea'
label='Recovery phrase'
onChange={this.handlePhraseChange}
required
phrase={phrase}
/>
{this.renderButton()}
</div>
const spacer = <div style={{ height: '0.5rem' }} />;
Luke Schoen
committed
<RequireHealthOverlay require='node'>
<div className='center-md'>
{!importingFromSigner && jsonCard}
Luke Schoen
committed
{signerCard}
Luke Schoen
committed
{!importingFromSigner && phraseCard}
<p>{error}</p>
<nav className='form-nav -space-around'>
{currentStep > 1 && (
<button className='button -back' onClick={history.goBack}>
Back
</button>
)}
</nav>
</div>
</RequireHealthOverlay>
const { isLoading, json, phrase } = this.state;
// If we are importing an existing account, the button goes to the next step
return (
<button
className='button'
disabled={(!json && !phrase) || isLoading}
onClick={this.handleSubmitPhrase}