aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase')
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js155
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.state.js41
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js1
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss48
4 files changed, 245 insertions, 0 deletions
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
new file mode 100644
index 000000000..59b4f73a6
--- /dev/null
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
@@ -0,0 +1,155 @@
+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,
+} from '../../../../helpers/constants/routes'
+import { exportAsFile } from '../../../../helpers/utils/util'
+import { selectSeedWord, deselectSeedWord } from './confirm-seed-phrase.state'
+
+export default class ConfirmSeedPhrase extends PureComponent {
+ static contextTypes = {
+ metricsEvent: PropTypes.func,
+ t: PropTypes.func,
+ }
+
+ static defaultProps = {
+ seedPhrase: '',
+ }
+
+ static propTypes = {
+ history: PropTypes.object,
+ onSubmit: PropTypes.func,
+ seedPhrase: PropTypes.string,
+ }
+
+ state = {
+ selectedSeedWords: [],
+ shuffledSeedWords: [],
+ // Hash of shuffledSeedWords index {Number} to selectedSeedWords index {Number}
+ selectedSeedWordsHash: {},
+ }
+
+ componentDidMount () {
+ const { seedPhrase = '' } = this.props
+ const shuffledSeedWords = shuffle(seedPhrase.split(' ')) || []
+ this.setState({ shuffledSeedWords })
+ }
+
+ handleExport = () => {
+ exportAsFile('MetaMask Secret Backup Phrase', this.props.seedPhrase, 'text/plain')
+ }
+
+ handleSubmit = async () => {
+ const { history } = this.props
+
+ if (!this.isValid()) {
+ return
+ }
+
+ try {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Seed Phrase Setup',
+ name: 'Verify Complete',
+ },
+ })
+ history.push(INITIALIZE_END_OF_FLOW_ROUTE)
+ } catch (error) {
+ console.error(error.message)
+ }
+ }
+
+ handleSelectSeedWord = (word, shuffledIndex) => {
+ this.setState(selectSeedWord(word, shuffledIndex))
+ }
+
+ handleDeselectSeedWord = shuffledIndex => {
+ this.setState(deselectSeedWord(shuffledIndex))
+ }
+
+ isValid () {
+ const { seedPhrase } = this.props
+ const { selectedSeedWords } = this.state
+ return seedPhrase === selectedSeedWords.join(' ')
+ }
+
+ render () {
+ const { t } = this.context
+ const { history } = this.props
+ const { selectedSeedWords, shuffledSeedWords, selectedSeedWordsHash } = this.state
+
+ return (
+ <div className="confirm-seed-phrase">
+ <div className="confirm-seed-phrase__back-button">
+ <a
+ onClick={e => {
+ e.preventDefault()
+ history.push(INITIALIZE_SEED_PHRASE_ROUTE)
+ }}
+ href="#"
+ >
+ {`< Back`}
+ </a>
+ </div>
+ <div className="first-time-flow__header">
+ { t('confirmSecretBackupPhrase') }
+ </div>
+ <div className="first-time-flow__text-block">
+ { t('selectEachPhrase') }
+ </div>
+ <div className="confirm-seed-phrase__selected-seed-words">
+ {
+ selectedSeedWords.map((word, index) => (
+ <div
+ key={index}
+ className="confirm-seed-phrase__seed-word"
+ >
+ { word }
+ </div>
+ ))
+ }
+ </div>
+ <div className="confirm-seed-phrase__shuffled-seed-words">
+ {
+ shuffledSeedWords.map((word, index) => {
+ const isSelected = index in selectedSeedWordsHash
+
+ return (
+ <div
+ key={index}
+ className={classnames(
+ 'confirm-seed-phrase__seed-word',
+ 'confirm-seed-phrase__seed-word--shuffled',
+ { 'confirm-seed-phrase__seed-word--selected': isSelected }
+ )}
+ onClick={() => {
+ if (!isSelected) {
+ this.handleSelectSeedWord(word, index)
+ } else {
+ this.handleDeselectSeedWord(index)
+ }
+ }}
+ >
+ { word }
+ </div>
+ )
+ })
+ }
+ </div>
+ <Button
+ type="confirm"
+ className="first-time-flow__button"
+ onClick={this.handleSubmit}
+ disabled={!this.isValid()}
+ >
+ { t('confirm') }
+ </Button>
+ </div>
+ )
+ }
+}
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.state.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.state.js
new file mode 100644
index 000000000..f2476fc5c
--- /dev/null
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.state.js
@@ -0,0 +1,41 @@
+export function selectSeedWord (word, shuffledIndex) {
+ return function update (state) {
+ const { selectedSeedWords, selectedSeedWordsHash } = state
+ const nextSelectedIndex = selectedSeedWords.length
+
+ return {
+ selectedSeedWords: [ ...selectedSeedWords, word ],
+ selectedSeedWordsHash: { ...selectedSeedWordsHash, [shuffledIndex]: nextSelectedIndex },
+ }
+ }
+}
+
+export function deselectSeedWord (shuffledIndex) {
+ return function update (state) {
+ const {
+ selectedSeedWords: prevSelectedSeedWords,
+ selectedSeedWordsHash: prevSelectedSeedWordsHash,
+ } = state
+
+ const selectedSeedWords = [...prevSelectedSeedWords]
+ const indexToRemove = prevSelectedSeedWordsHash[shuffledIndex]
+ selectedSeedWords.splice(indexToRemove, 1)
+ const selectedSeedWordsHash = Object.keys(prevSelectedSeedWordsHash).reduce((acc, index) => {
+ const output = { ...acc }
+ const selectedSeedWordIndex = prevSelectedSeedWordsHash[index]
+
+ if (selectedSeedWordIndex < indexToRemove) {
+ output[index] = selectedSeedWordIndex
+ } else if (selectedSeedWordIndex > indexToRemove) {
+ output[index] = selectedSeedWordIndex - 1
+ }
+
+ return output
+ }, {})
+
+ return {
+ selectedSeedWords,
+ selectedSeedWordsHash,
+ }
+ }
+}
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js
new file mode 100644
index 000000000..c7b511503
--- /dev/null
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js
@@ -0,0 +1 @@
+export { default } from './confirm-seed-phrase.component'
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss
new file mode 100644
index 000000000..93137618c
--- /dev/null
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss
@@ -0,0 +1,48 @@
+.confirm-seed-phrase {
+ &__back-button {
+ margin-bottom: 12px;
+ }
+
+ &__selected-seed-words {
+ min-height: 190px;
+ max-width: 496px;
+ border: 1px solid #CDCDCD;
+ border-radius: 6px;
+ background-color: $white;
+ margin: 24px 0 36px;
+ padding: 12px;
+ }
+
+ &__shuffled-seed-words {
+ max-width: 496px;
+ }
+
+ &__seed-word {
+ display: inline-block;
+ color: #5B5D67;
+ background-color: #E7E7E7;
+ padding: 8px 18px;
+ min-width: 64px;
+ margin: 4px;
+ text-align: center;
+
+ &--selected {
+ background-color: #85D1CC;
+ color: $white;
+ }
+
+ &--shuffled {
+ cursor: pointer;
+ margin: 6px;
+ }
+
+ @media screen and (max-width: 575px) {
+ font-size: .875rem;
+ padding: 6px 18px;
+ }
+ }
+
+ button {
+ margin-top: 0xp;
+ }
+}