mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
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
This commit is contained in:
parent
a2320c76fe
commit
cb2698d20e
@ -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"
|
||||
}
|
||||
|
5
app/images/download-alt.svg
Normal file
5
app/images/download-alt.svg
Normal file
@ -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 |
1
app/images/sleuth.svg
Normal file
1
app/images/sleuth.svg
Normal file
@ -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 |
4
app/images/thin-plus.svg
Normal file
4
app/images/thin-plus.svg
Normal file
@ -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 () {
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
@ -80,135 +80,112 @@ 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('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)
|
||||
})
|
||||
|
||||
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'))
|
||||
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)
|
||||
})
|
||||
|
||||
await passwordBox.sendKeys('correct horse battery staple')
|
||||
await passwordBoxConfirm.sendKeys('correct horse battery staple')
|
||||
await button.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
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'))
|
||||
|
||||
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)
|
||||
})
|
||||
await passwordBox.sendKeys('correct horse battery staple')
|
||||
await passwordBoxConfirm.sendKeys('correct horse battery staple')
|
||||
|
||||
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 delay(regularDelayMs)
|
||||
})
|
||||
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)
|
||||
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
|
||||
await nextScreen.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
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
})
|
||||
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)
|
||||
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)
|
||||
}
|
||||
await word0.click()
|
||||
await delay(tinyDelayMs)
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
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 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)
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Show account information', () => {
|
||||
it('show account details dropdown menu', async () => {
|
||||
await driver.findElement(By.css('div.menu-bar__open-in-browser')).click()
|
||||
|
@ -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')
|
||||
|
||||
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
|
||||
await tosCheckBox.click()
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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,
|
||||
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 }} />
|
||||
}
|
||||
|
@ -6,14 +6,12 @@ const mapStateToProps = ({ metamask }) => {
|
||||
completedOnboarding,
|
||||
isInitialized,
|
||||
isUnlocked,
|
||||
noActiveNotices,
|
||||
} = metamask
|
||||
|
||||
return {
|
||||
completedOnboarding,
|
||||
isInitialized,
|
||||
isUnlocked,
|
||||
noActiveNotices,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
@ -99,26 +98,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 => (
|
||||
@ -128,6 +122,11 @@ export default class FirstTimeFlow extends PureComponent {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={INITIALIZE_END_OF_FLOW_ROUTE}
|
||||
component={EndOfFlow}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={INITIALIZE_WELCOME_ROUTE}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 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>
|
||||
)
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,4 +50,8 @@
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 0xp;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
@ -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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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 }}/>
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user