import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' import shuffle from 'lodash.shuffle' import Button from '../../../../components/ui/button' import { INITIALIZE_END_OF_FLOW_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE, DEFAULT_ROUTE, } from '../../../../helpers/constants/routes' import { exportAsFile } from '../../../../helpers/utils/util' import DraggableSeed from './draggable-seed.component' const EMPTY_SEEDS = Array(12).fill(null) export default class ConfirmSeedPhrase extends PureComponent { static contextTypes = { metricsEvent: PropTypes.func, t: PropTypes.func, } static defaultProps = { seedPhrase: '', } static propTypes = { hideSeedPhraseBackupAfterOnboarding: PropTypes.func, history: PropTypes.object, onSubmit: PropTypes.func, seedPhrase: PropTypes.string, selectedAddress: PropTypes.string, initializeThreeBox: PropTypes.func, setSeedPhraseBackedUp: PropTypes.func, showingSeedPhraseBackupAfterOnboarding: PropTypes.bool, } state = { selectedSeedIndices: [], shuffledSeedWords: [], pendingSeedIndices: [], draggingSeedIndex: -1, hoveringIndex: -1, isDragging: false, } shouldComponentUpdate (nextProps, nextState) { const { seedPhrase } = this.props const { selectedSeedIndices, shuffledSeedWords, pendingSeedIndices, draggingSeedIndex, hoveringIndex, isDragging, } = this.state return seedPhrase !== nextProps.seedPhrase || draggingSeedIndex !== nextState.draggingSeedIndex || isDragging !== nextState.isDragging || hoveringIndex !== nextState.hoveringIndex || selectedSeedIndices.join(' ') !== nextState.selectedSeedIndices.join(' ') || shuffledSeedWords.join(' ') !== nextState.shuffledSeedWords.join(' ') || pendingSeedIndices.join(' ') !== nextState.pendingSeedIndices.join(' ') } componentDidMount () { const { seedPhrase = '' } = this.props const shuffledSeedWords = shuffle(seedPhrase.split(' ')) || [] this.setState({ shuffledSeedWords }) } setDraggingSeedIndex = draggingSeedIndex => this.setState({ draggingSeedIndex }) setHoveringIndex = hoveringIndex => this.setState({ hoveringIndex }) onDrop = targetIndex => { const { selectedSeedIndices, draggingSeedIndex, } = this.state const indices = insert(selectedSeedIndices, draggingSeedIndex, targetIndex, true) this.setState({ selectedSeedIndices: indices, pendingSeedIndices: indices, draggingSeedIndex: -1, hoveringIndex: -1, }) } handleExport = () => { exportAsFile('MetaMask Secret Backup Phrase', this.props.seedPhrase, 'text/plain') } handleSubmit = async () => { const { history, setSeedPhraseBackedUp, showingSeedPhraseBackupAfterOnboarding, hideSeedPhraseBackupAfterOnboarding, initializeThreeBox, } = this.props if (!this.isValid()) { return } try { this.context.metricsEvent({ eventOpts: { category: 'Onboarding', action: 'Seed Phrase Setup', name: 'Verify Complete', }, }) setSeedPhraseBackedUp(true).then(() => { if (showingSeedPhraseBackupAfterOnboarding) { hideSeedPhraseBackupAfterOnboarding() history.push(DEFAULT_ROUTE) } else { initializeThreeBox() history.push(INITIALIZE_END_OF_FLOW_ROUTE) } }) } catch (error) { console.error(error.message) } } handleSelectSeedWord = (shuffledIndex) => { this.setState({ selectedSeedIndices: [...this.state.selectedSeedIndices, shuffledIndex], pendingSeedIndices: [...this.state.pendingSeedIndices, shuffledIndex], }) } handleDeselectSeedWord = shuffledIndex => { this.setState({ selectedSeedIndices: this.state.selectedSeedIndices.filter(i => shuffledIndex !== i), pendingSeedIndices: this.state.pendingSeedIndices.filter(i => shuffledIndex !== i), }) } isValid () { const { seedPhrase } = this.props const { selectedSeedIndices, shuffledSeedWords } = this.state const selectedSeedWords = selectedSeedIndices.map(i => shuffledSeedWords[i]) return seedPhrase === selectedSeedWords.join(' ') } render () { const { t } = this.context const { history } = this.props const { selectedSeedIndices, shuffledSeedWords, draggingSeedIndex, } = this.state return (
{ e.preventDefault() history.push(INITIALIZE_SEED_PHRASE_ROUTE) }} href="#" > {`< Back`}
{ t('confirmSecretBackupPhrase') }
{ t('selectEachPhrase') }
-1, })} > { this.renderPendingSeeds() } { this.renderSelectedSeeds() }
{ shuffledSeedWords.map((word, index) => { const isSelected = selectedSeedIndices.includes(index) return ( { if (!isSelected) { this.handleSelectSeedWord(index) } else { this.handleDeselectSeedWord(index) } }} word={word} /> ) }) }
) } renderSelectedSeeds () { const { shuffledSeedWords, selectedSeedIndices, draggingSeedIndex } = this.state return EMPTY_SEEDS.map((_, index) => { const seedIndex = selectedSeedIndices[index] const word = shuffledSeedWords[seedIndex] return ( ) }) } renderPendingSeeds () { const { pendingSeedIndices, shuffledSeedWords, draggingSeedIndex, hoveringIndex, } = this.state const indices = insert(pendingSeedIndices, draggingSeedIndex, hoveringIndex) return EMPTY_SEEDS.map((_, index) => { const seedIndex = indices[index] const word = shuffledSeedWords[seedIndex] return ( ) }) } } function insert (list, value, target, removeOld) { let nextList = [...list] if (typeof list[target] === 'number') { nextList = [...list.slice(0, target), value, ...list.slice(target)] } if (removeOld) { nextList = nextList.filter((seed, i) => { return seed !== value || i === target }) } if (nextList.length > 12) { nextList.pop() } return nextList }