feature/default_network_editable
commit
8ee01f4e99
@ -1,4 +1,4 @@ |
||||
{ |
||||
"presets": [["env"], "react", "stage-0"], |
||||
"presets": [["env", { "targets": { "browsers": [">0.25%", "not ie 11", "not op_mini all"] } } ], "react", "stage-0"], |
||||
"plugins": ["transform-runtime", "transform-async-to-generator", "transform-class-properties"] |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,202 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../ui/app/actions') |
||||
const Tooltip = require('./components/tooltip.js') |
||||
const ethUtil = require('ethereumjs-util') |
||||
const Copyable = require('./components/copyable') |
||||
const addressSummary = require('./util').addressSummary |
||||
|
||||
|
||||
module.exports = connect(mapStateToProps)(AddSuggestedTokenScreen) |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
identities: state.metamask.identities, |
||||
suggestedTokens: state.metamask.suggestedTokens, |
||||
} |
||||
} |
||||
|
||||
inherits(AddSuggestedTokenScreen, Component) |
||||
function AddSuggestedTokenScreen () { |
||||
this.state = { |
||||
warning: null, |
||||
} |
||||
Component.call(this) |
||||
} |
||||
|
||||
AddSuggestedTokenScreen.prototype.render = function () { |
||||
const state = this.state |
||||
const props = this.props |
||||
const { warning } = state |
||||
const key = Object.keys(props.suggestedTokens)[0] |
||||
const { address, symbol, decimals } = props.suggestedTokens[key] |
||||
|
||||
return ( |
||||
h('.flex-column.flex-grow', [ |
||||
|
||||
// subtitle and nav
|
||||
h('.section-title.flex-row.flex-center', [ |
||||
h('h2.page-subtitle', 'Add Suggested Token'), |
||||
]), |
||||
|
||||
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', |
||||
}, |
||||
}, [ |
||||
|
||||
h('div', [ |
||||
h(Tooltip, { |
||||
position: 'top', |
||||
title: 'The contract of the actual token contract. Click for more info.', |
||||
}, [ |
||||
h('a', { |
||||
style: { fontWeight: 'bold', paddingRight: '10px'}, |
||||
href: 'https://support.metamask.io/kb/article/24-what-is-a-token-contract-address', |
||||
target: '_blank', |
||||
}, [ |
||||
h('span', 'Token Contract Address '), |
||||
h('i.fa.fa-question-circle'), |
||||
]), |
||||
]), |
||||
]), |
||||
|
||||
h('div', { |
||||
style: { display: 'flex' }, |
||||
}, [ |
||||
h(Copyable, { |
||||
value: ethUtil.toChecksumAddress(address), |
||||
}, [ |
||||
h('span#token-address', { |
||||
style: { |
||||
width: 'inherit', |
||||
flex: '1 0 auto', |
||||
height: '30px', |
||||
margin: '8px', |
||||
display: 'flex', |
||||
}, |
||||
}, addressSummary(address, 24, 4, false)), |
||||
]), |
||||
]), |
||||
|
||||
h('div', [ |
||||
h('span', { |
||||
style: { fontWeight: 'bold', paddingRight: '10px'}, |
||||
}, 'Token Symbol'), |
||||
]), |
||||
|
||||
h('div', { style: {display: 'flex'} }, [ |
||||
h('p#token_symbol', { |
||||
style: { |
||||
width: 'inherit', |
||||
flex: '1 0 auto', |
||||
height: '30px', |
||||
margin: '8px', |
||||
}, |
||||
}, symbol), |
||||
]), |
||||
|
||||
h('div', [ |
||||
h('span', { |
||||
style: { fontWeight: 'bold', paddingRight: '10px'}, |
||||
}, 'Decimals of Precision'), |
||||
]), |
||||
|
||||
h('div', { style: {display: 'flex'} }, [ |
||||
h('p#token_decimals', { |
||||
type: 'number', |
||||
style: { |
||||
width: 'inherit', |
||||
flex: '1 0 auto', |
||||
height: '30px', |
||||
margin: '8px', |
||||
}, |
||||
}, decimals), |
||||
]), |
||||
|
||||
h('button', { |
||||
style: { |
||||
alignSelf: 'center', |
||||
margin: '8px', |
||||
}, |
||||
onClick: (event) => { |
||||
this.props.dispatch(actions.removeSuggestedTokens()) |
||||
}, |
||||
}, 'Cancel'), |
||||
|
||||
h('button', { |
||||
style: { |
||||
alignSelf: 'center', |
||||
margin: '8px', |
||||
}, |
||||
onClick: (event) => { |
||||
const valid = this.validateInputs({ address, symbol, decimals }) |
||||
if (!valid) return |
||||
|
||||
this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals)) |
||||
.then(() => { |
||||
this.props.dispatch(actions.removeSuggestedTokens()) |
||||
}) |
||||
}, |
||||
}, 'Add'), |
||||
]), |
||||
]), |
||||
]) |
||||
) |
||||
} |
||||
|
||||
AddSuggestedTokenScreen.prototype.componentWillMount = function () { |
||||
if (typeof global.ethereumProvider === 'undefined') return |
||||
} |
||||
|
||||
AddSuggestedTokenScreen.prototype.validateInputs = function (opts) { |
||||
let msg = '' |
||||
const identitiesList = Object.keys(this.props.identities) |
||||
const { address, symbol, decimals } = opts |
||||
const standardAddress = ethUtil.addHexPrefix(address).toLowerCase() |
||||
|
||||
const validAddress = ethUtil.isValidAddress(address) |
||||
if (!validAddress) { |
||||
msg += 'Address is invalid.' |
||||
} |
||||
|
||||
const validDecimals = decimals >= 0 && decimals <= 36 |
||||
if (!validDecimals) { |
||||
msg += 'Decimals must be at least 0, and not over 36. ' |
||||
} |
||||
|
||||
const symbolLen = symbol.trim().length |
||||
const validSymbol = symbolLen > 0 && symbolLen < 10 |
||||
if (!validSymbol) { |
||||
msg += 'Symbol must be between 0 and 10 characters.' |
||||
} |
||||
|
||||
const ownAddress = identitiesList.includes(standardAddress) |
||||
if (ownAddress) { |
||||
msg = 'Personal address detected. Input the token contract address.' |
||||
} |
||||
|
||||
const isValid = validAddress && validDecimals && !ownAddress |
||||
|
||||
if (!isValid) { |
||||
this.setState({ |
||||
warning: msg, |
||||
}) |
||||
} else { |
||||
this.setState({ warning: null }) |
||||
} |
||||
|
||||
return isValid |
||||
} |
@ -1,33 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
// Main Views
|
||||
const TxView = require('./components/tx-view') |
||||
const WalletView = require('./components/wallet-view') |
||||
|
||||
module.exports = AccountAndTransactionDetails |
||||
|
||||
inherits(AccountAndTransactionDetails, Component) |
||||
function AccountAndTransactionDetails () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
AccountAndTransactionDetails.prototype.render = function () { |
||||
return h('div.account-and-transaction-details', [ |
||||
// wallet
|
||||
h(WalletView, { |
||||
style: { |
||||
}, |
||||
responsiveDisplayClassname: '.lap-visible', |
||||
}, [ |
||||
]), |
||||
|
||||
// transaction
|
||||
h(TxView, { |
||||
style: { |
||||
}, |
||||
}, [ |
||||
]), |
||||
]) |
||||
} |
||||
|
@ -1,267 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const PropTypes = require('prop-types') |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../actions') |
||||
const CoinbaseForm = require('./coinbase-form') |
||||
const ShapeshiftForm = require('./shapeshift-form') |
||||
const Loading = require('./loading-screen') |
||||
const AccountPanel = require('./account-panel') |
||||
const RadioList = require('./custom-radio-list') |
||||
const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util') |
||||
|
||||
BuyButtonSubview.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps)(BuyButtonSubview) |
||||
|
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
identity: state.appState.identity, |
||||
account: state.metamask.accounts[state.appState.buyView.buyAddress], |
||||
warning: state.appState.warning, |
||||
buyView: state.appState.buyView, |
||||
network: state.metamask.network, |
||||
provider: state.metamask.provider, |
||||
context: state.appState.currentView.context, |
||||
isSubLoading: state.appState.isSubLoading, |
||||
} |
||||
} |
||||
|
||||
inherits(BuyButtonSubview, Component) |
||||
function BuyButtonSubview () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.render = function () { |
||||
return ( |
||||
h('div', { |
||||
style: { |
||||
width: '100%', |
||||
}, |
||||
}, [ |
||||
this.headerSubview(), |
||||
this.primarySubview(), |
||||
]) |
||||
) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.headerSubview = function () { |
||||
const props = this.props |
||||
const isLoading = props.isSubLoading |
||||
return ( |
||||
|
||||
h('.flex-column', { |
||||
style: { |
||||
alignItems: 'center', |
||||
}, |
||||
}, [ |
||||
|
||||
// header bar (back button, label)
|
||||
h('.flex-row', { |
||||
style: { |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
}, |
||||
}, [ |
||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { |
||||
onClick: this.backButtonContext.bind(this), |
||||
style: { |
||||
position: 'absolute', |
||||
left: '10px', |
||||
}, |
||||
}), |
||||
h('h2.text-transform-uppercase.flex-center', { |
||||
style: { |
||||
width: '100vw', |
||||
background: 'rgb(235, 235, 235)', |
||||
color: 'rgb(174, 174, 174)', |
||||
paddingTop: '4px', |
||||
paddingBottom: '4px', |
||||
}, |
||||
}, this.context.t('depositEth')), |
||||
]), |
||||
|
||||
// loading indication
|
||||
h('div', { |
||||
style: { |
||||
position: 'absolute', |
||||
top: '57vh', |
||||
left: '49vw', |
||||
}, |
||||
}, [ |
||||
isLoading && h(Loading), |
||||
]), |
||||
|
||||
// account panel
|
||||
h('div', { |
||||
style: { |
||||
width: '80%', |
||||
}, |
||||
}, [ |
||||
h(AccountPanel, { |
||||
showFullAddress: true, |
||||
identity: props.identity, |
||||
account: props.account, |
||||
}), |
||||
]), |
||||
|
||||
h('.flex-row', { |
||||
style: { |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
}, |
||||
}, [ |
||||
h('h3.text-transform-uppercase.flex-center', { |
||||
style: { |
||||
paddingLeft: '15px', |
||||
width: '100vw', |
||||
background: 'rgb(235, 235, 235)', |
||||
color: 'rgb(174, 174, 174)', |
||||
paddingTop: '4px', |
||||
paddingBottom: '4px', |
||||
}, |
||||
}, this.context.t('selectService')), |
||||
]), |
||||
|
||||
]) |
||||
|
||||
) |
||||
} |
||||
|
||||
|
||||
BuyButtonSubview.prototype.primarySubview = function () { |
||||
const props = this.props |
||||
const network = props.network |
||||
|
||||
switch (network) { |
||||
case 'loading': |
||||
return |
||||
|
||||
case '1': |
||||
return this.mainnetSubview() |
||||
|
||||
// Ropsten, Rinkeby, Kovan
|
||||
case '3': |
||||
case '4': |
||||
case '42': |
||||
const networkName = getNetworkDisplayName(network) |
||||
const label = `${networkName} ${this.context.t('testFaucet')}` |
||||
return ( |
||||
h('div.flex-column', { |
||||
style: { |
||||
alignItems: 'center', |
||||
margin: '20px 50px', |
||||
}, |
||||
}, [ |
||||
h('button.text-transform-uppercase', { |
||||
onClick: () => this.props.dispatch(actions.buyEth({ network })), |
||||
style: { |
||||
marginTop: '15px', |
||||
}, |
||||
}, label), |
||||
// Kovan only: Dharma loans beta
|
||||
network === '42' ? ( |
||||
h('button.text-transform-uppercase', { |
||||
onClick: () => this.navigateTo('https://borrow.dharma.io/'), |
||||
style: { |
||||
marginTop: '15px', |
||||
}, |
||||
}, this.context.t('borrowDharma')) |
||||
) : null, |
||||
]) |
||||
) |
||||
|
||||
default: |
||||
return ( |
||||
h('h2.error', this.context.t('unknownNetworkId')) |
||||
) |
||||
|
||||
} |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.mainnetSubview = function () { |
||||
const props = this.props |
||||
|
||||
return ( |
||||
|
||||
h('.flex-column', { |
||||
style: { |
||||
alignItems: 'center', |
||||
}, |
||||
}, [ |
||||
|
||||
h('.flex-row.selected-exchange', { |
||||
style: { |
||||
position: 'relative', |
||||
right: '35px', |
||||
marginTop: '20px', |
||||
marginBottom: '20px', |
||||
}, |
||||
}, [ |
||||
h(RadioList, { |
||||
defaultFocus: props.buyView.subview, |
||||
labels: [ |
||||
'Coinbase', |
||||
'ShapeShift', |
||||
], |
||||
subtext: { |
||||
'Coinbase': `${this.context.t('crypto')}/${this.context.t('fiat')} (${this.context.t('usaOnly')})`, |
||||
'ShapeShift': this.context.t('crypto'), |
||||
}, |
||||
onClick: this.radioHandler.bind(this), |
||||
}), |
||||
]), |
||||
|
||||
h('h3.text-transform-uppercase', { |
||||
style: { |
||||
paddingLeft: '15px', |
||||
fontFamily: 'Montserrat Light', |
||||
width: '100vw', |
||||
background: 'rgb(235, 235, 235)', |
||||
color: 'rgb(174, 174, 174)', |
||||
paddingTop: '4px', |
||||
paddingBottom: '4px', |
||||
}, |
||||
}, props.buyView.subview), |
||||
|
||||
this.formVersionSubview(), |
||||
]) |
||||
|
||||
) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.formVersionSubview = function () { |
||||
const network = this.props.network |
||||
if (network === '1') { |
||||
if (this.props.buyView.formView.coinbase) { |
||||
return h(CoinbaseForm, this.props) |
||||
} else if (this.props.buyView.formView.shapeshift) { |
||||
return h(ShapeshiftForm, this.props) |
||||
} |
||||
} |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.navigateTo = function (url) { |
||||
global.platform.openWindow({ url }) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.backButtonContext = function () { |
||||
if (this.props.context === 'confTx') { |
||||
this.props.dispatch(actions.showConfTxPage({transForward: false})) |
||||
} else { |
||||
this.props.dispatch(actions.goHome()) |
||||
} |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.radioHandler = function (event) { |
||||
switch (event.target.title) { |
||||
case 'Coinbase': |
||||
return this.props.dispatch(actions.coinBaseSubview()) |
||||
case 'ShapeShift': |
||||
return this.props.dispatch(actions.shapeShiftSubview(this.props.provider.type)) |
||||
} |
||||
} |
@ -0,0 +1,26 @@ |
||||
import React, { PureComponent } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import { ETH } from '../../constants/common' |
||||
|
||||
export default class CurrencyDisplay extends PureComponent { |
||||
static propTypes = { |
||||
className: PropTypes.string, |
||||
displayValue: PropTypes.string, |
||||
prefix: PropTypes.string, |
||||
currency: PropTypes.oneOf([ETH]), |
||||
} |
||||
|
||||
render () { |
||||
const { className, displayValue, prefix } = this.props |
||||
const text = `${prefix || ''}${displayValue}` |
||||
|
||||
return ( |
||||
<div |
||||
className={className} |
||||
title={text} |
||||
> |
||||
{ text } |
||||
</div> |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
import { connect } from 'react-redux' |
||||
import CurrencyDisplay from './currency-display.component' |
||||
import { getValueFromWeiHex, formatCurrency } from '../../helpers/confirm-transaction/util' |
||||
|
||||
const mapStateToProps = (state, ownProps) => { |
||||
const { value, numberOfDecimals = 2, currency } = ownProps |
||||
const { metamask: { currentCurrency, conversionRate } } = state |
||||
|
||||
const toCurrency = currency || currentCurrency |
||||
const convertedValue = getValueFromWeiHex({ value, toCurrency, conversionRate, numberOfDecimals }) |
||||
const formattedValue = formatCurrency(convertedValue, toCurrency) |
||||
const displayValue = `${formattedValue} ${toCurrency.toUpperCase()}` |
||||
|
||||
return { |
||||
displayValue, |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(CurrencyDisplay) |
@ -0,0 +1 @@ |
||||
export { default } from './currency-display.container' |
@ -0,0 +1,27 @@ |
||||
import React from 'react' |
||||
import assert from 'assert' |
||||
import { shallow } from 'enzyme' |
||||
import CurrencyDisplay from '../currency-display.component' |
||||
|
||||
describe('CurrencyDisplay Component', () => { |
||||
it('should render text with a className', () => { |
||||
const wrapper = shallow(<CurrencyDisplay |
||||
displayValue="$123.45" |
||||
className="currency-display" |
||||
/>) |
||||
|
||||
assert.ok(wrapper.hasClass('currency-display')) |
||||
assert.equal(wrapper.text(), '$123.45') |
||||
}) |
||||
|
||||
it('should render text with a prefix', () => { |
||||
const wrapper = shallow(<CurrencyDisplay |
||||
displayValue="$123.45" |
||||
className="currency-display" |
||||
prefix="-" |
||||
/>) |
||||
|
||||
assert.ok(wrapper.hasClass('currency-display')) |
||||
assert.equal(wrapper.text(), '-$123.45') |
||||
}) |
||||
}) |
@ -0,0 +1,61 @@ |
||||
import assert from 'assert' |
||||
import proxyquire from 'proxyquire' |
||||
|
||||
let mapStateToProps |
||||
|
||||
proxyquire('../currency-display.container.js', { |
||||
'react-redux': { |
||||
connect: ms => { |
||||
mapStateToProps = ms |
||||
return () => ({}) |
||||
}, |
||||
}, |
||||
}) |
||||
|
||||
describe('CurrencyDisplay container', () => { |
||||
describe('mapStateToProps()', () => { |
||||
it('should return the correct props', () => { |
||||
const mockState = { |
||||
metamask: { |
||||
conversionRate: 280.45, |
||||
currentCurrency: 'usd', |
||||
}, |
||||
} |
||||
|
||||
const tests = [ |
||||
{ |
||||
props: { |
||||
value: '0x2386f26fc10000', |
||||
numberOfDecimals: 2, |
||||
currency: 'usd', |
||||
}, |
||||
result: { |
||||
displayValue: '$2.80 USD', |
||||
}, |
||||
}, |
||||
{ |
||||
props: { |
||||
value: '0x2386f26fc10000', |
||||
}, |
||||
result: { |
||||
displayValue: '$2.80 USD', |
||||
}, |
||||
}, |
||||
{ |
||||
props: { |
||||
value: '0x1193461d01595930', |
||||
currency: 'ETH', |
||||
numberOfDecimals: 3, |
||||
}, |
||||
result: { |
||||
displayValue: '1.266 ETH', |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
tests.forEach(({ props, result }) => { |
||||
assert.deepEqual(mapStateToProps(mockState, props), result) |
||||
}) |
||||
}) |
||||
}) |
||||
}) |
@ -1,60 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
|
||||
module.exports = RadioList |
||||
|
||||
inherits(RadioList, Component) |
||||
function RadioList () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
RadioList.prototype.render = function () { |
||||
const props = this.props |
||||
const activeClass = '.custom-radio-selected' |
||||
const inactiveClass = '.custom-radio-inactive' |
||||
const { |
||||
labels, |
||||
defaultFocus, |
||||
} = props |
||||
|
||||
|
||||
return ( |
||||
h('.flex-row', { |
||||
style: { |
||||
fontSize: '12px', |
||||
}, |
||||
}, [ |
||||
h('.flex-column.custom-radios', { |
||||
style: { |
||||
marginRight: '5px', |
||||
}, |
||||
}, |
||||
labels.map((lable, i) => { |
||||
let isSelcted = (this.state !== null) |
||||
isSelcted = isSelcted ? (this.state.selected === lable) : (defaultFocus === lable) |
||||
return h(isSelcted ? activeClass : inactiveClass, { |
||||
title: lable, |
||||
onClick: (event) => { |
||||
this.setState({selected: event.target.title}) |
||||
props.onClick(event) |
||||
}, |
||||
}) |
||||
}) |
||||
), |
||||
h('.text', {}, |
||||
labels.map((lable) => { |
||||
if (props.subtext) { |
||||
return h('.flex-row', {}, [ |
||||
h('.radio-titles', lable), |
||||
h('.radio-titles-subtext', `- ${props.subtext[lable]}`), |
||||
]) |
||||
} else { |
||||
return h('.radio-titles', lable) |
||||
} |
||||
}) |
||||
), |
||||
]) |
||||
) |
||||
} |
||||
|
@ -1,23 +1,39 @@ |
||||
@import './app-header/index'; |
||||
|
||||
@import './button-group/index'; |
||||
|
||||
@import './export-text-container/index'; |
||||
@import './confirm-page-container/index'; |
||||
|
||||
@import './selected-account/index'; |
||||
@import './export-text-container/index'; |
||||
|
||||
@import './info-box/index'; |
||||
|
||||
@import './network-display/index'; |
||||
@import './menu-bar/index'; |
||||
|
||||
@import './confirm-page-container/index'; |
||||
@import './modals/index'; |
||||
|
||||
@import './network-display/index'; |
||||
|
||||
@import './page-container/index'; |
||||
|
||||
@import './pages/index'; |
||||
|
||||
@import './modals/index'; |
||||
@import './selected-account/index'; |
||||
|
||||
@import './sender-to-recipient/index'; |
||||
|
||||
@import './tabs/index'; |
||||
|
||||
@import './transaction-view/index'; |
||||
|
||||
@import './transaction-view-balance/index'; |
||||
|
||||
@import './transaction-list/index'; |
||||
|
||||
@import './transaction-list-item/index'; |
||||
|
||||
@import './transaction-status/index'; |
||||
|
||||
@import './app-header/index'; |
||||
|
||||
@import './sidebars/index'; |
||||
|
@ -0,0 +1 @@ |
||||
export { default } from './menu-bar.container' |
@ -0,0 +1,23 @@ |
||||
.menu-bar { |
||||
display: flex; |
||||
flex-direction: row; |
||||
justify-content: center; |
||||
align-items: center; |
||||
flex: 0 0 auto; |
||||
margin-bottom: 16px; |
||||
padding: 5px; |
||||
border-bottom: 1px solid #e5e5e5; |
||||
|
||||
&__sidebar-button { |
||||
font-size: 1.25rem; |
||||
cursor: pointer; |
||||
padding: 10px; |
||||
} |
||||
|
||||
&__open-in-browser { |
||||
cursor: pointer; |
||||
display: flex; |
||||
justify-content: center; |
||||
padding: 10px; |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
import React, { PureComponent } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import Tooltip from '../tooltip' |
||||
import SelectedAccount from '../selected-account' |
||||
|
||||
export default class MenuBar extends PureComponent { |
||||
static contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
static propTypes = { |
||||
hideSidebar: PropTypes.func, |
||||
isMascara: PropTypes.bool, |
||||
sidebarOpen: PropTypes.bool, |
||||
showSidebar: PropTypes.func, |
||||
} |
||||
|
||||
render () { |
||||
const { t } = this.context |
||||
const { isMascara, sidebarOpen, hideSidebar, showSidebar } = this.props |
||||
|
||||
return ( |
||||
<div className="menu-bar"> |
||||
<Tooltip |
||||
title={t('menu')} |
||||
position="bottom" |
||||
> |
||||
<div |
||||
className="fa fa-bars menu-bar__sidebar-button" |
||||
onClick={() => sidebarOpen ? hideSidebar() : showSidebar()} |
||||
/> |
||||
</Tooltip> |
||||
<SelectedAccount /> |
||||
{ |
||||
!isMascara && ( |
||||
<Tooltip |
||||
title={t('openInTab')} |
||||
position="bottom" |
||||
> |
||||
<div |
||||
className="menu-bar__open-in-browser" |
||||
onClick={() => global.platform.openExtensionInBrowser()} |
||||
> |
||||
<img src="images/popout.svg" /> |
||||
</div> |
||||
</Tooltip> |
||||
) |
||||
} |
||||
</div> |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,26 @@ |
||||
import { connect } from 'react-redux' |
||||
import MenuBar from './menu-bar.component' |
||||
import { showSidebar, hideSidebar } from '../../actions' |
||||
|
||||
const mapStateToProps = state => { |
||||
const { appState: { sidebar: { isOpen }, isMascara } } = state |
||||
|
||||
return { |
||||
sidebarOpen: isOpen, |
||||
isMascara, |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
showSidebar: () => { |
||||
dispatch(showSidebar({ |
||||
transitionName: 'sidebar-right', |
||||
type: 'wallet-view', |
||||
})) |
||||
}, |
||||
hideSidebar: () => dispatch(hideSidebar()), |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MenuBar) |
@ -0,0 +1,126 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import { DEFAULT_ROUTE } from '../../../routes' |
||||
import Button from '../../button' |
||||
import Identicon from '../../../components/identicon' |
||||
import TokenBalance from '../../token-balance' |
||||
|
||||
export default class ConfirmAddSuggestedToken extends Component { |
||||
static contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
static propTypes = { |
||||
history: PropTypes.object, |
||||
clearPendingTokens: PropTypes.func, |
||||
addToken: PropTypes.func, |
||||
pendingTokens: PropTypes.object, |
||||
removeSuggestedTokens: PropTypes.func, |
||||
} |
||||
|
||||
componentDidMount () { |
||||
const { pendingTokens = {}, history } = this.props |
||||
|
||||
if (Object.keys(pendingTokens).length === 0) { |
||||
history.push(DEFAULT_ROUTE) |
||||
} |
||||
} |
||||
|
||||
getTokenName (name, symbol) { |
||||
return typeof name === 'undefined' |
||||
? symbol |
||||
: `${name} (${symbol})` |
||||
} |
||||
|
||||
render () { |
||||
const { addToken, pendingTokens, removeSuggestedTokens, history } = this.props |
||||
const pendingTokenKey = Object.keys(pendingTokens)[0] |
||||
const pendingToken = pendingTokens[pendingTokenKey] |
||||
|
||||
return ( |
||||
<div className="page-container"> |
||||
<div className="page-container__header"> |
||||
<div className="page-container__title"> |
||||
{ this.context.t('addSuggestedTokens') } |
||||
</div> |
||||
<div className="page-container__subtitle"> |
||||
{ this.context.t('likeToAddTokens') } |
||||
</div> |
||||
</div> |
||||
<div className="page-container__content"> |
||||
<div className="confirm-add-token"> |
||||
<div className="confirm-add-token__header"> |
||||
<div className="confirm-add-token__token"> |
||||
{ this.context.t('token') } |
||||
</div> |
||||
<div className="confirm-add-token__balance"> |
||||
{ this.context.t('balance') } |
||||
</div> |
||||
</div> |
||||
<div className="confirm-add-token__token-list"> |
||||
{ |
||||
Object.entries(pendingTokens) |
||||
.map(([ address, token ]) => { |
||||
const { name, symbol, image } = token |
||||
|
||||
return ( |
||||
<div |
||||
className="confirm-add-token__token-list-item" |
||||
key={address} |
||||
> |
||||
<div className="confirm-add-token__token confirm-add-token__data"> |
||||
<Identicon |
||||
className="confirm-add-token__token-icon" |
||||
diameter={48} |
||||
address={address} |
||||
image={image} |
||||
/> |
||||
<div className="confirm-add-token__name"> |
||||
{ this.getTokenName(name, symbol) } |
||||
</div> |
||||
</div> |
||||
<div className="confirm-add-token__balance"> |
||||
<TokenBalance token={token} /> |
||||
</div> |
||||
</div> |
||||
) |
||||
}) |
||||
} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div className="page-container__footer"> |
||||
<Button |
||||
type="default" |
||||
large |
||||
className="page-container__footer-button" |
||||
onClick={() => { |
||||
removeSuggestedTokens() |
||||
.then(() => { |
||||
history.push(DEFAULT_ROUTE) |
||||
}) |
||||
}} |
||||
> |
||||
{ this.context.t('cancel') } |
||||
</Button> |
||||
<Button |
||||
type="primary" |
||||
large |
||||
className="page-container__footer-button" |
||||
onClick={() => { |
||||
addToken(pendingToken) |
||||
.then(() => { |
||||
removeSuggestedTokens() |
||||
.then(() => { |
||||
history.push(DEFAULT_ROUTE) |
||||
}) |
||||
}) |
||||
}} |
||||
> |
||||
{ this.context.t('addToken') } |
||||
</Button> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
import { connect } from 'react-redux' |
||||
import { compose } from 'recompose' |
||||
import ConfirmAddSuggestedToken from './confirm-add-suggested-token.component' |
||||
import { withRouter } from 'react-router-dom' |
||||
|
||||
const extend = require('xtend') |
||||
|
||||
const { addToken, removeSuggestedTokens } = require('../../../actions') |
||||
|
||||
const mapStateToProps = ({ metamask }) => { |
||||
const { pendingTokens, suggestedTokens } = metamask |
||||
const params = extend(pendingTokens, suggestedTokens) |
||||
|
||||
return { |
||||
pendingTokens: params, |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
addToken: ({address, symbol, decimals, image}) => dispatch(addToken(address, symbol, decimals, image)), |
||||
removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), |
||||
} |
||||
} |
||||
|
||||
export default compose( |
||||
withRouter, |
||||
connect(mapStateToProps, mapDispatchToProps) |
||||
)(ConfirmAddSuggestedToken) |
@ -0,0 +1,2 @@ |
||||
import ConfirmAddSuggestedToken from './confirm-add-suggested-token.container' |
||||
module.exports = ConfirmAddSuggestedToken |
@ -1,2 +0,0 @@ |
||||
import TokenBalance from './token-balance.container' |
||||
module.exports = TokenBalance |
@ -1,16 +0,0 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
|
||||
export default class TokenBalance extends Component { |
||||
static propTypes = { |
||||
string: PropTypes.string, |
||||
symbol: PropTypes.string, |
||||
error: PropTypes.string, |
||||
} |
||||
|
||||
render () { |
||||
return ( |
||||
<div className="hide-text-overflow">{ this.props.string }</div> |
||||
) |
||||
} |
||||
} |
@ -1,3 +0,0 @@ |
||||
export const TOKEN_METHOD_TRANSFER = 'transfer' |
||||
export const TOKEN_METHOD_APPROVE = 'approve' |
||||
export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom' |
@ -1,239 +0,0 @@ |
||||
const { Component } = require('react') |
||||
const { connect } = require('react-redux') |
||||
const PropTypes = require('prop-types') |
||||
const { Redirect, withRouter } = require('react-router-dom') |
||||
const { compose } = require('recompose') |
||||
const h = require('react-hyperscript') |
||||
const actions = require('../../actions') |
||||
const log = require('loglevel') |
||||
|
||||
// init
|
||||
const NewKeyChainScreen = require('../../new-keychain') |
||||
// mascara
|
||||
const MascaraBuyEtherScreen = require('../../../../mascara/src/app/first-time/buy-ether-screen').default |
||||
|
||||
// accounts
|
||||
const MainContainer = require('../../main-container') |
||||
|
||||
// other views
|
||||
const BuyView = require('../../components/buy-button-subview') |
||||
const QrView = require('../../components/qr-code') |
||||
|
||||
// Routes
|
||||
const { |
||||
INITIALIZE_BACKUP_PHRASE_ROUTE, |
||||
RESTORE_VAULT_ROUTE, |
||||
CONFIRM_TRANSACTION_ROUTE, |
||||
NOTICE_ROUTE, |
||||
} = require('../../routes') |
||||
|
||||
const { unconfirmedTransactionsCountSelector } = require('../../selectors/confirm-transaction') |
||||
|
||||
class Home extends Component { |
||||
componentDidMount () { |
||||
const { |
||||
history, |
||||
unconfirmedTransactionsCount = 0, |
||||
} = this.props |
||||
|
||||
// unapprovedTxs and unapproved messages
|
||||
if (unconfirmedTransactionsCount > 0) { |
||||
history.push(CONFIRM_TRANSACTION_ROUTE) |
||||
} |
||||
} |
||||
|
||||
render () { |
||||
log.debug('rendering primary') |
||||
const { |
||||
noActiveNotices, |
||||
lostAccounts, |
||||
forgottenPassword, |
||||
currentView, |
||||
activeAddress, |
||||
seedWords, |
||||
} = this.props |
||||
|
||||
// notices
|
||||
if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) { |
||||
return h(Redirect, { |
||||
to: { |
||||
pathname: NOTICE_ROUTE, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
// seed words
|
||||
if (seedWords) { |
||||
log.debug('rendering seed words') |
||||
return h(Redirect, { |
||||
to: { |
||||
pathname: INITIALIZE_BACKUP_PHRASE_ROUTE, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
if (forgottenPassword) { |
||||
log.debug('rendering restore vault screen') |
||||
return h(Redirect, { |
||||
to: { |
||||
pathname: RESTORE_VAULT_ROUTE, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
// show current view
|
||||
switch (currentView.name) { |
||||
|
||||
case 'accountDetail': |
||||
log.debug('rendering main container') |
||||
return h(MainContainer, {key: 'account-detail'}) |
||||
|
||||
case 'newKeychain': |
||||
log.debug('rendering new keychain screen') |
||||
return h(NewKeyChainScreen, {key: 'new-keychain'}) |
||||
|
||||
case 'buyEth': |
||||
log.debug('rendering buy ether screen') |
||||
return h(BuyView, {key: 'buyEthView'}) |
||||
|
||||
case 'onboardingBuyEth': |
||||
log.debug('rendering onboarding buy ether screen') |
||||
return h(MascaraBuyEtherScreen, {key: 'buyEthView'}) |
||||
|
||||
case 'qr': |
||||
log.debug('rendering show qr screen') |
||||
return h('div', { |
||||
style: { |
||||
position: 'absolute', |
||||
height: '100%', |
||||
top: '0px', |
||||
left: '0px', |
||||
}, |
||||
}, [ |
||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { |
||||
onClick: () => this.props.dispatch(actions.backToAccountDetail(activeAddress)), |
||||
style: { |
||||
marginLeft: '10px', |
||||
marginTop: '50px', |
||||
}, |
||||
}), |
||||
h('div', { |
||||
style: { |
||||
position: 'absolute', |
||||
left: '44px', |
||||
width: '285px', |
||||
}, |
||||
}, [ |
||||
h(QrView, {key: 'qr'}), |
||||
]), |
||||
]) |
||||
|
||||
default: |
||||
log.debug('rendering default, account detail screen') |
||||
return h(MainContainer, {key: 'account-detail'}) |
||||
} |
||||
} |
||||
} |
||||
|
||||
Home.propTypes = { |
||||
currentCurrency: PropTypes.string, |
||||
isLoading: PropTypes.bool, |
||||
loadingMessage: PropTypes.string, |
||||
network: PropTypes.string, |
||||
provider: PropTypes.object, |
||||
frequentRpcList: PropTypes.array, |
||||
currentView: PropTypes.object, |
||||
sidebarOpen: PropTypes.bool, |
||||
isMascara: PropTypes.bool, |
||||
isOnboarding: PropTypes.bool, |
||||
isUnlocked: PropTypes.bool, |
||||
networkDropdownOpen: PropTypes.bool, |
||||
history: PropTypes.object, |
||||
dispatch: PropTypes.func, |
||||
selectedAddress: PropTypes.string, |
||||
noActiveNotices: PropTypes.bool, |
||||
lostAccounts: PropTypes.array, |
||||
isInitialized: PropTypes.bool, |
||||
forgottenPassword: PropTypes.bool, |
||||
activeAddress: PropTypes.string, |
||||
unapprovedTxs: PropTypes.object, |
||||
seedWords: PropTypes.string, |
||||
unapprovedMsgCount: PropTypes.number, |
||||
unapprovedPersonalMsgCount: PropTypes.number, |
||||
unapprovedTypedMessagesCount: PropTypes.number, |
||||
welcomeScreenSeen: PropTypes.bool, |
||||
isPopup: PropTypes.bool, |
||||
isMouseUser: PropTypes.bool, |
||||
t: PropTypes.func, |
||||
unconfirmedTransactionsCount: PropTypes.number, |
||||
} |
||||
|
||||
function mapStateToProps (state) { |
||||
const { appState, metamask } = state |
||||
const { |
||||
networkDropdownOpen, |
||||
sidebarOpen, |
||||
isLoading, |
||||
loadingMessage, |
||||
} = appState |
||||
|
||||
const { |
||||
accounts, |
||||
address, |
||||
isInitialized, |
||||
noActiveNotices, |
||||
seedWords, |
||||
unapprovedTxs, |
||||
nextUnreadNotice, |
||||
lostAccounts, |
||||
unapprovedMsgCount, |
||||
unapprovedPersonalMsgCount, |
||||
unapprovedTypedMessagesCount, |
||||
} = metamask |
||||
const selected = address || Object.keys(accounts)[0] |
||||
|
||||
return { |
||||
// state from plugin
|
||||
networkDropdownOpen, |
||||
sidebarOpen, |
||||
isLoading, |
||||
loadingMessage, |
||||
noActiveNotices, |
||||
isInitialized, |
||||
isUnlocked: state.metamask.isUnlocked, |
||||
selectedAddress: state.metamask.selectedAddress, |
||||
currentView: state.appState.currentView, |
||||
activeAddress: state.appState.activeAddress, |
||||
transForward: state.appState.transForward, |
||||
isMascara: state.metamask.isMascara, |
||||
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized), |
||||
isPopup: state.metamask.isPopup, |
||||
seedWords: state.metamask.seedWords, |
||||
unapprovedTxs, |
||||
unapprovedMsgs: state.metamask.unapprovedMsgs, |
||||
unapprovedMsgCount, |
||||
unapprovedPersonalMsgCount, |
||||
unapprovedTypedMessagesCount, |
||||
menuOpen: state.appState.menuOpen, |
||||
network: state.metamask.network, |
||||
provider: state.metamask.provider, |
||||
forgottenPassword: state.appState.forgottenPassword, |
||||
nextUnreadNotice, |
||||
lostAccounts, |
||||
frequentRpcList: state.metamask.frequentRpcList || [], |
||||
currentCurrency: state.metamask.currentCurrency, |
||||
isMouseUser: state.appState.isMouseUser, |
||||
isRevealingSeedWords: state.metamask.isRevealingSeedWords, |
||||
Qr: state.appState.Qr, |
||||
welcomeScreenSeen: state.metamask.welcomeScreenSeen, |
||||
|
||||
// state needed to get account dropdown temporarily rendering from app bar
|
||||
selected, |
||||
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), |
||||
} |
||||
} |
||||
|
||||
module.exports = compose( |
||||
withRouter, |
||||
connect(mapStateToProps) |
||||
)(Home) |
@ -0,0 +1,77 @@ |
||||
import React, { PureComponent } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import Media from 'react-media' |
||||
import { Redirect } from 'react-router-dom' |
||||
import WalletView from '../../wallet-view' |
||||
import TransactionView from '../../transaction-view' |
||||
import { |
||||
INITIALIZE_BACKUP_PHRASE_ROUTE, |
||||
RESTORE_VAULT_ROUTE, |
||||
CONFIRM_TRANSACTION_ROUTE, |
||||
NOTICE_ROUTE, |
||||
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, |
||||
} from '../../../routes' |
||||
|
||||
export default class Home extends PureComponent { |
||||
static propTypes = { |
||||
history: PropTypes.object, |
||||
noActiveNotices: PropTypes.bool, |
||||
lostAccounts: PropTypes.array, |
||||
forgottenPassword: PropTypes.bool, |
||||
seedWords: PropTypes.string, |
||||
suggestedTokens: PropTypes.object, |
||||
unconfirmedTransactionsCount: PropTypes.number, |
||||
} |
||||
|
||||
componentDidMount () { |
||||
const { |
||||
history, |
||||
suggestedTokens = {}, |
||||
unconfirmedTransactionsCount = 0, |
||||
} = this.props |
||||
|
||||
// suggested new tokens
|
||||
if (Object.keys(suggestedTokens).length > 0) { |
||||
history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE) |
||||
} |
||||
|
||||
if (unconfirmedTransactionsCount > 0) { |
||||
history.push(CONFIRM_TRANSACTION_ROUTE) |
||||
} |
||||
} |
||||
|
||||
render () { |
||||
const { |
||||
noActiveNotices, |
||||
lostAccounts, |
||||
forgottenPassword, |
||||
seedWords, |
||||
} = this.props |
||||
|
||||
// notices
|
||||
if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) { |
||||
return <Redirect to={{ pathname: NOTICE_ROUTE }} /> |
||||
} |
||||
|
||||
// seed words
|
||||
if (seedWords) { |
||||
return <Redirect to={{ pathname: INITIALIZE_BACKUP_PHRASE_ROUTE }}/> |
||||
} |
||||
|
||||
if (forgottenPassword) { |
||||
return <Redirect to={{ pathname: RESTORE_VAULT_ROUTE }} /> |
||||
} |
||||
|
||||
return ( |
||||
<div className="main-container"> |
||||
<div className="account-and-transaction-details"> |
||||
<Media |
||||
query="(min-width: 576px)" |
||||
render={() => <WalletView />} |
||||
/> |
||||
<TransactionView /> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
import Home from './home.component' |
||||
import { compose } from 'recompose' |
||||
import { connect } from 'react-redux' |
||||
import { withRouter } from 'react-router-dom' |
||||
import { unconfirmedTransactionsCountSelector } from '../../../selectors/confirm-transaction' |
||||
|
||||
const mapStateToProps = state => { |
||||
const { metamask, appState } = state |
||||
const { |
||||
noActiveNotices, |
||||
lostAccounts, |
||||
seedWords, |
||||
suggestedTokens, |
||||
} = metamask |
||||
const { forgottenPassword } = appState |
||||
|
||||
return { |
||||
noActiveNotices, |
||||
lostAccounts, |
||||
forgottenPassword, |
||||
seedWords, |
||||
suggestedTokens, |
||||
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), |
||||
} |
||||
} |
||||
|
||||
export default compose( |
||||
withRouter, |
||||
connect(mapStateToProps) |
||||
)(Home) |
@ -0,0 +1 @@ |
||||
export { default } from './home.container' |
@ -1,56 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const PropTypes = require('prop-types') |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
|
||||
const AccountPanel = require('./account-panel') |
||||
|
||||
PendingMsgDetails.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
module.exports = connect()(PendingMsgDetails) |
||||
|
||||
|
||||
inherits(PendingMsgDetails, Component) |
||||
function PendingMsgDetails () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
PendingMsgDetails.prototype.render = function () { |
||||
var state = this.props |
||||
var msgData = state.txData |
||||
|
||||
var msgParams = msgData.msgParams || {} |
||||
var address = msgParams.from || state.selectedAddress |
||||
var identity = state.identities[address] || { address: address } |
||||
var account = state.accounts[address] || { address: address } |
||||
|
||||
return ( |
||||
h('div', { |
||||
key: msgData.id, |
||||
style: { |
||||
margin: '10px 20px', |
||||
}, |
||||
}, [ |
||||
|
||||
// account that will sign
|
||||
h(AccountPanel, { |
||||
showFullAddress: true, |
||||
identity: identity, |
||||
account: account, |
||||
imageifyIdenticons: state.imageifyIdenticons, |
||||
}), |
||||
|
||||
// message data
|
||||
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ |
||||
h('.flex-column.flex-space-between', [ |
||||
h('label.font-small.allcaps', this.context.t('message')), |
||||
h('span.font-small', msgParams.data), |
||||
]), |
||||
]), |
||||
|
||||
]) |
||||
) |
||||
} |
@ -1,73 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const PropTypes = require('prop-types') |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const PendingTxDetails = require('./pending-msg-details') |
||||
const connect = require('react-redux').connect |
||||
|
||||
PendingMsg.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
module.exports = connect()(PendingMsg) |
||||
|
||||
|
||||
inherits(PendingMsg, Component) |
||||
function PendingMsg () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
PendingMsg.prototype.render = function () { |
||||
var state = this.props |
||||
var msgData = state.txData |
||||
|
||||
return ( |
||||
|
||||
h('div', { |
||||
key: msgData.id, |
||||
style: { |
||||
maxWidth: '350px', |
||||
}, |
||||
}, [ |
||||
|
||||
// header
|
||||
h('h3', { |
||||
style: { |
||||
fontWeight: 'bold', |
||||
textAlign: 'center', |
||||
}, |
||||
}, this.context.t('signMessage')), |
||||
|
||||
h('.error', { |
||||
style: { |
||||
margin: '10px', |
||||
}, |
||||
}, [ |
||||
this.context.t('signNotice'), |
||||
h('a', { |
||||
href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527', |
||||
style: { color: 'rgb(247, 134, 28)' }, |
||||
onClick: (event) => { |
||||
event.preventDefault() |
||||
const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527' |
||||
global.platform.openWindow({ url }) |
||||
}, |
||||
}, this.context.t('readMore')), |
||||
]), |
||||
|
||||
// message details
|
||||
h(PendingTxDetails, state), |
||||
|
||||
// sign + cancel
|
||||
h('.flex-row.flex-space-around', [ |
||||
h('button', { |
||||
onClick: state.cancelMessage, |
||||
}, this.context.t('cancel')), |
||||
h('button', { |
||||
onClick: state.signMessage, |
||||
}, this.context.t('sign')), |
||||
]), |
||||
]) |
||||
|
||||
) |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue