diff options
author | Dan J Miller <danjm.com@gmail.com> | 2019-02-27 22:46:41 +0800 |
---|---|---|
committer | Whymarrh Whitby <whymarrh.whitby@gmail.com> | 2019-02-27 22:46:41 +0800 |
commit | cb2698d20eae273d372d03e11fa765a91c330c17 (patch) | |
tree | f23b852b074dad770bfd970799220b63c40459b7 | |
parent | a2320c76fef084b7ec01839ab9c17b474839b3c0 (diff) | |
download | tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.tar tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.tar.gz tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.tar.bz2 tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.tar.lz tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.tar.xz tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.tar.zst tangerine-wallet-browser-cb2698d20eae273d372d03e11fa765a91c330c17.zip |
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
41 files changed, 763 insertions, 528 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 2d0098a24..d4860bbfb 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -92,6 +92,15 @@ "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": { "message": "Amount" }, @@ -239,6 +248,9 @@ "confirmTransaction": { "message": "Confirm Transaction" }, + "congratulations": { + "message": "Congratulations" + }, "connectHardwareWallet": { "message": "Connect Hardware Wallet" }, @@ -326,6 +338,9 @@ "createAccount": { "message": "Create Account" }, + "createAWallet": { + "message": "Create a Wallet" + }, "createDen": { "message": "Create" }, @@ -448,6 +463,24 @@ "encryptNewDen": { "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": { "message": "ENS name not found" }, @@ -581,10 +614,16 @@ "getHelp": { "message": "Get Help." }, + "getStarted": { + "message": "Get Started" + }, "greaterThanMin": { "message": "must be greater than or equal to $1.", "description": "helper for inputting hex as decimal input" }, + "happyToSeeYou": { + "message": "We’re happy to see you." + }, "hardware": { "message": "hardware" }, @@ -647,6 +686,9 @@ "importDen": { "message": "Import Existing DEN" }, + "importWallet": { + "message": "Import Wallet" + }, "imported": { "message": "Imported", "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.", "description": "helper for inputting hex as decimal input" }, + "letsGoSetUp": { + "message": "Yes, let’s get set up!" + }, "likeToAddTokens": { "message": "Would you like to add these tokens?" }, @@ -777,7 +822,7 @@ "message": "Message" }, "metamaskDescription": { - "message": "MetaMask is a secure identity vault for Ethereum." + "message": "Connecting you to Ethereum and the Decentralized Web." }, "metamaskSeedWords": { "message": "MetaMask Seed Words" @@ -848,6 +893,21 @@ "newNetwork": { "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": { "message": "New RPC URL" }, @@ -1609,6 +1669,9 @@ "yourUniqueAccountImageDescription2": { "message": "You’ll see this image everytime you need to confirm a transaction." }, + "yourUniqueAccountImageDescription3": { + "message": "MetaMask will never ask for your seed phrase!" + }, "zeroGasPriceOnSpeedUpError": { "message":"Zero gas price on speed up" } diff --git a/app/images/download-alt.svg b/app/images/download-alt.svg new file mode 100644 index 000000000..11c54fd66 --- /dev/null +++ b/app/images/download-alt.svg @@ -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> diff --git a/app/images/sleuth.svg b/app/images/sleuth.svg new file mode 100644 index 000000000..f4ac8c787 --- /dev/null +++ b/app/images/sleuth.svg @@ -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> diff --git a/app/images/thin-plus.svg b/app/images/thin-plus.svg new file mode 100644 index 000000000..f46c72a4a --- /dev/null +++ b/app/images/thin-plus.svg @@ -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> diff --git a/test/e2e/beta/from-import-beta-ui.spec.js b/test/e2e/beta/from-import-beta-ui.spec.js index d4a380584..ad3e8b1fe 100644 --- a/test/e2e/beta/from-import-beta-ui.spec.js +++ b/test/e2e/beta/from-import-beta-ui.spec.js @@ -65,13 +65,16 @@ describe('Using MetaMask with an existing account', function () { beforeEach(async function () { await driver.executeScript( + 'window.origFetch = window.fetch.bind(window);' + 'window.fetch = ' + '(...args) => { ' + 'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' + '(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' + - 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } ' + - 'return window.fetch(...args); }' + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' + + '(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', () => { 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() await delay(largeDelayMs) }) - it('imports a seed phrase', async () => { - const [seedPhrase] = await findElements(driver, By.xpath(`//a[contains(text(), 'Import with seed phrase')]`)) - await seedPhrase.click() - await delay(regularDelayMs) + it('clicks the "Import Wallet" option', async () => { + const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`)) + customRpcButton.click() + await delay(largeDelayMs) + }) + it('imports a seed phrase', async () => { const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea')) await seedTextArea.sendKeys(testSeedPhrase) await delay(regularDelayMs) @@ -114,39 +120,25 @@ describe('Using MetaMask with an existing account', function () { const [confirmPassword] = await findElements(driver, By.id('confirm-password')) 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')]`)) await importButton.click() await delay(regularDelayMs) }) - it('clicks through the ToS', async () => { - // 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 + it('clicks through the security warning screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`)) const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) }) - it('clicks through the phishing notice', async () => { - // phishing notice - 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')) - await nextScreen.click() + it('clicks through the success screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`)) + const doneButton = await findElement(driver, By.css('button.first-time-flow__button')) + await doneButton.click() await delay(regularDelayMs) }) }) diff --git a/test/e2e/beta/metamask-beta-responsive-ui.spec.js b/test/e2e/beta/metamask-beta-responsive-ui.spec.js index 099ed944e..781811d6d 100644 --- a/test/e2e/beta/metamask-beta-responsive-ui.spec.js +++ b/test/e2e/beta/metamask-beta-responsive-ui.spec.js @@ -80,134 +80,111 @@ describe('MetaMask', function () { }) describe('Going through the first time flow', () => { - it('clicks the continue button on the welcome screen', async () => { - const welcomeScreenBtn = await findElement(driver, By.css('.welcome-page .first-time-flow__button')) - welcomeScreenBtn.click() - await delay(largeDelayMs) - }) - - it('accepts a secure password', async () => { - 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 button = await findElement(driver, By.css('.first-time-flow__form button')) - - await passwordBox.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 () => { - // 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) - }) - - it('clicks through the phishing notice', async () => { - // phishing notice - 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')) - await nextScreen.click() - await delay(regularDelayMs) - }) - - let seedPhrase - - it('reveals the seed phrase', async () => { - const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') - await driver.wait(until.elementLocated(byRevealButton, 10000)) - const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) - await revealSeedPhraseButton.click() - await delay(regularDelayMs) - - seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText() - assert.equal(seedPhrase.split(' ').length, 12) - await delay(regularDelayMs) - - const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) - await nextScreen.click() - await delay(regularDelayMs) - }) - - async function clickWordAndWait (word) { - const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]` - const word0 = await findElement(driver, By.xpath(xpath), 10000) - - await word0.click() - await delay(tinyDelayMs) - } + it('clicks the continue button on the welcome screen', async () => { + await findElement(driver, By.css('.welcome-page__header')) + const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button')) + welcomeScreenBtn.click() + await delay(largeDelayMs) + }) - async function retypeSeedPhrase (words, wasReloaded, count = 0) { - try { - if (wasReloaded) { - const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') - await driver.wait(until.elementLocated(byRevealButton, 10000)) - const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) - await revealSeedPhraseButton.click() - await delay(regularDelayMs) - - const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) - await nextScreen.click() - await delay(regularDelayMs) - } + 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 () => { + 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 button = await findElement(driver, By.css('.first-time-flow__form button')) + + await passwordBox.sendKeys('correct horse battery staple') + await passwordBoxConfirm.sendKeys('correct horse battery staple') + + const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox')) + await tosCheckBox.click() + + await button.click() + await delay(regularDelayMs) + }) + + it('clicks through the security warning screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`)) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) + await nextScreen.click() + await delay(regularDelayMs) + }) + + let seedPhrase + + it('reveals the seed phrase', async () => { + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') + await driver.wait(until.elementLocated(byRevealButton, 10000)) + const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) + await revealSeedPhraseButton.click() + await delay(regularDelayMs) + + seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText() + assert.equal(seedPhrase.split(' ').length, 12) + await delay(regularDelayMs) + + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) + await nextScreen.click() + await delay(regularDelayMs) + }) + + async function clickWordAndWait (word) { + const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]` + const word0 = await findElement(driver, By.xpath(xpath), 10000) + + await word0.click() + await delay(tinyDelayMs) + } - for (let i = 0; i < 12; i++) { - await clickWordAndWait(words[i]) - } - } catch (e) { - if (count > 2) { - throw e - } else { - await loadExtension(driver, extensionId) - await retypeSeedPhrase(words, true, count + 1) + async function retypeSeedPhrase (words, wasReloaded, count = 0) { + try { + if (wasReloaded) { + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') + await driver.wait(until.elementLocated(byRevealButton, 10000)) + const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) + await revealSeedPhraseButton.click() + await delay(regularDelayMs) + + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) + await nextScreen.click() + await delay(regularDelayMs) + } + + for (let i = 0; i < 12; i++) { + await clickWordAndWait(words[i]) + } + } catch (e) { + if (count > 2) { + throw e + } else { + await loadExtension(driver, extensionId) + await retypeSeedPhrase(words, true, count + 1) + } } } - } - it('can retype the seed phrase', async () => { - const words = seedPhrase.split(' ') + it('can retype the seed phrase', async () => { + const words = seedPhrase.split(' ') - await retypeSeedPhrase(words) - await delay(regularDelayMs) + await retypeSeedPhrase(words) - const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) - await confirm.click() - await delay(regularDelayMs) - }) + const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) + await confirm.click() + await delay(regularDelayMs) + }) - it('clicks through the deposit modal', async () => { - const byBuyModal = By.css('span .modal') - const buyModal = await driver.wait(until.elementLocated(byBuyModal)) - const closeModal = await findElement(driver, By.css('.page-container__header-close')) - await closeModal.click() - await driver.wait(until.stalenessOf(buyModal)) - await delay(regularDelayMs) + it('clicks through the success screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`)) + const doneButton = await findElement(driver, By.css('button.first-time-flow__button')) + await doneButton.click() + await delay(regularDelayMs) + }) }) - }) describe('Show account information', () => { it('show account details dropdown menu', async () => { diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js index 798cce372..26aa50a43 100644 --- a/test/e2e/beta/metamask-beta-ui.spec.js +++ b/test/e2e/beta/metamask-beta-ui.spec.js @@ -102,11 +102,18 @@ describe('MetaMask', function () { describe('Going through the first time flow', () => { 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() 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 () => { 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')) @@ -114,43 +121,16 @@ describe('MetaMask', function () { await passwordBox.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 () => { - // 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) - }) + const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox')) + await tosCheckBox.click() - 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 button.click() await delay(regularDelayMs) }) - it('clicks through the phishing notice', async () => { - // phishing notice - const noticeElement = await driver.findElement(By.css('.first-time-flow__markdown')) - await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) - await delay(regularDelayMs) + it('clicks through the security warning screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`)) const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) @@ -219,12 +199,10 @@ describe('MetaMask', function () { await delay(regularDelayMs) }) - it('clicks through the deposit modal', async () => { - const byBuyModal = By.css('span .modal') - const buyModal = await driver.wait(until.elementLocated(byBuyModal)) - const closeModal = await findElement(driver, By.css('.page-container__header-close')) - await closeModal.click() - await driver.wait(until.stalenessOf(buyModal)) + it('clicks through the success screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`)) + const doneButton = await findElement(driver, By.css('button.first-time-flow__button')) + await doneButton.click() await delay(regularDelayMs) }) }) diff --git a/ui/app/components/pages/first-time-flow/create-password/create-password.component.js b/ui/app/components/pages/first-time-flow/create-password/create-password.component.js index 69b1e549f..7cca82ca6 100644 --- a/ui/app/components/pages/first-time-flow/create-password/create-password.component.js +++ b/ui/app/components/pages/first-time-flow/create-password/create-password.component.js @@ -8,13 +8,13 @@ import { INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE, - INITIALIZE_NOTICE_ROUTE, } from '../../../../routes' export default class CreatePassword extends PureComponent { static propTypes = { history: PropTypes.object, isInitialized: PropTypes.bool, + isImportedKeyring: PropTypes.bool, onCreateNewAccount: PropTypes.func, onCreateNewAccountFromSeed: PropTypes.func, } @@ -23,17 +23,38 @@ export default class CreatePassword extends PureComponent { const { isInitialized, history } = this.props if (isInitialized) { - history.push(INITIALIZE_NOTICE_ROUTE) + history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE) } } render () { - const { onCreateNewAccount, onCreateNewAccountFromSeed } = this.props + const { onCreateNewAccount, onCreateNewAccountFromSeed, isImportedKeyring } = this.props return ( <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> - <Route exact path={INITIALIZE_UNIQUE_IMAGE_ROUTE} component={UniqueImage} /> + <Route exact + path={INITIALIZE_UNIQUE_IMAGE_ROUTE} + render={props => ( + <UniqueImage + { ...props } + isImportedKeyring={isImportedKeyring} + /> + )} + /> <Route exact path={INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE} diff --git a/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js b/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js index 6f8ab031a..2e99147bb 100644 --- a/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js +++ b/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js @@ -3,10 +3,9 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import TextField from '../../../../text-field' import Button from '../../../../button' -import Breadcrumbs from '../../../../breadcrumbs' import { - INITIALIZE_CREATE_PASSWORD_ROUTE, - INITIALIZE_NOTICE_ROUTE, + INITIALIZE_SELECT_ACTION_ROUTE, + INITIALIZE_UNIQUE_IMAGE_ROUTE, } from '../../../../../routes' export default class ImportWithSeedPhrase extends PureComponent { @@ -26,6 +25,7 @@ export default class ImportWithSeedPhrase extends PureComponent { seedPhraseError: '', passwordError: '', confirmPasswordError: '', + termsChecked: false, } parseSeedPhrase = (seedPhrase) => { @@ -104,7 +104,7 @@ export default class ImportWithSeedPhrase extends PureComponent { try { await onSubmit(password, seedPhrase) - history.push(INITIALIZE_NOTICE_ROUTE) + history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE) } catch (error) { this.setState({ seedPhraseError: error.message }) } @@ -131,20 +131,26 @@ export default class ImportWithSeedPhrase extends PureComponent { return !passwordError && !confirmPasswordError && !seedPhraseError } + toggleTermsCheck = () => { + this.setState((prevState) => ({ + termsChecked: !prevState.termsChecked, + })) + } + render () { const { t } = this.context - const { seedPhraseError, passwordError, confirmPasswordError } = this.state + const { seedPhraseError, passwordError, confirmPasswordError, termsChecked } = this.state return ( <form className="first-time-flow__form" onSubmit={this.handleImport} > - <div> + <div className="first-time-flow__create-back"> <a onClick={e => { e.preventDefault() - this.props.history.push(INITIALIZE_CREATE_PASSWORD_ROUTE) + this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE) }} href="#" > @@ -197,19 +203,22 @@ export default class ImportWithSeedPhrase extends PureComponent { margin="normal" 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 - type="first-time" + type="confirm" className="first-time-flow__button" - disabled={!this.isValid()} + disabled={!this.isValid() || !termsChecked} onClick={this.handleImport} > { t('import') } </Button> - <Breadcrumbs - className="first-time-flow__breadcrumbs" - total={2} - currentIndex={0} - /> </form> ) } diff --git a/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js b/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js index 54f8c1a70..b82cba0c5 100644 --- a/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js +++ b/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js @@ -1,10 +1,10 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' -import Breadcrumbs from '../../../../breadcrumbs' import Button from '../../../../button' import { INITIALIZE_UNIQUE_IMAGE_ROUTE, INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, + INITIALIZE_SELECT_ACTION_ROUTE, } from '../../../../../routes' import TextField from '../../../../text-field' @@ -23,6 +23,7 @@ export default class NewAccount extends PureComponent { confirmPassword: '', passwordError: '', confirmPasswordError: '', + termsChecked: false, } isValid () { @@ -111,12 +112,29 @@ export default class NewAccount extends PureComponent { history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE) } + toggleTermsCheck = () => { + this.setState((prevState) => ({ + termsChecked: !prevState.termsChecked, + })) + } + render () { const { t } = this.context - const { password, confirmPassword, passwordError, confirmPasswordError } = this.state + const { password, confirmPassword, passwordError, confirmPasswordError, termsChecked } = this.state return ( <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"> { t('createPassword') } </div> @@ -151,27 +169,23 @@ export default class NewAccount extends PureComponent { fullWidth 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 - type="first-time" + type="confirm" className="first-time-flow__button" - disabled={!this.isValid()} + disabled={!this.isValid() || !termsChecked} onClick={this.handleCreate} > { t('create') } </Button> </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> ) } diff --git a/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js b/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js index 41a566f0a..fa76074f5 100644 --- a/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js +++ b/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js @@ -1,9 +1,7 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' -import Identicon from '../../../../identicon' -import Breadcrumbs from '../../../../breadcrumbs' 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 { static contextTypes = { @@ -11,42 +9,43 @@ export default class UniqueImageScreen extends PureComponent { } static propTypes = { - address: PropTypes.string, history: PropTypes.object, + isImportedKeyring: PropTypes.bool, } render () { const { t } = this.context - const { address, history } = this.props + const { history, isImportedKeyring } = this.props return ( <div> - <Identicon - className="first-time-flow__unique-image" - address={address} - diameter={70} + <img + src="/images/sleuth.svg" + height={42} + width={42} /> <div className="first-time-flow__header"> - { t('yourUniqueAccountImage') } + { t('protectYourKeys') } </div> <div className="first-time-flow__text-block"> - { t('yourUniqueAccountImageDescription1') } + { t('protectYourKeysMessage1') } </div> <div className="first-time-flow__text-block"> - { t('yourUniqueAccountImageDescription2') } + { t('protectYourKeysMessage2') } </div> <Button - type="first-time" + type="confirm" 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') } </Button> - <Breadcrumbs - className="first-time-flow__breadcrumbs" - total={3} - currentIndex={0} - /> </div> ) } diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js new file mode 100644 index 000000000..2ca5fd8ec --- /dev/null +++ b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js @@ -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> + ) + } +} diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js new file mode 100644 index 000000000..ffe2c0efb --- /dev/null +++ b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js @@ -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) diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/index.js b/ui/app/components/pages/first-time-flow/end-of-flow/index.js new file mode 100644 index 000000000..b0643d155 --- /dev/null +++ b/ui/app/components/pages/first-time-flow/end-of-flow/index.js @@ -0,0 +1 @@ +export { default } from './end-of-flow.container' diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/index.scss b/ui/app/components/pages/first-time-flow/end-of-flow/index.scss new file mode 100644 index 000000000..5f5cc5991 --- /dev/null +++ b/ui/app/components/pages/first-time-flow/end-of-flow/index.scss @@ -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; + } +}
\ No newline at end of file diff --git a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js index 9e8bce2c8..43f792e06 100644 --- a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js +++ b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js @@ -5,7 +5,6 @@ import { DEFAULT_ROUTE, LOCK_ROUTE, INITIALIZE_WELCOME_ROUTE, - INITIALIZE_NOTICE_ROUTE, INITIALIZE_UNLOCK_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE, } from '../../../../routes' @@ -15,7 +14,6 @@ export default class FirstTimeFlowSwitch extends PureComponent { completedOnboarding: PropTypes.bool, isInitialized: PropTypes.bool, isUnlocked: PropTypes.bool, - noActiveNotices: PropTypes.bool, seedPhrase: PropTypes.string, } @@ -24,7 +22,6 @@ export default class FirstTimeFlowSwitch extends PureComponent { completedOnboarding, isInitialized, isUnlocked, - noActiveNotices, seedPhrase, } = this.props @@ -44,10 +41,6 @@ export default class FirstTimeFlowSwitch extends PureComponent { return <Redirect to={{ pathname: INITIALIZE_UNLOCK_ROUTE }} /> } - if (!noActiveNotices) { - return <Redirect to={{ pathname: INITIALIZE_NOTICE_ROUTE }} /> - } - if (seedPhrase) { return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }} /> } diff --git a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js index 8b7a74880..e44c216c0 100644 --- a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js +++ b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js @@ -6,14 +6,12 @@ const mapStateToProps = ({ metamask }) => { completedOnboarding, isInitialized, isUnlocked, - noActiveNotices, } = metamask return { completedOnboarding, isInitialized, isUnlocked, - noActiveNotices, } } diff --git a/ui/app/components/pages/first-time-flow/first-time-flow.component.js b/ui/app/components/pages/first-time-flow/first-time-flow.component.js index cde077803..82308dda2 100644 --- a/ui/app/components/pages/first-time-flow/first-time-flow.component.js +++ b/ui/app/components/pages/first-time-flow/first-time-flow.component.js @@ -3,17 +3,19 @@ import PropTypes from 'prop-types' import { Switch, Route } from 'react-router-dom' import FirstTimeFlowSwitch from './first-time-flow-switch' import Welcome from './welcome' +import SelectAction from './select-action' +import EndOfFlow from './end-of-flow' import Unlock from '../unlock-page' import CreatePassword from './create-password' -import Notices from './notices' import SeedPhrase from './seed-phrase' import { DEFAULT_ROUTE, INITIALIZE_WELCOME_ROUTE, INITIALIZE_CREATE_PASSWORD_ROUTE, - INITIALIZE_NOTICE_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_UNLOCK_ROUTE, + INITIALIZE_SELECT_ACTION_ROUTE, + INITIALIZE_END_OF_FLOW_ROUTE, } from '../../../routes' export default class FirstTimeFlow extends PureComponent { @@ -24,7 +26,6 @@ export default class FirstTimeFlow extends PureComponent { history: PropTypes.object, isInitialized: PropTypes.bool, isUnlocked: PropTypes.bool, - noActiveNotices: PropTypes.bool, unlockAccount: PropTypes.func, } @@ -70,14 +71,12 @@ export default class FirstTimeFlow extends PureComponent { } handleUnlock = async password => { - const { unlockAccount, history, noActiveNotices } = this.props + const { unlockAccount, history } = this.props try { const seedPhrase = await unlockAccount(password) this.setState({ seedPhrase }, () => { - noActiveNotices - ? history.push(INITIALIZE_SEED_PHRASE_ROUTE) - : history.push(INITIALIZE_NOTICE_ROUTE) + history.push(INITIALIZE_SEED_PHRASE_ROUTE) }) } catch (error) { throw new Error(error.message) @@ -100,26 +99,21 @@ export default class FirstTimeFlow extends PureComponent { )} /> <Route - exact - path={INITIALIZE_NOTICE_ROUTE} - render={props => ( - <Notices - { ...props } - isImportedKeyring={isImportedKeyring} - /> - )} - /> - <Route path={INITIALIZE_CREATE_PASSWORD_ROUTE} render={props => ( <CreatePassword { ...props } + isImportedKeyring={isImportedKeyring} onCreateNewAccount={this.handleCreateNewAccount} onCreateNewAccountFromSeed={this.handleImportWithSeedPhrase} /> )} /> <Route + path={INITIALIZE_SELECT_ACTION_ROUTE} + component={SelectAction} + /> + <Route path={INITIALIZE_UNLOCK_ROUTE} render={props => ( <Unlock @@ -130,6 +124,11 @@ export default class FirstTimeFlow extends PureComponent { /> <Route exact + path={INITIALIZE_END_OF_FLOW_ROUTE} + component={EndOfFlow} + /> + <Route + exact path={INITIALIZE_WELCOME_ROUTE} component={Welcome} /> diff --git a/ui/app/components/pages/first-time-flow/first-time-flow.container.js b/ui/app/components/pages/first-time-flow/first-time-flow.container.js index 782eddb74..1419dd59f 100644 --- a/ui/app/components/pages/first-time-flow/first-time-flow.container.js +++ b/ui/app/components/pages/first-time-flow/first-time-flow.container.js @@ -7,13 +7,12 @@ import { } from '../../../actions' const mapStateToProps = state => { - const { metamask: { completedOnboarding, isInitialized, isUnlocked, noActiveNotices } } = state + const { metamask: { completedOnboarding, isInitialized, isUnlocked } } = state return { completedOnboarding, isInitialized, isUnlocked, - noActiveNotices, } } diff --git a/ui/app/components/pages/first-time-flow/index.scss b/ui/app/components/pages/first-time-flow/index.scss index e3aca0694..e14d57f58 100644 --- a/ui/app/components/pages/first-time-flow/index.scss +++ b/ui/app/components/pages/first-time-flow/index.scss @@ -1,18 +1,28 @@ @import './welcome/index'; +@import './select-action/index'; + @import './seed-phrase/index'; +@import './end-of-flow/index'; + .first-time-flow { width: 100%; background-color: $white; + display: flex; + justify-content: center; &__wrapper { @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) { - padding: 36px; + .app-header__metafox-logo { + margin-bottom: 40px; } } @@ -21,9 +31,14 @@ flex-direction: column; } + &__create-back { + margin-bottom: 16px; + } + &__header { font-size: 2.5rem; margin-bottom: 24px; + color: black; } &__subheader { @@ -86,6 +101,7 @@ &__text-block { margin-bottom: 24px; + color: black; @media screen and (max-width: $break-small) { margin-bottom: 16px; @@ -95,5 +111,42 @@ &__button { 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; } } diff --git a/ui/app/components/pages/first-time-flow/notices/index.js b/ui/app/components/pages/first-time-flow/notices/index.js deleted file mode 100644 index 024daaa68..000000000 --- a/ui/app/components/pages/first-time-flow/notices/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './notices.container' diff --git a/ui/app/components/pages/first-time-flow/notices/notices.component.js b/ui/app/components/pages/first-time-flow/notices/notices.component.js deleted file mode 100644 index fefaedd6f..000000000 --- a/ui/app/components/pages/first-time-flow/notices/notices.component.js +++ /dev/null @@ -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> - ) - } -} diff --git a/ui/app/components/pages/first-time-flow/notices/notices.container.js b/ui/app/components/pages/first-time-flow/notices/notices.container.js deleted file mode 100644 index c65c5b7de..000000000 --- a/ui/app/components/pages/first-time-flow/notices/notices.container.js +++ /dev/null @@ -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) diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js index bc0f73a27..b5c4bf463 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js +++ b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js @@ -2,10 +2,8 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' import shuffle from 'lodash.shuffle' -import Identicon from '../../../../identicon' import Button from '../../../../button' -import Breadcrumbs from '../../../../breadcrumbs' -import { DEFAULT_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../../routes' +import { INITIALIZE_END_OF_FLOW_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../../routes' import { exportAsFile } from '../../../../../../app/util' import { selectSeedWord, deselectSeedWord } from './confirm-seed-phrase.state' @@ -19,11 +17,8 @@ export default class ConfirmSeedPhrase extends PureComponent { } static propTypes = { - address: PropTypes.string, - completeOnboarding: PropTypes.func, history: PropTypes.object, onSubmit: PropTypes.func, - openBuyEtherModal: PropTypes.func, seedPhrase: PropTypes.string, } @@ -45,16 +40,14 @@ export default class ConfirmSeedPhrase extends PureComponent { } handleSubmit = async () => { - const { completeOnboarding, history, openBuyEtherModal } = this.props + const { history } = this.props if (!this.isValid()) { return } try { - await completeOnboarding() - history.push(DEFAULT_ROUTE) - openBuyEtherModal() + history.push(INITIALIZE_END_OF_FLOW_ROUTE) } catch (error) { console.error(error.message) } @@ -76,11 +69,11 @@ export default class ConfirmSeedPhrase extends PureComponent { render () { const { t } = this.context - const { address, history } = this.props + const { history } = this.props const { selectedSeedWords, shuffledSeedWords, selectedSeedWordsHash } = this.state return ( - <div> + <div className="confirm-seed-phrase"> <div className="confirm-seed-phrase__back-button"> <a onClick={e => { @@ -92,11 +85,6 @@ export default class ConfirmSeedPhrase extends PureComponent { {`< Back`} </a> </div> - <Identicon - className="first-time-flow__unique-image" - address={address} - diameter={70} - /> <div className="first-time-flow__header"> { t('confirmSecretBackupPhrase') } </div> @@ -143,18 +131,13 @@ export default class ConfirmSeedPhrase extends PureComponent { } </div> <Button - type="first-time" + type="confirm" className="first-time-flow__button" onClick={this.handleSubmit} disabled={!this.isValid()} > { t('confirm') } </Button> - <Breadcrumbs - className="first-time-flow__breadcrumbs" - total={3} - currentIndex={2} - /> </div> ) } diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js deleted file mode 100644 index 5fa2bec1e..000000000 --- a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js +++ /dev/null @@ -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) diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js index beb53b383..c7b511503 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js +++ b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js @@ -1 +1 @@ -export { default } from './confirm-seed-phrase.container' +export { default } from './confirm-seed-phrase.component' diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss index e0444571f..93137618c 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss +++ b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss @@ -41,4 +41,8 @@ padding: 6px 18px; } } + + button { + margin-top: 0xp; + } } diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/index.js b/ui/app/components/pages/first-time-flow/seed-phrase/index.js index 7355bfb2c..185b3f089 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/index.js +++ b/ui/app/components/pages/first-time-flow/seed-phrase/index.js @@ -1 +1 @@ -export { default } from './seed-phrase.container' +export { default } from './seed-phrase.component' diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/index.scss b/ui/app/components/pages/first-time-flow/seed-phrase/index.scss index 88b28950c..e4fd7be4f 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/index.scss +++ b/ui/app/components/pages/first-time-flow/seed-phrase/index.scss @@ -26,11 +26,15 @@ min-width: 0; @media screen and (min-width: $break-large) { - margin-left: 48px; + margin-left: 81px; } @media screen and (max-width: $break-small) { margin-top: 24px; } + + .first-time-flow__text-block { + color: #5A5A5A; + } } } diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss b/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss index 568359d31..8a47447ed 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss +++ b/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss @@ -50,4 +50,8 @@ cursor: pointer; font-weight: 500; } + + button { + margin-top: 0xp; + } } diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js b/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js index bb822d1d5..732ce14af 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js +++ b/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js @@ -1,10 +1,8 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -import Identicon from '../../../../identicon' import LockIcon from '../../../../lock-icon' import Button from '../../../../button' -import Breadcrumbs from '../../../../breadcrumbs' import { INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE } from '../../../../../routes' import { exportAsFile } from '../../../../../../app/util' @@ -14,7 +12,6 @@ export default class RevealSeedPhrase extends PureComponent { } static propTypes = { - address: PropTypes.string, history: PropTypes.object, seedPhrase: PropTypes.string, } @@ -75,16 +72,10 @@ export default class RevealSeedPhrase extends PureComponent { render () { const { t } = this.context - const { address } = this.props const { isShowingSeedPhrase } = this.state return ( - <div> - <Identicon - className="first-time-flow__unique-image" - address={address} - diameter={70} - /> + <div className="reveal-seed-phrase"> <div className="seed-phrase__sections"> <div className="seed-phrase__main"> <div className="first-time-flow__header"> @@ -121,18 +112,13 @@ export default class RevealSeedPhrase extends PureComponent { </div> </div> <Button - type="first-time" + type="confirm" className="first-time-flow__button" onClick={this.handleNext} disabled={!isShowingSeedPhrase} > { t('next') } </Button> - <Breadcrumbs - className="first-time-flow__breadcrumbs" - total={3} - currentIndex={2} - /> </div> ) } diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.component.js b/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.component.js index 5f5b8a0b2..9eec89cdd 100644 --- a/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.component.js +++ b/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.component.js @@ -25,10 +25,23 @@ export default class SeedPhrase extends PureComponent { } render () { - const { address, seedPhrase } = this.props + const { seedPhrase } = this.props return ( <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> <Route exact @@ -36,7 +49,6 @@ export default class SeedPhrase extends PureComponent { render={props => ( <ConfirmSeedPhrase { ...props } - address={address} seedPhrase={seedPhrase} /> )} @@ -47,7 +59,6 @@ export default class SeedPhrase extends PureComponent { render={props => ( <RevealSeedPhrase { ...props } - address={address} seedPhrase={seedPhrase} /> )} diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.container.js b/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.container.js deleted file mode 100644 index 4df024ffc..000000000 --- a/ui/app/components/pages/first-time-flow/seed-phrase/seed-phrase.container.js +++ /dev/null @@ -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) diff --git a/ui/app/components/pages/first-time-flow/select-action/index.js b/ui/app/components/pages/first-time-flow/select-action/index.js new file mode 100644 index 000000000..3aa968834 --- /dev/null +++ b/ui/app/components/pages/first-time-flow/select-action/index.js @@ -0,0 +1 @@ +export { default } from './select-action.component' diff --git a/ui/app/components/pages/first-time-flow/select-action/index.scss b/ui/app/components/pages/first-time-flow/select-action/index.scss new file mode 100644 index 000000000..b9585eb3b --- /dev/null +++ b/ui/app/components/pages/first-time-flow/select-action/index.scss @@ -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; + } +}
\ No newline at end of file diff --git a/ui/app/components/pages/first-time-flow/select-action/select-action.component.js b/ui/app/components/pages/first-time-flow/select-action/select-action.component.js new file mode 100644 index 000000000..385efe02a --- /dev/null +++ b/ui/app/components/pages/first-time-flow/select-action/select-action.component.js @@ -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> + ) + } +} diff --git a/ui/app/components/pages/first-time-flow/select-action/select-action.container.js b/ui/app/components/pages/first-time-flow/select-action/select-action.container.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/pages/first-time-flow/select-action/select-action.container.js diff --git a/ui/app/components/pages/first-time-flow/welcome/index.scss b/ui/app/components/pages/first-time-flow/welcome/index.scss index 7527ceb35..3b5071480 100644 --- a/ui/app/components/pages/first-time-flow/welcome/index.scss +++ b/ui/app/components/pages/first-time-flow/welcome/index.scss @@ -1,43 +1,42 @@ .welcome-page { display: flex; flex-direction: column; - justify-content: center; + justify-content: flex-start; align-items: center; - width: 400px; + max-width: 442px; padding: 0 18px; + color: black; &__wrapper { display: flex; flex-direction: row; justify-content: center; - align-items: center; + align-items: flex-start; height: 100%; + margin-top: 110px; } &__header { - font-size: 1.5rem; - margin-bottom: 14px; + font-size: 28px; + margin-bottom: 22px; + margin-top: 50px; } &__description { text-align: center; + div { + font-size: 16px; + } + @media screen and (max-width: 575px) { font-size: .9rem; } } - &__button { - height: 54px; - width: 198px; - font-family: Roboto; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .14); - color: $white; - font-size: 1.25rem; + .first-time-flow__button { + width: 184px; font-weight: 500; - text-transform: uppercase; - margin: 35px 0 14px; - transition: 200ms ease-in-out; - background-color: rgba(247, 134, 28, .9); + margin-top: 44px; } } diff --git a/ui/app/components/pages/first-time-flow/welcome/welcome.component.js b/ui/app/components/pages/first-time-flow/welcome/welcome.component.js index f28a8210d..08eb86939 100644 --- a/ui/app/components/pages/first-time-flow/welcome/welcome.component.js +++ b/ui/app/components/pages/first-time-flow/welcome/welcome.component.js @@ -3,7 +3,7 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import Mascot from '../../../mascot' 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 { static propTypes = { @@ -25,12 +25,12 @@ export default class Welcome extends PureComponent { const { history, isInitialized } = this.props if (isInitialized) { - history.push(INITIALIZE_NOTICE_ROUTE) + history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE) } } handleContinue = () => { - this.props.history.push(INITIALIZE_CREATE_PASSWORD_ROUTE) + this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE) } render () { @@ -41,22 +41,22 @@ export default class Welcome extends PureComponent { <div className="welcome-page"> <Mascot animationEventEmitter={this.animationEventEmitter} - width="225" - height="225" + width="125" + height="125" /> <div className="welcome-page__header"> { t('welcome') } </div> <div className="welcome-page__description"> <div>{ t('metamaskDescription') }</div> - <div>{ t('holdEther') }</div> + <div>{ t('happyToSeeYou') }</div> </div> <Button - type="first-time" + type="confirm" className="first-time-flow__button" onClick={this.handleContinue} > - { t('continue') } + { t('getStarted') } </Button> </div> </div> diff --git a/ui/app/components/pages/home/home.component.js b/ui/app/components/pages/home/home.component.js index 469c760a6..953d43aba 100644 --- a/ui/app/components/pages/home/home.component.js +++ b/ui/app/components/pages/home/home.component.js @@ -10,15 +10,12 @@ import { INITIALIZE_SEED_PHRASE_ROUTE, RESTORE_VAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE, - NOTICE_ROUTE, CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, } from '../../../routes' export default class Home extends PureComponent { static propTypes = { history: PropTypes.object, - noActiveNotices: PropTypes.bool, - lostAccounts: PropTypes.array, forgottenPassword: PropTypes.bool, seedWords: PropTypes.string, suggestedTokens: PropTypes.object, @@ -45,18 +42,11 @@ export default class Home extends PureComponent { render () { const { - noActiveNotices, - lostAccounts, forgottenPassword, seedWords, providerRequests, } = this.props - // notices - if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) { - return <Redirect to={{ pathname: NOTICE_ROUTE }} /> - } - // seed words if (seedWords) { return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }}/> diff --git a/ui/app/routes.js b/ui/app/routes.js index 2f4863547..7c4e805ab 100644 --- a/ui/app/routes.js +++ b/ui/app/routes.js @@ -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_UNIQUE_IMAGE_ROUTE = '/initialize/create-password/unique-image' const INITIALIZE_NOTICE_ROUTE = '/initialize/notice' +const INITIALIZE_SELECT_ACTION_ROUTE = '/initialize/select-action' 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 CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction' @@ -64,8 +66,10 @@ module.exports = { INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE, INITIALIZE_NOTICE_ROUTE, + INITIALIZE_SELECT_ACTION_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE, + INITIALIZE_END_OF_FLOW_ROUTE, CONFIRM_TRANSACTION_ROUTE, CONFIRM_SEND_ETHER_PATH, CONFIRM_SEND_TOKEN_PATH, |