mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
New settings page rebased (#6333)
* New setting tab * Add InfoTab * Add Advanced tab * Add Security Tab * Finish mobile view * Make new setting page responsive * Fix linter * Fix y scrolling * Update link in network dropdown * Fix e2e tests * Remove duplicate translation key * Resolve merge conflict * Only change settings header in popup view. * Place mobile-sync button in advanced-tab of settings
This commit is contained in:
parent
4ff9126ff2
commit
961ad267df
@ -44,6 +44,9 @@
|
|||||||
"providerRequestInfo": {
|
"providerRequestInfo": {
|
||||||
"message": "This site is requesting access to view your current account address. Always make sure you trust the sites you interact with."
|
"message": "This site is requesting access to view your current account address. Always make sure you trust the sites you interact with."
|
||||||
},
|
},
|
||||||
|
"aboutUs": {
|
||||||
|
"message": "About Us"
|
||||||
|
},
|
||||||
"accept": {
|
"accept": {
|
||||||
"message": "Accept"
|
"message": "Accept"
|
||||||
},
|
},
|
||||||
@ -74,6 +77,12 @@
|
|||||||
"address": {
|
"address": {
|
||||||
"message": "Address"
|
"message": "Address"
|
||||||
},
|
},
|
||||||
|
"advanced": {
|
||||||
|
"message": "Advanced"
|
||||||
|
},
|
||||||
|
"advancedSettingsDescription": {
|
||||||
|
"message": "Access developer features, download State Logs, Reset Account, setup testnets and custom RPC."
|
||||||
|
},
|
||||||
"advancedOptions": {
|
"advancedOptions": {
|
||||||
"message": "Advanced Options"
|
"message": "Advanced Options"
|
||||||
},
|
},
|
||||||
@ -92,9 +101,6 @@
|
|||||||
"addAcquiredTokens": {
|
"addAcquiredTokens": {
|
||||||
"message": "Add the tokens you've acquired using MetaMask"
|
"message": "Add the tokens you've acquired using MetaMask"
|
||||||
},
|
},
|
||||||
"advanced": {
|
|
||||||
"message": "Advanced"
|
|
||||||
},
|
|
||||||
"agreeTermsOfService": {
|
"agreeTermsOfService": {
|
||||||
"message": "I agree to the Terms of Service"
|
"message": "I agree to the Terms of Service"
|
||||||
},
|
},
|
||||||
@ -236,6 +242,9 @@
|
|||||||
"chromeRequiredForHardwareWallets": {
|
"chromeRequiredForHardwareWallets": {
|
||||||
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
|
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
|
||||||
},
|
},
|
||||||
|
"company": {
|
||||||
|
"message": "Company"
|
||||||
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"message": "Confirm"
|
"message": "Confirm"
|
||||||
},
|
},
|
||||||
@ -619,6 +628,12 @@
|
|||||||
"gasPriceRequired": {
|
"gasPriceRequired": {
|
||||||
"message": "Gas Price Required"
|
"message": "Gas Price Required"
|
||||||
},
|
},
|
||||||
|
"general": {
|
||||||
|
"message": "General"
|
||||||
|
},
|
||||||
|
"generalSettingsDescription": {
|
||||||
|
"message": "Currency conversion, primary currency, language, blockies identicon"
|
||||||
|
},
|
||||||
"generatingTransaction": {
|
"generatingTransaction": {
|
||||||
"message": "Generating transaction"
|
"message": "Generating transaction"
|
||||||
},
|
},
|
||||||
@ -790,6 +805,9 @@
|
|||||||
"ledgerAccountRestriction": {
|
"ledgerAccountRestriction": {
|
||||||
"message": "You need to make use your last account before you can add a new one."
|
"message": "You need to make use your last account before you can add a new one."
|
||||||
},
|
},
|
||||||
|
"legal": {
|
||||||
|
"message": "Legal"
|
||||||
|
},
|
||||||
"lessThanMax": {
|
"lessThanMax": {
|
||||||
"message": "must be less than or equal to $1.",
|
"message": "must be less than or equal to $1.",
|
||||||
"description": "helper for inputting hex as decimal input"
|
"description": "helper for inputting hex as decimal input"
|
||||||
@ -1240,6 +1258,12 @@
|
|||||||
"secretPhrase": {
|
"secretPhrase": {
|
||||||
"message": "Enter your secret twelve word phrase here to restore your vault."
|
"message": "Enter your secret twelve word phrase here to restore your vault."
|
||||||
},
|
},
|
||||||
|
"securityAndPrivacy": {
|
||||||
|
"message": "Security & Privacy"
|
||||||
|
},
|
||||||
|
"securitySettingsDescription": {
|
||||||
|
"message": "Privacy settings and wallet seed phrase"
|
||||||
|
},
|
||||||
"secondsShorthand": {
|
"secondsShorthand": {
|
||||||
"message": "Sec"
|
"message": "Sec"
|
||||||
},
|
},
|
||||||
|
18
app/images/caret-left-black.svg
Normal file
18
app/images/caret-left-black.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="9px" height="15px" viewBox="0 0 9 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: sketchtool 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>8439120D-5704-4273-B416-FEE134322584</title>
|
||||||
|
<desc>Created with sketchtool.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Action-Screens" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Approve---insufficient-amount" transform="translate(-75.000000, -69.000000)" stroke="#000000" stroke-width="2">
|
||||||
|
<g id="Group-7" transform="translate(53.000000, 51.000000)">
|
||||||
|
<g id="cancel" transform="translate(24.000000, 14.000000)">
|
||||||
|
<g id="Group">
|
||||||
|
<polyline id="Path-8" points="6.1263881 18.0633906 0 11.6306831 6.31493631 5"></polyline>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 913 B |
14
package-lock.json
generated
14
package-lock.json
generated
@ -9849,7 +9849,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babelify": {
|
"babelify": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
"resolved": "http://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
||||||
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-core": "^6.0.14",
|
"babel-core": "^6.0.14",
|
||||||
@ -9901,7 +9901,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereumjs-abi": {
|
"ethereumjs-abi": {
|
||||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.11.8",
|
"bn.js": "^4.11.8",
|
||||||
@ -10189,7 +10189,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereumjs-abi": {
|
"ethereumjs-abi": {
|
||||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.11.8",
|
"bn.js": "^4.11.8",
|
||||||
@ -10484,7 +10484,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereumjs-abi": {
|
"ethereumjs-abi": {
|
||||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.11.8",
|
"bn.js": "^4.11.8",
|
||||||
@ -24076,7 +24076,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babelify": {
|
"babelify": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
"resolved": "http://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
||||||
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-core": "^6.0.14",
|
"babel-core": "^6.0.14",
|
||||||
@ -24115,7 +24115,7 @@
|
|||||||
},
|
},
|
||||||
"babelify": {
|
"babelify": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
"resolved": "http://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
||||||
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-core": "^6.0.14",
|
"babel-core": "^6.0.14",
|
||||||
@ -26541,7 +26541,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babelify": {
|
"babelify": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
"resolved": "http://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
||||||
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-core": "^6.0.14",
|
"babel-core": "^6.0.14",
|
||||||
|
@ -233,7 +233,11 @@ describe('MetaMask', function () {
|
|||||||
await customRpcButton.click()
|
await customRpcButton.click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
const privacyToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(10) .settings-page__content-item-col > div'))
|
const securityTab = await findElement(driver, By.xpath(`//div[contains(text(), 'Security & Privacy')]`))
|
||||||
|
await securityTab.click()
|
||||||
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
|
const privacyToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(1) .settings-page__content-item-col > div'))
|
||||||
await privacyToggle.click()
|
await privacyToggle.click()
|
||||||
await delay(largeDelayMs * 2)
|
await delay(largeDelayMs * 2)
|
||||||
})
|
})
|
||||||
@ -472,15 +476,19 @@ describe('MetaMask', function () {
|
|||||||
const settingsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Settings')]`))
|
const settingsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Settings')]`))
|
||||||
settingsButton.click()
|
settingsButton.click()
|
||||||
|
|
||||||
await findElement(driver, By.css('.tab-bar'))
|
// await findElement(driver, By.css('.tab-bar'))
|
||||||
|
|
||||||
const showConversionToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(3) .settings-page__content-item-col > div'))
|
const advancedTab = await findElement(driver, By.xpath(`//div[contains(text(), 'Advanced')]`))
|
||||||
|
await advancedTab.click()
|
||||||
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
|
const showConversionToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(7) .settings-page__content-item-col > div'))
|
||||||
await showConversionToggle.click()
|
await showConversionToggle.click()
|
||||||
|
|
||||||
const advancedGasTitle = await findElement(driver, By.xpath(`//span[contains(text(), 'Advanced gas controls')]`))
|
const advancedGasTitle = await findElement(driver, By.xpath(`//span[contains(text(), 'Advanced gas controls')]`))
|
||||||
await driver.executeScript('arguments[0].scrollIntoView(true)', advancedGasTitle)
|
await driver.executeScript('arguments[0].scrollIntoView(true)', advancedGasTitle)
|
||||||
|
|
||||||
const advancedGasToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(12) .settings-page__content-item-col > div'))
|
const advancedGasToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(5) .settings-page__content-item-col > div'))
|
||||||
await advancedGasToggle.click()
|
await advancedGasToggle.click()
|
||||||
windowHandles = await driver.getAllWindowHandles()
|
windowHandles = await driver.getAllWindowHandles()
|
||||||
extension = windowHandles[0]
|
extension = windowHandles[0]
|
||||||
|
@ -10,7 +10,7 @@ const Dropdown = require('./components/dropdown').Dropdown
|
|||||||
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
|
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
|
||||||
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
|
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
|
||||||
const R = require('ramda')
|
const R = require('ramda')
|
||||||
const { SETTINGS_ROUTE } = require('../../../helpers/constants/routes')
|
const { ADVANCED_ROUTE } = require('../../../helpers/constants/routes')
|
||||||
|
|
||||||
// classes from nodes of the toggle element.
|
// classes from nodes of the toggle element.
|
||||||
const notToggleElementClassnames = [
|
const notToggleElementClassnames = [
|
||||||
@ -233,7 +233,7 @@ NetworkDropdown.prototype.render = function () {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
{
|
{
|
||||||
closeMenu: () => this.props.hideNetworkDropdown(),
|
closeMenu: () => this.props.hideNetworkDropdown(),
|
||||||
onClick: () => this.props.history.push(SETTINGS_ROUTE),
|
onClick: () => this.props.history.push(ADVANCED_ROUTE),
|
||||||
style: dropdownMenuItemStyle,
|
style: dropdownMenuItemStyle,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { Component } = require('react')
|
import React, { Component } from 'react'
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const PropTypes = require('prop-types')
|
const PropTypes = require('prop-types')
|
||||||
const classnames = require('classnames')
|
const classnames = require('classnames')
|
||||||
|
|
||||||
@ -8,18 +7,23 @@ class TabBar extends Component {
|
|||||||
const { tabs = [], onSelect, isActive } = this.props
|
const { tabs = [], onSelect, isActive } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('.tab-bar', {}, [
|
<div className="tab-bar">
|
||||||
tabs.map(({ key, content }) => {
|
{tabs.map(({ key, content, description }) => (
|
||||||
return h('div', {
|
<div
|
||||||
className: classnames('tab-bar__tab pointer', {
|
key={key}
|
||||||
|
className={classnames('tab-bar__tab pointer', {
|
||||||
'tab-bar__tab--active': isActive(key, content),
|
'tab-bar__tab--active': isActive(key, content),
|
||||||
}),
|
})}
|
||||||
onClick: () => onSelect(key),
|
onClick={() => onSelect(key)}
|
||||||
key,
|
>
|
||||||
}, content)
|
<div className="tab-bar__tab__content">
|
||||||
}),
|
<div className="tab-bar__tab__content__title">{content}</div>
|
||||||
h('div.tab-bar__tab.tab-bar__grow-tab'),
|
<div className="tab-bar__tab__content__description">{description}</div>
|
||||||
])
|
</div>
|
||||||
|
<div className="tab-bar__tab__caret" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,73 @@
|
|||||||
.tab-bar {
|
.tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-bar__tab {
|
.tab-bar__tab {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
align-items: flex-start;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 15px 25px;
|
|
||||||
border-bottom: 1px solid $alto;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
}
|
padding: 16px 24px;
|
||||||
|
opacity: .5;
|
||||||
|
transition: opacity 200ms ease-in-out;
|
||||||
|
|
||||||
.tab-bar__tab--active {
|
@media screen and (min-width: 576px) {
|
||||||
border-color: $black;
|
&:hover {
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 24px;
|
||||||
|
border-bottom: 1px solid $alto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 300;
|
||||||
|
margin-top: 8px;
|
||||||
|
min-height: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__caret {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
display: block;
|
||||||
|
background-image: url('/images/caret-right.svg');
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
opacity: .5;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-bar__grow-tab {
|
.tab-bar__grow-tab {
|
||||||
|
@ -2,7 +2,12 @@ const DEFAULT_ROUTE = '/'
|
|||||||
const UNLOCK_ROUTE = '/unlock'
|
const UNLOCK_ROUTE = '/unlock'
|
||||||
const LOCK_ROUTE = '/lock'
|
const LOCK_ROUTE = '/lock'
|
||||||
const SETTINGS_ROUTE = '/settings'
|
const SETTINGS_ROUTE = '/settings'
|
||||||
|
const GENERAL_ROUTE = '/settings/general'
|
||||||
const INFO_ROUTE = '/settings/info'
|
const INFO_ROUTE = '/settings/info'
|
||||||
|
const ADVANCED_ROUTE = '/settings/advanced'
|
||||||
|
const SECURITY_ROUTE = '/settings/security'
|
||||||
|
const COMPANY_ROUTE = '/settings/company'
|
||||||
|
const ABOUT_US_ROUTE = '/settings/about-us'
|
||||||
const REVEAL_SEED_ROUTE = '/seed'
|
const REVEAL_SEED_ROUTE = '/seed'
|
||||||
const MOBILE_SYNC_ROUTE = '/mobile-sync'
|
const MOBILE_SYNC_ROUTE = '/mobile-sync'
|
||||||
const CONFIRM_SEED_ROUTE = '/confirm-seed'
|
const CONFIRM_SEED_ROUTE = '/confirm-seed'
|
||||||
@ -80,4 +85,9 @@ module.exports = {
|
|||||||
CONFIRM_TOKEN_METHOD_PATH,
|
CONFIRM_TOKEN_METHOD_PATH,
|
||||||
SIGNATURE_REQUEST_PATH,
|
SIGNATURE_REQUEST_PATH,
|
||||||
INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
|
INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
|
||||||
|
ADVANCED_ROUTE,
|
||||||
|
SECURITY_ROUTE,
|
||||||
|
COMPANY_ROUTE,
|
||||||
|
GENERAL_ROUTE,
|
||||||
|
ABOUT_US_ROUTE,
|
||||||
}
|
}
|
||||||
|
378
ui/app/pages/settings/advanced-tab/advanced-tab.component.js
Normal file
378
ui/app/pages/settings/advanced-tab/advanced-tab.component.js
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import validUrl from 'valid-url'
|
||||||
|
import { exportAsFile } from '../../../helpers/utils/util'
|
||||||
|
import ToggleButton from 'react-toggle-button'
|
||||||
|
import TextField from '../../../components/ui/text-field'
|
||||||
|
import Button from '../../../components/ui/button'
|
||||||
|
import { MOBILE_SYNC_ROUTE } from '../../../helpers/constants/routes'
|
||||||
|
|
||||||
|
export default class AdvancedTab extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
metricsEvent: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
setHexDataFeatureFlag: PropTypes.func,
|
||||||
|
setRpcTarget: PropTypes.func,
|
||||||
|
displayWarning: PropTypes.func,
|
||||||
|
showResetAccountConfirmationModal: PropTypes.func,
|
||||||
|
warning: PropTypes.string,
|
||||||
|
history: PropTypes.object,
|
||||||
|
sendHexData: PropTypes.bool,
|
||||||
|
setAdvancedInlineGasFeatureFlag: PropTypes.func,
|
||||||
|
advancedInlineGas: PropTypes.bool,
|
||||||
|
showFiatInTestnets: PropTypes.bool,
|
||||||
|
setShowFiatConversionOnTestnetsPreference: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
newRpc: '',
|
||||||
|
chainId: '',
|
||||||
|
showOptions: false,
|
||||||
|
ticker: '',
|
||||||
|
nickname: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNewRpcUrl () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { newRpc, chainId, ticker, nickname } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('newNetwork') }</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
id="new-rpc"
|
||||||
|
placeholder={t('rpcURL')}
|
||||||
|
value={newRpc}
|
||||||
|
onChange={e => this.setState({ newRpc: e.target.value })}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.validateRpc(newRpc, chainId, ticker, nickname)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
id="chainid"
|
||||||
|
placeholder={t('optionalChainId')}
|
||||||
|
value={chainId}
|
||||||
|
onChange={e => this.setState({ chainId: e.target.value })}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.validateRpc(newRpc, chainId, ticker, nickname)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: this.state.showOptions ? null : 'none',
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
id="ticker"
|
||||||
|
placeholder={t('optionalSymbol')}
|
||||||
|
value={ticker}
|
||||||
|
onChange={e => this.setState({ ticker: e.target.value })}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.validateRpc(newRpc, chainId, ticker, nickname)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: this.state.showOptions ? null : 'none',
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
id="nickname"
|
||||||
|
placeholder={t('optionalNickname')}
|
||||||
|
value={nickname}
|
||||||
|
onChange={e => this.setState({ nickname: e.target.value })}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.validateRpc(newRpc, chainId, ticker, nickname)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: this.state.showOptions ? null : 'none',
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
<div className="flex-row flex-align-center space-between">
|
||||||
|
<span className="settings-tab__advanced-link"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.setState({ showOptions: !this.state.showOptions })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t(this.state.showOptions ? 'hideAdvancedOptions' : 'showAdvancedOptions') }
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="button btn-primary settings-tab__rpc-save-button"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.validateRpc(newRpc, chainId, ticker, nickname)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('save') }
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
validateRpc (newRpc, chainId, ticker = 'ETH', nickname) {
|
||||||
|
const { setRpcTarget, displayWarning } = this.props
|
||||||
|
if (validUrl.isWebUri(newRpc)) {
|
||||||
|
this.context.metricsEvent({
|
||||||
|
eventOpts: {
|
||||||
|
category: 'Settings',
|
||||||
|
action: 'Custom RPC',
|
||||||
|
name: 'Success',
|
||||||
|
},
|
||||||
|
customVariables: {
|
||||||
|
networkId: newRpc,
|
||||||
|
chainId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!!chainId && Number.isNaN(parseInt(chainId))) {
|
||||||
|
return displayWarning(`${this.context.t('invalidInput')} chainId`)
|
||||||
|
}
|
||||||
|
|
||||||
|
setRpcTarget(newRpc, chainId, ticker, nickname)
|
||||||
|
} else {
|
||||||
|
this.context.metricsEvent({
|
||||||
|
eventOpts: {
|
||||||
|
category: 'Settings',
|
||||||
|
action: 'Custom RPC',
|
||||||
|
name: 'Error',
|
||||||
|
},
|
||||||
|
customVariables: {
|
||||||
|
networkId: newRpc,
|
||||||
|
chainId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const appendedRpc = `http://${newRpc}`
|
||||||
|
|
||||||
|
if (validUrl.isWebUri(appendedRpc)) {
|
||||||
|
displayWarning(this.context.t('uriErrorMsg'))
|
||||||
|
} else {
|
||||||
|
displayWarning(this.context.t('invalidRPC'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMobileSync () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { history } = this.props
|
||||||
|
//
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('syncWithMobile') }</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
large
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault()
|
||||||
|
history.push(MOBILE_SYNC_ROUTE)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('syncWithMobile') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStateLogs () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { displayWarning } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('stateLogs') }</span>
|
||||||
|
<span className="settings-page__content-description">
|
||||||
|
{ t('stateLogsDescription') }
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
large
|
||||||
|
onClick={() => {
|
||||||
|
window.logStateString((err, result) => {
|
||||||
|
if (err) {
|
||||||
|
displayWarning(t('stateLogError'))
|
||||||
|
} else {
|
||||||
|
exportAsFile('MetaMask State Logs.json', result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('downloadStateLogs') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderResetAccount () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { showResetAccountConfirmationModal } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('resetAccount') }</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
large
|
||||||
|
className="settings-tab__button--orange"
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault()
|
||||||
|
this.context.metricsEvent({
|
||||||
|
eventOpts: {
|
||||||
|
category: 'Settings',
|
||||||
|
action: 'Reset Account',
|
||||||
|
name: 'Reset Account',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
showResetAccountConfirmationModal()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('resetAccount') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHexDataOptIn () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { sendHexData, setHexDataFeatureFlag } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('showHexData') }</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
{ t('showHexDataDescription') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={sendHexData}
|
||||||
|
onToggle={value => setHexDataFeatureFlag(!value)}
|
||||||
|
activeLabel=""
|
||||||
|
inactiveLabel=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAdvancedGasInputInline () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { advancedInlineGas, setAdvancedInlineGasFeatureFlag } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('showAdvancedGasInline') }</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
{ t('showAdvancedGasInlineDescription') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={advancedInlineGas}
|
||||||
|
onToggle={value => setAdvancedInlineGasFeatureFlag(!value)}
|
||||||
|
activeLabel=""
|
||||||
|
inactiveLabel=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderShowConversionInTestnets () {
|
||||||
|
const { t } = this.context
|
||||||
|
const {
|
||||||
|
showFiatInTestnets,
|
||||||
|
setShowFiatConversionOnTestnetsPreference,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('showFiatConversionInTestnets') }</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
{ t('showFiatConversionInTestnetsDescription') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={showFiatInTestnets}
|
||||||
|
onToggle={value => setShowFiatConversionOnTestnetsPreference(!value)}
|
||||||
|
activeLabel=""
|
||||||
|
inactiveLabel=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent () {
|
||||||
|
const { warning } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__body">
|
||||||
|
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
||||||
|
{ this.renderStateLogs() }
|
||||||
|
{ this.renderMobileSync() }
|
||||||
|
{ this.renderNewRpcUrl() }
|
||||||
|
{ this.renderResetAccount() }
|
||||||
|
{ this.renderAdvancedGasInputInline() }
|
||||||
|
{ this.renderHexDataOptIn() }
|
||||||
|
{ this.renderShowConversionInTestnets() }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return this.renderContent()
|
||||||
|
}
|
||||||
|
}
|
48
ui/app/pages/settings/advanced-tab/advanced-tab.container.js
Normal file
48
ui/app/pages/settings/advanced-tab/advanced-tab.container.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import AdvancedTab from './advanced-tab.component'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
import {
|
||||||
|
updateAndSetCustomRpc,
|
||||||
|
displayWarning,
|
||||||
|
setFeatureFlag,
|
||||||
|
showModal,
|
||||||
|
setShowFiatConversionOnTestnetsPreference,
|
||||||
|
} from '../../../store/actions'
|
||||||
|
import {preferencesSelector} from '../../../selectors/selectors'
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
const { appState: { warning }, metamask } = state
|
||||||
|
const {
|
||||||
|
featureFlags: {
|
||||||
|
sendHexData,
|
||||||
|
advancedInlineGas,
|
||||||
|
} = {},
|
||||||
|
} = metamask
|
||||||
|
const { showFiatInTestnets } = preferencesSelector(state)
|
||||||
|
|
||||||
|
return {
|
||||||
|
warning,
|
||||||
|
sendHexData,
|
||||||
|
advancedInlineGas,
|
||||||
|
showFiatInTestnets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
|
||||||
|
setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(updateAndSetCustomRpc(newRpc, chainId, ticker, nickname)),
|
||||||
|
displayWarning: warning => dispatch(displayWarning(warning)),
|
||||||
|
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
|
||||||
|
setAdvancedInlineGasFeatureFlag: shouldShow => dispatch(setFeatureFlag('advancedInlineGas', shouldShow)),
|
||||||
|
setShowFiatConversionOnTestnetsPreference: value => {
|
||||||
|
return dispatch(setShowFiatConversionOnTestnetsPreference(value))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
)(AdvancedTab)
|
1
ui/app/pages/settings/advanced-tab/index.js
Normal file
1
ui/app/pages/settings/advanced-tab/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './advanced-tab.container'
|
@ -9,34 +9,79 @@
|
|||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
padding: 25px 25px 0;
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
padding: 12px 24px;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid $alto;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__back-button {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
display: block;
|
||||||
|
background-image: url('/images/caret-left-black.svg');
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
opacity: .5;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
margin-right: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__close-button::after {
|
&__close-button::after {
|
||||||
content: '\00D7';
|
content: '\00D7';
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
color: $dusty-gray;
|
color: $dusty-gray;
|
||||||
position: absolute;
|
|
||||||
top: 25px;
|
|
||||||
right: 30px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
padding: 25px;
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
height: auto;
|
height: auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
|
&__tabs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
@media screen and (min-width: 576px) {
|
||||||
|
flex: 0 0 32%;
|
||||||
|
max-width: 210px;
|
||||||
|
border-right: 1px solid $alto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__modules {
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 12px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content-row {
|
&__content-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
padding: 10px 0 20px;
|
padding: 10px 0 20px;
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content-item {
|
&__content-item {
|
||||||
@ -77,4 +122,22 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
.settings-page {
|
||||||
|
&__content {
|
||||||
|
&__tabs {
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__modules {
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,11 +101,11 @@ export default class InfoTab extends PureComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
renderContent () {
|
||||||
const { t } = this.context
|
const { t } = this.context
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-page__content">
|
<div className="settings-page__body">
|
||||||
<div className="settings-page__content-row">
|
<div className="settings-page__content-row">
|
||||||
<div className="settings-page__content-item settings-page__content-item--without-height">
|
<div className="settings-page__content-item settings-page__content-item--without-height">
|
||||||
<div className="info-tab__logo-wrapper">
|
<div className="info-tab__logo-wrapper">
|
||||||
@ -133,4 +133,8 @@ export default class InfoTab extends PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return this.renderContent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
1
ui/app/pages/settings/security-tab/index.js
Normal file
1
ui/app/pages/settings/security-tab/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './security-tab.container'
|
195
ui/app/pages/settings/security-tab/security-tab.component.js
Normal file
195
ui/app/pages/settings/security-tab/security-tab.component.js
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { exportAsFile } from '../../../helpers/utils/util'
|
||||||
|
import ToggleButton from 'react-toggle-button'
|
||||||
|
import { REVEAL_SEED_ROUTE } from '../../../helpers/constants/routes'
|
||||||
|
import Button from '../../../components/ui/button'
|
||||||
|
|
||||||
|
export default class SecurityTab extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
metricsEvent: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
setPrivacyMode: PropTypes.func,
|
||||||
|
privacyMode: PropTypes.bool,
|
||||||
|
displayWarning: PropTypes.func,
|
||||||
|
revealSeedConfirmation: PropTypes.func,
|
||||||
|
showClearApprovalModal: PropTypes.func,
|
||||||
|
warning: PropTypes.string,
|
||||||
|
history: PropTypes.object,
|
||||||
|
mobileSync: PropTypes.bool,
|
||||||
|
participateInMetaMetrics: PropTypes.bool,
|
||||||
|
setParticipateInMetaMetrics: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStateLogs () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { displayWarning } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('stateLogs') }</span>
|
||||||
|
<span className="settings-page__content-description">
|
||||||
|
{ t('stateLogsDescription') }
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
large
|
||||||
|
onClick={() => {
|
||||||
|
window.logStateString((err, result) => {
|
||||||
|
if (err) {
|
||||||
|
displayWarning(t('stateLogError'))
|
||||||
|
} else {
|
||||||
|
exportAsFile('MetaMask State Logs.json', result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('downloadStateLogs') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderClearApproval () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { showClearApprovalModal } = this.props
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('approvalData') }</span>
|
||||||
|
<span className="settings-page__content-description">
|
||||||
|
{ t('approvalDataDescription') }
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
large
|
||||||
|
className="settings-tab__button--orange"
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault()
|
||||||
|
showClearApprovalModal()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('clearApprovalData') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSeedWords () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { history } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('revealSeedWords') }</span>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
large
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault()
|
||||||
|
this.context.metricsEvent({
|
||||||
|
eventOpts: {
|
||||||
|
category: 'Settings',
|
||||||
|
action: 'Reveal Seed Phrase',
|
||||||
|
name: 'Reveal Seed Phrase',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
history.push(REVEAL_SEED_ROUTE)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ t('revealSeedWords') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPrivacyOptIn () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { privacyMode, setPrivacyMode } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('privacyMode') }</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
{ t('privacyModeDescription') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={privacyMode}
|
||||||
|
onToggle={value => setPrivacyMode(!value)}
|
||||||
|
activeLabel=""
|
||||||
|
inactiveLabel=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMetaMetricsOptIn () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { participateInMetaMetrics, setParticipateInMetaMetrics } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{ t('participateInMetaMetrics') }</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
<span>{ t('participateInMetaMetricsDescription') }</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={participateInMetaMetrics}
|
||||||
|
onToggle={value => setParticipateInMetaMetrics(!value)}
|
||||||
|
activeLabel=""
|
||||||
|
inactiveLabel=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent () {
|
||||||
|
const { warning } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="settings-page__body">
|
||||||
|
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
||||||
|
{ this.renderPrivacyOptIn() }
|
||||||
|
{ this.renderClearApproval() }
|
||||||
|
{ this.renderSeedWords() }
|
||||||
|
{ this.renderMetaMetricsOptIn() }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return this.renderContent()
|
||||||
|
}
|
||||||
|
}
|
42
ui/app/pages/settings/security-tab/security-tab.container.js
Normal file
42
ui/app/pages/settings/security-tab/security-tab.container.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import SecurityTab from './security-tab.component'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
import {
|
||||||
|
displayWarning,
|
||||||
|
revealSeedConfirmation,
|
||||||
|
setFeatureFlag,
|
||||||
|
showModal,
|
||||||
|
setParticipateInMetaMetrics,
|
||||||
|
} from '../../../store/actions'
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
const { appState: { warning }, metamask } = state
|
||||||
|
const {
|
||||||
|
featureFlags: {
|
||||||
|
privacyMode,
|
||||||
|
} = {},
|
||||||
|
participateInMetaMetrics,
|
||||||
|
} = metamask
|
||||||
|
|
||||||
|
return {
|
||||||
|
warning,
|
||||||
|
privacyMode,
|
||||||
|
participateInMetaMetrics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
displayWarning: warning => dispatch(displayWarning(warning)),
|
||||||
|
revealSeedConfirmation: () => dispatch(revealSeedConfirmation()),
|
||||||
|
setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)),
|
||||||
|
showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })),
|
||||||
|
setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
)(SecurityTab)
|
@ -1,14 +1,9 @@
|
|||||||
import React, { PureComponent } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import infuraCurrencies from '../../../helpers/constants/infura-conversion.json'
|
import infuraCurrencies from '../../../helpers/constants/infura-conversion.json'
|
||||||
import validUrl from 'valid-url'
|
|
||||||
import { exportAsFile } from '../../../helpers/utils/util'
|
|
||||||
import SimpleDropdown from '../../../components/app/dropdowns/simple-dropdown'
|
import SimpleDropdown from '../../../components/app/dropdowns/simple-dropdown'
|
||||||
import ToggleButton from 'react-toggle-button'
|
import ToggleButton from 'react-toggle-button'
|
||||||
import { REVEAL_SEED_ROUTE, MOBILE_SYNC_ROUTE } from '../../../helpers/constants/routes'
|
|
||||||
import locales from '../../../../../app/_locales/index.json'
|
import locales from '../../../../../app/_locales/index.json'
|
||||||
import TextField from '../../../components/ui/text-field'
|
|
||||||
import Button from '../../../components/ui/button'
|
|
||||||
|
|
||||||
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
||||||
return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase())
|
return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase())
|
||||||
@ -37,44 +32,19 @@ export default class SettingsTab extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
metamask: PropTypes.object,
|
|
||||||
setUseBlockie: PropTypes.func,
|
setUseBlockie: PropTypes.func,
|
||||||
setHexDataFeatureFlag: PropTypes.func,
|
|
||||||
setPrivacyMode: PropTypes.func,
|
|
||||||
privacyMode: PropTypes.bool,
|
|
||||||
setCurrentCurrency: PropTypes.func,
|
setCurrentCurrency: PropTypes.func,
|
||||||
setRpcTarget: PropTypes.func,
|
|
||||||
delRpcTarget: PropTypes.func,
|
|
||||||
displayWarning: PropTypes.func,
|
displayWarning: PropTypes.func,
|
||||||
revealSeedConfirmation: PropTypes.func,
|
|
||||||
setFeatureFlagToBeta: PropTypes.func,
|
|
||||||
showClearApprovalModal: PropTypes.func,
|
|
||||||
showResetAccountConfirmationModal: PropTypes.func,
|
|
||||||
warning: PropTypes.string,
|
warning: PropTypes.string,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
updateCurrentLocale: PropTypes.func,
|
updateCurrentLocale: PropTypes.func,
|
||||||
currentLocale: PropTypes.string,
|
currentLocale: PropTypes.string,
|
||||||
useBlockie: PropTypes.bool,
|
useBlockie: PropTypes.bool,
|
||||||
sendHexData: PropTypes.bool,
|
|
||||||
currentCurrency: PropTypes.string,
|
currentCurrency: PropTypes.string,
|
||||||
conversionDate: PropTypes.number,
|
conversionDate: PropTypes.number,
|
||||||
nativeCurrency: PropTypes.string,
|
nativeCurrency: PropTypes.string,
|
||||||
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
||||||
setUseNativeCurrencyAsPrimaryCurrencyPreference: PropTypes.func,
|
setUseNativeCurrencyAsPrimaryCurrencyPreference: PropTypes.func,
|
||||||
setAdvancedInlineGasFeatureFlag: PropTypes.func,
|
|
||||||
advancedInlineGas: PropTypes.bool,
|
|
||||||
showFiatInTestnets: PropTypes.bool,
|
|
||||||
setShowFiatConversionOnTestnetsPreference: PropTypes.func.isRequired,
|
|
||||||
participateInMetaMetrics: PropTypes.bool,
|
|
||||||
setParticipateInMetaMetrics: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
newRpc: '',
|
|
||||||
chainId: '',
|
|
||||||
showOptions: false,
|
|
||||||
ticker: '',
|
|
||||||
nickname: '',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCurrentConversion () {
|
renderCurrentConversion () {
|
||||||
@ -133,310 +103,6 @@ export default class SettingsTab extends PureComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNewRpcUrl () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { newRpc, chainId, ticker, nickname } = this.state
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('newNetwork') }</span>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<TextField
|
|
||||||
type="text"
|
|
||||||
id="new-rpc"
|
|
||||||
placeholder={t('rpcURL')}
|
|
||||||
value={newRpc}
|
|
||||||
onChange={e => this.setState({ newRpc: e.target.value })}
|
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.validateRpc(newRpc, chainId, ticker, nickname)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
margin="dense"
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
type="text"
|
|
||||||
id="chainid"
|
|
||||||
placeholder={t('optionalChainId')}
|
|
||||||
value={chainId}
|
|
||||||
onChange={e => this.setState({ chainId: e.target.value })}
|
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.validateRpc(newRpc, chainId, ticker, nickname)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
display: this.state.showOptions ? null : 'none',
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
margin="dense"
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
type="text"
|
|
||||||
id="ticker"
|
|
||||||
placeholder={t('optionalSymbol')}
|
|
||||||
value={ticker}
|
|
||||||
onChange={e => this.setState({ ticker: e.target.value })}
|
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.validateRpc(newRpc, chainId, ticker, nickname)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
display: this.state.showOptions ? null : 'none',
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
margin="dense"
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
type="text"
|
|
||||||
id="nickname"
|
|
||||||
placeholder={t('optionalNickname')}
|
|
||||||
value={nickname}
|
|
||||||
onChange={e => this.setState({ nickname: e.target.value })}
|
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.validateRpc(newRpc, chainId, ticker, nickname)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
display: this.state.showOptions ? null : 'none',
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
margin="dense"
|
|
||||||
/>
|
|
||||||
<div className="flex-row flex-align-center space-between">
|
|
||||||
<span className="settings-tab__advanced-link"
|
|
||||||
onClick={e => {
|
|
||||||
e.preventDefault()
|
|
||||||
this.setState({ showOptions: !this.state.showOptions })
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t(this.state.showOptions ? 'hideAdvancedOptions' : 'showAdvancedOptions') }
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className="button btn-primary settings-tab__rpc-save-button"
|
|
||||||
onClick={e => {
|
|
||||||
e.preventDefault()
|
|
||||||
this.validateRpc(newRpc, chainId, ticker, nickname)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t('save') }
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
validateRpc (newRpc, chainId, ticker = 'ETH', nickname) {
|
|
||||||
const { setRpcTarget, displayWarning } = this.props
|
|
||||||
if (validUrl.isWebUri(newRpc)) {
|
|
||||||
this.context.metricsEvent({
|
|
||||||
eventOpts: {
|
|
||||||
category: 'Settings',
|
|
||||||
action: 'Custom RPC',
|
|
||||||
name: 'Success',
|
|
||||||
},
|
|
||||||
customVariables: {
|
|
||||||
networkId: newRpc,
|
|
||||||
chainId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (!!chainId && Number.isNaN(parseInt(chainId))) {
|
|
||||||
return displayWarning(`${this.context.t('invalidInput')} chainId`)
|
|
||||||
}
|
|
||||||
|
|
||||||
setRpcTarget(newRpc, chainId, ticker, nickname)
|
|
||||||
} else {
|
|
||||||
this.context.metricsEvent({
|
|
||||||
eventOpts: {
|
|
||||||
category: 'Settings',
|
|
||||||
action: 'Custom RPC',
|
|
||||||
name: 'Error',
|
|
||||||
},
|
|
||||||
customVariables: {
|
|
||||||
networkId: newRpc,
|
|
||||||
chainId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const appendedRpc = `http://${newRpc}`
|
|
||||||
|
|
||||||
if (validUrl.isWebUri(appendedRpc)) {
|
|
||||||
displayWarning(this.context.t('uriErrorMsg'))
|
|
||||||
} else {
|
|
||||||
displayWarning(this.context.t('invalidRPC'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderStateLogs () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { displayWarning } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('stateLogs') }</span>
|
|
||||||
<span className="settings-page__content-description">
|
|
||||||
{ t('stateLogsDescription') }
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
large
|
|
||||||
onClick={() => {
|
|
||||||
window.logStateString((err, result) => {
|
|
||||||
if (err) {
|
|
||||||
displayWarning(t('stateLogError'))
|
|
||||||
} else {
|
|
||||||
exportAsFile('MetaMask State Logs.json', result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t('downloadStateLogs') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderClearApproval () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { showClearApprovalModal } = this.props
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('approvalData') }</span>
|
|
||||||
<span className="settings-page__content-description">
|
|
||||||
{ t('approvalDataDescription') }
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<Button
|
|
||||||
type="secondary"
|
|
||||||
large
|
|
||||||
className="settings-tab__button--orange"
|
|
||||||
onClick={event => {
|
|
||||||
event.preventDefault()
|
|
||||||
showClearApprovalModal()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t('clearApprovalData') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSeedWords () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { history } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('revealSeedWords') }</span>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<Button
|
|
||||||
type="secondary"
|
|
||||||
large
|
|
||||||
onClick={event => {
|
|
||||||
event.preventDefault()
|
|
||||||
this.context.metricsEvent({
|
|
||||||
eventOpts: {
|
|
||||||
category: 'Settings',
|
|
||||||
action: 'Reveal Seed Phrase',
|
|
||||||
name: 'Reveal Seed Phrase',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
history.push(REVEAL_SEED_ROUTE)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t('revealSeedWords') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
renderMobileSync () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { history } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('syncWithMobile') }</span>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
large
|
|
||||||
onClick={event => {
|
|
||||||
event.preventDefault()
|
|
||||||
history.push(MOBILE_SYNC_ROUTE)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t('syncWithMobile') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
renderResetAccount () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { showResetAccountConfirmationModal } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('resetAccount') }</span>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<Button
|
|
||||||
type="secondary"
|
|
||||||
large
|
|
||||||
className="settings-tab__button--orange"
|
|
||||||
onClick={event => {
|
|
||||||
event.preventDefault()
|
|
||||||
this.context.metricsEvent({
|
|
||||||
eventOpts: {
|
|
||||||
category: 'Settings',
|
|
||||||
action: 'Reset Account',
|
|
||||||
name: 'Reset Account',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
showResetAccountConfirmationModal()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ t('resetAccount') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBlockieOptIn () {
|
renderBlockieOptIn () {
|
||||||
const { useBlockie, setUseBlockie } = this.props
|
const { useBlockie, setUseBlockie } = this.props
|
||||||
@ -460,58 +126,6 @@ export default class SettingsTab extends PureComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHexDataOptIn () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { sendHexData, setHexDataFeatureFlag } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('showHexData') }</span>
|
|
||||||
<div className="settings-page__content-description">
|
|
||||||
{ t('showHexDataDescription') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<ToggleButton
|
|
||||||
value={sendHexData}
|
|
||||||
onToggle={value => setHexDataFeatureFlag(!value)}
|
|
||||||
activeLabel=""
|
|
||||||
inactiveLabel=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderAdvancedGasInputInline () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { advancedInlineGas, setAdvancedInlineGasFeatureFlag } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('showAdvancedGasInline') }</span>
|
|
||||||
<div className="settings-page__content-description">
|
|
||||||
{ t('showAdvancedGasInlineDescription') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<ToggleButton
|
|
||||||
value={advancedInlineGas}
|
|
||||||
onToggle={value => setAdvancedInlineGasFeatureFlag(!value)}
|
|
||||||
activeLabel=""
|
|
||||||
inactiveLabel=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderUsePrimaryCurrencyOptions () {
|
renderUsePrimaryCurrencyOptions () {
|
||||||
const { t } = this.context
|
const { t } = this.context
|
||||||
const {
|
const {
|
||||||
@ -566,109 +180,21 @@ export default class SettingsTab extends PureComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderShowConversionInTestnets () {
|
renderContent () {
|
||||||
const { t } = this.context
|
const { warning } = this.props
|
||||||
const {
|
|
||||||
showFiatInTestnets,
|
|
||||||
setShowFiatConversionOnTestnetsPreference,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-page__content-row">
|
<div className="settings-page__body">
|
||||||
<div className="settings-page__content-item">
|
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
||||||
<span>{ t('showFiatConversionInTestnets') }</span>
|
{ this.renderCurrentConversion() }
|
||||||
<div className="settings-page__content-description">
|
{ this.renderUsePrimaryCurrencyOptions() }
|
||||||
{ t('showFiatConversionInTestnetsDescription') }
|
{ this.renderCurrentLocale() }
|
||||||
</div>
|
{ this.renderBlockieOptIn() }
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<ToggleButton
|
|
||||||
value={showFiatInTestnets}
|
|
||||||
onToggle={value => setShowFiatConversionOnTestnetsPreference(!value)}
|
|
||||||
activeLabel=""
|
|
||||||
inactiveLabel=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPrivacyOptIn () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { privacyMode, setPrivacyMode } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('privacyMode') }</span>
|
|
||||||
<div className="settings-page__content-description">
|
|
||||||
{ t('privacyModeDescription') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<ToggleButton
|
|
||||||
value={privacyMode}
|
|
||||||
onToggle={value => setPrivacyMode(!value)}
|
|
||||||
activeLabel=""
|
|
||||||
inactiveLabel=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMetaMetricsOptIn () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { participateInMetaMetrics, setParticipateInMetaMetrics } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content-row">
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<span>{ t('participateInMetaMetrics') }</span>
|
|
||||||
<div className="settings-page__content-description">
|
|
||||||
<span>{ t('participateInMetaMetricsDescription') }</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="settings-page__content-item">
|
|
||||||
<div className="settings-page__content-item-col">
|
|
||||||
<ToggleButton
|
|
||||||
value={participateInMetaMetrics}
|
|
||||||
onToggle={value => setParticipateInMetaMetrics(!value)}
|
|
||||||
activeLabel=""
|
|
||||||
inactiveLabel=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { warning } = this.props
|
return this.renderContent()
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings-page__content">
|
|
||||||
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
|
||||||
{ this.renderCurrentConversion() }
|
|
||||||
{ this.renderUsePrimaryCurrencyOptions() }
|
|
||||||
{ this.renderShowConversionInTestnets() }
|
|
||||||
{ this.renderCurrentLocale() }
|
|
||||||
{ this.renderNewRpcUrl() }
|
|
||||||
{ this.renderStateLogs() }
|
|
||||||
{ this.renderSeedWords() }
|
|
||||||
{ this.renderResetAccount() }
|
|
||||||
{ this.renderClearApproval() }
|
|
||||||
{ this.renderPrivacyOptIn() }
|
|
||||||
{ this.renderHexDataOptIn() }
|
|
||||||
{ this.renderAdvancedGasInputInline() }
|
|
||||||
{ this.renderBlockieOptIn() }
|
|
||||||
{ this.renderMobileSync() }
|
|
||||||
{ this.renderMetaMetricsOptIn() }
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,10 @@ import { connect } from 'react-redux'
|
|||||||
import { withRouter } from 'react-router-dom'
|
import { withRouter } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
setCurrentCurrency,
|
setCurrentCurrency,
|
||||||
updateAndSetCustomRpc,
|
|
||||||
displayWarning,
|
displayWarning,
|
||||||
revealSeedConfirmation,
|
|
||||||
setUseBlockie,
|
setUseBlockie,
|
||||||
updateCurrentLocale,
|
updateCurrentLocale,
|
||||||
setFeatureFlag,
|
|
||||||
showModal,
|
|
||||||
setUseNativeCurrencyAsPrimaryCurrencyPreference,
|
setUseNativeCurrencyAsPrimaryCurrencyPreference,
|
||||||
setShowFiatConversionOnTestnetsPreference,
|
|
||||||
setParticipateInMetaMetrics,
|
setParticipateInMetaMetrics,
|
||||||
} from '../../../store/actions'
|
} from '../../../store/actions'
|
||||||
import { preferencesSelector } from '../../../selectors/selectors'
|
import { preferencesSelector } from '../../../selectors/selectors'
|
||||||
@ -24,16 +19,9 @@ const mapStateToProps = state => {
|
|||||||
conversionDate,
|
conversionDate,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
useBlockie,
|
useBlockie,
|
||||||
featureFlags: {
|
|
||||||
sendHexData,
|
|
||||||
privacyMode,
|
|
||||||
advancedInlineGas,
|
|
||||||
} = {},
|
|
||||||
provider = {},
|
|
||||||
currentLocale,
|
currentLocale,
|
||||||
participateInMetaMetrics,
|
|
||||||
} = metamask
|
} = metamask
|
||||||
const { useNativeCurrencyAsPrimaryCurrency, showFiatInTestnets } = preferencesSelector(state)
|
const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
warning,
|
warning,
|
||||||
@ -42,35 +30,19 @@ const mapStateToProps = state => {
|
|||||||
conversionDate,
|
conversionDate,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
useBlockie,
|
useBlockie,
|
||||||
sendHexData,
|
|
||||||
advancedInlineGas,
|
|
||||||
privacyMode,
|
|
||||||
provider,
|
|
||||||
useNativeCurrencyAsPrimaryCurrency,
|
useNativeCurrencyAsPrimaryCurrency,
|
||||||
showFiatInTestnets,
|
|
||||||
participateInMetaMetrics,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)),
|
setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)),
|
||||||
setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(updateAndSetCustomRpc(newRpc, chainId, ticker, nickname)),
|
|
||||||
displayWarning: warning => dispatch(displayWarning(warning)),
|
displayWarning: warning => dispatch(displayWarning(warning)),
|
||||||
revealSeedConfirmation: () => dispatch(revealSeedConfirmation()),
|
|
||||||
setUseBlockie: value => dispatch(setUseBlockie(value)),
|
setUseBlockie: value => dispatch(setUseBlockie(value)),
|
||||||
updateCurrentLocale: key => dispatch(updateCurrentLocale(key)),
|
updateCurrentLocale: key => dispatch(updateCurrentLocale(key)),
|
||||||
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
|
|
||||||
setAdvancedInlineGasFeatureFlag: shouldShow => dispatch(setFeatureFlag('advancedInlineGas', shouldShow)),
|
|
||||||
setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)),
|
|
||||||
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
|
|
||||||
setUseNativeCurrencyAsPrimaryCurrencyPreference: value => {
|
setUseNativeCurrencyAsPrimaryCurrencyPreference: value => {
|
||||||
return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value))
|
return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value))
|
||||||
},
|
},
|
||||||
setShowFiatConversionOnTestnetsPreference: value => {
|
|
||||||
return dispatch(setShowFiatConversionOnTestnetsPreference(value))
|
|
||||||
},
|
|
||||||
showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })),
|
|
||||||
setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)),
|
setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,29 @@
|
|||||||
import React, { PureComponent } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Switch, Route, matchPath } from 'react-router-dom'
|
import { Switch, Route, matchPath } from 'react-router-dom'
|
||||||
|
import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums'
|
||||||
|
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
|
||||||
import TabBar from '../../components/app/tab-bar'
|
import TabBar from '../../components/app/tab-bar'
|
||||||
|
import c from 'classnames'
|
||||||
import SettingsTab from './settings-tab'
|
import SettingsTab from './settings-tab'
|
||||||
|
import AdvancedTab from './advanced-tab'
|
||||||
import InfoTab from './info-tab'
|
import InfoTab from './info-tab'
|
||||||
import { DEFAULT_ROUTE, SETTINGS_ROUTE, INFO_ROUTE } from '../../helpers/constants/routes'
|
import SecurityTab from './security-tab'
|
||||||
|
import {
|
||||||
|
DEFAULT_ROUTE,
|
||||||
|
ADVANCED_ROUTE,
|
||||||
|
SECURITY_ROUTE,
|
||||||
|
GENERAL_ROUTE,
|
||||||
|
ABOUT_US_ROUTE,
|
||||||
|
SETTINGS_ROUTE,
|
||||||
|
} from '../../helpers/constants/routes'
|
||||||
|
|
||||||
|
const ROUTES_TO_I18N_KEYS = {
|
||||||
|
[GENERAL_ROUTE]: 'general',
|
||||||
|
[ADVANCED_ROUTE]: 'advanced',
|
||||||
|
[SECURITY_ROUTE]: 'securityAndPrivacy',
|
||||||
|
[ABOUT_US_ROUTE]: 'aboutUs',
|
||||||
|
}
|
||||||
|
|
||||||
export default class SettingsPage extends PureComponent {
|
export default class SettingsPage extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -17,38 +36,102 @@ export default class SettingsPage extends PureComponent {
|
|||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCurrentPath (pathname) {
|
||||||
|
return this.props.location.pathname === pathname
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { t } = this.context
|
||||||
const { history, location } = this.props
|
const { history, location } = this.props
|
||||||
|
|
||||||
|
const pathnameI18nKey = ROUTES_TO_I18N_KEYS[location.pathname]
|
||||||
|
const isPopupView = getEnvironmentType(location.href) === ENVIRONMENT_TYPE_POPUP
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main-container settings-page">
|
<div
|
||||||
|
className={c('main-container settings-page', {
|
||||||
|
'settings-page--selected': !this.isCurrentPath(SETTINGS_ROUTE),
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div className="settings-page__header">
|
<div className="settings-page__header">
|
||||||
|
{
|
||||||
|
!this.isCurrentPath(SETTINGS_ROUTE) && (
|
||||||
|
<div
|
||||||
|
className="settings-page__back-button"
|
||||||
|
onClick={() => history.push(SETTINGS_ROUTE)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div className="settings-page__header__title">
|
||||||
|
{t(pathnameI18nKey && isPopupView ? pathnameI18nKey : 'settings')}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="settings-page__close-button"
|
className="settings-page__close-button"
|
||||||
onClick={() => history.push(DEFAULT_ROUTE)}
|
onClick={() => history.push(DEFAULT_ROUTE)}
|
||||||
/>
|
/>
|
||||||
<TabBar
|
|
||||||
tabs={[
|
|
||||||
{ content: this.context.t('settings'), key: SETTINGS_ROUTE },
|
|
||||||
{ content: this.context.t('info'), key: INFO_ROUTE },
|
|
||||||
]}
|
|
||||||
isActive={key => matchPath(location.pathname, { path: key, exact: true })}
|
|
||||||
onSelect={key => history.push(key)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<Switch>
|
<div className="settings-page__content">
|
||||||
<Route
|
<div className="settings-page__content__tabs">
|
||||||
exact
|
{ this.renderTabs() }
|
||||||
path={INFO_ROUTE}
|
</div>
|
||||||
component={InfoTab}
|
<div className="settings-page__content__modules">
|
||||||
/>
|
{ this.renderContent() }
|
||||||
<Route
|
</div>
|
||||||
exact
|
</div>
|
||||||
path={SETTINGS_ROUTE}
|
|
||||||
component={SettingsTab}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderTabs () {
|
||||||
|
const { history, location } = this.props
|
||||||
|
const { t } = this.context
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabBar
|
||||||
|
tabs={[
|
||||||
|
{ content: t('general'), description: t('generalSettingsDescription'), key: GENERAL_ROUTE },
|
||||||
|
{ content: t('advanced'), description: t('advancedSettingsDescription'), key: ADVANCED_ROUTE },
|
||||||
|
{ content: t('securityAndPrivacy'), description: t('securitySettingsDescription'), key: SECURITY_ROUTE },
|
||||||
|
{ content: t('aboutUs'), key: ABOUT_US_ROUTE },
|
||||||
|
]}
|
||||||
|
isActive={key => {
|
||||||
|
if (key === GENERAL_ROUTE && this.isCurrentPath(SETTINGS_ROUTE)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return matchPath(location.pathname, { path: key, exact: true })
|
||||||
|
}}
|
||||||
|
onSelect={key => history.push(key)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent () {
|
||||||
|
return (
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={GENERAL_ROUTE}
|
||||||
|
component={SettingsTab}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={ABOUT_US_ROUTE}
|
||||||
|
component={InfoTab}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={ADVANCED_ROUTE}
|
||||||
|
component={AdvancedTab}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={SECURITY_ROUTE}
|
||||||
|
component={SecurityTab}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
component={SettingsTab}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user