mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-10-22 19:26:13 +02:00
Merge pull request #2430 from alextsg/cb-130
[NewUI] Update settings screen to new UI
This commit is contained in:
commit
4d968fba1e
@ -21,7 +21,7 @@ const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
|
|||||||
const WalletView = require('./components/wallet-view')
|
const WalletView = require('./components/wallet-view')
|
||||||
|
|
||||||
// other views
|
// other views
|
||||||
const ConfigScreen = require('./config')
|
const Settings = require('./settings')
|
||||||
const AddTokenScreen = require('./add-token')
|
const AddTokenScreen = require('./add-token')
|
||||||
const Import = require('./accounts/import')
|
const Import = require('./accounts/import')
|
||||||
const InfoScreen = require('./info')
|
const InfoScreen = require('./info')
|
||||||
@ -383,7 +383,7 @@ App.prototype.renderPrimary = function () {
|
|||||||
|
|
||||||
case 'config':
|
case 'config':
|
||||||
log.debug('rendering config screen')
|
log.debug('rendering config screen')
|
||||||
return h(ConfigScreen, {key: 'config'})
|
return h(Settings, {key: 'config'})
|
||||||
|
|
||||||
case 'import-menu':
|
case 'import-menu':
|
||||||
log.debug('rendering import screen')
|
log.debug('rendering import screen')
|
||||||
|
91
ui/app/components/dropdowns/simple-dropdown.js
Normal file
91
ui/app/components/dropdowns/simple-dropdown.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
const { Component, PropTypes } = require('react')
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const classnames = require('classnames')
|
||||||
|
const R = require('ramda')
|
||||||
|
|
||||||
|
class SimpleDropdown extends Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
isOpen: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayValue () {
|
||||||
|
const { selectedOption, options } = this.props
|
||||||
|
const matchesOption = option => option.value === selectedOption
|
||||||
|
const matchingOption = R.find(matchesOption)(options)
|
||||||
|
return matchingOption
|
||||||
|
? matchingOption.displayValue || matchingOption.value
|
||||||
|
: selectedOption
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClose () {
|
||||||
|
this.setState({ isOpen: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleOpen () {
|
||||||
|
const { isOpen } = this.state
|
||||||
|
this.setState({ isOpen: !isOpen })
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOptions () {
|
||||||
|
const { options, onSelect, selectedOption } = this.props
|
||||||
|
|
||||||
|
return h('div', [
|
||||||
|
h('div.simple-dropdown__close-area', {
|
||||||
|
onClick: event => {
|
||||||
|
event.stopPropagation()
|
||||||
|
this.handleClose()
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('div.simple-dropdown__options', [
|
||||||
|
...options.map(option => {
|
||||||
|
return h(
|
||||||
|
'div.simple-dropdown__option',
|
||||||
|
{
|
||||||
|
className: classnames({
|
||||||
|
'simple-dropdown__option--selected': option.value === selectedOption,
|
||||||
|
}),
|
||||||
|
key: option.value,
|
||||||
|
onClick: () => {
|
||||||
|
if (option.value !== selectedOption) {
|
||||||
|
onSelect(option.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleClose()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
option.displayValue || option.value,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { placeholder } = this.props
|
||||||
|
const { isOpen } = this.state
|
||||||
|
|
||||||
|
return h(
|
||||||
|
'div.simple-dropdown',
|
||||||
|
{
|
||||||
|
onClick: () => this.toggleOpen(),
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h('div.simple-dropdown__selected', this.getDisplayValue() || placeholder || 'Select'),
|
||||||
|
h('i.fa.fa-caret-down.fa-lg.simple-dropdown__caret'),
|
||||||
|
isOpen && this.renderOptions(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleDropdown.propTypes = {
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
onSelect: PropTypes.func,
|
||||||
|
selectedOption: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SimpleDropdown
|
@ -1,37 +1,40 @@
|
|||||||
const Component = require('react').Component
|
const { Component } = require('react')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const classnames = require('classnames')
|
||||||
|
|
||||||
|
class TabBar extends Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
const { defaultTab, tabs } = props
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
subview: defaultTab || tabs[0].key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { tabs = [], tabSelected } = this.props
|
||||||
|
const { subview } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.tab-bar', {}, [
|
||||||
|
tabs.map((tab) => {
|
||||||
|
const { key, content } = tab
|
||||||
|
return h('div', {
|
||||||
|
className: classnames('tab-bar__tab pointer', {
|
||||||
|
'tab-bar__tab--active': subview === key,
|
||||||
|
}),
|
||||||
|
onClick: () => {
|
||||||
|
this.setState({ subview: key })
|
||||||
|
tabSelected(key)
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
}, content)
|
||||||
|
}),
|
||||||
|
h('div.tab-bar__tab.tab-bar__grow-tab'),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = TabBar
|
module.exports = TabBar
|
||||||
|
|
||||||
inherits(TabBar, Component)
|
|
||||||
function TabBar () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
TabBar.prototype.render = function () {
|
|
||||||
const props = this.props
|
|
||||||
const state = this.state || {}
|
|
||||||
const { tabs = [], defaultTab, tabSelected } = props
|
|
||||||
const { subview = defaultTab } = state
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.flex-row.space-around.text-transform-uppercase', {
|
|
||||||
style: {
|
|
||||||
background: '#EBEBEB',
|
|
||||||
color: '#AEAEAE',
|
|
||||||
paddingTop: '4px',
|
|
||||||
minHeight: '30px',
|
|
||||||
},
|
|
||||||
}, tabs.map((tab) => {
|
|
||||||
const { key, content } = tab
|
|
||||||
return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', {
|
|
||||||
onClick: () => {
|
|
||||||
this.setState({ subview: key })
|
|
||||||
tabSelected(key)
|
|
||||||
},
|
|
||||||
}, content)
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
215
ui/app/config.js
215
ui/app/config.js
@ -1,215 +0,0 @@
|
|||||||
const inherits = require('util').inherits
|
|
||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const actions = require('./actions')
|
|
||||||
const infuraCurrencies = require('./infura-conversion.json').objects.sort((a, b) => {
|
|
||||||
return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase())
|
|
||||||
})
|
|
||||||
const validUrl = require('valid-url')
|
|
||||||
const exportAsFile = require('./util').exportAsFile
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(ConfigScreen)
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
metamask: state.metamask,
|
|
||||||
warning: state.appState.warning,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(ConfigScreen, Component)
|
|
||||||
function ConfigScreen () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigScreen.prototype.render = function () {
|
|
||||||
var state = this.props
|
|
||||||
var metamaskState = state.metamask
|
|
||||||
var warning = state.warning
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.flex-column.flex-grow', { style: { marginTop: '32px' } }, [
|
|
||||||
|
|
||||||
// subtitle and nav
|
|
||||||
h('.section-title.flex-row.flex-center', [
|
|
||||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
|
||||||
onClick: (event) => {
|
|
||||||
state.dispatch(actions.goHome())
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
h('h2.page-subtitle', 'Settings'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('.error', {
|
|
||||||
style: {
|
|
||||||
display: warning ? 'block' : 'none',
|
|
||||||
padding: '0 20px',
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
}, warning),
|
|
||||||
|
|
||||||
// conf view
|
|
||||||
h('.flex-column.flex-justify-center.flex-grow.select-none', [
|
|
||||||
h('.flex-space-around', {
|
|
||||||
style: {
|
|
||||||
padding: '20px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
currentProviderDisplay(metamaskState),
|
|
||||||
|
|
||||||
h('div', { style: {display: 'flex'} }, [
|
|
||||||
h('input#new_rpc', {
|
|
||||||
placeholder: 'New RPC URL',
|
|
||||||
style: {
|
|
||||||
width: 'inherit',
|
|
||||||
flex: '1 0 auto',
|
|
||||||
height: '30px',
|
|
||||||
margin: '8px',
|
|
||||||
},
|
|
||||||
onKeyPress (event) {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
var element = event.target
|
|
||||||
var newRpc = element.value
|
|
||||||
rpcValidation(newRpc, state)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
h('button', {
|
|
||||||
style: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
},
|
|
||||||
onClick (event) {
|
|
||||||
event.preventDefault()
|
|
||||||
var element = document.querySelector('input#new_rpc')
|
|
||||||
var newRpc = element.value
|
|
||||||
rpcValidation(newRpc, state)
|
|
||||||
},
|
|
||||||
}, 'Save'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('hr.horizontal-line'),
|
|
||||||
|
|
||||||
currentConversionInformation(metamaskState, state),
|
|
||||||
|
|
||||||
h('hr.horizontal-line'),
|
|
||||||
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
marginTop: '20px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('p', {
|
|
||||||
style: {
|
|
||||||
fontFamily: 'Montserrat Light',
|
|
||||||
fontSize: '13px',
|
|
||||||
},
|
|
||||||
}, `State logs contain your public account addresses and sent transactions.`),
|
|
||||||
h('br'),
|
|
||||||
h('button', {
|
|
||||||
style: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
},
|
|
||||||
onClick (event) {
|
|
||||||
exportAsFile('MetaMask State Logs', window.logState())
|
|
||||||
},
|
|
||||||
}, 'Download State Logs'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('hr.horizontal-line'),
|
|
||||||
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
marginTop: '20px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('button', {
|
|
||||||
style: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
},
|
|
||||||
onClick (event) {
|
|
||||||
event.preventDefault()
|
|
||||||
state.dispatch(actions.revealSeedConfirmation())
|
|
||||||
},
|
|
||||||
}, 'Reveal Seed Words'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function rpcValidation (newRpc, state) {
|
|
||||||
if (validUrl.isWebUri(newRpc)) {
|
|
||||||
state.dispatch(actions.setRpcTarget(newRpc))
|
|
||||||
} else {
|
|
||||||
var appendedRpc = `http://${newRpc}`
|
|
||||||
if (validUrl.isWebUri(appendedRpc)) {
|
|
||||||
state.dispatch(actions.displayWarning('URIs require the appropriate HTTP/HTTPS prefix.'))
|
|
||||||
} else {
|
|
||||||
state.dispatch(actions.displayWarning('Invalid RPC URI'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentConversionInformation (metamaskState, state) {
|
|
||||||
var currentCurrency = metamaskState.currentCurrency
|
|
||||||
var conversionDate = metamaskState.conversionDate
|
|
||||||
return h('div', [
|
|
||||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
|
|
||||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
|
|
||||||
h('select#currentCurrency', {
|
|
||||||
onChange (event) {
|
|
||||||
event.preventDefault()
|
|
||||||
var element = document.getElementById('currentCurrency')
|
|
||||||
var newCurrency = element.value
|
|
||||||
state.dispatch(actions.setCurrentCurrency(newCurrency))
|
|
||||||
},
|
|
||||||
defaultValue: currentCurrency,
|
|
||||||
}, infuraCurrencies.map((currency) => {
|
|
||||||
console.log(`currency`, currency);
|
|
||||||
return h('option', {key: currency.quote.code, value: currency.quote.code}, `${currency.quote.code.toUpperCase()} - ${currency.quote.name}`)
|
|
||||||
})
|
|
||||||
),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentProviderDisplay (metamaskState) {
|
|
||||||
var provider = metamaskState.provider
|
|
||||||
var title, value
|
|
||||||
|
|
||||||
switch (provider.type) {
|
|
||||||
|
|
||||||
case 'mainnet':
|
|
||||||
title = 'Current Network'
|
|
||||||
value = 'Main Ethereum Network'
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'ropsten':
|
|
||||||
title = 'Current Network'
|
|
||||||
value = 'Ropsten Test Network'
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'kovan':
|
|
||||||
title = 'Current Network'
|
|
||||||
value = 'Kovan Test Network'
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'rinkeby':
|
|
||||||
title = 'Current Network'
|
|
||||||
value = 'Rinkeby Test Network'
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
title = 'Current RPC'
|
|
||||||
value = metamaskState.provider.rpcTarget
|
|
||||||
}
|
|
||||||
|
|
||||||
return h('div', [
|
|
||||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, title),
|
|
||||||
h('span', value),
|
|
||||||
])
|
|
||||||
}
|
|
@ -38,3 +38,8 @@
|
|||||||
|
|
||||||
@import './gas-slider.scss';
|
@import './gas-slider.scss';
|
||||||
|
|
||||||
|
@import './settings.scss';
|
||||||
|
|
||||||
|
@import './tab-bar.scss';
|
||||||
|
|
||||||
|
@import './simple-dropdown.scss';
|
||||||
|
135
ui/app/css/itcss/components/settings.scss
Normal file
135
ui/app/css/itcss/components/settings.scss
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
.settings {
|
||||||
|
position: relative;
|
||||||
|
background: $white;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
height: auto;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__header {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__close-button::after {
|
||||||
|
content: '\00D7';
|
||||||
|
font-size: 40px;
|
||||||
|
color: $dusty-gray;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
right: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__error {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: $crimson;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__content {
|
||||||
|
padding: 0 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__content-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 10px 0 20px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__content-item {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 5px;
|
||||||
|
height: 71px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__content-item-col {
|
||||||
|
max-width: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__content-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $dusty-gray;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__input {
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__input::-webkit-input-placeholder {
|
||||||
|
font-weight: 100;
|
||||||
|
color: $dusty-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__input::-moz-placeholder {
|
||||||
|
font-weight: 100;
|
||||||
|
color: $dusty-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__input:-ms-input-placeholder {
|
||||||
|
font-weight: 100;
|
||||||
|
color: $dusty-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__input:-moz-placeholder {
|
||||||
|
font-weight: 100;
|
||||||
|
color: $dusty-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__provider-wrapper {
|
||||||
|
font-size: 16px;
|
||||||
|
border: 1px solid $alto;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: $white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__provider-icon {
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__rpc-save-button {
|
||||||
|
align-self: flex-end;
|
||||||
|
padding: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: $dusty-gray;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__clear-button {
|
||||||
|
font-size: 16px;
|
||||||
|
border: 1px solid $curious-blue;
|
||||||
|
color: $curious-blue;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 18px;
|
||||||
|
background-color: $white;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings__clear-button--red {
|
||||||
|
border: 1px solid $monzo;
|
||||||
|
color: $monzo;
|
||||||
|
}
|
65
ui/app/css/itcss/components/simple-dropdown.scss
Normal file
65
ui/app/css/itcss/components/simple-dropdown.scss
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
.simple-dropdown {
|
||||||
|
height: 56px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid $alto;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: $white;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #4d4d4d;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-dropdown__caret {
|
||||||
|
color: $silver;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-dropdown__selected {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-dropdown__options {
|
||||||
|
z-index: 1050;
|
||||||
|
position: absolute;
|
||||||
|
height: 220px;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #d2d8dd;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
-webkit-box-shadow: 0 3px 6px 0 rgba(0, 0, 0, .11);
|
||||||
|
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, .11);
|
||||||
|
margin-top: 10px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-dropdown__option {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $gallery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-dropdown__option--selected {
|
||||||
|
background-color: $alto;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $alto;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-dropdown__close-area {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
23
ui/app/css/itcss/components/tab-bar.scss
Normal file
23
ui/app/css/itcss/components/tab-bar.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.tab-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar__tab {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 15px 25px;
|
||||||
|
border-bottom: 1px solid $alto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar__tab--active {
|
||||||
|
border-color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar__grow-tab {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
@ -3,7 +3,7 @@ const h = require('react-hyperscript')
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const AccountAndTransactionDetails = require('./account-and-transaction-details')
|
const AccountAndTransactionDetails = require('./account-and-transaction-details')
|
||||||
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
||||||
const ConfigScreen = require('./config')
|
const Settings = require('./settings')
|
||||||
const UnlockScreen = require('./unlock')
|
const UnlockScreen = require('./unlock')
|
||||||
|
|
||||||
module.exports = MainContainer
|
module.exports = MainContainer
|
||||||
@ -38,7 +38,7 @@ MainContainer.prototype.render = function () {
|
|||||||
case 'config':
|
case 'config':
|
||||||
log.debug('rendering config screen from unlock screen.')
|
log.debug('rendering config screen from unlock screen.')
|
||||||
contents = {
|
contents = {
|
||||||
component: ConfigScreen,
|
component: Settings,
|
||||||
key: 'config',
|
key: 'config',
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -1,59 +1,261 @@
|
|||||||
const inherits = require('util').inherits
|
const { Component } = require('react')
|
||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const { connect } = require('react-redux')
|
||||||
const actions = require('./actions')
|
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')
|
||||||
|
|
||||||
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 sortedCurrencies.map(({ quote: { code, name } }) => {
|
||||||
return {}
|
return {
|
||||||
|
displayValue: `${code.toUpperCase()} - ${name}`,
|
||||||
|
key: code,
|
||||||
|
value: code,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(AppSettingsPage, Component)
|
class Settings extends Component {
|
||||||
function AppSettingsPage () {
|
constructor (args) {
|
||||||
Component.call(this)
|
super(args)
|
||||||
}
|
this.state = {
|
||||||
|
activeTab: 'settings',
|
||||||
|
newRpc: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AppSettingsPage.prototype.render = function () {
|
renderTabs () {
|
||||||
return (
|
return h('div.settings__tabs', [
|
||||||
|
h(TabBar, {
|
||||||
h('.account-detail-section.flex-column.flex-grow', [
|
tabs: [
|
||||||
|
{ content: 'Settings', key: 'settings' },
|
||||||
// subtitle and nav
|
{ content: 'Info', key: 'info' },
|
||||||
h('.flex-row.flex-center', [
|
],
|
||||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
defaultTab: 'settings',
|
||||||
onClick: this.navigateToAccounts.bind(this),
|
tabSelected: key => this.setState({ activeTab: key }),
|
||||||
}),
|
|
||||||
h('h2.page-subtitle', 'Settings'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('label', {
|
|
||||||
htmlFor: 'settings-rpc-endpoint',
|
|
||||||
}, 'RPC Endpoint:'),
|
|
||||||
h('input', {
|
|
||||||
type: 'url',
|
|
||||||
id: 'settings-rpc-endpoint',
|
|
||||||
onKeyPress: this.onKeyPress.bind(this),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
|
||||||
)
|
renderCurrentConversion () {
|
||||||
}
|
const { metamask: { currentCurrency, conversionDate }, setCurrentCurrency } = this.props
|
||||||
|
|
||||||
AppSettingsPage.prototype.componentDidMount = function () {
|
return h('div.settings__content-row', [
|
||||||
document.querySelector('input').focus()
|
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),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
AppSettingsPage.prototype.onKeyPress = function (event) {
|
renderCurrentProvider () {
|
||||||
// get submit event
|
const { metamask: { provider = {} } } = this.props
|
||||||
if (event.key === 'Enter') {
|
let title, value, color
|
||||||
// this.submitPassword(event)
|
|
||||||
|
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'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSettingsContent () {
|
||||||
|
const { warning } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('div.settings__content', [
|
||||||
|
warning && h('div.settings__error', warning),
|
||||||
|
this.renderCurrentConversion(),
|
||||||
|
this.renderCurrentProvider(),
|
||||||
|
this.renderNewRpcUrl(),
|
||||||
|
this.renderStateLogs(),
|
||||||
|
this.renderSeedWords(),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInfoContent () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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.navigateToAccounts = function (event) {
|
const mapStateToProps = state => {
|
||||||
event.stopPropagation()
|
return {
|
||||||
this.props.dispatch(actions.showAccountsPage())
|
metamask: state.metamask,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings)
|
||||||
|
Loading…
Reference in New Issue
Block a user