First time flow updates (#6192)

* Action select step of onboarding flow added.

* Update navigation on create and import password screens.

* Adds terms of service checkbox to create and import account screens.

* Add security warning to jazzicon intro step

* Update and streamline unique image to confirm seed steps of first time flow.

* UI touch ups to welcome screen.

* UI touch up on select action page

* Fix first time import flow.

* Add end of flow screen to first time flow

* Replace unique image screen with updated fishing warning screen.

* Update e2e tests for onboarding flow changes.

* Add required translations to onboarding flow.

* Update design of select action screen to emphasize create new wallet option.

* Clean up onboarding flow code.

* Remove notice related code from first-time-flow directory.

* Use updater function argument in new-account.component
feature/default_network_editable
Dan J Miller 6 years ago committed by Whymarrh Whitby
parent a2320c76fe
commit cb2698d20e
  1. 65
      app/_locales/en/messages.json
  2. 5
      app/images/download-alt.svg
  3. 1
      app/images/sleuth.svg
  4. 4
      app/images/thin-plus.svg
  5. 52
      test/e2e/beta/from-import-beta-ui.spec.js
  6. 57
      test/e2e/beta/metamask-beta-responsive-ui.spec.js
  7. 56
      test/e2e/beta/metamask-beta-ui.spec.js
  8. 29
      ui/app/components/pages/first-time-flow/create-password/create-password.component.js
  9. 37
      ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
  10. 46
      ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js
  11. 37
      ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js
  12. 70
      ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js
  13. 11
      ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js
  14. 1
      ui/app/components/pages/first-time-flow/end-of-flow/index.js
  15. 47
      ui/app/components/pages/first-time-flow/end-of-flow/index.scss
  16. 7
      ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js
  17. 2
      ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js
  18. 33
      ui/app/components/pages/first-time-flow/first-time-flow.component.js
  19. 3
      ui/app/components/pages/first-time-flow/first-time-flow.container.js
  20. 59
      ui/app/components/pages/first-time-flow/index.scss
  21. 1
      ui/app/components/pages/first-time-flow/notices/index.js
  22. 124
      ui/app/components/pages/first-time-flow/notices/notices.component.js
  23. 27
      ui/app/components/pages/first-time-flow/notices/notices.container.js
  24. 29
      ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
  25. 12
      ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js
  26. 2
      ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js
  27. 4
      ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss
  28. 2
      ui/app/components/pages/first-time-flow/seed-phrase/index.js
  29. 6
      ui/app/components/pages/first-time-flow/seed-phrase/index.scss
  30. 4
      ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss
  31. 18
      ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
  32. 17
      ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.component.js
  33. 12
      ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.container.js
  34. 1
      ui/app/components/pages/first-time-flow/select-action/index.js
  35. 87
      ui/app/components/pages/first-time-flow/select-action/index.scss
  36. 104
      ui/app/components/pages/first-time-flow/select-action/select-action.component.js
  37. 0
      ui/app/components/pages/first-time-flow/select-action/select-action.container.js
  38. 31
      ui/app/components/pages/first-time-flow/welcome/index.scss
  39. 16
      ui/app/components/pages/first-time-flow/welcome/welcome.component.js
  40. 10
      ui/app/components/pages/home/home.component.js
  41. 4
      ui/app/routes.js

@ -92,6 +92,15 @@
"advanced": { "advanced": {
"message": "Advanced" "message": "Advanced"
}, },
"agreeTermsOfService": {
"message": "I agree to the Terms of Service"
},
"allDone": {
"message": "All Done"
},
"alreadyHaveSeedPhrase": {
"message": "No, I already have a seed phrase"
},
"amount": { "amount": {
"message": "Amount" "message": "Amount"
}, },
@ -239,6 +248,9 @@
"confirmTransaction": { "confirmTransaction": {
"message": "Confirm Transaction" "message": "Confirm Transaction"
}, },
"congratulations": {
"message": "Congratulations"
},
"connectHardwareWallet": { "connectHardwareWallet": {
"message": "Connect Hardware Wallet" "message": "Connect Hardware Wallet"
}, },
@ -326,6 +338,9 @@
"createAccount": { "createAccount": {
"message": "Create Account" "message": "Create Account"
}, },
"createAWallet": {
"message": "Create a Wallet"
},
"createDen": { "createDen": {
"message": "Create" "message": "Create"
}, },
@ -448,6 +463,24 @@
"encryptNewDen": { "encryptNewDen": {
"message": "Encrypt your new DEN" "message": "Encrypt your new DEN"
}, },
"endOfFlowMessage1": {
"message": "You passed the test - keep your seedphrase safe, it's your responsibility!"
},
"endOfFlowMessage2": {
"message": "Tips on storing it safely"
},
"endOfFlowMessage3": {
"message": "Save a backup in multiple places"
},
"endOfFlowMessage4": {
"message": "Never tell anyone"
},
"endOfFlowMessage5": {
"message": "If you need to back your seed phrase again, you can find it in Settings -> Security."
},
"endOfFlowMessage6": {
"message": "MetaMask cannot recover your seedphrase. Learn more."
},
"ensNameNotFound": { "ensNameNotFound": {
"message": "ENS name not found" "message": "ENS name not found"
}, },
@ -581,10 +614,16 @@
"getHelp": { "getHelp": {
"message": "Get Help." "message": "Get Help."
}, },
"getStarted": {
"message": "Get Started"
},
"greaterThanMin": { "greaterThanMin": {
"message": "must be greater than or equal to $1.", "message": "must be greater than or equal to $1.",
"description": "helper for inputting hex as decimal input" "description": "helper for inputting hex as decimal input"
}, },
"happyToSeeYou": {
"message": "We’re happy to see you."
},
"hardware": { "hardware": {
"message": "hardware" "message": "hardware"
}, },
@ -647,6 +686,9 @@
"importDen": { "importDen": {
"message": "Import Existing DEN" "message": "Import Existing DEN"
}, },
"importWallet": {
"message": "Import Wallet"
},
"imported": { "imported": {
"message": "Imported", "message": "Imported",
"description": "status showing that an account has been fully loaded into the keyring" "description": "status showing that an account has been fully loaded into the keyring"
@ -731,6 +773,9 @@
"message": "must be less than or equal to $1.", "message": "must be less than or equal to $1.",
"description": "helper for inputting hex as decimal input" "description": "helper for inputting hex as decimal input"
}, },
"letsGoSetUp": {
"message": "Yes, let’s get set up!"
},
"likeToAddTokens": { "likeToAddTokens": {
"message": "Would you like to add these tokens?" "message": "Would you like to add these tokens?"
}, },
@ -777,7 +822,7 @@
"message": "Message" "message": "Message"
}, },
"metamaskDescription": { "metamaskDescription": {
"message": "MetaMask is a secure identity vault for Ethereum." "message": "Connecting you to Ethereum and the Decentralized Web."
}, },
"metamaskSeedWords": { "metamaskSeedWords": {
"message": "MetaMask Seed Words" "message": "MetaMask Seed Words"
@ -848,6 +893,21 @@
"newNetwork": { "newNetwork": {
"message": "New Network" "message": "New Network"
}, },
"newToMetaMask": {
"message": "New to MetaMask?"
},
"noAlreadyHaveSeed": {
"message": "No, I already have a seed phrase"
},
"protectYourKeys": {
"message": "Protect Your Keys!"
},
"protectYourKeysMessage1": {
"message": "Be careful with your seed phrase — there have been reports of websites that attempt to imitate MetaMask. MetaMask will never ask for your seed phrase!"
},
"protectYourKeysMessage2": {
"message": "Keep your phrase safe. If you see something fishy, or you’re uncertain about a website, email support@metamask.io"
},
"rpcURL": { "rpcURL": {
"message": "New RPC URL" "message": "New RPC URL"
}, },
@ -1609,6 +1669,9 @@
"yourUniqueAccountImageDescription2": { "yourUniqueAccountImageDescription2": {
"message": "You’ll see this image everytime you need to confirm a transaction." "message": "You’ll see this image everytime you need to confirm a transaction."
}, },
"yourUniqueAccountImageDescription3": {
"message": "MetaMask will never ask for your seed phrase!"
},
"zeroGasPriceOnSpeedUpError": { "zeroGasPriceOnSpeedUpError": {
"message":"Zero gas price on speed up" "message":"Zero gas price on speed up"
} }

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="40" y="40" width="40" height="4.57143" rx="2.28571" transform="rotate(-180 40 40)" fill="#979797"/>
<rect x="22.5641" y="21.7144" width="4.10256" height="21.7143" rx="2.05128" transform="rotate(-180 22.5641 21.7144)" fill="#979797"/>
<path d="M20.5 30L7.07661 12L33.9234 12L20.5 30Z" fill="#979797"/>
</svg>

After

Width:  |  Height:  |  Size: 413 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#66757F" d="M33 36v-1c0-3.313-2.687-6-6-6H9c-3.313 0-6 2.687-6 6v1h30z"/><path fill="#EF9645" d="M12 27.482C13.672 29.057 15.746 30 18 30s4.327-.944 6-2.518V26H12v1.482z"/><path fill="#66757F" d="M26.75 20.435c1.188.208 2.619.129 2.416.917-.479 1.854-2.604 1.167-2.979 1.188-.375.02.563-2.105.563-2.105z"/><path fill="#292F33" d="M27.062 20.645c1.875.25 2.541.416 1.166.958-.772.305-2.243 4.803-3.331 4.118-1.087-.685 2.165-5.076 2.165-5.076z"/><path fill="#66757F" d="M9.255 20.435c-1.188.208-2.619.129-2.416.917.479 1.854 2.604 1.167 2.979 1.188.375.02-.563-2.105-.563-2.105z"/><path fill="#292F33" d="M8.943 20.645c-1.875.25-2.541.416-1.166.958.772.305 2.243 4.803 3.331 4.118 1.088-.685-2.165-5.076-2.165-5.076z"/><path fill="#FFAC33" d="M8.055 11.031c-1.953 0-2.305 3.164-.664 3.594 0 0-1.367 3.32 1.953 3.32-.547-1.68-1.562-4.414-.781-6.406m19.38-.508c1.953 0 2.305 3.164.664 3.594 0 0 1.367 3.32-1.953 3.32.547-1.68 1.562-4.414.781-6.406"/><ellipse fill="#FFDC5D" cx="18" cy="15.5" rx="10" ry="12.5"/><path fill="#662113" d="M14 17c-.552 0-1-.448-1-1v-1c0-.552.448-1 1-1s1 .448 1 1v1c0 .552-.448 1-1 1zm8 0c-.553 0-1-.448-1-1v-1c0-.552.447-1 1-1s1 .448 1 1v1c0 .552-.447 1-1 1z"/><path fill="#C1694F" d="M19 20.5c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h1c.276 0 .5.224.5.5z"/><path fill-rule="evenodd" clip-rule="evenodd" fill="#292F33" d="M7.657 14.788c.148.147.888.591 1.036 1.034.148.443.445 2.954 1.333 3.693.916.762 4.37.478 5.032.149 1.48-.738 1.662-2.798 1.924-3.842.148-.591 1.036-.591 1.036-.591s.888 0 1.036.591c.262 1.044.444 3.104 1.924 3.841.662.33 4.116.614 5.034-.147.887-.739 1.183-3.25 1.331-3.694.146-.443.888-.886 1.035-1.034.148-.148.148-.739 0-.887-.296-.295-3.788-.559-7.548-.148-.75.082-1.035.295-2.812.295-1.776 0-2.062-.214-2.812-.295-3.759-.411-7.252-.148-7.548.148-.149.148-.149.74-.001.887z"/><path fill="#66757F" d="M7.858 8.395S9.217-.506 13.79.023c3.512.406 4.89.825 7.833.097 1.947-.482 4.065 1.136 5.342 4.379.816 2.068 1.224 4.041 1.224 4.041s3.938-.385 4.165 1.732c.228 2.117-4.354 4.716-15.889 4.716C10 14.987 3.33 12.63 3.013 10.657c-.317-1.973 4.845-2.262 4.845-2.262z"/><path fill="#292F33" d="M8.125 7.15s-.27 1.104-.406 1.871c-.136.768.226 1.296 2.705 1.824 3.287.7 10.679.692 15.058-.383 1.759-.432 2.886-.72 2.751-1.583-.167-1.068-.196-1.066-.541-2.208 0 0-1.477.502-3.427.96-2.66.624-9.964.911-13.481.144-1.874-.41-2.659-.625-2.659-.625zm-.136 13.953c-.354.145 2.921 1.378 7.48 1.458 4.771.084 6.234.39 5.146 1.459-1.146 1.125-.852 2.894-.771 3.418.081.524 2.047 1.916 2.208 2.56.161.645-1.229 5.961-1.229 5.961l-8.729-.252c-2.565-8.844-2.883-8.501-4.105-13.604-.241-1.008 0-1 0-1z"/><path fill="#66757F" d="M6.989 21.144c-.354.146 2.921 1.378 7.48 1.458 4.771.084 6.234.39 5.146 1.459-1.146 1.125-.664 2.894-.583 3.418.081.524 1.859 1.916 2.021 2.561.16.644-1.231 5.96-1.231 5.96l-8.729-.252c-2.565-8.844-2.883-8.501-4.105-13.604-.24-1.008.001-1 .001-1z"/><path fill="#292F33" d="M28.052 21.103c.354.145-2.921 1.378-7.479 1.458-4.771.084-6.234.39-5.146 1.459 1.146 1.125 2.976 2.892 2.896 3.416-.081.524-4.172 1.918-4.333 2.562-.161.645 1.229 5.961 1.229 5.961l8.729-.252c2.565-8.844 2.883-8.501 4.104-13.604.241-1.008 0-1 0-1z"/><path fill="#66757F" d="M28.958 21.103c.354.145-2.921 1.378-7.479 1.458-4.771.084-6.234.39-5.146 1.459 1.146 1.125 2.977 2.892 2.896 3.416-.081.524-4.172 1.918-4.333 2.562-.161.645 1.229 5.961 1.229 5.961l8.657.01c2.565-8.844 2.955-8.763 4.177-13.866.24-1.008-.001-1-.001-1z"/></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="18" width="40" height="4" rx="2" fill="#979797"/>
<rect x="18" width="4" height="40" rx="2" fill="#979797"/>
</svg>

After

Width:  |  Height:  |  Size: 221 B

@ -65,13 +65,16 @@ describe('Using MetaMask with an existing account', function () {
beforeEach(async function () { beforeEach(async function () {
await driver.executeScript( await driver.executeScript(
'window.origFetch = window.fetch.bind(window);' +
'window.fetch = ' + 'window.fetch = ' +
'(...args) => { ' + '(...args) => { ' +
'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' + 'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' +
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' + '(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } ' + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' +
'return window.fetch(...args); }' '(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' +
'return window.origFetch(...args); }'
) )
}) })
@ -95,16 +98,19 @@ describe('Using MetaMask with an existing account', function () {
describe('First time flow starting from an existing seed phrase', () => { describe('First time flow starting from an existing seed phrase', () => {
it('clicks the continue button on the welcome screen', async () => { it('clicks the continue button on the welcome screen', async () => {
const welcomeScreenBtn = await findElement(driver, By.css('.welcome-page .first-time-flow__button')) await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button'))
welcomeScreenBtn.click() welcomeScreenBtn.click()
await delay(largeDelayMs) await delay(largeDelayMs)
}) })
it('imports a seed phrase', async () => { it('clicks the "Import Wallet" option', async () => {
const [seedPhrase] = await findElements(driver, By.xpath(`//a[contains(text(), 'Import with seed phrase')]`)) const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
await seedPhrase.click() customRpcButton.click()
await delay(regularDelayMs) await delay(largeDelayMs)
})
it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea')) const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase) await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs) await delay(regularDelayMs)
@ -114,39 +120,25 @@ describe('Using MetaMask with an existing account', function () {
const [confirmPassword] = await findElements(driver, By.id('confirm-password')) const [confirmPassword] = await findElements(driver, By.id('confirm-password'))
confirmPassword.sendKeys('correct horse battery staple') confirmPassword.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`)) const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
await importButton.click() await importButton.click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
it('clicks through the ToS', async () => { it('clicks through the security warning screen', async () => {
// terms of use await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`))
await findElement(driver, By.css('.first-time-flow__markdown'))
const canClickThrough = await driver.findElement(By.css('button.first-time-flow__button')).isEnabled()
assert.equal(canClickThrough, false, 'disabled continue button')
const bottomOfTos = await findElement(driver, By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
await delay(regularDelayMs)
const acceptTos = await findElement(driver, By.css('button.first-time-flow__button'))
driver.wait(until.elementIsEnabled(acceptTos))
await acceptTos.click()
await delay(regularDelayMs)
})
it('clicks through the privacy notice', async () => {
// privacy notice
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click() await nextScreen.click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
it('clicks through the phishing notice', async () => { it('clicks through the success screen', async () => {
// phishing notice await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const noticeElement = await driver.findElement(By.css('.first-time-flow__markdown')) const doneButton = await findElement(driver, By.css('button.first-time-flow__button'))
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) await doneButton.click()
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
}) })

@ -81,11 +81,18 @@ describe('MetaMask', function () {
describe('Going through the first time flow', () => { describe('Going through the first time flow', () => {
it('clicks the continue button on the welcome screen', async () => { it('clicks the continue button on the welcome screen', async () => {
const welcomeScreenBtn = await findElement(driver, By.css('.welcome-page .first-time-flow__button')) await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button'))
welcomeScreenBtn.click() welcomeScreenBtn.click()
await delay(largeDelayMs) await delay(largeDelayMs)
}) })
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
})
it('accepts a secure password', async () => { it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password')) const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password')) const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
@ -93,43 +100,16 @@ describe('MetaMask', function () {
await passwordBox.sendKeys('correct horse battery staple') await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple') await passwordBoxConfirm.sendKeys('correct horse battery staple')
await button.click()
await delay(regularDelayMs)
})
it('clicks through the unique image screen', async () => { const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
await findElement(driver, By.css('.first-time-flow__unique-image')) await tosCheckBox.click()
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs)
})
it('clicks through the ToS', async () => { await button.click()
// terms of use
await findElement(driver, By.css('.first-time-flow__markdown'))
const canClickThrough = await driver.findElement(By.css('button.first-time-flow__button')).isEnabled()
assert.equal(canClickThrough, false, 'disabled continue button')
const bottomOfTos = await findElement(driver, By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
await delay(regularDelayMs)
const acceptTos = await findElement(driver, By.css('button.first-time-flow__button'))
driver.wait(until.elementIsEnabled(acceptTos))
await acceptTos.click()
await delay(regularDelayMs)
})
it('clicks through the privacy notice', async () => {
// privacy notice
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
it('clicks through the phishing notice', async () => { it('clicks through the security warning screen', async () => {
// phishing notice await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`))
const noticeElement = await driver.findElement(By.css('.first-time-flow__markdown'))
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement)
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click() await nextScreen.click()
await delay(regularDelayMs) await delay(regularDelayMs)
@ -192,19 +172,16 @@ describe('MetaMask', function () {
const words = seedPhrase.split(' ') const words = seedPhrase.split(' ')
await retypeSeedPhrase(words) await retypeSeedPhrase(words)
await delay(regularDelayMs)
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirm.click() await confirm.click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
it('clicks through the deposit modal', async () => { it('clicks through the success screen', async () => {
const byBuyModal = By.css('span .modal') await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const buyModal = await driver.wait(until.elementLocated(byBuyModal)) const doneButton = await findElement(driver, By.css('button.first-time-flow__button'))
const closeModal = await findElement(driver, By.css('.page-container__header-close')) await doneButton.click()
await closeModal.click()
await driver.wait(until.stalenessOf(buyModal))
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
}) })

@ -102,11 +102,18 @@ describe('MetaMask', function () {
describe('Going through the first time flow', () => { describe('Going through the first time flow', () => {
it('clicks the continue button on the welcome screen', async () => { it('clicks the continue button on the welcome screen', async () => {
const welcomeScreenBtn = await findElement(driver, By.css('.welcome-page .first-time-flow__button')) await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button'))
welcomeScreenBtn.click() welcomeScreenBtn.click()
await delay(largeDelayMs) await delay(largeDelayMs)
}) })
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
})
it('accepts a secure password', async () => { it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password')) const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password')) const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
@ -114,43 +121,16 @@ describe('MetaMask', function () {
await passwordBox.sendKeys('correct horse battery staple') await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple') await passwordBoxConfirm.sendKeys('correct horse battery staple')
await button.click()
await delay(regularDelayMs)
})
it('clicks through the unique image screen', async () => {
await findElement(driver, By.css('.first-time-flow__unique-image'))
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs)
})
it('clicks through the ToS', async () => { const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
// terms of use await tosCheckBox.click()
await findElement(driver, By.css('.first-time-flow__markdown'))
const canClickThrough = await driver.findElement(By.css('button.first-time-flow__button')).isEnabled()
assert.equal(canClickThrough, false, 'disabled continue button')
const bottomOfTos = await findElement(driver, By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
await delay(regularDelayMs)
const acceptTos = await findElement(driver, By.css('button.first-time-flow__button'))
driver.wait(until.elementIsEnabled(acceptTos))
await acceptTos.click()
await delay(regularDelayMs)
})
it('clicks through the privacy notice', async () => { await button.click()
// privacy notice
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
it('clicks through the phishing notice', async () => { it('clicks through the security warning screen', async () => {
// phishing notice await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`))
const noticeElement = await driver.findElement(By.css('.first-time-flow__markdown'))
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement)
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click() await nextScreen.click()
await delay(regularDelayMs) await delay(regularDelayMs)
@ -219,12 +199,10 @@ describe('MetaMask', function () {
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
it('clicks through the deposit modal', async () => { it('clicks through the success screen', async () => {
const byBuyModal = By.css('span .modal') await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const buyModal = await driver.wait(until.elementLocated(byBuyModal)) const doneButton = await findElement(driver, By.css('button.first-time-flow__button'))
const closeModal = await findElement(driver, By.css('.page-container__header-close')) await doneButton.click()
await closeModal.click()
await driver.wait(until.stalenessOf(buyModal))
await delay(regularDelayMs) await delay(regularDelayMs)
}) })
}) })

@ -8,13 +8,13 @@ import {
INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_CREATE_PASSWORD_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
INITIALIZE_UNIQUE_IMAGE_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE,
INITIALIZE_NOTICE_ROUTE,
} from '../../../../routes' } from '../../../../routes'
export default class CreatePassword extends PureComponent { export default class CreatePassword extends PureComponent {
static propTypes = { static propTypes = {
history: PropTypes.object, history: PropTypes.object,
isInitialized: PropTypes.bool, isInitialized: PropTypes.bool,
isImportedKeyring: PropTypes.bool,
onCreateNewAccount: PropTypes.func, onCreateNewAccount: PropTypes.func,
onCreateNewAccountFromSeed: PropTypes.func, onCreateNewAccountFromSeed: PropTypes.func,
} }
@ -23,17 +23,38 @@ export default class CreatePassword extends PureComponent {
const { isInitialized, history } = this.props const { isInitialized, history } = this.props
if (isInitialized) { if (isInitialized) {
history.push(INITIALIZE_NOTICE_ROUTE) history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
} }
} }
render () { render () {
const { onCreateNewAccount, onCreateNewAccountFromSeed } = this.props const { onCreateNewAccount, onCreateNewAccountFromSeed, isImportedKeyring } = this.props
return ( return (
<div className="first-time-flow__wrapper"> <div className="first-time-flow__wrapper">
<div className="app-header__logo-container">
<img
className="app-header__metafox-logo app-header__metafox-logo--horizontal"
src="/images/logo/metamask-logo-horizontal.svg"
height={30}
/>
<img
className="app-header__metafox-logo app-header__metafox-logo--icon"
src="/images/logo/metamask-fox.svg"
height={42}
width={42}
/>
</div>
<Switch> <Switch>
<Route exact path={INITIALIZE_UNIQUE_IMAGE_ROUTE} component={UniqueImage} /> <Route exact
path={INITIALIZE_UNIQUE_IMAGE_ROUTE}
render={props => (
<UniqueImage
{ ...props }
isImportedKeyring={isImportedKeyring}
/>
)}
/>
<Route <Route
exact exact
path={INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE} path={INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE}

@ -3,10 +3,9 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import TextField from '../../../../text-field' import TextField from '../../../../text-field'
import Button from '../../../../button' import Button from '../../../../button'
import Breadcrumbs from '../../../../breadcrumbs'
import { import {
INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_SELECT_ACTION_ROUTE,
INITIALIZE_NOTICE_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE,
} from '../../../../../routes' } from '../../../../../routes'
export default class ImportWithSeedPhrase extends PureComponent { export default class ImportWithSeedPhrase extends PureComponent {
@ -26,6 +25,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
seedPhraseError: '', seedPhraseError: '',
passwordError: '', passwordError: '',
confirmPasswordError: '', confirmPasswordError: '',
termsChecked: false,
} }
parseSeedPhrase = (seedPhrase) => { parseSeedPhrase = (seedPhrase) => {
@ -104,7 +104,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
try { try {
await onSubmit(password, seedPhrase) await onSubmit(password, seedPhrase)
history.push(INITIALIZE_NOTICE_ROUTE) history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
} catch (error) { } catch (error) {
this.setState({ seedPhraseError: error.message }) this.setState({ seedPhraseError: error.message })
} }
@ -131,20 +131,26 @@ export default class ImportWithSeedPhrase extends PureComponent {
return !passwordError && !confirmPasswordError && !seedPhraseError return !passwordError && !confirmPasswordError && !seedPhraseError
} }
toggleTermsCheck = () => {
this.setState((prevState) => ({
termsChecked: !prevState.termsChecked,
}))
}
render () { render () {
const { t } = this.context const { t } = this.context
const { seedPhraseError, passwordError, confirmPasswordError } = this.state const { seedPhraseError, passwordError, confirmPasswordError, termsChecked } = this.state
return ( return (
<form <form
className="first-time-flow__form" className="first-time-flow__form"
onSubmit={this.handleImport} onSubmit={this.handleImport}
> >
<div> <div className="first-time-flow__create-back">
<a <a
onClick={e => { onClick={e => {
e.preventDefault() e.preventDefault()
this.props.history.push(INITIALIZE_CREATE_PASSWORD_ROUTE) this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE)
}} }}
href="#" href="#"
> >
@ -197,19 +203,22 @@ export default class ImportWithSeedPhrase extends PureComponent {
margin="normal" margin="normal"
largeLabel largeLabel
/> />
<div className="first-time-flow__checkbox-container" onClick={this.toggleTermsCheck}>
<div className="first-time-flow__checkbox">
{termsChecked ? <i className="fa fa-check fa-2x" /> : null}
</div>
<span className="first-time-flow__checkbox-label">
{ t('agreeTermsOfService') }
</span>
</div>
<Button <Button
type="first-time" type="confirm"
className="first-time-flow__button" className="first-time-flow__button"
disabled={!this.isValid()} disabled={!this.isValid() || !termsChecked}
onClick={this.handleImport} onClick={this.handleImport}
> >
{ t('import') } { t('import') }
</Button> </Button>
<Breadcrumbs
className="first-time-flow__breadcrumbs"
total={2}
currentIndex={0}
/>
</form> </form>
) )
} }

@ -1,10 +1,10 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Breadcrumbs from '../../../../breadcrumbs'
import Button from '../../../../button' import Button from '../../../../button'
import { import {
INITIALIZE_UNIQUE_IMAGE_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
INITIALIZE_SELECT_ACTION_ROUTE,
} from '../../../../../routes' } from '../../../../../routes'
import TextField from '../../../../text-field' import TextField from '../../../../text-field'
@ -23,6 +23,7 @@ export default class NewAccount extends PureComponent {
confirmPassword: '', confirmPassword: '',
passwordError: '', passwordError: '',
confirmPasswordError: '', confirmPasswordError: '',
termsChecked: false,
} }
isValid () { isValid () {
@ -111,12 +112,29 @@ export default class NewAccount extends PureComponent {
history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE) history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE)
} }
toggleTermsCheck = () => {
this.setState((prevState) => ({
termsChecked: !prevState.termsChecked,
}))
}
render () { render () {
const { t } = this.context const { t } = this.context
const { password, confirmPassword, passwordError, confirmPasswordError } = this.state const { password, confirmPassword, passwordError, confirmPasswordError, termsChecked } = this.state
return ( return (
<div> <div>
<div className="first-time-flow__create-back">
<a
onClick={e => {
e.preventDefault()
this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE)
}}
href="#"
>
{`< Back`}
</a>
</div>
<div className="first-time-flow__header"> <div className="first-time-flow__header">
{ t('createPassword') } { t('createPassword') }
</div> </div>
@ -151,27 +169,23 @@ export default class NewAccount extends PureComponent {
fullWidth fullWidth
largeLabel largeLabel
/> />
<div className="first-time-flow__checkbox-container" onClick={this.toggleTermsCheck}>
<div className="first-time-flow__checkbox">
{termsChecked ? <i className="fa fa-check fa-2x" /> : null}
</div>
<span className="first-time-flow__checkbox-label">
I agree to the Terms Of Service
</span>
</div>
<Button <Button
type="first-time" type="confirm"
className="first-time-flow__button" className="first-time-flow__button"
disabled={!this.isValid()} disabled={!this.isValid() || !termsChecked}
onClick={this.handleCreate} onClick={this.handleCreate}
> >
{ t('create') } { t('create') }
</Button> </Button>
</form> </form>
<a
href=""
className="first-time-flow__link create-password__import-link"
onClick={this.handleImportWithSeedPhrase}
>
{ t('importWithSeedPhrase') }
</a>
<Breadcrumbs
className="first-time-flow__breadcrumbs"
total={3}
currentIndex={0}
/>
</div> </div>
) )
} }

@ -1,9 +1,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Identicon from '../../../../identicon'
import Breadcrumbs from '../../../../breadcrumbs'
import Button from '../../../../button' import Button from '../../../../button'
import { INITIALIZE_NOTICE_ROUTE } from '../../../../../routes' import { INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_END_OF_FLOW_ROUTE } from '../../../../../routes'
export default class UniqueImageScreen extends PureComponent { export default class UniqueImageScreen extends PureComponent {
static contextTypes = { static contextTypes = {
@ -11,42 +9,43 @@ export default class UniqueImageScreen extends PureComponent {
} }
static propTypes = { static propTypes = {
address: PropTypes.string,
history: PropTypes.object, history: PropTypes.object,
isImportedKeyring: PropTypes.bool,
} }
render () { render () {
const { t } = this.context const { t } = this.context
const { address, history } = this.props const { history, isImportedKeyring } = this.props
return ( return (
<div> <div>
<Identicon <img
className="first-time-flow__unique-image" src="/images/sleuth.svg"
address={address} height={42}
diameter={70} width={42}
/> />
<div className="first-time-flow__header"> <div className="first-time-flow__header">
{ t('yourUniqueAccountImage') } { t('protectYourKeys') }
</div> </div>
<div className="first-time-flow__text-block"> <div className="first-time-flow__text-block">
{ t('yourUniqueAccountImageDescription1') } { t('protectYourKeysMessage1') }
</div> </div>
<div className="first-time-flow__text-block"> <div className="first-time-flow__text-block">
{ t('yourUniqueAccountImageDescription2') } { t('protectYourKeysMessage2') }
</div> </div>
<Button <Button
type="first-time" type="confirm"
className="first-time-flow__button" className="first-time-flow__button"
onClick={() => history.push(INITIALIZE_NOTICE_ROUTE)} onClick={() => {
if (isImportedKeyring) {
history.push(INITIALIZE_END_OF_FLOW_ROUTE)
} else {
history.push(INITIALIZE_SEED_PHRASE_ROUTE)
}
}}
> >
{ t('next') } { t('next') }
</Button> </Button>
<Breadcrumbs
className="first-time-flow__breadcrumbs"
total={3}
currentIndex={0}
/>
</div> </div>
) )
} }

@ -0,0 +1,70 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Button from '../../../button'
import { DEFAULT_ROUTE } from '../../../../routes'
export default class EndOfFlowScreen extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
history: PropTypes.object,
completeOnboarding: PropTypes.func,
}
render () {
const { t } = this.context
const { history, completeOnboarding } = this.props
return (
<div className="end-of-flow">
<div className="app-header__logo-container">
<img
className="app-header__metafox-logo app-header__metafox-logo--horizontal"
src="/images/logo/metamask-logo-horizontal.svg"
height={30}
/>
<img
className="app-header__metafox-logo app-header__metafox-logo--icon"
src="/images/logo/metamask-fox.svg"
height={42}
width={42}
/>
</div>
<div className="end-of-flow__emoji">🎉</div>
<div className="first-time-flow__header">
{ t('congratulations') }
</div>
<div className="first-time-flow__text-block end-of-flow__text-1">
{ t('endOfFlowMessage1') }
</div>
<div className="first-time-flow__text-block end-of-flow__text-2">
{ t('endOfFlowMessage2') }
</div>
<div className="first-time-flow__text-block end-of-flow__text-3">
{ '• ' + t('endOfFlowMessage3') }
</div>
<div className="first-time-flow__text-block end-of-flow__text-4">
{ '• ' + t('endOfFlowMessage4') }
</div>
<div className="first-time-flow__text-block end-of-flow__text-3">
{ t('endOfFlowMessage5') }
</div>
<div className="first-time-flow__text-block end-of-flow__text-3">
{ '*' + t('endOfFlowMessage6') }
</div>
<Button
type="confirm"
className="first-time-flow__button"
onClick={async () => {
await completeOnboarding()
history.push(DEFAULT_ROUTE)
}}
>
{ 'All Done' }
</Button>
</div>
)
}
}

@ -0,0 +1,11 @@
import { connect } from 'react-redux'
import EndOfFlow from './end-of-flow.component'
import { setCompletedOnboarding } from '../../../../actions'
const mapDispatchToProps = dispatch => {
return {
completeOnboarding: () => dispatch(setCompletedOnboarding()),
}
}
export default connect(null, mapDispatchToProps)(EndOfFlow)

@ -0,0 +1 @@
export { default } from './end-of-flow.container'

@ -0,0 +1,47 @@
.end-of-flow {
color: black;
font-family: Roboto;
font-style: normal;
.app-header__logo-container {
width: 742px;
margin-top: 3%;
@media screen and (max-width: $break-small) {
width: 100%;
}
}
&__text-1, &__text-3 {
font-weight: normal;
font-size: 16px;
margin-top: 18px;
}
&__text-2 {
font-weight: bold;
font-size: 16px;
margin-top: 26px;
}
&__text-3 {
margin-top: 26px;
}
&__text-3 {
margin-top: 2px;
}
button {
width: 207px;
}
&__start-over-button {
width: 744px;
}
&__emoji {
font-size: 80px;
margin-top: 70px;
}
}

@ -5,7 +5,6 @@ import {
DEFAULT_ROUTE, DEFAULT_ROUTE,
LOCK_ROUTE, LOCK_ROUTE,
INITIALIZE_WELCOME_ROUTE, INITIALIZE_WELCOME_ROUTE,
INITIALIZE_NOTICE_ROUTE,
INITIALIZE_UNLOCK_ROUTE, INITIALIZE_UNLOCK_ROUTE,
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE,
} from '../../../../routes' } from '../../../../routes'
@ -15,7 +14,6 @@ export default class FirstTimeFlowSwitch extends PureComponent {
completedOnboarding: PropTypes.bool, completedOnboarding: PropTypes.bool,
isInitialized: PropTypes.bool, isInitialized: PropTypes.bool,
isUnlocked: PropTypes.bool, isUnlocked: PropTypes.bool,
noActiveNotices: PropTypes.bool,
seedPhrase: PropTypes.string, seedPhrase: PropTypes.string,
} }
@ -24,7 +22,6 @@ export default class FirstTimeFlowSwitch extends PureComponent {
completedOnboarding, completedOnboarding,
isInitialized, isInitialized,
isUnlocked, isUnlocked,
noActiveNotices,
seedPhrase, seedPhrase,
} = this.props } = this.props
@ -44,10 +41,6 @@ export default class FirstTimeFlowSwitch extends PureComponent {
return <Redirect to={{ pathname: INITIALIZE_UNLOCK_ROUTE }} /> return <Redirect to={{ pathname: INITIALIZE_UNLOCK_ROUTE }} />
} }
if (!noActiveNotices) {
return <Redirect to={{ pathname: INITIALIZE_NOTICE_ROUTE }} />
}
if (seedPhrase) { if (seedPhrase) {
return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }} /> return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }} />
} }

@ -6,14 +6,12 @@ const mapStateToProps = ({ metamask }) => {
completedOnboarding, completedOnboarding,
isInitialized, isInitialized,
isUnlocked, isUnlocked,
noActiveNotices,
} = metamask } = metamask
return { return {
completedOnboarding, completedOnboarding,
isInitialized, isInitialized,
isUnlocked, isUnlocked,
noActiveNotices,
} }
} }

@ -3,17 +3,19 @@ import PropTypes from 'prop-types'
import { Switch, Route } from 'react-router-dom' import { Switch, Route } from 'react-router-dom'
import FirstTimeFlowSwitch from './first-time-flow-switch' import FirstTimeFlowSwitch from './first-time-flow-switch'
import Welcome from './welcome' import Welcome from './welcome'
import SelectAction from './select-action'
import EndOfFlow from './end-of-flow'
import Unlock from '../unlock-page' import Unlock from '../unlock-page'
import CreatePassword from './create-password' import CreatePassword from './create-password'
import Notices from './notices'
import SeedPhrase from './seed-phrase' import SeedPhrase from './seed-phrase'
import { import {
DEFAULT_ROUTE, DEFAULT_ROUTE,
INITIALIZE_WELCOME_ROUTE, INITIALIZE_WELCOME_ROUTE,
INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_CREATE_PASSWORD_ROUTE,
INITIALIZE_NOTICE_ROUTE,
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE,
INITIALIZE_UNLOCK_ROUTE, INITIALIZE_UNLOCK_ROUTE,
INITIALIZE_SELECT_ACTION_ROUTE,
INITIALIZE_END_OF_FLOW_ROUTE,
} from '../../../routes' } from '../../../routes'
export default class FirstTimeFlow extends PureComponent { export default class FirstTimeFlow extends PureComponent {
@ -24,7 +26,6 @@ export default class FirstTimeFlow extends PureComponent {
history: PropTypes.object, history: PropTypes.object,
isInitialized: PropTypes.bool, isInitialized: PropTypes.bool,
isUnlocked: PropTypes.bool, isUnlocked: PropTypes.bool,
noActiveNotices: PropTypes.bool,
unlockAccount: PropTypes.func, unlockAccount: PropTypes.func,
} }
@ -70,14 +71,12 @@ export default class FirstTimeFlow extends PureComponent {
} }
handleUnlock = async password => { handleUnlock = async password => {
const { unlockAccount, history, noActiveNotices } = this.props const { unlockAccount, history } = this.props
try { try {
const seedPhrase = await unlockAccount(password) const seedPhrase = await unlockAccount(password)
this.setState({ seedPhrase }, () => { this.setState({ seedPhrase }, () => {
noActiveNotices history.push(INITIALIZE_SEED_PHRASE_ROUTE)
? history.push(INITIALIZE_SEED_PHRASE_ROUTE)
: history.push(INITIALIZE_NOTICE_ROUTE)
}) })
} catch (error) { } catch (error) {
throw new Error(error.message) throw new Error(error.message)
@ -99,26 +98,21 @@ export default class FirstTimeFlow extends PureComponent {
/> />
)} )}
/> />
<Route
exact
path={INITIALIZE_NOTICE_ROUTE}
render={props => (
<Notices
{ ...props }
isImportedKeyring={isImportedKeyring}
/>
)}
/>
<Route <Route
path={INITIALIZE_CREATE_PASSWORD_ROUTE} path={INITIALIZE_CREATE_PASSWORD_ROUTE}
render={props => ( render={props => (
<CreatePassword <CreatePassword
{ ...props } { ...props }
isImportedKeyring={isImportedKeyring}
onCreateNewAccount={this.handleCreateNewAccount} onCreateNewAccount={this.handleCreateNewAccount}
onCreateNewAccountFromSeed={this.handleImportWithSeedPhrase} onCreateNewAccountFromSeed={this.handleImportWithSeedPhrase}
/> />
)} )}
/> />
<Route
path={INITIALIZE_SELECT_ACTION_ROUTE}
component={SelectAction}
/>
<Route <Route
path={INITIALIZE_UNLOCK_ROUTE} path={INITIALIZE_UNLOCK_ROUTE}
render={props => ( render={props => (
@ -128,6 +122,11 @@ export default class FirstTimeFlow extends PureComponent {
/> />
)} )}
/> />
<Route
exact
path={INITIALIZE_END_OF_FLOW_ROUTE}
component={EndOfFlow}
/>
<Route <Route
exact exact
path={INITIALIZE_WELCOME_ROUTE} path={INITIALIZE_WELCOME_ROUTE}

@ -7,13 +7,12 @@ import {
} from '../../../actions' } from '../../../actions'
const mapStateToProps = state => { const mapStateToProps = state => {
const { metamask: { completedOnboarding, isInitialized, isUnlocked, noActiveNotices } } = state const { metamask: { completedOnboarding, isInitialized, isUnlocked } } = state
return { return {
completedOnboarding, completedOnboarding,
isInitialized, isInitialized,
isUnlocked, isUnlocked,
noActiveNotices,
} }
} }

@ -1,18 +1,28 @@
@import './welcome/index'; @import './welcome/index';
@import './select-action/index';
@import './seed-phrase/index'; @import './seed-phrase/index';
@import './end-of-flow/index';
.first-time-flow { .first-time-flow {
width: 100%; width: 100%;
background-color: $white; background-color: $white;
display: flex;
justify-content: center;
&__wrapper { &__wrapper {
@media screen and (min-width: $break-large) { @media screen and (min-width: $break-large) {
padding: 60px 275px 0 275px; max-width: 742px;
display: flex;
flex-direction: column;
width: 100%;
margin-top: 2%;
} }
@media screen and (max-width: 1100px) { .app-header__metafox-logo {
padding: 36px; margin-bottom: 40px;
} }
} }
@ -21,9 +31,14 @@
flex-direction: column; flex-direction: column;
} }
&__create-back {
margin-bottom: 16px;
}
&__header { &__header {
font-size: 2.5rem; font-size: 2.5rem;
margin-bottom: 24px; margin-bottom: 24px;
color: black;
} }
&__subheader { &__subheader {
@ -86,6 +101,7 @@
&__text-block { &__text-block {
margin-bottom: 24px; margin-bottom: 24px;
color: black;
@media screen and (max-width: $break-small) { @media screen and (max-width: $break-small) {
margin-bottom: 16px; margin-bottom: 16px;
@ -95,5 +111,42 @@
&__button { &__button {
margin: 35px 0 14px; margin: 35px 0 14px;
width: 140px;
height: 44px;
}
&__checkbox-container {
display: flex;
align-items: center;
margin-top: 24px;
}
&__checkbox {
background: #FFFFFF;
border: 1px solid #CDCDCD;
box-sizing: border-box;
height: 34px;
width: 34px;
display: flex;
justify-content: center;
align-items: center;
&:hover {
border: 1.5px solid #2f9ae0;
}
.fa-check {
color: #2f9ae0
}
}
&__checkbox-label {
font-family: Roboto;
font-style: normal;
font-weight: normal;
line-height: normal;
font-size: 18px;
color: #939090;
margin-left: 18px;
} }
} }

@ -1 +0,0 @@
export { default } from './notices.container'

@ -1,124 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Markdown from 'react-markdown'
import debounce from 'lodash.debounce'
import Button from '../../../button'
import Identicon from '../../../identicon'
import Breadcrumbs from '../../../breadcrumbs'
import { DEFAULT_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../routes'
export default class Notices extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
address: PropTypes.string.isRequired,
completeOnboarding: PropTypes.func,
history: PropTypes.object,
isImportedKeyring: PropTypes.bool,
markNoticeRead: PropTypes.func,
nextUnreadNotice: PropTypes.shape({
title: PropTypes.string,
date: PropTypes.string,
body: PropTypes.string,
}),
noActiveNotices: PropTypes.bool,
}
static defaultProps = {
nextUnreadNotice: {},
}
state = {
atBottom: false,
}
componentDidMount () {
const { noActiveNotices, history } = this.props
if (noActiveNotices) {
history.push(INITIALIZE_SEED_PHRASE_ROUTE)
}
this.onScroll()
}
acceptTerms = async () => {
const {
completeOnboarding,
history,
isImportedKeyring,
markNoticeRead,
nextUnreadNotice,
} = this.props
const hasActiveNotices = await markNoticeRead(nextUnreadNotice)
if (!hasActiveNotices) {
if (isImportedKeyring) {
await completeOnboarding()
history.push(DEFAULT_ROUTE)
} else {
history.push(INITIALIZE_SEED_PHRASE_ROUTE)
}
} else {
this.setState({ atBottom: false }, () => this.onScroll())
}
}
onScroll = debounce(() => {
if (this.state.atBottom) {
return
}
const target = document.querySelector('.first-time-flow__markdown')
if (target) {
const { scrollTop, offsetHeight, scrollHeight } = target
const atBottom = scrollTop + offsetHeight >= scrollHeight
this.setState({ atBottom })
}
}, 25)
render () {
const { t } = this.context
const { isImportedKeyring, address, nextUnreadNotice: { title, body } } = this.props
const { atBottom } = this.state
return (
<div
className="first-time-flow__wrapper"
onScroll={this.onScroll}
>
<Identicon
className="first-time-flow__unique-image"
address={address}
diameter={70}
/>
<div className="first-time-flow__header">
{ title }
</div>
<Markdown
className="first-time-flow__markdown"
source={body}
skipHtml
/>
<Button
type="first-time"
className="first-time-flow__button"
onClick={atBottom && this.acceptTerms}
disabled={!atBottom}
>
{ t('accept') }
</Button>
<Breadcrumbs
className="first-time-flow__breadcrumbs"
total={isImportedKeyring ? 2 : 3}
currentIndex={1}
/>
</div>
)
}
}

@ -1,27 +0,0 @@
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { compose } from 'recompose'
import { markNoticeRead, setCompletedOnboarding } from '../../../../actions'
import Notices from './notices.component'
const mapStateToProps = ({ metamask }) => {
const { selectedAddress, nextUnreadNotice, noActiveNotices } = metamask
return {
address: selectedAddress,
nextUnreadNotice,
noActiveNotices,
}
}
const mapDispatchToProps = dispatch => {
return {
markNoticeRead: notice => dispatch(markNoticeRead(notice)),
completeOnboarding: () => dispatch(setCompletedOnboarding()),
}
}
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(Notices)

@ -2,10 +2,8 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import shuffle from 'lodash.shuffle' import shuffle from 'lodash.shuffle'
import Identicon from '../../../../identicon'
import Button from '../../../../button' import Button from '../../../../button'
import Breadcrumbs from '../../../../breadcrumbs' import { INITIALIZE_END_OF_FLOW_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../../routes'
import { DEFAULT_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../../routes'
import { exportAsFile } from '../../../../../../app/util' import { exportAsFile } from '../../../../../../app/util'
import { selectSeedWord, deselectSeedWord } from './confirm-seed-phrase.state' import { selectSeedWord, deselectSeedWord } from './confirm-seed-phrase.state'
@ -19,11 +17,8 @@ export default class ConfirmSeedPhrase extends PureComponent {
} }
static propTypes = { static propTypes = {
address: PropTypes.string,
completeOnboarding: PropTypes.func,
history: PropTypes.object, history: PropTypes.object,
onSubmit: PropTypes.func, onSubmit: PropTypes.func,
openBuyEtherModal: PropTypes.func,
seedPhrase: PropTypes.string, seedPhrase: PropTypes.string,
} }
@ -45,16 +40,14 @@ export default class ConfirmSeedPhrase extends PureComponent {
} }
handleSubmit = async () => { handleSubmit = async () => {
const { completeOnboarding, history, openBuyEtherModal } = this.props const { history } = this.props
if (!this.isValid()) { if (!this.isValid()) {
return return
} }
try { try {
await completeOnboarding() history.push(INITIALIZE_END_OF_FLOW_ROUTE)
history.push(DEFAULT_ROUTE)
openBuyEtherModal()
} catch (error) { } catch (error) {
console.error(error.message) console.error(error.message)
} }
@ -76,11 +69,11 @@ export default class ConfirmSeedPhrase extends PureComponent {
render () { render () {
const { t } = this.context const { t } = this.context
const { address, history } = this.props const { history } = this.props
const { selectedSeedWords, shuffledSeedWords, selectedSeedWordsHash } = this.state const { selectedSeedWords, shuffledSeedWords, selectedSeedWordsHash } = this.state
return ( return (
<div> <div className="confirm-seed-phrase">
<div className="confirm-seed-phrase__back-button"> <div className="confirm-seed-phrase__back-button">
<a <a
onClick={e => { onClick={e => {
@ -92,11 +85,6 @@ export default class ConfirmSeedPhrase extends PureComponent {
{`< Back`} {`< Back`}
</a> </a>
</div> </div>
<Identicon
className="first-time-flow__unique-image"
address={address}
diameter={70}
/>
<div className="first-time-flow__header"> <div className="first-time-flow__header">
{ t('confirmSecretBackupPhrase') } { t('confirmSecretBackupPhrase') }
</div> </div>
@ -143,18 +131,13 @@ export default class ConfirmSeedPhrase extends PureComponent {
} }
</div> </div>
<Button <Button
type="first-time" type="confirm"
className="first-time-flow__button" className="first-time-flow__button"
onClick={this.handleSubmit} onClick={this.handleSubmit}
disabled={!this.isValid()} disabled={!this.isValid()}
> >
{ t('confirm') } { t('confirm') }
</Button> </Button>
<Breadcrumbs
className="first-time-flow__breadcrumbs"
total={3}
currentIndex={2}
/>
</div> </div>
) )
} }

@ -1,12 +0,0 @@
import { connect } from 'react-redux'
import ConfirmSeedPhrase from './confirm-seed-phrase.component'
import { setCompletedOnboarding, showModal } from '../../../../../actions'
const mapDispatchToProps = dispatch => {
return {
completeOnboarding: () => dispatch(setCompletedOnboarding()),
openBuyEtherModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER'})),
}
}
export default connect(null, mapDispatchToProps)(ConfirmSeedPhrase)

@ -1 +1 @@
export { default } from './confirm-seed-phrase.container' export { default } from './confirm-seed-phrase.component'

@ -41,4 +41,8 @@
padding: 6px 18px; padding: 6px 18px;
} }
} }
button {
margin-top: 0xp;
}
} }

@ -1 +1 @@
export { default } from './seed-phrase.container' export { default } from './seed-phrase.component'

@ -26,11 +26,15 @@
min-width: 0; min-width: 0;
@media screen and (min-width: $break-large) { @media screen and (min-width: $break-large) {
margin-left: 48px; margin-left: 81px;
} }
@media screen and (max-width: $break-small) { @media screen and (max-width: $break-small) {
margin-top: 24px; margin-top: 24px;
} }
.first-time-flow__text-block {
color: #5A5A5A;
}
} }
} }

@ -50,4 +50,8 @@
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
} }
button {
margin-top: 0xp;
}
} }

@ -1,10 +1,8 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import Identicon from '../../../../identicon'
import LockIcon from '../../../../lock-icon' import LockIcon from '../../../../lock-icon'
import Button from '../../../../button' import Button from '../../../../button'
import Breadcrumbs from '../../../../breadcrumbs'
import { INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE } from '../../../../../routes' import { INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE } from '../../../../../routes'
import { exportAsFile } from '../../../../../../app/util' import { exportAsFile } from '../../../../../../app/util'
@ -14,7 +12,6 @@ export default class RevealSeedPhrase extends PureComponent {
} }
static propTypes = { static propTypes = {
address: PropTypes.string,
history: PropTypes.object, history: PropTypes.object,
seedPhrase: PropTypes.string, seedPhrase: PropTypes.string,
} }
@ -75,16 +72,10 @@ export default class RevealSeedPhrase extends PureComponent {
render () { render () {
const { t } = this.context const { t } = this.context
const { address } = this.props
const { isShowingSeedPhrase } = this.state const { isShowingSeedPhrase } = this.state
return ( return (
<div> <div className="reveal-seed-phrase">
<Identicon
className="first-time-flow__unique-image"
address={address}
diameter={70}
/>
<div className="seed-phrase__sections"> <div className="seed-phrase__sections">
<div className="seed-phrase__main"> <div className="seed-phrase__main">
<div className="first-time-flow__header"> <div className="first-time-flow__header">
@ -121,18 +112,13 @@ export default class RevealSeedPhrase extends PureComponent {
</div> </div>
</div> </div>
<Button <Button
type="first-time" type="confirm"
className="first-time-flow__button" className="first-time-flow__button"
onClick={this.handleNext} onClick={this.handleNext}
disabled={!isShowingSeedPhrase} disabled={!isShowingSeedPhrase}
> >
{ t('next') } { t('next') }
</Button> </Button>
<Breadcrumbs
className="first-time-flow__breadcrumbs"
total={3}
currentIndex={2}
/>
</div> </div>
) )
} }

@ -25,10 +25,23 @@ export default class SeedPhrase extends PureComponent {
} }
render () { render () {
const { address, seedPhrase } = this.props const { seedPhrase } = this.props
return ( return (
<div className="first-time-flow__wrapper"> <div className="first-time-flow__wrapper">
<div className="app-header__logo-container">
<img
className="app-header__metafox-logo app-header__metafox-logo--horizontal"
src="/images/logo/metamask-logo-horizontal.svg"
height={30}
/>
<img
className="app-header__metafox-logo app-header__metafox-logo--icon"
src="/images/logo/metamask-fox.svg"
height={42}
width={42}
/>
</div>
<Switch> <Switch>
<Route <Route
exact exact
@ -36,7 +49,6 @@ export default class SeedPhrase extends PureComponent {
render={props => ( render={props => (
<ConfirmSeedPhrase <ConfirmSeedPhrase
{ ...props } { ...props }
address={address}
seedPhrase={seedPhrase} seedPhrase={seedPhrase}
/> />
)} )}
@ -47,7 +59,6 @@ export default class SeedPhrase extends PureComponent {
render={props => ( render={props => (
<RevealSeedPhrase <RevealSeedPhrase
{ ...props } { ...props }
address={address}
seedPhrase={seedPhrase} seedPhrase={seedPhrase}
/> />
)} )}

@ -1,12 +0,0 @@
import { connect } from 'react-redux'
import SeedPhrase from './seed-phrase.component'
const mapStateToProps = state => {
const { metamask: { selectedAddress } } = state
return {
address: selectedAddress,
}
}
export default connect(mapStateToProps)(SeedPhrase)

@ -0,0 +1 @@
export { default } from './select-action.component'

@ -0,0 +1,87 @@
.select-action {
.app-header__logo-container {
width: 742px;
margin-top: 3%;
}
&__body {
display: flex;
flex-direction: column;
align-items: center;
}
&__body-header {
font-family: Roboto;
font-style: normal;
font-weight: normal;
line-height: 39px;
font-size: 28px;
text-align: center;
margin-top: 65px;
color: black;
}
&__select-buttons {
display: flex;
flex-direction: row;
margin-top: 40px;
}
&__select-button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
width: 269px;
height: 278px;
border: 1px solid #D8D8D8;
box-sizing: border-box;
border-radius: 10px;
margin-left: 22px;
.first-time-flow__button {
max-width: 221px;
height: 44px;
}
}
&__button-symbol {
color: #C4C4C4;
margin-top: 41px;
}
&__button-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 144px;
}
&__button-text-big {
font-family: Roboto;
font-style: normal;
font-weight: normal;
line-height: 28px;
font-size: 20px;
color: #000000;
margin-top: 12px;
text-align: center;
}
&__button-text-small {
font-family: Roboto;
font-style: normal;
font-weight: normal;
line-height: 20px;
font-size: 14px;
color: #7A7A7B;
margin-top: 10px;
}
button {
font-weight: 500;
width: 221px;
}
}

@ -0,0 +1,104 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Button from '../../../button'
import {
INITIALIZE_CREATE_PASSWORD_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
INITIALIZE_UNIQUE_IMAGE_ROUTE,
} from '../../../../routes'
export default class SelectAction extends PureComponent {
static propTypes = {
history: PropTypes.object,
isInitialized: PropTypes.bool,
}
static contextTypes = {
t: PropTypes.func,
}
componentDidMount () {
const { history, isInitialized } = this.props
if (isInitialized) {
history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
}
}
handleCreate = () => {
this.props.history.push(INITIALIZE_CREATE_PASSWORD_ROUTE)
}
handleImport = () => {
this.props.history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE)
}
render () {
const { t } = this.context
return (
<div className="select-action">
<div className="app-header__logo-container">
<img
className="app-header__metafox-logo app-header__metafox-logo--horizontal"
src="/images/logo/metamask-logo-horizontal.svg"
height={30}
/>
<img
className="app-header__metafox-logo app-header__metafox-logo--icon"
src="/images/logo/metamask-fox.svg"
height={42}
width={42}
/>
</div>
<div className="select-action__wrapper">
<div className="select-action__body">
<div className="select-action__body-header">
{ t('newToMetaMask') }
</div>
<div className="select-action__select-buttons">
<div className="select-action__select-button">
<div className="select-action__button-content">
<div className="select-action__button-symbol">
<img src="/images/download-alt.svg" />
</div>
<div className="select-action__button-text-big">
{ t('noAlreadyHaveSeed') }
</div>
</div>
<Button
type="primary"
className="first-time-flow__button"
onClick={this.handleImport}
>
{ t('importWallet') }
</Button>
</div>
<div className="select-action__select-button">
<div className="select-action__button-content">
<div className="select-action__button-symbol">
<img src="/images/thin-plus.svg" />
</div>
<div className="select-action__button-text-big">
{ t('letsGoSetUp') }
</div>
</div>
<Button
type="confirm"
className="first-time-flow__button"
onClick={this.handleCreate}
>
{ t('createAWallet') }
</Button>
</div>
</div>
</div>
</div>
</div>
)
}
}

@ -1,43 +1,42 @@
.welcome-page { .welcome-page {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: flex-start;
align-items: center; align-items: center;
width: 400px; max-width: 442px;
padding: 0 18px; padding: 0 18px;
color: black;
&__wrapper { &__wrapper {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: flex-start;
height: 100%; height: 100%;
margin-top: 110px;
} }
&__header { &__header {
font-size: 1.5rem; font-size: 28px;
margin-bottom: 14px; margin-bottom: 22px;
margin-top: 50px;
} }
&__description { &__description {
text-align: center; text-align: center;
div {
font-size: 16px;
}
@media screen and (max-width: 575px) { @media screen and (max-width: 575px) {
font-size: .9rem; font-size: .9rem;
} }
} }
&__button { .first-time-flow__button {
height: 54px; width: 184px;
width: 198px;
font-family: Roboto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .14);
color: $white;
font-size: 1.25rem;
font-weight: 500; font-weight: 500;
text-transform: uppercase; margin-top: 44px;
margin: 35px 0 14px;
transition: 200ms ease-in-out;
background-color: rgba(247, 134, 28, .9);
} }
} }

@ -3,7 +3,7 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Mascot from '../../../mascot' import Mascot from '../../../mascot'
import Button from '../../../button' import Button from '../../../button'
import { INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_NOTICE_ROUTE } from '../../../../routes' import { INITIALIZE_SELECT_ACTION_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE } from '../../../../routes'
export default class Welcome extends PureComponent { export default class Welcome extends PureComponent {
static propTypes = { static propTypes = {
@ -25,12 +25,12 @@ export default class Welcome extends PureComponent {
const { history, isInitialized } = this.props const { history, isInitialized } = this.props
if (isInitialized) { if (isInitialized) {
history.push(INITIALIZE_NOTICE_ROUTE) history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
} }
} }
handleContinue = () => { handleContinue = () => {
this.props.history.push(INITIALIZE_CREATE_PASSWORD_ROUTE) this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE)
} }
render () { render () {
@ -41,22 +41,22 @@ export default class Welcome extends PureComponent {
<div className="welcome-page"> <div className="welcome-page">
<Mascot <Mascot
animationEventEmitter={this.animationEventEmitter} animationEventEmitter={this.animationEventEmitter}
width="225" width="125"
height="225" height="125"
/> />
<div className="welcome-page__header"> <div className="welcome-page__header">
{ t('welcome') } { t('welcome') }
</div> </div>
<div className="welcome-page__description"> <div className="welcome-page__description">
<div>{ t('metamaskDescription') }</div> <div>{ t('metamaskDescription') }</div>
<div>{ t('holdEther') }</div> <div>{ t('happyToSeeYou') }</div>
</div> </div>
<Button <Button
type="first-time" type="confirm"
className="first-time-flow__button" className="first-time-flow__button"
onClick={this.handleContinue} onClick={this.handleContinue}
> >
{ t('continue') } { t('getStarted') }
</Button> </Button>
</div> </div>
</div> </div>

@ -10,15 +10,12 @@ import {
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE,
RESTORE_VAULT_ROUTE, RESTORE_VAULT_ROUTE,
CONFIRM_TRANSACTION_ROUTE, CONFIRM_TRANSACTION_ROUTE,
NOTICE_ROUTE,
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
} from '../../../routes' } from '../../../routes'
export default class Home extends PureComponent { export default class Home extends PureComponent {
static propTypes = { static propTypes = {
history: PropTypes.object, history: PropTypes.object,
noActiveNotices: PropTypes.bool,
lostAccounts: PropTypes.array,
forgottenPassword: PropTypes.bool, forgottenPassword: PropTypes.bool,
seedWords: PropTypes.string, seedWords: PropTypes.string,
suggestedTokens: PropTypes.object, suggestedTokens: PropTypes.object,
@ -45,18 +42,11 @@ export default class Home extends PureComponent {
render () { render () {
const { const {
noActiveNotices,
lostAccounts,
forgottenPassword, forgottenPassword,
seedWords, seedWords,
providerRequests, providerRequests,
} = this.props } = this.props
// notices
if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) {
return <Redirect to={{ pathname: NOTICE_ROUTE }} />
}
// seed words // seed words
if (seedWords) { if (seedWords) {
return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }}/> return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }}/>

@ -25,7 +25,9 @@ const INITIALIZE_IMPORT_ACCOUNT_ROUTE = '/initialize/create-password/import-acco
const INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE = '/initialize/create-password/import-with-seed-phrase' const INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE = '/initialize/create-password/import-with-seed-phrase'
const INITIALIZE_UNIQUE_IMAGE_ROUTE = '/initialize/create-password/unique-image' const INITIALIZE_UNIQUE_IMAGE_ROUTE = '/initialize/create-password/unique-image'
const INITIALIZE_NOTICE_ROUTE = '/initialize/notice' const INITIALIZE_NOTICE_ROUTE = '/initialize/notice'
const INITIALIZE_SELECT_ACTION_ROUTE = '/initialize/select-action'
const INITIALIZE_SEED_PHRASE_ROUTE = '/initialize/seed-phrase' const INITIALIZE_SEED_PHRASE_ROUTE = '/initialize/seed-phrase'
const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow'
const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm' const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm'
const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction' const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction'
@ -64,8 +66,10 @@ module.exports = {
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
INITIALIZE_UNIQUE_IMAGE_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE,
INITIALIZE_NOTICE_ROUTE, INITIALIZE_NOTICE_ROUTE,
INITIALIZE_SELECT_ACTION_ROUTE,
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE,
INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE, INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE,
INITIALIZE_END_OF_FLOW_ROUTE,
CONFIRM_TRANSACTION_ROUTE, CONFIRM_TRANSACTION_ROUTE,
CONFIRM_SEND_ETHER_PATH, CONFIRM_SEND_ETHER_PATH,
CONFIRM_SEND_TOKEN_PATH, CONFIRM_SEND_TOKEN_PATH,

Loading…
Cancel
Save