diff options
Diffstat (limited to 'ui/app/settings.js')
-rw-r--r-- | ui/app/settings.js | 431 |
1 files changed, 394 insertions, 37 deletions
diff --git a/ui/app/settings.js b/ui/app/settings.js index 454cc95e0..a3dd65f14 100644 --- a/ui/app/settings.js +++ b/ui/app/settings.js @@ -1,59 +1,416 @@ -const inherits = require('util').inherits -const Component = require('react').Component +const { Component } = require('react') +const PropTypes = require('prop-types') const h = require('react-hyperscript') -const connect = require('react-redux').connect +const { connect } = require('react-redux') const actions = require('./actions') +const infuraCurrencies = require('./infura-conversion.json') +const validUrl = require('valid-url') +const { exportAsFile } = require('./util') +const TabBar = require('./components/tab-bar') +const SimpleDropdown = require('./components/dropdowns/simple-dropdown') +const ToggleButton = require('react-toggle-button') +const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums -module.exports = connect(mapStateToProps)(AppSettingsPage) +const getInfuraCurrencyOptions = () => { + const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { + return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase()) + }) -function mapStateToProps (state) { - return {} + return sortedCurrencies.map(({ quote: { code, name } }) => { + return { + displayValue: `${code.toUpperCase()} - ${name}`, + key: code, + value: code, + } + }) } -inherits(AppSettingsPage, Component) -function AppSettingsPage () { - Component.call(this) -} +class Settings extends Component { + constructor (props) { + super(props) -AppSettingsPage.prototype.render = function () { - return ( + const { tab } = props + const activeTab = tab === 'info' ? 'info' : 'settings' - h('.account-detail-section.flex-column.flex-grow', [ + this.state = { + activeTab, + newRpc: '', + } + } - // subtitle and nav - h('.flex-row.flex-center', [ - h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { - onClick: this.navigateToAccounts.bind(this), - }), - h('h2.page-subtitle', 'Settings'), - ]), + renderTabs () { + const { activeTab } = this.state - h('label', { - htmlFor: 'settings-rpc-endpoint', - }, 'RPC Endpoint:'), - h('input', { - type: 'url', - id: 'settings-rpc-endpoint', - onKeyPress: this.onKeyPress.bind(this), + return h('div.settings__tabs', [ + h(TabBar, { + tabs: [ + { content: 'Settings', key: 'settings' }, + { content: 'Info', key: 'info' }, + ], + defaultTab: activeTab, + tabSelected: key => this.setState({ activeTab: key }), }), + ]) + } + + renderBlockieOptIn () { + const { metamask: { useBlockie }, setUseBlockie } = this.props + + return h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('span', 'Use Blockies Identicon'), + ]), + 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', 'Current Conversion'), + h('span.settings__content-description', `Updated ${Date(conversionDate)}`), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h(SimpleDropdown, { + placeholder: 'Select Currency', + options: getInfuraCurrencyOptions(), + selectedOption: currentCurrency, + onSelect: newCurrency => setCurrentCurrency(newCurrency), + }), + ]), + ]), ]) + } + + renderCurrentProvider () { + const { metamask: { provider = {} } } = this.props + let title, value, color + + switch (provider.type) { + + case 'mainnet': + title = 'Current Network' + value = 'Main Ethereum Network' + color = '#038789' + break + + case 'ropsten': + title = 'Current Network' + value = 'Ropsten Test Network' + color = '#e91550' + break + + case 'kovan': + title = 'Current Network' + value = 'Kovan Test Network' + color = '#690496' + break + + case 'rinkeby': + title = 'Current Network' + value = 'Rinkeby Test Network' + color = '#ebb33f' + break + + default: + title = 'Current RPC' + 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', 'New RPC URL'), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('input.settings__input', { + placeholder: 'New RPC URL', + 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) + }, + }, 'Save'), + ]), + ]), + ]) + ) + } + + validateRpc (newRpc) { + const { setRpcTarget, displayWarning } = this.props + + if (validUrl.isWebUri(newRpc)) { + setRpcTarget(newRpc) + } else { + const appendedRpc = `http://${newRpc}` + + if (validUrl.isWebUri(appendedRpc)) { + displayWarning('URIs require the appropriate HTTP/HTTPS prefix.') + } else { + displayWarning('Invalid RPC URI') + } + } + } + + renderStateLogs () { + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('div', 'State Logs'), + h( + 'div.settings__content-description', + 'State logs contain your public account addresses and sent transactions.' + ), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.settings__clear-button', { + onClick (event) { + exportAsFile('MetaMask State Logs', window.logState()) + }, + }, 'Download State Logs'), + ]), + ]), + ]) + ) + } + + renderSeedWords () { + const { revealSeedConfirmation } = this.props + + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', 'Reveal Seed Words'), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.settings__clear-button.settings__clear-button--red', { + onClick (event) { + event.preventDefault() + revealSeedConfirmation() + }, + }, 'Reveal Seed Words'), + ]), + ]), + ]) + ) + } + + renderOldUI () { + const { setFeatureFlagToBeta } = this.props + + return ( + h('div.settings__content-row', [ + h('div.settings__content-item', 'Use old UI'), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h('button.settings__clear-button.settings__clear-button--orange', { + onClick (event) { + event.preventDefault() + setFeatureFlagToBeta() + }, + }, 'Use old UI'), + ]), + ]), + ]) + ) + } + + renderSettingsContent () { + const { warning, isMascara } = this.props - ) + return ( + h('div.settings__content', [ + warning && h('div.settings__error', warning), + this.renderBlockieOptIn(), + this.renderCurrentConversion(), + // this.renderCurrentProvider(), + this.renderNewRpcUrl(), + this.renderStateLogs(), + this.renderSeedWords(), + !isMascara && this.renderOldUI(), + ]) + ) + } + + 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', 'Links'), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/privacy.html', + target: '_blank', + }, [ + h('span.settings__info-link', 'Privacy Policy'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/terms.html', + target: '_blank', + }, [ + h('span.settings__info-link', 'Terms of Use'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/attributions.html', + target: '_blank', + }, [ + h('span.settings__info-link', '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', 'Visit our Support Center'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/', + target: '_blank', + }, [ + h('span.settings__info-link', 'Visit our web site'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + target: '_blank', + href: 'mailto:help@metamask.io?subject=Feedback', + }, [ + h('span.settings__info-link', 'Email us!'), + ]), + ]), + ]) + ) + } + + renderInfoContent () { + 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', + 'MetaMask is designed and built in California.' + ), + ]), + ]), + this.renderInfoLinks(), + ]), + ]) + ) + } + + render () { + const { goHome } = this.props + const { activeTab } = this.state + + return ( + h('.main-container.settings', {}, [ + h('.settings__header', [ + h('div.settings__close-button', { + onClick: goHome, + }), + this.renderTabs(), + ]), + + activeTab === 'settings' + ? this.renderSettingsContent() + : this.renderInfoContent(), + ]) + ) + } } -AppSettingsPage.prototype.componentDidMount = function () { - document.querySelector('input').focus() +Settings.propTypes = { + tab: PropTypes.string, + metamask: PropTypes.object, + setUseBlockie: PropTypes.func, + setCurrentCurrency: PropTypes.func, + setRpcTarget: PropTypes.func, + displayWarning: PropTypes.func, + revealSeedConfirmation: PropTypes.func, + setFeatureFlagToBeta: PropTypes.func, + warning: PropTypes.string, + goHome: PropTypes.func, + isMascara: PropTypes.bool, } -AppSettingsPage.prototype.onKeyPress = function (event) { - // get submit event - if (event.key === 'Enter') { - // this.submitPassword(event) +const mapStateToProps = state => { + return { + metamask: state.metamask, + warning: state.appState.warning, + isMascara: state.metamask.isMascara, } } -AppSettingsPage.prototype.navigateToAccounts = function (event) { - event.stopPropagation() - this.props.dispatch(actions.showAccountsPage()) +const mapDispatchToProps = dispatch => { + return { + goHome: () => dispatch(actions.goHome()), + 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)), + setFeatureFlagToBeta: () => { + return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) + .then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE))) + }, + } } + +module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings) |