diff options
Merge branch 'master' into i3725-refactor-send-component
Diffstat (limited to 'ui/app/components/pages/settings')
-rw-r--r-- | ui/app/components/pages/settings/index.js | 59 | ||||
-rw-r--r-- | ui/app/components/pages/settings/info.js | 112 | ||||
-rw-r--r-- | ui/app/components/pages/settings/settings.js | 367 |
3 files changed, 538 insertions, 0 deletions
diff --git a/ui/app/components/pages/settings/index.js b/ui/app/components/pages/settings/index.js new file mode 100644 index 000000000..384ae4b41 --- /dev/null +++ b/ui/app/components/pages/settings/index.js @@ -0,0 +1,59 @@ +const { Component } = require('react') +const { Switch, Route, matchPath } = require('react-router-dom') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const TabBar = require('../../tab-bar') +const Settings = require('./settings') +const Info = require('./info') +const { DEFAULT_ROUTE, SETTINGS_ROUTE, INFO_ROUTE } = require('../../../routes') + +class Config extends Component { + renderTabs () { + const { history, location } = this.props + + return h('div.settings__tabs', [ + h(TabBar, { + tabs: [ + { content: 'Settings', key: SETTINGS_ROUTE }, + { content: 'Info', key: INFO_ROUTE }, + ], + isActive: key => matchPath(location.pathname, { path: key, exact: true }), + onSelect: key => history.push(key), + }), + ]) + } + + render () { + const { history } = this.props + + return ( + h('.main-container.settings', {}, [ + h('.settings__header', [ + h('div.settings__close-button', { + onClick: () => history.push(DEFAULT_ROUTE), + }), + this.renderTabs(), + ]), + h(Switch, [ + h(Route, { + exact: true, + path: INFO_ROUTE, + component: Info, + }), + h(Route, { + exact: true, + path: SETTINGS_ROUTE, + component: Settings, + }), + ]), + ]) + ) + } +} + +Config.propTypes = { + location: PropTypes.object, + history: PropTypes.object, +} + +module.exports = Config diff --git a/ui/app/components/pages/settings/info.js b/ui/app/components/pages/settings/info.js new file mode 100644 index 000000000..eb9be66e6 --- /dev/null +++ b/ui/app/components/pages/settings/info.js @@ -0,0 +1,112 @@ +const { Component } = require('react') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') + +class Info extends Component { + renderLogo () { + return ( + h('div.settings__info-logo-wrapper', [ + h('img.settings__info-logo', { src: 'images/info-logo.png' }), + ]) + ) + } + + renderInfoLinks () { + return ( + h('div.settings__content-item.settings__content-item--without-height', [ + h('div.settings__info-link-header', this.context.t('links')), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/privacy.html', + target: '_blank', + }, [ + h('span.settings__info-link', this.context.t('privacyMsg')), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/terms.html', + target: '_blank', + }, [ + h('span.settings__info-link', this.context.t('terms')), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/attributions.html', + target: '_blank', + }, [ + h('span.settings__info-link', this.context.t('attributions')), + ]), + ]), + h('hr.settings__info-separator'), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://support.metamask.io', + target: '_blank', + }, [ + h('span.settings__info-link', this.context.t('supportCenter')), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/', + target: '_blank', + }, [ + h('span.settings__info-link', this.context.t('visitWebSite')), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + target: '_blank', + href: 'mailto:help@metamask.io?subject=Feedback', + }, [ + h('span.settings__info-link', this.context.t('emailUs')), + ]), + ]), + ]) + ) + } + + render () { + return ( + h('div.settings__content', [ + h('div.settings__content-row', [ + h('div.settings__content-item.settings__content-item--without-height', [ + this.renderLogo(), + h('div.settings__info-item', [ + h('div.settings__info-version-header', 'MetaMask Version'), + h('div.settings__info-version-number', '4.0.0'), + ]), + h('div.settings__info-item', [ + h( + 'div.settings__info-about', + this.context.t('builtInCalifornia') + ), + ]), + ]), + this.renderInfoLinks(), + ]), + ]) + ) + } +} + +Info.propTypes = { + tab: PropTypes.string, + metamask: PropTypes.object, + setCurrentCurrency: PropTypes.func, + setRpcTarget: PropTypes.func, + displayWarning: PropTypes.func, + revealSeedConfirmation: PropTypes.func, + warning: PropTypes.string, + location: PropTypes.object, + history: PropTypes.object, + t: PropTypes.func, +} + +Info.contextTypes = { + t: PropTypes.func, +} + +module.exports = Info diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js new file mode 100644 index 000000000..05a7379fb --- /dev/null +++ b/ui/app/components/pages/settings/settings.js @@ -0,0 +1,367 @@ +const { Component } = require('react') +const { withRouter } = require('react-router-dom') +const { compose } = require('recompose') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const actions = require('../../../actions') +const infuraCurrencies = require('../../../infura-conversion.json') +const validUrl = require('valid-url') +const { exportAsFile } = require('../../../util') +const SimpleDropdown = require('../../dropdowns/simple-dropdown') +const ToggleButton = require('react-toggle-button') +const { REVEAL_SEED_ROUTE } = require('../../../routes') +const locales = require('../../../../../app/_locales/index.json') +const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/config').enums + +const getInfuraCurrencyOptions = () => { + const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { + return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase()) + }) + + return sortedCurrencies.map(({ quote: { code, name } }) => { + return { + displayValue: `${code.toUpperCase()} - ${name}`, + key: code, + value: code, + } + }) +} + +const getLocaleOptions = () => { + return locales.map((locale) => { + return { + displayValue: `${locale.name}`, + key: locale.code, + value: locale.code, + } + }) +} + +class Settings extends Component { + constructor (props) { + super(props) + + this.state = { + newRpc: '', + } + } + + renderBlockieOptIn () { + const { metamask: { useBlockie }, setUseBlockie } = this.props + + return h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('span', this.context.t('blockiesIdenticon')), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h(ToggleButton, { + value: useBlockie, + onToggle: (value) => setUseBlockie(!value), + activeLabel: '', + inactiveLabel: '', + }), + ]), + ]), + ]) + } + + renderCurrentConversion () { + const { metamask: { currentCurrency, conversionDate }, setCurrentCurrency } = this.props + + return h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('span', this.context.t('currentConversion')), + h('span.settings__content-description', `Updated ${Date(conversionDate)}`), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h(SimpleDropdown, { + placeholder: this.context.t('selectCurrency'), + options: getInfuraCurrencyOptions(), + selectedOption: currentCurrency, + onSelect: newCurrency => setCurrentCurrency(newCurrency), + }), + ]), + ]), + ]) + } + + renderCurrentLocale () { + const { updateCurrentLocale, currentLocale } = this.props + const currentLocaleMeta = locales.find(locale => locale.code === currentLocale) + const currentLocaleName = currentLocaleMeta ? currentLocaleMeta.name : '' + + return h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('span', 'Current Language'), + h('span.settings__content-description', `${currentLocaleName}`), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h(SimpleDropdown, { + placeholder: 'Select Locale', + options: getLocaleOptions(), + selectedOption: currentLocale, + onSelect: async (newLocale) => { + updateCurrentLocale(newLocale) + }, + }), + ]), + ]), + ]) + } + + renderCurrentProvider () { + const { metamask: { provider = {} } } = this.props + let title, value, color + + switch (provider.type) { + + case 'mainnet': + title = this.context.t('currentNetwork') + value = this.context.t('mainnet') + color = '#038789' + break + + case 'ropsten': + title = this.context.t('currentNetwork') + value = this.context.t('ropsten') + color = '#e91550' + break + + case 'kovan': + title = this.context.t('currentNetwork') + value = this.context.t('kovan') + color = '#690496' + break + + case 'rinkeby': + title = this.context.t('currentNetwork') + value = this.context.t('rinkeby') + color = '#ebb33f' + break + + default: + title = this.context.t('currentRpc') + value = provider.rpcTarget + } + + return h('div.settings__content-row', [ + h('div.settings__content-item', title), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('div.settings__provider-wrapper', [ + h('div.settings__provider-icon', { style: { background: color } }), + h('div', value), + ]), + ]), + ]), + ]) + } + + renderNewRpcUrl () { + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('span', this.context.t('newRPC')), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('input.settings__input', { + placeholder: this.context.t('newRPC'), + onChange: event => this.setState({ newRpc: event.target.value }), + onKeyPress: event => { + if (event.key === 'Enter') { + this.validateRpc(this.state.newRpc) + } + }, + }), + h('div.settings__rpc-save-button', { + onClick: event => { + event.preventDefault() + this.validateRpc(this.state.newRpc) + }, + }, this.context.t('save')), + ]), + ]), + ]) + ) + } + + validateRpc (newRpc) { + const { setRpcTarget, displayWarning } = this.props + + if (validUrl.isWebUri(newRpc)) { + setRpcTarget(newRpc) + } else { + const appendedRpc = `http://${newRpc}` + + if (validUrl.isWebUri(appendedRpc)) { + displayWarning(this.context.t('uriErrorMsg')) + } else { + displayWarning(this.context.t('invalidRPC')) + } + } + } + + renderStateLogs () { + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('div', this.context.t('stateLogs')), + h( + 'div.settings__content-description', + this.context.t('stateLogsDescription') + ), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.btn-primary--lg.settings__button', { + onClick (event) { + window.logStateString((err, result) => { + if (err) { + this.state.dispatch(actions.displayWarning(this.context.t('stateLogError'))) + } else { + exportAsFile('MetaMask State Logs.json', result) + } + }) + }, + }, this.context.t('downloadStateLogs')), + ]), + ]), + ]) + ) + } + + renderSeedWords () { + const { history } = this.props + + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', this.context.t('revealSeedWords')), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.btn-primary--lg.settings__button--red', { + onClick: event => { + event.preventDefault() + history.push(REVEAL_SEED_ROUTE) + }, + }, this.context.t('revealSeedWords')), + ]), + ]), + ]) + ) + } + + renderOldUI () { + const { setFeatureFlagToBeta } = this.props + + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', this.context.t('useOldUI')), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.btn-primary--lg.settings__button--orange', { + onClick (event) { + event.preventDefault() + setFeatureFlagToBeta() + }, + }, this.context.t('useOldUI')), + ]), + ]), + ]) + ) + } + + renderResetAccount () { + const { showResetAccountConfirmationModal } = this.props + + return h('div.settings__content-row', [ + h('div.settings__content-item', this.context.t('resetAccount')), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.btn-primary--lg.settings__button--orange', { + onClick (event) { + event.preventDefault() + showResetAccountConfirmationModal() + }, + }, this.context.t('resetAccount')), + ]), + ]), + ]) + } + + render () { + const { warning, isMascara } = this.props + + return ( + h('div.settings__content', [ + warning && h('div.settings__error', warning), + this.renderCurrentConversion(), + this.renderCurrentLocale(), + // this.renderCurrentProvider(), + this.renderNewRpcUrl(), + this.renderStateLogs(), + this.renderSeedWords(), + !isMascara && this.renderOldUI(), + this.renderResetAccount(), + this.renderBlockieOptIn(), + ]) + ) + } +} + +Settings.propTypes = { + metamask: PropTypes.object, + setUseBlockie: PropTypes.func, + setCurrentCurrency: PropTypes.func, + setRpcTarget: PropTypes.func, + displayWarning: PropTypes.func, + revealSeedConfirmation: PropTypes.func, + setFeatureFlagToBeta: PropTypes.func, + showResetAccountConfirmationModal: PropTypes.func, + warning: PropTypes.string, + history: PropTypes.object, + isMascara: PropTypes.bool, + updateCurrentLocale: PropTypes.func, + currentLocale: PropTypes.string, + t: PropTypes.func, +} + +const mapStateToProps = state => { + return { + metamask: state.metamask, + warning: state.appState.warning, + isMascara: state.metamask.isMascara, + currentLocale: state.metamask.currentLocale, + } +} + +const mapDispatchToProps = dispatch => { + return { + setCurrentCurrency: currency => dispatch(actions.setCurrentCurrency(currency)), + setRpcTarget: newRpc => dispatch(actions.setRpcTarget(newRpc)), + displayWarning: warning => dispatch(actions.displayWarning(warning)), + revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()), + setUseBlockie: value => dispatch(actions.setUseBlockie(value)), + updateCurrentLocale: key => dispatch(actions.updateCurrentLocale(key)), + setFeatureFlagToBeta: () => { + return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) + .then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE))) + }, + showResetAccountConfirmationModal: () => { + return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' })) + }, + } +} + +Settings.contextTypes = { + t: PropTypes.func, +} + +module.exports = compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(Settings) |