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": {
|
||||
"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": {
|
||||
"message": "Accept"
|
||||
},
|
||||
@ -74,6 +77,12 @@
|
||||
"address": {
|
||||
"message": "Address"
|
||||
},
|
||||
"advanced": {
|
||||
"message": "Advanced"
|
||||
},
|
||||
"advancedSettingsDescription": {
|
||||
"message": "Access developer features, download State Logs, Reset Account, setup testnets and custom RPC."
|
||||
},
|
||||
"advancedOptions": {
|
||||
"message": "Advanced Options"
|
||||
},
|
||||
@ -92,9 +101,6 @@
|
||||
"addAcquiredTokens": {
|
||||
"message": "Add the tokens you've acquired using MetaMask"
|
||||
},
|
||||
"advanced": {
|
||||
"message": "Advanced"
|
||||
},
|
||||
"agreeTermsOfService": {
|
||||
"message": "I agree to the Terms of Service"
|
||||
},
|
||||
@ -236,6 +242,9 @@
|
||||
"chromeRequiredForHardwareWallets": {
|
||||
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
|
||||
},
|
||||
"company": {
|
||||
"message": "Company"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
@ -619,6 +628,12 @@
|
||||
"gasPriceRequired": {
|
||||
"message": "Gas Price Required"
|
||||
},
|
||||
"general": {
|
||||
"message": "General"
|
||||
},
|
||||
"generalSettingsDescription": {
|
||||
"message": "Currency conversion, primary currency, language, blockies identicon"
|
||||
},
|
||||
"generatingTransaction": {
|
||||
"message": "Generating transaction"
|
||||
},
|
||||
@ -790,6 +805,9 @@
|
||||
"ledgerAccountRestriction": {
|
||||
"message": "You need to make use your last account before you can add a new one."
|
||||
},
|
||||
"legal": {
|
||||
"message": "Legal"
|
||||
},
|
||||
"lessThanMax": {
|
||||
"message": "must be less than or equal to $1.",
|
||||
"description": "helper for inputting hex as decimal input"
|
||||
@ -1240,6 +1258,12 @@
|
||||
"secretPhrase": {
|
||||
"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": {
|
||||
"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": {
|
||||
"babelify": {
|
||||
"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=",
|
||||
"requires": {
|
||||
"babel-core": "^6.0.14",
|
||||
@ -9901,7 +9901,7 @@
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
@ -10189,7 +10189,7 @@
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
@ -10484,7 +10484,7 @@
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
@ -24076,7 +24076,7 @@
|
||||
"dependencies": {
|
||||
"babelify": {
|
||||
"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=",
|
||||
"requires": {
|
||||
"babel-core": "^6.0.14",
|
||||
@ -24115,7 +24115,7 @@
|
||||
},
|
||||
"babelify": {
|
||||
"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=",
|
||||
"requires": {
|
||||
"babel-core": "^6.0.14",
|
||||
@ -26541,7 +26541,7 @@
|
||||
"dependencies": {
|
||||
"babelify": {
|
||||
"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=",
|
||||
"requires": {
|
||||
"babel-core": "^6.0.14",
|
||||
|
@ -233,7 +233,11 @@ describe('MetaMask', function () {
|
||||
await customRpcButton.click()
|
||||
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 delay(largeDelayMs * 2)
|
||||
})
|
||||
@ -472,15 +476,19 @@ describe('MetaMask', function () {
|
||||
const settingsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Settings')]`))
|
||||
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()
|
||||
|
||||
const advancedGasTitle = await findElement(driver, By.xpath(`//span[contains(text(), 'Advanced gas controls')]`))
|
||||
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()
|
||||
windowHandles = await driver.getAllWindowHandles()
|
||||
extension = windowHandles[0]
|
||||
|
@ -10,7 +10,7 @@ const Dropdown = require('./components/dropdown').Dropdown
|
||||
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
|
||||
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
|
||||
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.
|
||||
const notToggleElementClassnames = [
|
||||
@ -233,7 +233,7 @@ NetworkDropdown.prototype.render = function () {
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => this.props.hideNetworkDropdown(),
|
||||
onClick: () => this.props.history.push(SETTINGS_ROUTE),
|
||||
onClick: () => this.props.history.push(ADVANCED_ROUTE),
|
||||
style: dropdownMenuItemStyle,
|
||||
},
|
||||
[
|
||||
|
@ -1,5 +1,4 @@
|
||||
const { Component } = require('react')
|
||||
const h = require('react-hyperscript')
|
||||
import React, { Component } from 'react'
|
||||
const PropTypes = require('prop-types')
|
||||
const classnames = require('classnames')
|
||||
|
||||
@ -8,18 +7,23 @@ class TabBar extends Component {
|
||||
const { tabs = [], onSelect, isActive } = this.props
|
||||
|
||||
return (
|
||||
h('.tab-bar', {}, [
|
||||
tabs.map(({ key, content }) => {
|
||||
return h('div', {
|
||||
className: classnames('tab-bar__tab pointer', {
|
||||
<div className="tab-bar">
|
||||
{tabs.map(({ key, content, description }) => (
|
||||
<div
|
||||
key={key}
|
||||
className={classnames('tab-bar__tab pointer', {
|
||||
'tab-bar__tab--active': isActive(key, content),
|
||||
}),
|
||||
onClick: () => onSelect(key),
|
||||
key,
|
||||
}, content)
|
||||
}),
|
||||
h('div.tab-bar__tab.tab-bar__grow-tab'),
|
||||
])
|
||||
})}
|
||||
onClick={() => onSelect(key)}
|
||||
>
|
||||
<div className="tab-bar__tab__content">
|
||||
<div className="tab-bar__tab__content__title">{content}</div>
|
||||
<div className="tab-bar__tab__content__description">{description}</div>
|
||||
</div>
|
||||
<div className="tab-bar__tab__caret" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,73 @@
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.tab-bar__tab {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
flex: 0 0 auto;
|
||||
padding: 15px 25px;
|
||||
border-bottom: 1px solid $alto;
|
||||
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 {
|
||||
border-color: $black;
|
||||
@media screen and (min-width: 576px) {
|
||||
&: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 {
|
||||
|
@ -2,7 +2,12 @@ const DEFAULT_ROUTE = '/'
|
||||
const UNLOCK_ROUTE = '/unlock'
|
||||
const LOCK_ROUTE = '/lock'
|
||||
const SETTINGS_ROUTE = '/settings'
|
||||
const GENERAL_ROUTE = '/settings/general'
|
||||
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 MOBILE_SYNC_ROUTE = '/mobile-sync'
|
||||
const CONFIRM_SEED_ROUTE = '/confirm-seed'
|
||||
@ -80,4 +85,9 @@ module.exports = {
|
||||
CONFIRM_TOKEN_METHOD_PATH,
|
||||
SIGNATURE_REQUEST_PATH,
|
||||
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;
|
||||
|
||||
&__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 {
|
||||
content: '\00D7';
|
||||
font-size: 40px;
|
||||
color: $dusty-gray;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
height: 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 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
padding: 10px 0 20px;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__content-item {
|
||||
@ -77,4 +122,22 @@
|
||||
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
|
||||
|
||||
return (
|
||||
<div className="settings-page__content">
|
||||
<div className="settings-page__body">
|
||||
<div className="settings-page__content-row">
|
||||
<div className="settings-page__content-item settings-page__content-item--without-height">
|
||||
<div className="info-tab__logo-wrapper">
|
||||
@ -133,4 +133,8 @@ export default class InfoTab extends PureComponent {
|
||||
</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 PropTypes from 'prop-types'
|
||||
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 ToggleButton from 'react-toggle-button'
|
||||
import { REVEAL_SEED_ROUTE, MOBILE_SYNC_ROUTE } from '../../../helpers/constants/routes'
|
||||
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) => {
|
||||
return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase())
|
||||
@ -37,44 +32,19 @@ export default class SettingsTab extends PureComponent {
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
metamask: PropTypes.object,
|
||||
setUseBlockie: PropTypes.func,
|
||||
setHexDataFeatureFlag: PropTypes.func,
|
||||
setPrivacyMode: PropTypes.func,
|
||||
privacyMode: PropTypes.bool,
|
||||
setCurrentCurrency: PropTypes.func,
|
||||
setRpcTarget: PropTypes.func,
|
||||
delRpcTarget: PropTypes.func,
|
||||
displayWarning: PropTypes.func,
|
||||
revealSeedConfirmation: PropTypes.func,
|
||||
setFeatureFlagToBeta: PropTypes.func,
|
||||
showClearApprovalModal: PropTypes.func,
|
||||
showResetAccountConfirmationModal: PropTypes.func,
|
||||
warning: PropTypes.string,
|
||||
history: PropTypes.object,
|
||||
updateCurrentLocale: PropTypes.func,
|
||||
currentLocale: PropTypes.string,
|
||||
useBlockie: PropTypes.bool,
|
||||
sendHexData: PropTypes.bool,
|
||||
currentCurrency: PropTypes.string,
|
||||
conversionDate: PropTypes.number,
|
||||
nativeCurrency: PropTypes.string,
|
||||
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
||||
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 () {
|
||||
@ -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 () {
|
||||
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 () {
|
||||
const { t } = this.context
|
||||
const {
|
||||
@ -566,109 +180,21 @@ export default class SettingsTab extends PureComponent {
|
||||
)
|
||||
}
|
||||
|
||||
renderShowConversionInTestnets () {
|
||||
const { t } = this.context
|
||||
const {
|
||||
showFiatInTestnets,
|
||||
setShowFiatConversionOnTestnetsPreference,
|
||||
} = this.props
|
||||
renderContent () {
|
||||
const { warning } = 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>
|
||||
)
|
||||
}
|
||||
|
||||
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 className="settings-page__body">
|
||||
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
||||
{ this.renderCurrentConversion() }
|
||||
{ this.renderUsePrimaryCurrencyOptions() }
|
||||
{ this.renderCurrentLocale() }
|
||||
{ this.renderBlockieOptIn() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { warning } = this.props
|
||||
|
||||
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>
|
||||
)
|
||||
return this.renderContent()
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,10 @@ import { connect } from 'react-redux'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import {
|
||||
setCurrentCurrency,
|
||||
updateAndSetCustomRpc,
|
||||
displayWarning,
|
||||
revealSeedConfirmation,
|
||||
setUseBlockie,
|
||||
updateCurrentLocale,
|
||||
setFeatureFlag,
|
||||
showModal,
|
||||
setUseNativeCurrencyAsPrimaryCurrencyPreference,
|
||||
setShowFiatConversionOnTestnetsPreference,
|
||||
setParticipateInMetaMetrics,
|
||||
} from '../../../store/actions'
|
||||
import { preferencesSelector } from '../../../selectors/selectors'
|
||||
@ -24,16 +19,9 @@ const mapStateToProps = state => {
|
||||
conversionDate,
|
||||
nativeCurrency,
|
||||
useBlockie,
|
||||
featureFlags: {
|
||||
sendHexData,
|
||||
privacyMode,
|
||||
advancedInlineGas,
|
||||
} = {},
|
||||
provider = {},
|
||||
currentLocale,
|
||||
participateInMetaMetrics,
|
||||
} = metamask
|
||||
const { useNativeCurrencyAsPrimaryCurrency, showFiatInTestnets } = preferencesSelector(state)
|
||||
const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state)
|
||||
|
||||
return {
|
||||
warning,
|
||||
@ -42,35 +30,19 @@ const mapStateToProps = state => {
|
||||
conversionDate,
|
||||
nativeCurrency,
|
||||
useBlockie,
|
||||
sendHexData,
|
||||
advancedInlineGas,
|
||||
privacyMode,
|
||||
provider,
|
||||
useNativeCurrencyAsPrimaryCurrency,
|
||||
showFiatInTestnets,
|
||||
participateInMetaMetrics,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)),
|
||||
setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(updateAndSetCustomRpc(newRpc, chainId, ticker, nickname)),
|
||||
displayWarning: warning => dispatch(displayWarning(warning)),
|
||||
revealSeedConfirmation: () => dispatch(revealSeedConfirmation()),
|
||||
setUseBlockie: value => dispatch(setUseBlockie(value)),
|
||||
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 => {
|
||||
return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value))
|
||||
},
|
||||
setShowFiatConversionOnTestnetsPreference: value => {
|
||||
return dispatch(setShowFiatConversionOnTestnetsPreference(value))
|
||||
},
|
||||
showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })),
|
||||
setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)),
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,29 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
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 c from 'classnames'
|
||||
import SettingsTab from './settings-tab'
|
||||
import AdvancedTab from './advanced-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 {
|
||||
static propTypes = {
|
||||
@ -17,38 +36,102 @@ export default class SettingsPage extends PureComponent {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
isCurrentPath (pathname) {
|
||||
return this.props.location.pathname === pathname
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { history, location } = this.props
|
||||
|
||||
const pathnameI18nKey = ROUTES_TO_I18N_KEYS[location.pathname]
|
||||
const isPopupView = getEnvironmentType(location.href) === ENVIRONMENT_TYPE_POPUP
|
||||
|
||||
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">
|
||||
{
|
||||
!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
|
||||
className="settings-page__close-button"
|
||||
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>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path={INFO_ROUTE}
|
||||
component={InfoTab}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={SETTINGS_ROUTE}
|
||||
component={SettingsTab}
|
||||
/>
|
||||
</Switch>
|
||||
<div className="settings-page__content">
|
||||
<div className="settings-page__content__tabs">
|
||||
{ this.renderTabs() }
|
||||
</div>
|
||||
<div className="settings-page__content__modules">
|
||||
{ this.renderContent() }
|
||||
</div>
|
||||
</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