@ -0,0 +1,10 @@ |
||||
app/ |
||||
development/ |
||||
dist/ |
||||
docs/ |
||||
fonts/ |
||||
images/ |
||||
mascara/ |
||||
node_modules/ |
||||
notices/ |
||||
test/ |
@ -0,0 +1,50 @@ |
||||
{ |
||||
"extends": "stylelint-config-standard", |
||||
"rules": { |
||||
"color-named": "never", |
||||
"font-family-name-quotes": "always-where-recommended", |
||||
"font-weight-notation": "numeric", |
||||
"function-url-quotes": "always", |
||||
"number-leading-zero": "never", |
||||
"value-no-vendor-prefix": true, |
||||
"value-list-comma-newline-before": "never-multi-line", |
||||
"custom-property-empty-line-before": "never", |
||||
"property-no-unknown": [ |
||||
true, |
||||
{ |
||||
"ignoreProperties": [ |
||||
"composes", |
||||
"all", |
||||
"-webkit-appearance" |
||||
] |
||||
} |
||||
], |
||||
"declaration-block-semicolon-newline-after": "always", |
||||
"block-opening-brace-newline-after": "always", |
||||
"selector-attribute-quotes": "always", |
||||
"selector-max-specificity": "0,5,2", |
||||
"selector-pseudo-class-no-unknown": [ |
||||
true, |
||||
{ |
||||
"ignorePseudoClasses": ["local", "global"] |
||||
} |
||||
], |
||||
"at-rule-empty-line-before": [ |
||||
"always", |
||||
{ |
||||
"ignore": [ |
||||
"after-comment", |
||||
] |
||||
} |
||||
], |
||||
"indentation": [ |
||||
2, |
||||
{ |
||||
"indentInsideParens": "once-at-root-twice-in-block" |
||||
} |
||||
], |
||||
"max-nesting-depth": 3, |
||||
"no-duplicate-selectors": true, |
||||
"no-unknown-animations": true |
||||
} |
||||
} |
@ -0,0 +1,93 @@ |
||||
Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" |
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1. |
||||
This license is copied below, and is also available with a FAQ at: |
||||
http://scripts.sil.org/OFL |
||||
|
||||
|
||||
----------------------------------------------------------- |
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 |
||||
----------------------------------------------------------- |
||||
|
||||
PREAMBLE |
||||
The goals of the Open Font License (OFL) are to stimulate worldwide |
||||
development of collaborative font projects, to support the font creation |
||||
efforts of academic and linguistic communities, and to provide a free and |
||||
open framework in which fonts may be shared and improved in partnership |
||||
with others. |
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and |
||||
redistributed freely as long as they are not sold by themselves. The |
||||
fonts, including any derivative works, can be bundled, embedded, |
||||
redistributed and/or sold with any software provided that any reserved |
||||
names are not used by derivative works. The fonts and derivatives, |
||||
however, cannot be released under any other type of license. The |
||||
requirement for fonts to remain under this license does not apply |
||||
to any document created using the fonts or their derivatives. |
||||
|
||||
DEFINITIONS |
||||
"Font Software" refers to the set of files released by the Copyright |
||||
Holder(s) under this license and clearly marked as such. This may |
||||
include source files, build scripts and documentation. |
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the |
||||
copyright statement(s). |
||||
|
||||
"Original Version" refers to the collection of Font Software components as |
||||
distributed by the Copyright Holder(s). |
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting, |
||||
or substituting -- in part or in whole -- any of the components of the |
||||
Original Version, by changing formats or by porting the Font Software to a |
||||
new environment. |
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical |
||||
writer or other person who contributed to the Font Software. |
||||
|
||||
PERMISSION & CONDITIONS |
||||
Permission is hereby granted, free of charge, to any person obtaining |
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify, |
||||
redistribute, and sell modified and unmodified copies of the Font |
||||
Software, subject to the following conditions: |
||||
|
||||
1) Neither the Font Software nor any of its individual components, |
||||
in Original or Modified Versions, may be sold by itself. |
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled, |
||||
redistributed and/or sold with any software, provided that each copy |
||||
contains the above copyright notice and this license. These can be |
||||
included either as stand-alone text files, human-readable headers or |
||||
in the appropriate machine-readable metadata fields within text or |
||||
binary files as long as those fields can be easily viewed by the user. |
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font |
||||
Name(s) unless explicit written permission is granted by the corresponding |
||||
Copyright Holder. This restriction only applies to the primary font name as |
||||
presented to the users. |
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font |
||||
Software shall not be used to promote, endorse or advertise any |
||||
Modified Version, except to acknowledge the contribution(s) of the |
||||
Copyright Holder(s) and the Author(s) or with their explicit written |
||||
permission. |
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole, |
||||
must be distributed entirely under this license, and must not be |
||||
distributed under any other license. The requirement for fonts to |
||||
remain under this license does not apply to any document created |
||||
using the Font Software. |
||||
|
||||
TERMINATION |
||||
This license becomes null and void if any of the above conditions are |
||||
not met. |
||||
|
||||
DISCLAIMER |
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF |
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT |
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE |
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL |
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM |
||||
OTHER DEALINGS IN THE FONT SOFTWARE. |
After Width: | Height: | Size: 854 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 732 B |
After Width: | Height: | Size: 689 B |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.2 KiB |
@ -0,0 +1,10 @@ |
||||
module.exports = function environmentType () { |
||||
const url = window.location.href |
||||
if (url.match(/popup.html$/)) { |
||||
return 'popup' |
||||
} else if (url.match(/home.html$/)) { |
||||
return 'responsive' |
||||
} else { |
||||
return 'notification' |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
var assert = require('assert') |
||||
var BalanceComponent = require('../../../ui/app/components/balance-component') |
||||
|
||||
describe('BalanceComponent', function () { |
||||
let balanceComponent |
||||
|
||||
beforeEach(function () { |
||||
balanceComponent = new BalanceComponent() |
||||
}) |
||||
|
||||
it('shows token balance and convert to fiat value based on conversion rate', function () { |
||||
const formattedBalance = '1.23 ETH' |
||||
|
||||
const tokenBalance = balanceComponent.getTokenBalance(formattedBalance, false) |
||||
const fiatDisplayNumber = balanceComponent.getFiatDisplayNumber(formattedBalance, 2) |
||||
|
||||
assert.equal('1.23 ETH', tokenBalance) |
||||
assert.equal(2.46, fiatDisplayNumber) |
||||
}) |
||||
|
||||
it('shows only the token balance when conversion rate is not available', function () { |
||||
const formattedBalance = '1.23 ETH' |
||||
|
||||
const tokenBalance = balanceComponent.getTokenBalance(formattedBalance, false) |
||||
const fiatDisplayNumber = balanceComponent.getFiatDisplayNumber(formattedBalance, 0) |
||||
|
||||
assert.equal('1.23 ETH', tokenBalance) |
||||
assert.equal('N/A', fiatDisplayNumber) |
||||
}) |
||||
|
||||
}) |
||||
|
@ -0,0 +1,38 @@ |
||||
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', { |
||||
style: { |
||||
display: 'flex', |
||||
flex: '1 0 auto', |
||||
}, |
||||
}, [ |
||||
// wallet
|
||||
h(WalletView, { |
||||
style: { |
||||
}, |
||||
responsiveDisplayClassname: '.lap-visible', |
||||
}, [ |
||||
]), |
||||
|
||||
// transaction
|
||||
h(TxView, { |
||||
style: { |
||||
}, |
||||
}, [ |
||||
]), |
||||
]) |
||||
} |
||||
|
@ -0,0 +1,154 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const connect = require('react-redux').connect |
||||
const h = require('react-hyperscript') |
||||
const actions = require('../../actions') |
||||
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu') |
||||
const Identicon = require('../identicon') |
||||
const { formatBalance } = require('../../util') |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountMenu) |
||||
|
||||
inherits(AccountMenu, Component) |
||||
function AccountMenu () { Component.call(this) } |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
selectedAddress: state.metamask.selectedAddress, |
||||
isAccountMenuOpen: state.metamask.isAccountMenuOpen, |
||||
keyrings: state.metamask.keyrings, |
||||
identities: state.metamask.identities, |
||||
accounts: state.metamask.accounts, |
||||
|
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()), |
||||
showAccountDetail: address => { |
||||
dispatch(actions.showAccountDetail(address)) |
||||
dispatch(actions.toggleAccountMenu()) |
||||
}, |
||||
lockMetamask: () => { |
||||
dispatch(actions.lockMetamask()) |
||||
dispatch(actions.toggleAccountMenu()) |
||||
}, |
||||
showConfigPage: () => { |
||||
console.log('hihihih') |
||||
dispatch(actions.showConfigPage()) |
||||
dispatch(actions.toggleAccountMenu()) |
||||
}, |
||||
showNewAccountModal: () => { |
||||
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' })) |
||||
dispatch(actions.toggleAccountMenu()) |
||||
}, |
||||
showImportPage: () => { |
||||
dispatch(actions.showImportPage()) |
||||
dispatch(actions.toggleAccountMenu()) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
AccountMenu.prototype.render = function () { |
||||
const { |
||||
isAccountMenuOpen, |
||||
toggleAccountMenu, |
||||
showNewAccountModal, |
||||
showImportPage, |
||||
lockMetamask, |
||||
showConfigPage, |
||||
} = this.props |
||||
|
||||
console.log(showConfigPage) |
||||
return h(Menu, { className: 'account-menu', isShowing: isAccountMenuOpen }, [ |
||||
h(CloseArea, { onClick: toggleAccountMenu }), |
||||
h(Item, { |
||||
className: 'account-menu__header', |
||||
onClick: lockMetamask, |
||||
}, [ |
||||
'My Accounts', |
||||
h('button.account-menu__logout-button', 'Log out'), |
||||
]), |
||||
h(Divider), |
||||
h('div.account-menu__accounts', this.renderAccounts()), |
||||
h(Divider), |
||||
h(Item, { |
||||
onClick: showNewAccountModal, |
||||
icon: h('img', { src: 'images/plus-btn-white.svg' }), |
||||
text: 'Create Account', |
||||
}), |
||||
h(Item, { |
||||
onClick: showImportPage, |
||||
icon: h('img', { src: 'images/import-account.svg' }), |
||||
text: 'Import Account', |
||||
}), |
||||
h(Divider), |
||||
h(Item, { |
||||
icon: h('img', { src: 'images/mm-info-icon.svg' }), |
||||
text: 'Info & Help', |
||||
}), |
||||
h(Item, { |
||||
onClick: showConfigPage, |
||||
icon: h('img', { src: 'images/settings.svg' }), |
||||
text: 'Settings', |
||||
}), |
||||
]) |
||||
} |
||||
|
||||
AccountMenu.prototype.renderAccounts = function () { |
||||
const { |
||||
identities, |
||||
accounts, |
||||
selected, |
||||
keyrings, |
||||
showAccountDetail, |
||||
} = this.props |
||||
|
||||
return Object.keys(identities).map((key, index) => { |
||||
const identity = identities[key] |
||||
const isSelected = identity.address === selected |
||||
|
||||
const balanceValue = accounts[key].balance |
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' |
||||
const simpleAddress = identity.address.substring(2).toLowerCase() |
||||
|
||||
const keyring = keyrings.find((kr) => { |
||||
return kr.accounts.includes(simpleAddress) || |
||||
kr.accounts.includes(identity.address) |
||||
}) |
||||
|
||||
return h( |
||||
'div.account-menu__account.menu__item--clickable', |
||||
{ onClick: () => showAccountDetail(identity.address) }, |
||||
[ |
||||
h('div.account-menu__check-mark', [ |
||||
isSelected ? h('i.fa.fa-check') : null, |
||||
]), |
||||
|
||||
h( |
||||
Identicon, |
||||
{ |
||||
address: identity.address, |
||||
diameter: 24, |
||||
}, |
||||
), |
||||
|
||||
h('div.account-menu__account-info', [ |
||||
h('div.account-menu__name', identity.name || ''), |
||||
h('div.account-menu__balance', formattedBalance), |
||||
]), |
||||
|
||||
this.indicateIfLoose(keyring), |
||||
], |
||||
) |
||||
}) |
||||
} |
||||
|
||||
AccountMenu.prototype.indicateIfLoose = function (keyring) { |
||||
try { // Sometimes keyrings aren't loaded yet:
|
||||
const type = keyring.type |
||||
const isLoose = type !== 'HD Key Tree' |
||||
return isLoose ? h('.keyring-label', 'LOOSE') : null |
||||
} catch (e) { return } |
||||
} |
@ -0,0 +1,120 @@ |
||||
const Component = require('react').Component |
||||
const connect = require('react-redux').connect |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const TokenBalance = require('./token-balance') |
||||
const Identicon = require('./identicon') |
||||
|
||||
const { formatBalance, generateBalanceObject } = require('../util') |
||||
|
||||
module.exports = connect(mapStateToProps)(BalanceComponent) |
||||
|
||||
function mapStateToProps (state) { |
||||
const accounts = state.metamask.accounts |
||||
const network = state.metamask.network |
||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] |
||||
const account = accounts[selectedAddress] |
||||
|
||||
return { |
||||
account, |
||||
network, |
||||
conversionRate: state.metamask.conversionRate, |
||||
currentCurrency: state.metamask.currentCurrency, |
||||
} |
||||
} |
||||
|
||||
inherits(BalanceComponent, Component) |
||||
function BalanceComponent () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
BalanceComponent.prototype.render = function () { |
||||
const props = this.props |
||||
const { token, network } = props |
||||
|
||||
return h('div.balance-container', {}, [ |
||||
|
||||
// TODO: balance icon needs to be passed in
|
||||
// h('img.balance-icon', {
|
||||
// src: '../images/eth_logo.svg',
|
||||
// style: {},
|
||||
// }),
|
||||
h(Identicon, { |
||||
diameter: 45, |
||||
address: token && token.address, |
||||
network, |
||||
}), |
||||
|
||||
token ? this.renderTokenBalance() : this.renderBalance(), |
||||
]) |
||||
} |
||||
|
||||
BalanceComponent.prototype.renderTokenBalance = function () { |
||||
const { token } = this.props |
||||
|
||||
return h('div.flex-column.balance-display', [ |
||||
h('div.token-amount', [ h(TokenBalance, { token }) ]), |
||||
]) |
||||
} |
||||
|
||||
BalanceComponent.prototype.renderBalance = function () { |
||||
const props = this.props |
||||
const { shorten, account } = props |
||||
const balanceValue = account && account.balance |
||||
const needsParse = 'needsParse' in props ? props.needsParse : true |
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...' |
||||
const showFiat = 'showFiat' in props ? props.showFiat : true |
||||
|
||||
if (formattedBalance === 'None' || formattedBalance === '...') { |
||||
return h('div.flex-column.balance-display', {}, [ |
||||
h('div.token-amount', { |
||||
style: {}, |
||||
}, formattedBalance), |
||||
]) |
||||
} |
||||
|
||||
return h('div.flex-column.balance-display', {}, [ |
||||
h('div.token-amount', { |
||||
style: {}, |
||||
}, this.getTokenBalance(formattedBalance, shorten)), |
||||
|
||||
showFiat ? this.renderFiatValue(formattedBalance) : null, |
||||
]) |
||||
} |
||||
|
||||
BalanceComponent.prototype.renderFiatValue = function (formattedBalance) { |
||||
|
||||
const { conversionRate, currentCurrency } = this.props |
||||
|
||||
const fiatDisplayNumber = this.getFiatDisplayNumber(formattedBalance, conversionRate) |
||||
|
||||
const fiatPrefix = currentCurrency === 'USD' ? '$' : '' |
||||
|
||||
return this.renderFiatAmount(fiatDisplayNumber, currentCurrency, fiatPrefix) |
||||
} |
||||
|
||||
BalanceComponent.prototype.renderFiatAmount = function (fiatDisplayNumber, fiatSuffix, fiatPrefix) { |
||||
if (fiatDisplayNumber === 'N/A') return null |
||||
|
||||
return h('div.fiat-amount', { |
||||
style: {}, |
||||
}, `${fiatPrefix}${fiatDisplayNumber} ${fiatSuffix}`) |
||||
} |
||||
|
||||
BalanceComponent.prototype.getTokenBalance = function (formattedBalance, shorten) { |
||||
const balanceObj = generateBalanceObject(formattedBalance, shorten ? 1 : 3) |
||||
|
||||
const balanceValue = shorten ? balanceObj.shortBalance : balanceObj.balance |
||||
const label = balanceObj.label |
||||
|
||||
return `${balanceValue} ${label}` |
||||
} |
||||
|
||||
BalanceComponent.prototype.getFiatDisplayNumber = function (formattedBalance, conversionRate) { |
||||
if (formattedBalance === 'None') return formattedBalance |
||||
if (conversionRate === 0) return 'N/A' |
||||
|
||||
const splitBalance = formattedBalance.split(' ') |
||||
|
||||
return (Number(splitBalance[0]) * conversionRate).toFixed(2) |
||||
} |
@ -0,0 +1,55 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const InputNumber = require('../input-number.js') |
||||
const GasSlider = require('./gas-slider.js') |
||||
|
||||
module.exports = GasModalCard |
||||
|
||||
inherits(GasModalCard, Component) |
||||
function GasModalCard () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
GasModalCard.prototype.render = function () { |
||||
const { |
||||
memo, |
||||
identities, |
||||
onChange, |
||||
unitLabel, |
||||
value, |
||||
min, |
||||
max, |
||||
step, |
||||
title, |
||||
copy |
||||
} = this.props |
||||
|
||||
return h('div.send-v2__gas-modal-card', [ |
||||
|
||||
h('div.send-v2__gas-modal-card__title', {}, title), |
||||
|
||||
h('div.send-v2__gas-modal-card__copy', {}, copy), |
||||
|
||||
h(InputNumber, { |
||||
unitLabel, |
||||
step, |
||||
max, |
||||
min, |
||||
placeholder: '0', |
||||
value, |
||||
onChange, |
||||
}), |
||||
|
||||
h(GasSlider, { |
||||
value, |
||||
step, |
||||
max, |
||||
min, |
||||
onChange, |
||||
}), |
||||
|
||||
]) |
||||
|
||||
} |
||||
|
@ -0,0 +1,50 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
|
||||
module.exports = GasSlider |
||||
|
||||
inherits(GasSlider, Component) |
||||
function GasSlider () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
GasSlider.prototype.render = function () { |
||||
const { |
||||
memo, |
||||
identities, |
||||
onChange, |
||||
unitLabel, |
||||
value, |
||||
id, |
||||
step, |
||||
max, |
||||
min, |
||||
} = this.props |
||||
|
||||
return h('div.gas-slider', [ |
||||
|
||||
h('input.gas-slider__input', { |
||||
type: 'range', |
||||
step, |
||||
max, |
||||
min, |
||||
value, |
||||
id: 'gasSlider', |
||||
onChange: event => onChange(event.target.value), |
||||
}, []), |
||||
|
||||
h('div.gas-slider__bar', [ |
||||
|
||||
h('div.gas-slider__low'), |
||||
|
||||
h('div.gas-slider__mid'), |
||||
|
||||
h('div.gas-slider__high'), |
||||
|
||||
]), |
||||
|
||||
]) |
||||
|
||||
} |
||||
|
@ -0,0 +1,158 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const GasModalCard = require('./gas-modal-card') |
||||
|
||||
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') |
||||
|
||||
const { |
||||
getGasPrice, |
||||
getGasLimit, |
||||
conversionRateSelector, |
||||
} = require('../../selectors') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
gasPrice: getGasPrice(state), |
||||
gasLimit: getGasLimit(state), |
||||
conversionRate: conversionRateSelector(state), |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => dispatch(actions.hideModal()), |
||||
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)), |
||||
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)), |
||||
updateGasTotal: newGasTotal => dispatch(actions.updateGasTotal(newGasTotal)), |
||||
} |
||||
} |
||||
|
||||
inherits(CustomizeGasModal, Component) |
||||
function CustomizeGasModal (props) { |
||||
Component.call(this) |
||||
|
||||
this.state = { |
||||
gasPrice: props.gasPrice, |
||||
gasLimit: props.gasLimit, |
||||
} |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal) |
||||
|
||||
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) { |
||||
const { |
||||
updateGasPrice, |
||||
updateGasLimit, |
||||
hideModal, |
||||
updateGasTotal |
||||
} = this.props |
||||
|
||||
const newGasTotal = multiplyCurrencies(gasLimit, gasPrice, { |
||||
toNumericBase: 'hex', |
||||
multiplicandBase: 16, |
||||
multiplierBase: 16, |
||||
}) |
||||
|
||||
updateGasPrice(gasPrice) |
||||
updateGasLimit(gasLimit) |
||||
updateGasTotal(newGasTotal) |
||||
hideModal() |
||||
} |
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) { |
||||
const convertedGasLimit = conversionUtil(newGasLimit, { |
||||
fromNumericBase: 'dec', |
||||
toNumericBase: 'hex', |
||||
}) |
||||
|
||||
this.setState({ gasLimit: convertedGasLimit }) |
||||
} |
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) { |
||||
const convertedGasPrice = conversionUtil(newGasPrice, { |
||||
fromNumericBase: 'dec', |
||||
toNumericBase: 'hex', |
||||
fromDenomination: 'GWEI', |
||||
toDenomination: 'WEI', |
||||
}) |
||||
|
||||
this.setState({ gasPrice: convertedGasPrice }) |
||||
} |
||||
|
||||
CustomizeGasModal.prototype.render = function () { |
||||
const { hideModal, conversionRate } = this.props |
||||
const { gasPrice, gasLimit } = this.state |
||||
|
||||
const convertedGasPrice = conversionUtil(gasPrice, { |
||||
fromNumericBase: 'hex', |
||||
toNumericBase: 'dec', |
||||
fromDenomination: 'WEI', |
||||
toDenomination: 'GWEI', |
||||
}) |
||||
|
||||
const convertedGasLimit = conversionUtil(gasLimit, { |
||||
fromNumericBase: 'hex', |
||||
toNumericBase: 'dec', |
||||
}) |
||||
|
||||
return h('div.send-v2__customize-gas', {}, [ |
||||
h('div', { |
||||
}, [ |
||||
h('div.send-v2__customize-gas__header', {}, [ |
||||
|
||||
h('div.send-v2__customize-gas__title', 'Customize Gas'), |
||||
|
||||
h('div.send-v2__customize-gas__close', { |
||||
onClick: hideModal, |
||||
}), |
||||
|
||||
]), |
||||
|
||||
h('div.send-v2__customize-gas__body', {}, [ |
||||
|
||||
h(GasModalCard, { |
||||
value: convertedGasPrice, |
||||
min: 0, |
||||
max: 1000, |
||||
step: 1, |
||||
onChange: value => this.convertAndSetGasPrice(value), |
||||
title: 'Gas Price', |
||||
copy: 'We calculate the suggested gas prices based on network success rates.', |
||||
}), |
||||
|
||||
h(GasModalCard, { |
||||
value: convertedGasLimit, |
||||
min: 20000, |
||||
max: 100000, |
||||
step: 1, |
||||
onChange: value => this.convertAndSetGasLimit(value), |
||||
title: 'Gas Limit', |
||||
copy: 'We calculate the suggested gas limit based on network success rates.', |
||||
}), |
||||
|
||||
]), |
||||
|
||||
h('div.send-v2__customize-gas__footer', {}, [ |
||||
|
||||
h('div.send-v2__customize-gas__revert', { |
||||
onClick: () => console.log('Revert'), |
||||
}, ['Revert']), |
||||
|
||||
h('div.send-v2__customize-gas__buttons', [ |
||||
h('div.send-v2__customize-gas__cancel', { |
||||
onClick: this.props.hideModal, |
||||
}, ['CANCEL']), |
||||
|
||||
h('div.send-v2__customize-gas__save', { |
||||
onClick: () => this.save(gasPrice, gasLimit), |
||||
}, ['SAVE']), |
||||
]) |
||||
|
||||
]), |
||||
|
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,29 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const AccountDropdowns = require('./components/account-dropdowns') |
||||
|
||||
inherits(AccountOptionsDropdown, Component) |
||||
function AccountOptionsDropdown () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = AccountOptionsDropdown |
||||
|
||||
// TODO: specify default props and proptypes
|
||||
// TODO: hook up to state, connect to redux to clean up API
|
||||
// TODO: selectedAddress is not defined... should we use selected?
|
||||
AccountOptionsDropdown.prototype.render = function () { |
||||
const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props |
||||
|
||||
return h(AccountDropdowns, { |
||||
enableAccountOptions: true, |
||||
enableAccountsSelector: false, |
||||
selected: selectedAddress, |
||||
network, |
||||
identities, |
||||
style: style || {}, |
||||
dropdownWrapperStyle: dropdownWrapperStyle || {}, |
||||
menuItemStyles: menuItemStyles || {}, |
||||
}, []) |
||||
} |
@ -0,0 +1,29 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const AccountDropdowns = require('./components/account-dropdowns') |
||||
|
||||
inherits(AccountSelectionDropdown, Component) |
||||
function AccountSelectionDropdown () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = AccountSelectionDropdown |
||||
|
||||
// TODO: specify default props and proptypes
|
||||
// TODO: hook up to state, connect to redux to clean up API
|
||||
// TODO: selectedAddress is not defined... should we use selected?
|
||||
AccountSelectionDropdown.prototype.render = function () { |
||||
const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props |
||||
|
||||
return h(AccountDropdowns, { |
||||
enableAccountOptions: false, |
||||
enableAccountsSelector: true, |
||||
selected: selectedAddress, |
||||
network, |
||||
identities, |
||||
style: style || {}, |
||||
dropdownWrapperStyle: dropdownWrapperStyle || {}, |
||||
menuItemStyles: menuItemStyles || {}, |
||||
}, []) |
||||
} |
@ -0,0 +1,469 @@ |
||||
const Component = require('react').Component |
||||
const PropTypes = require('react').PropTypes |
||||
const h = require('react-hyperscript') |
||||
const actions = require('../../../actions') |
||||
const genAccountLink = require('../../../../lib/account-link.js') |
||||
const connect = require('react-redux').connect |
||||
const Dropdown = require('./dropdown').Dropdown |
||||
const DropdownMenuItem = require('./dropdown').DropdownMenuItem |
||||
const Identicon = require('../../identicon') |
||||
const ethUtil = require('ethereumjs-util') |
||||
const copyToClipboard = require('copy-to-clipboard') |
||||
const { formatBalance } = require('../../../util') |
||||
|
||||
class AccountDropdowns extends Component { |
||||
constructor (props) { |
||||
super(props) |
||||
this.state = { |
||||
accountSelectorActive: false, |
||||
optionsMenuActive: false, |
||||
} |
||||
// Used for orangeaccount selector icon
|
||||
// this.accountSelectorToggleClassName = 'accounts-selector'
|
||||
this.accountSelectorToggleClassName = 'fa-angle-down' |
||||
this.optionsMenuToggleClassName = 'fa-ellipsis-h' |
||||
} |
||||
|
||||
renderAccounts () { |
||||
const { identities, accounts, selected, menuItemStyles, actions, keyrings } = this.props |
||||
|
||||
return Object.keys(identities).map((key, index) => { |
||||
const identity = identities[key] |
||||
const isSelected = identity.address === selected |
||||
|
||||
const balanceValue = accounts[key].balance |
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' |
||||
const simpleAddress = identity.address.substring(2).toLowerCase() |
||||
|
||||
const keyring = keyrings.find((kr) => { |
||||
return kr.accounts.includes(simpleAddress) || |
||||
kr.accounts.includes(identity.address) |
||||
}) |
||||
|
||||
return h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
onClick: () => { |
||||
this.props.actions.showAccountDetail(identity.address) |
||||
}, |
||||
style: Object.assign( |
||||
{ |
||||
marginTop: index === 0 ? '5px' : '', |
||||
fontSize: '24px', |
||||
width: '260px', |
||||
}, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
[ |
||||
h('div.flex-row.flex-center', {}, [ |
||||
|
||||
h('span', { |
||||
style: { |
||||
flex: '1 1 0', |
||||
minWidth: '20px', |
||||
minHeight: '30px', |
||||
}, |
||||
}, [ |
||||
h('span', { |
||||
style: { |
||||
flex: '1 1 auto', |
||||
fontSize: '14px', |
||||
}, |
||||
}, isSelected ? h('i.fa.fa-check') : null), |
||||
]), |
||||
|
||||
h( |
||||
Identicon, |
||||
{ |
||||
address: identity.address, |
||||
diameter: 24, |
||||
style: { |
||||
flex: '1 1 auto', |
||||
marginLeft: '10px', |
||||
}, |
||||
}, |
||||
), |
||||
|
||||
h('span.flex-column', { |
||||
style: { |
||||
flex: '10 10 auto', |
||||
width: '175px', |
||||
alignItems: 'flex-start', |
||||
justifyContent: 'center', |
||||
marginLeft: '10px', |
||||
position: 'relative', |
||||
}, |
||||
}, [ |
||||
this.indicateIfLoose(keyring), |
||||
h('span.account-dropdown-name', { |
||||
style: { |
||||
fontSize: '18px', |
||||
maxWidth: '145px', |
||||
whiteSpace: 'nowrap', |
||||
overflow: 'hidden', |
||||
textOverflow: 'ellipsis', |
||||
}, |
||||
}, identity.name || ''), |
||||
|
||||
h('span.account-dropdown-balance', { |
||||
style: { |
||||
fontSize: '14px', |
||||
fontFamily: 'Avenir', |
||||
fontWeight: 500, |
||||
}, |
||||
}, formattedBalance), |
||||
]), |
||||
|
||||
h('span', { |
||||
style: { |
||||
flex: '3 3 auto', |
||||
}, |
||||
}, [ |
||||
h('span.account-dropdown-edit-button', { |
||||
style: { |
||||
fontSize: '16px', |
||||
}, |
||||
onClick: () => { |
||||
actions.showEditAccountModal(identity) |
||||
}, |
||||
}, [ |
||||
'Edit', |
||||
]), |
||||
]), |
||||
|
||||
]), |
||||
// =======
|
||||
// },
|
||||
// ),
|
||||
// this.indicateIfLoose(keyring),
|
||||
// h('span', {
|
||||
// style: {
|
||||
// marginLeft: '20px',
|
||||
// fontSize: '24px',
|
||||
// maxWidth: '145px',
|
||||
// whiteSpace: 'nowrap',
|
||||
// overflow: 'hidden',
|
||||
// textOverflow: 'ellipsis',
|
||||
// },
|
||||
// }, identity.name || ''),
|
||||
// h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
|
||||
// >>>>>>> master:ui/app/components/account-dropdowns.js
|
||||
] |
||||
) |
||||
}) |
||||
} |
||||
|
||||
indicateIfLoose (keyring) { |
||||
try { // Sometimes keyrings aren't loaded yet:
|
||||
const type = keyring.type |
||||
const isLoose = type !== 'HD Key Tree' |
||||
return isLoose ? h('.keyring-label', 'LOOSE') : null |
||||
} catch (e) { return } |
||||
} |
||||
|
||||
renderAccountSelector () { |
||||
const { actions, useCssTransition, innerStyle, sidebarOpen } = this.props |
||||
const { accountSelectorActive, menuItemStyles } = this.state |
||||
|
||||
return h( |
||||
Dropdown, |
||||
{ |
||||
useCssTransition, |
||||
style: { |
||||
marginLeft: '-185px', |
||||
marginTop: '50px', |
||||
minWidth: '180px', |
||||
overflowY: 'auto', |
||||
maxHeight: '300px', |
||||
width: '300px', |
||||
}, |
||||
innerStyle, |
||||
isOpen: accountSelectorActive, |
||||
onClickOutside: (event) => { |
||||
const { classList } = event.target |
||||
const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName) |
||||
if (accountSelectorActive && isNotToggleElement) { |
||||
this.setState({ accountSelectorActive: false }) |
||||
} |
||||
}, |
||||
}, |
||||
[ |
||||
...this.renderAccounts(), |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
style: Object.assign( |
||||
{}, |
||||
menuItemStyles, |
||||
), |
||||
onClick: () => actions.showNewAccountModal(), |
||||
}, |
||||
[ |
||||
h( |
||||
'i.fa.fa-plus.fa-lg', |
||||
{ |
||||
style: { |
||||
marginLeft: '8px', |
||||
}, |
||||
} |
||||
), |
||||
h('span', { |
||||
style: { |
||||
marginLeft: '14px', |
||||
fontFamily: 'DIN OT', |
||||
fontSize: '16px', |
||||
lineHeight: '23px', |
||||
}, |
||||
}, 'Create Account'), |
||||
], |
||||
), |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => { |
||||
if (sidebarOpen) { |
||||
actions.hideSidebar() |
||||
} |
||||
}, |
||||
onClick: () => actions.showImportPage(), |
||||
style: Object.assign( |
||||
{}, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
[ |
||||
h( |
||||
'i.fa.fa-download.fa-lg', |
||||
{ |
||||
style: { |
||||
marginLeft: '8px', |
||||
}, |
||||
} |
||||
), |
||||
h('span', { |
||||
style: { |
||||
marginLeft: '20px', |
||||
marginBottom: '5px', |
||||
fontFamily: 'DIN OT', |
||||
fontSize: '16px', |
||||
lineHeight: '23px', |
||||
}, |
||||
}, 'Import Account'), |
||||
] |
||||
), |
||||
] |
||||
) |
||||
} |
||||
|
||||
renderAccountOptions () { |
||||
const { actions, dropdownWrapperStyle, useCssTransition } = this.props |
||||
const { optionsMenuActive, menuItemStyles } = this.state |
||||
const dropdownMenuItemStyle = { |
||||
fontFamily: 'DIN OT', |
||||
fontSize: 16, |
||||
lineHeight: '24px', |
||||
padding: '8px', |
||||
} |
||||
|
||||
return h( |
||||
Dropdown, |
||||
{ |
||||
useCssTransition, |
||||
style: Object.assign( |
||||
{ |
||||
marginLeft: '-10px', |
||||
position: 'absolute', |
||||
width: '29vh', // affects both mobile and laptop views
|
||||
}, |
||||
dropdownWrapperStyle, |
||||
), |
||||
isOpen: optionsMenuActive, |
||||
onClickOutside: () => { |
||||
const { classList } = event.target |
||||
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName) |
||||
if (optionsMenuActive && isNotToggleElement) { |
||||
this.setState({ optionsMenuActive: false }) |
||||
} |
||||
}, |
||||
}, |
||||
[ |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
onClick: () => { |
||||
this.props.actions.showAccountDetailModal() |
||||
}, |
||||
style: Object.assign( |
||||
dropdownMenuItemStyle, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
'Account Details', |
||||
), |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
onClick: () => { |
||||
const { selected, network } = this.props |
||||
const url = genAccountLink(selected, network) |
||||
global.platform.openWindow({ url }) |
||||
}, |
||||
style: Object.assign( |
||||
dropdownMenuItemStyle, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
'View account on Etherscan', |
||||
), |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
onClick: () => { |
||||
const { selected } = this.props |
||||
const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) |
||||
copyToClipboard(checkSumAddress) |
||||
}, |
||||
style: Object.assign( |
||||
dropdownMenuItemStyle, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
'Copy Address to clipboard', |
||||
), |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
onClick: () => this.props.actions.showExportPrivateKeyModal(), |
||||
style: Object.assign( |
||||
dropdownMenuItemStyle, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
'Export Private Key', |
||||
), |
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => {}, |
||||
onClick: () => { |
||||
actions.hideSidebar() |
||||
actions.showAddTokenPage() |
||||
}, |
||||
style: Object.assign( |
||||
dropdownMenuItemStyle, |
||||
menuItemStyles, |
||||
), |
||||
}, |
||||
'Add Token', |
||||
), |
||||
|
||||
] |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const { style, enableAccountsSelector, enableAccountOptions } = this.props |
||||
const { optionsMenuActive, accountSelectorActive } = this.state |
||||
|
||||
return h( |
||||
'span', |
||||
{ |
||||
style: style, |
||||
}, |
||||
[ |
||||
enableAccountsSelector && h( |
||||
'i.fa.fa-angle-down', |
||||
{ |
||||
style: { |
||||
cursor: 'pointer', |
||||
}, |
||||
onClick: (event) => { |
||||
event.stopPropagation() |
||||
this.setState({ |
||||
accountSelectorActive: !accountSelectorActive, |
||||
optionsMenuActive: false, |
||||
}) |
||||
}, |
||||
}, |
||||
this.renderAccountSelector(), |
||||
), |
||||
enableAccountOptions && h( |
||||
'i.fa.fa-ellipsis-h', |
||||
{ |
||||
style: { |
||||
fontSize: '135%', |
||||
cursor: 'pointer', |
||||
}, |
||||
onClick: (event) => { |
||||
event.stopPropagation() |
||||
this.setState({ |
||||
accountSelectorActive: false, |
||||
optionsMenuActive: !optionsMenuActive, |
||||
}) |
||||
}, |
||||
}, |
||||
this.renderAccountOptions() |
||||
), |
||||
] |
||||
) |
||||
} |
||||
} |
||||
|
||||
AccountDropdowns.defaultProps = { |
||||
enableAccountsSelector: false, |
||||
enableAccountOptions: false, |
||||
} |
||||
|
||||
AccountDropdowns.propTypes = { |
||||
identities: PropTypes.objectOf(PropTypes.object), |
||||
selected: PropTypes.string, |
||||
keyrings: PropTypes.array, |
||||
} |
||||
|
||||
const mapDispatchToProps = (dispatch) => { |
||||
return { |
||||
actions: { |
||||
hideSidebar: () => dispatch(actions.hideSidebar()), |
||||
showConfigPage: () => dispatch(actions.showConfigPage()), |
||||
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)), |
||||
showAccountDetailModal: () => { |
||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })) |
||||
}, |
||||
showEditAccountModal: (identity) => { |
||||
dispatch(actions.showModal({ |
||||
name: 'EDIT_ACCOUNT_NAME', |
||||
identity, |
||||
})) |
||||
}, |
||||
showNewAccountModal: () => { |
||||
dispatch(actions.showModal({ name: 'NEW_ACCOUNT' })) |
||||
}, |
||||
showExportPrivateKeyModal: () => { |
||||
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) |
||||
}, |
||||
showAddTokenPage: () => { |
||||
dispatch(actions.showAddTokenPage()) |
||||
}, |
||||
addNewAccount: () => dispatch(actions.addNewAccount()), |
||||
showImportPage: () => dispatch(actions.showImportPage()), |
||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)), |
||||
}, |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
keyrings: state.metamask.keyrings, |
||||
sidebarOpen: state.appState.sidebarOpen, |
||||
} |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns) |
||||
|
@ -0,0 +1,51 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
|
||||
inherits(Menu, Component) |
||||
function Menu () { Component.call(this) } |
||||
|
||||
Menu.prototype.render = function () { |
||||
const { className = '', children, isShowing } = this.props |
||||
return isShowing |
||||
? h('div', { className: `menu ${className}` }, children) |
||||
: h('noscript') |
||||
} |
||||
|
||||
inherits(Item, Component) |
||||
function Item () { Component.call(this) } |
||||
|
||||
Item.prototype.render = function () { |
||||
const { |
||||
icon, |
||||
children, |
||||
text, |
||||
className = '', |
||||
onClick, |
||||
} = this.props |
||||
const itemClassName = `menu__item ${className} ${onClick ? 'menu__item--clickable' : ''}` |
||||
const iconComponent = icon ? h('div.menu__item__icon', [icon]) : null |
||||
const textComponent = text ? h('div.menu__item__text', text) : null |
||||
|
||||
return children |
||||
? h('div', { className: itemClassName, onClick }, children) |
||||
: h('div.menu__item', { className: itemClassName, onClick }, [ iconComponent, textComponent ] |
||||
.filter(d => Boolean(d)) |
||||
) |
||||
} |
||||
|
||||
inherits(Divider, Component) |
||||
function Divider () { Component.call(this) } |
||||
|
||||
Divider.prototype.render = function () { |
||||
return h('div.menu__divider') |
||||
} |
||||
|
||||
inherits(CloseArea, Component) |
||||
function CloseArea () { Component.call(this) } |
||||
|
||||
CloseArea.prototype.render = function () { |
||||
return h('div.menu__close-area', { onClick: this.props.onClick }) |
||||
} |
||||
|
||||
module.exports = { Menu, Item, Divider, CloseArea } |
@ -0,0 +1,28 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
|
||||
|
||||
inherits(NetworkDropdownIcon, Component) |
||||
module.exports = NetworkDropdownIcon |
||||
|
||||
function NetworkDropdownIcon () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
NetworkDropdownIcon.prototype.render = function () { |
||||
const { |
||||
backgroundColor, |
||||
isSelected, |
||||
innerBorder = 'none', |
||||
} = this.props |
||||
|
||||
return h(`.menu-icon-circle${isSelected ? '--active' : ''}`, {}, |
||||
h('div', { |
||||
style: { |
||||
background: backgroundColor, |
||||
border: innerBorder, |
||||
}, |
||||
}) |
||||
) |
||||
} |
@ -0,0 +1,17 @@ |
||||
// Reusable Dropdown Components
|
||||
// TODO: Refactor into separate components
|
||||
const Dropdown = require('./components/dropdown').Dropdown |
||||
const AccountDropdowns = require('./components/account-dropdowns') |
||||
|
||||
// App-Specific Instances
|
||||
const AccountSelectionDropdown = require('./account-selection-dropdown') |
||||
const AccountOptionsDropdown = require('./account-options-dropdown') |
||||
const NetworkDropdown = require('./network-dropdown').default |
||||
|
||||
module.exports = { |
||||
AccountSelectionDropdown, |
||||
AccountOptionsDropdown, |
||||
NetworkDropdown, |
||||
Dropdown, |
||||
AccountDropdowns, |
||||
} |
@ -0,0 +1,316 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const Dropdown = require('./components/dropdown').Dropdown |
||||
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem |
||||
const NetworkDropdownIcon = require('./components/network-dropdown-icon') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
provider: state.metamask.provider, |
||||
frequentRpcList: state.metamask.frequentRpcList || [], |
||||
networkDropdownOpen: state.appState.networkDropdownOpen, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
setProviderType: (type) => { |
||||
dispatch(actions.setProviderType(type)) |
||||
}, |
||||
setDefaultRpcTarget: type => { |
||||
dispatch(actions.setDefaultRpcTarget(type)) |
||||
}, |
||||
setRpcTarget: (target) => { |
||||
dispatch(actions.setRpcTarget(target)) |
||||
}, |
||||
showConfigPage: () => { |
||||
dispatch(actions.showConfigPage()) |
||||
}, |
||||
showNetworkDropdown: () => { dispatch(actions.showNetworkDropdown()) }, |
||||
hideNetworkDropdown: () => { dispatch(actions.hideNetworkDropdown()) }, |
||||
} |
||||
} |
||||
|
||||
|
||||
inherits(NetworkDropdown, Component) |
||||
function NetworkDropdown () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(NetworkDropdown) |
||||
|
||||
// TODO: specify default props and proptypes
|
||||
NetworkDropdown.prototype.render = function () { |
||||
const props = this.props |
||||
const { provider: { type: providerType, rpcTarget: activeNetwork } } = props |
||||
const rpcList = props.frequentRpcList |
||||
const isOpen = this.props.networkDropdownOpen |
||||
const dropdownMenuItemStyle = { |
||||
fontFamily: 'DIN OT', |
||||
fontSize: '16px', |
||||
lineHeight: '20px', |
||||
padding: '12px 0', |
||||
} |
||||
|
||||
return h(Dropdown, { |
||||
useCssTransition: true, |
||||
isOpen, |
||||
onClickOutside: (event) => { |
||||
const { classList } = event.target |
||||
const isNotToggleElement = [ |
||||
classList.contains('menu-icon'), |
||||
classList.contains('network-name'), |
||||
classList.contains('network-indicator'), |
||||
].filter(bool => bool).length === 0 |
||||
// classes from three constituent nodes of the toggle element
|
||||
|
||||
if (isNotToggleElement) { |
||||
this.props.hideNetworkDropdown() |
||||
} |
||||
}, |
||||
containerClassName: 'network-droppo', |
||||
zIndex: 11, |
||||
style: { |
||||
position: 'absolute', |
||||
top: '38px', |
||||
minWidth: '309px', |
||||
}, |
||||
innerStyle: { |
||||
padding: '18px 8px', |
||||
}, |
||||
}, [ |
||||
|
||||
h('div.network-dropdown-header', {}, [ |
||||
h('div.network-dropdown-title', {}, 'Networks'), |
||||
|
||||
h('div.network-dropdown-divider'), |
||||
|
||||
h('div.network-dropdown-content', |
||||
{}, |
||||
'The default network for Ether transactions is Main Net.' |
||||
), |
||||
]), |
||||
|
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: 'main', |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => props.setProviderType('mainnet'), |
||||
style: { ...dropdownMenuItemStyle, borderColor: '#038789' }, |
||||
}, |
||||
[ |
||||
providerType === 'mainnet' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), |
||||
h(NetworkDropdownIcon, { |
||||
backgroundColor: '#038789', // $blue-lagoon
|
||||
isSelected: providerType === 'mainnet', |
||||
}), |
||||
h('span.network-name-item', { |
||||
style: { |
||||
color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b', |
||||
}, |
||||
}, 'Main Ethereum Network'), |
||||
] |
||||
), |
||||
|
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: 'ropsten', |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => props.setProviderType('ropsten'), |
||||
style: dropdownMenuItemStyle, |
||||
}, |
||||
[ |
||||
providerType === 'ropsten' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), |
||||
h(NetworkDropdownIcon, { |
||||
backgroundColor: '#e91550', // $crimson
|
||||
isSelected: providerType === 'ropsten', |
||||
}), |
||||
h('span.network-name-item', { |
||||
style: { |
||||
color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b', |
||||
}, |
||||
}, 'Ropsten Test Network'), |
||||
] |
||||
), |
||||
|
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: 'kovan', |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => props.setProviderType('kovan'), |
||||
style: dropdownMenuItemStyle, |
||||
}, |
||||
[ |
||||
providerType === 'kovan' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), |
||||
h(NetworkDropdownIcon, { |
||||
backgroundColor: '#690496', // $purple
|
||||
isSelected: providerType === 'kovan', |
||||
}), |
||||
h('span.network-name-item', { |
||||
style: { |
||||
color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b', |
||||
}, |
||||
}, 'Kovan Test Network'), |
||||
] |
||||
), |
||||
|
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: 'rinkeby', |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => props.setProviderType('rinkeby'), |
||||
style: dropdownMenuItemStyle, |
||||
}, |
||||
[ |
||||
providerType === 'rinkeby' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), |
||||
h(NetworkDropdownIcon, { |
||||
backgroundColor: '#ebb33f', // $tulip-tree
|
||||
isSelected: providerType === 'rinkeby', |
||||
}), |
||||
h('span.network-name-item', { |
||||
style: { |
||||
color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b', |
||||
}, |
||||
}, 'Rinkeby Test Network'), |
||||
] |
||||
), |
||||
|
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: 'default', |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => props.setRpcTarget('http://localhost:8545'), |
||||
style: dropdownMenuItemStyle, |
||||
}, |
||||
[ |
||||
activeNetwork === 'http://localhost:8545' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), |
||||
h(NetworkDropdownIcon, { |
||||
isSelected: activeNetwork === 'http://localhost:8545', |
||||
innerBorder: '1px solid #9b9b9b', |
||||
}), |
||||
h('span.network-name-item', { |
||||
style: { |
||||
color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b', |
||||
}, |
||||
}, 'Localhost 8545'), |
||||
] |
||||
), |
||||
|
||||
this.renderCustomOption(props.provider), |
||||
this.renderCommonRpc(rpcList, props.provider), |
||||
|
||||
h( |
||||
DropdownMenuItem, |
||||
{ |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => this.props.showConfigPage(), |
||||
style: dropdownMenuItemStyle, |
||||
}, |
||||
[ |
||||
activeNetwork === 'custom' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), |
||||
h(NetworkDropdownIcon, { |
||||
isSelected: activeNetwork === 'custom', |
||||
innerBorder: '1px solid #9b9b9b', |
||||
}), |
||||
h('span.network-name-item', { |
||||
style: { |
||||
color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b', |
||||
}, |
||||
}, 'Custom RPC'), |
||||
] |
||||
), |
||||
|
||||
]) |
||||
} |
||||
|
||||
|
||||
NetworkDropdown.prototype.getNetworkName = function () { |
||||
const { provider } = this.props |
||||
const providerName = provider.type |
||||
|
||||
let name |
||||
|
||||
if (providerName === 'mainnet') { |
||||
name = 'Main Ethereum Network' |
||||
} else if (providerName === 'ropsten') { |
||||
name = 'Ropsten Test Network' |
||||
} else if (providerName === 'kovan') { |
||||
name = 'Kovan Test Network' |
||||
} else if (providerName === 'rinkeby') { |
||||
name = 'Rinkeby Test Network' |
||||
} else { |
||||
name = 'Unknown Private Network' |
||||
} |
||||
|
||||
return name |
||||
} |
||||
|
||||
NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { |
||||
const props = this.props |
||||
const rpcTarget = provider.rpcTarget |
||||
|
||||
return rpcList.map((rpc) => { |
||||
if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) { |
||||
return null |
||||
} else { |
||||
return h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: `common${rpc}`, |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
onClick: () => props.setRpcTarget(rpc), |
||||
}, |
||||
[ |
||||
h('i.fa.fa-question-circle.fa-lg.menu-icon'), |
||||
rpc, |
||||
rpcTarget === rpc ? h('.check', '✓') : null, |
||||
] |
||||
) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
NetworkDropdown.prototype.renderCustomOption = function (provider) { |
||||
const { rpcTarget, type } = provider |
||||
const props = this.props |
||||
|
||||
if (type !== 'rpc') return null |
||||
|
||||
// Concatenate long URLs
|
||||
let label = rpcTarget |
||||
if (rpcTarget.length > 31) { |
||||
label = label.substr(0, 34) + '...' |
||||
} |
||||
|
||||
switch (rpcTarget) { |
||||
|
||||
case 'http://localhost:8545': |
||||
return null |
||||
|
||||
default: |
||||
return h( |
||||
DropdownMenuItem, |
||||
{ |
||||
key: rpcTarget, |
||||
onClick: () => props.setRpcTarget(rpcTarget), |
||||
closeMenu: () => this.props.hideNetworkDropdown(), |
||||
}, |
||||
[ |
||||
h('i.fa.fa-question-circle.fa-lg.menu-icon'), |
||||
label, |
||||
h('.check', '✓'), |
||||
] |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
|
||||
module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown) |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
showHideTokenConfirmationModal: (token) => { |
||||
dispatch(actions.showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token })) |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
inherits(TokenMenuDropdown, Component) |
||||
function TokenMenuDropdown () { |
||||
Component.call(this) |
||||
|
||||
this.onClose = this.onClose.bind(this) |
||||
} |
||||
|
||||
TokenMenuDropdown.prototype.onClose = function (e) { |
||||
e.stopPropagation() |
||||
this.props.onClose() |
||||
} |
||||
|
||||
TokenMenuDropdown.prototype.render = function () { |
||||
const { showHideTokenConfirmationModal } = this.props |
||||
|
||||
return h('div.token-menu-dropdown', {}, [ |
||||
h('div.token-menu-dropdown__close-area', { |
||||
onClick: this.onClose, |
||||
}), |
||||
h('div.token-menu-dropdown__container', {}, [ |
||||
h('div.token-menu-dropdown__options', {}, [ |
||||
|
||||
h('div.token-menu-dropdown__option', { |
||||
onClick: (e) => { |
||||
e.stopPropagation() |
||||
showHideTokenConfirmationModal(this.props.token) |
||||
this.props.onClose() |
||||
}, |
||||
}, 'Hide Token') |
||||
|
||||
]), |
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,46 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const { addCurrencies } = require('../conversion-util') |
||||
|
||||
module.exports = InputNumber |
||||
|
||||
inherits(InputNumber, Component) |
||||
function InputNumber () { |
||||
Component.call(this) |
||||
|
||||
this.setValue = this.setValue.bind(this) |
||||
} |
||||
|
||||
InputNumber.prototype.setValue = function (newValue) { |
||||
const { fixed, min = -1, max = Infinity, onChange } = this.props |
||||
|
||||
newValue = Number(fixed ? newValue.toFixed(4) : newValue) |
||||
|
||||
if (newValue >= min && newValue <= max) { |
||||
onChange(newValue) |
||||
} |
||||
} |
||||
|
||||
InputNumber.prototype.render = function () { |
||||
const { unitLabel, step = 1, placeholder, value = 0 } = this.props |
||||
|
||||
return h('div.customize-gas-input-wrapper', {}, [ |
||||
h('input.customize-gas-input', { |
||||
placeholder, |
||||
type: 'number', |
||||
value: value, |
||||
onChange: (e) => this.setValue(e.target.value), |
||||
}), |
||||
h('span.gas-tooltip-input-detail', {}, [unitLabel]), |
||||
h('div.gas-tooltip-input-arrows', {}, [ |
||||
h('i.fa.fa-angle-up', { |
||||
onClick: () => this.setValue(addCurrencies(value, step)), |
||||
}), |
||||
h('i.fa.fa-angle-down', { |
||||
style: { cursor: 'pointer' }, |
||||
onClick: () => this.setValue(addCurrencies(value, step * -1)), |
||||
}), |
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,70 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const AccountModalContainer = require('./account-modal-container') |
||||
const { getSelectedIdentity, getSelectedAddress } = require('../../selectors') |
||||
const genAccountLink = require('../../../lib/account-link.js') |
||||
const QrView = require('../qr-code') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
network: state.metamask.network, |
||||
selectedIdentity: getSelectedIdentity(state), |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
// Is this supposed to be used somewhere?
|
||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)), |
||||
showExportPrivateKeyModal: () => { |
||||
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) |
||||
}, |
||||
hideModal: () => dispatch(actions.hideModal()), |
||||
} |
||||
} |
||||
|
||||
inherits(AccountDetailsModal, Component) |
||||
function AccountDetailsModal () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal) |
||||
|
||||
// Not yet pixel perfect todos:
|
||||
// fonts of qr-header
|
||||
|
||||
AccountDetailsModal.prototype.render = function () { |
||||
const { |
||||
selectedIdentity, |
||||
network, |
||||
showExportPrivateKeyModal, |
||||
hideModal, |
||||
} = this.props |
||||
const { name, address } = selectedIdentity |
||||
|
||||
return h(AccountModalContainer, {}, [ |
||||
h(QrView, { |
||||
Qr: { |
||||
message: name, |
||||
data: address, |
||||
}, |
||||
}), |
||||
|
||||
h('div.account-modal-divider'), |
||||
|
||||
h('button.btn-clear', { |
||||
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }), |
||||
}, [ 'View account on Etherscan' ]), |
||||
|
||||
// Holding on redesign for Export Private Key functionality
|
||||
h('button.btn-clear', { |
||||
onClick: () => { |
||||
showExportPrivateKeyModal() |
||||
}, |
||||
}, [ 'Export private key' ]), |
||||
|
||||
]) |
||||
} |
@ -0,0 +1,74 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const { getSelectedIdentity } = require('../../selectors') |
||||
const Identicon = require('../identicon') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
selectedIdentity: getSelectedIdentity(state), |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
inherits(AccountModalContainer, Component) |
||||
function AccountModalContainer () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountModalContainer) |
||||
|
||||
AccountModalContainer.prototype.render = function () { |
||||
const { |
||||
selectedIdentity, |
||||
showBackButton = false, |
||||
backButtonAction, |
||||
} = this.props |
||||
let { children } = this.props |
||||
|
||||
if (children.constructor !== Array) { |
||||
children = [children] |
||||
} |
||||
|
||||
return h('div', { style: { borderRadius: '4px' }}, [ |
||||
h('div.account-modal-container', [ |
||||
|
||||
h('div', [ |
||||
|
||||
// Needs a border; requires changes to svg
|
||||
h(Identicon, { |
||||
address: selectedIdentity.address, |
||||
diameter: 64, |
||||
style: {}, |
||||
}), |
||||
|
||||
]), |
||||
|
||||
showBackButton && h('div.account-modal-back', { |
||||
onClick: backButtonAction, |
||||
}, [ |
||||
|
||||
h('i.fa.fa-angle-left.fa-lg'), |
||||
|
||||
h('span.account-modal-back__text', ' Back'), |
||||
|
||||
]), |
||||
|
||||
h('div.account-modal-close', { |
||||
onClick: this.props.hideModal, |
||||
}), |
||||
|
||||
...children, |
||||
|
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,87 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
network: state.metamask.network, |
||||
address: state.metamask.selectedAddress, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
toCoinbase: (address) => { |
||||
dispatch(actions.buyEth({ network: '1', address, amount: 0 })) |
||||
}, |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
showAccountDetailModal: () => { |
||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
inherits(BuyOptions, Component) |
||||
function BuyOptions () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(BuyOptions) |
||||
|
||||
BuyOptions.prototype.render = function () { |
||||
return h('div', {}, [ |
||||
h('div.buy-modal-content.transfers-subview', { |
||||
}, [ |
||||
h('div.buy-modal-content-title-wrapper.flex-column.flex-center', { |
||||
style: {}, |
||||
}, [ |
||||
h('div.buy-modal-content-title', { |
||||
style: {}, |
||||
}, 'Transfers'), |
||||
h('div', {}, 'How would you like to buy Ether?'), |
||||
]), |
||||
|
||||
h('div.buy-modal-content-options.flex-column.flex-center', {}, [ |
||||
|
||||
h('div.buy-modal-content-option', { |
||||
onClick: () => { |
||||
const { toCoinbase, address } = this.props |
||||
toCoinbase(address) |
||||
}, |
||||
}, [ |
||||
h('div.buy-modal-content-option-title', {}, 'Coinbase'), |
||||
h('div.buy-modal-content-option-subtitle', {}, 'Buy with Fiat'), |
||||
]), |
||||
|
||||
// h('div.buy-modal-content-option', {}, [
|
||||
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
|
||||
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
|
||||
// ]),
|
||||
|
||||
h('div.buy-modal-content-option', { |
||||
onClick: () => this.goToAccountDetailsModal(), |
||||
}, [ |
||||
h('div.buy-modal-content-option-title', {}, 'Direct Deposit'), |
||||
h('div.buy-modal-content-option-subtitle', {}, 'Deposit from another account'), |
||||
]), |
||||
|
||||
]), |
||||
|
||||
h('button', { |
||||
style: { |
||||
background: 'white', |
||||
}, |
||||
onClick: () => { this.props.hideModal() }, |
||||
}, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, 'Cancel')), |
||||
]), |
||||
]) |
||||
} |
||||
|
||||
BuyOptions.prototype.goToAccountDetailsModal = function () { |
||||
this.props.hideModal() |
||||
this.props.showAccountDetailModal() |
||||
} |
@ -0,0 +1,77 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const { getSelectedAccount } = require('../../selectors') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
selectedAccount: getSelectedAccount(state), |
||||
identity: state.appState.modal.modalState.identity, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
saveAccountLabel: (account, label) => { |
||||
dispatch(actions.saveAccountLabel(account, label)) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
inherits(EditAccountNameModal, Component) |
||||
function EditAccountNameModal (props) { |
||||
Component.call(this) |
||||
|
||||
this.state = { |
||||
inputText: props.identity.name, |
||||
} |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(EditAccountNameModal) |
||||
|
||||
EditAccountNameModal.prototype.render = function () { |
||||
const { hideModal, saveAccountLabel, identity } = this.props |
||||
|
||||
return h('div', {}, [ |
||||
h('div.flex-column.edit-account-name-modal-content', { |
||||
}, [ |
||||
|
||||
h('div.edit-account-name-modal-cancel', { |
||||
onClick: () => { |
||||
hideModal() |
||||
}, |
||||
}, [ |
||||
h('i.fa.fa-times'), |
||||
]), |
||||
|
||||
h('div.edit-account-name-modal-title', { |
||||
}, ['Edit Account Name']), |
||||
|
||||
h('input.edit-account-name-modal-input', { |
||||
placeholder: identity.name, |
||||
onChange: (event) => { |
||||
this.setState({ inputText: event.target.value }) |
||||
}, |
||||
value: this.state.inputText, |
||||
}, []), |
||||
|
||||
h('button.btn-clear.edit-account-name-modal-save-button', { |
||||
onClick: () => { |
||||
if (this.state.inputText.length !== 0) { |
||||
saveAccountLabel(identity.address, this.state.inputText) |
||||
hideModal() |
||||
} |
||||
}, |
||||
disabled: this.state.inputText.length === 0, |
||||
}, [ |
||||
'SAVE', |
||||
]), |
||||
|
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,139 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const ethUtil = require('ethereumjs-util') |
||||
const actions = require('../../actions') |
||||
const AccountModalContainer = require('./account-modal-container') |
||||
const { getSelectedIdentity } = require('../../selectors') |
||||
const ReadOnlyInput = require('../readonly-input') |
||||
const copyToClipboard = require('copy-to-clipboard') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
warning: state.appState.warning, |
||||
privateKey: state.appState.accountDetail.privateKey, |
||||
network: state.metamask.network, |
||||
selectedIdentity: getSelectedIdentity(state), |
||||
previousModalState: state.appState.modal.previousModalState.name, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
exportAccount: (password, address) => dispatch(actions.exportAccount(password, address)), |
||||
showAccountDetailModal: () => dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })), |
||||
hideModal: () => dispatch(actions.hideModal()), |
||||
} |
||||
} |
||||
|
||||
inherits(ExportPrivateKeyModal, Component) |
||||
function ExportPrivateKeyModal () { |
||||
Component.call(this) |
||||
|
||||
this.state = { |
||||
password: '', |
||||
privateKey: null, |
||||
} |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ExportPrivateKeyModal) |
||||
|
||||
ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (password, address) { |
||||
const { exportAccount } = this.props |
||||
|
||||
exportAccount(password, address) |
||||
.then(privateKey => this.setState({ privateKey })) |
||||
} |
||||
|
||||
ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) { |
||||
return h('span.private-key-password-label', privateKey |
||||
? 'This is your private key (click to copy)' |
||||
: 'Type Your Password' |
||||
) |
||||
} |
||||
|
||||
ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) { |
||||
const plainKey = privateKey && ethUtil.stripHexPrefix(privateKey) |
||||
|
||||
return privateKey |
||||
? h(ReadOnlyInput, { |
||||
wrapperClass: 'private-key-password-display-wrapper', |
||||
inputClass: 'private-key-password-display-textarea', |
||||
textarea: true, |
||||
value: plainKey, |
||||
onClick: () => copyToClipboard(plainKey), |
||||
}) |
||||
: h('input.private-key-password-input', { |
||||
type: 'password', |
||||
placeholder: 'Type password', |
||||
onChange: event => this.setState({ password: event.target.value }), |
||||
}) |
||||
} |
||||
|
||||
ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, label) { |
||||
return h('button', { |
||||
className, |
||||
onClick, |
||||
}, label) |
||||
} |
||||
|
||||
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) { |
||||
return h('div.export-private-key-buttons', {}, [ |
||||
!privateKey && this.renderButton('btn-clear btn-cancel', () => hideModal(), 'Cancel'), |
||||
|
||||
(privateKey |
||||
? this.renderButton('btn-clear', () => hideModal(), 'Done') |
||||
: this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Download') |
||||
), |
||||
|
||||
]) |
||||
} |
||||
|
||||
ExportPrivateKeyModal.prototype.render = function () { |
||||
const { |
||||
selectedIdentity, |
||||
network, |
||||
warning, |
||||
showAccountDetailModal, |
||||
hideModal, |
||||
previousModalState, |
||||
} = this.props |
||||
const { name, address } = selectedIdentity |
||||
|
||||
const { privateKey } = this.state |
||||
|
||||
return h(AccountModalContainer, { |
||||
showBackButton: previousModalState === 'ACCOUNT_DETAILS', |
||||
backButtonAction: () => showAccountDetailModal(), |
||||
}, [ |
||||
|
||||
h('span.account-name', name), |
||||
|
||||
h(ReadOnlyInput, { |
||||
wrapperClass: 'ellip-address-wrapper', |
||||
inputClass: 'qr-ellip-address ellip-address', |
||||
value: address, |
||||
}), |
||||
|
||||
h('div.account-modal-divider'), |
||||
|
||||
h('span.modal-body-title', 'Download Private Keys'), |
||||
|
||||
h('div.private-key-password', {}, [ |
||||
this.renderPasswordLabel(privateKey), |
||||
|
||||
this.renderPasswordInput(privateKey), |
||||
|
||||
!warning ? null : h('span.private-key-password-error', warning), |
||||
]), |
||||
|
||||
h('div.private-key-password-warning', `Warning: Never disclose this key.
|
||||
Anyone with your private keys can take steal any assets held in your |
||||
account.` |
||||
), |
||||
|
||||
this.renderButtons(privateKey, this.state.password, address, hideModal), |
||||
|
||||
]) |
||||
} |
@ -0,0 +1,74 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const Identicon = require('../identicon') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
network: state.metamask.network, |
||||
token: state.appState.modal.modalState.token, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => dispatch(actions.hideModal()), |
||||
hideToken: address => { |
||||
dispatch(actions.removeToken(address)) |
||||
.then(() => { |
||||
dispatch(actions.hideModal()) |
||||
}) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
inherits(HideTokenConfirmationModal, Component) |
||||
function HideTokenConfirmationModal () { |
||||
Component.call(this) |
||||
|
||||
this.state = {} |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(HideTokenConfirmationModal) |
||||
|
||||
HideTokenConfirmationModal.prototype.render = function () { |
||||
const { token, network, hideToken, hideModal } = this.props |
||||
const { symbol, address } = token |
||||
|
||||
return h('div.hide-token-confirmation', {}, [ |
||||
h('div.hide-token-confirmation__container', { |
||||
}, [ |
||||
h('div.hide-token-confirmation__title', {}, [ |
||||
'Hide Token?', |
||||
]), |
||||
|
||||
h(Identicon, { |
||||
className: 'hide-token-confirmation__identicon', |
||||
diameter: 45, |
||||
address, |
||||
network, |
||||
}), |
||||
|
||||
h('div.hide-token-confirmation__symbol', {}, symbol), |
||||
|
||||
h('div.hide-token-confirmation__copy', {}, [ |
||||
'You can add this token back in the future by going go to “Add token” in your accounts options menu.', |
||||
]), |
||||
|
||||
h('div.hide-token-confirmation__buttons', {}, [ |
||||
h('button.btn-clear', { |
||||
onClick: () => hideModal(), |
||||
}, [ |
||||
'CANCEL', |
||||
]), |
||||
h('button.btn-clear', { |
||||
onClick: () => hideToken(address), |
||||
}, [ |
||||
'HIDE', |
||||
]), |
||||
]), |
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,5 @@ |
||||
const Modal = require('./modal') |
||||
|
||||
module.exports = { |
||||
Modal, |
||||
} |
@ -0,0 +1,263 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const FadeModal = require('boron').FadeModal |
||||
const actions = require('../../actions') |
||||
const isMobileView = require('../../../lib/is-mobile-view') |
||||
const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification') |
||||
|
||||
// Modal Components
|
||||
const BuyOptions = require('./buy-options-modal') |
||||
const AccountDetailsModal = require('./account-details-modal') |
||||
const EditAccountNameModal = require('./edit-account-name-modal') |
||||
const ExportPrivateKeyModal = require('./export-private-key-modal') |
||||
const NewAccountModal = require('./new-account-modal') |
||||
const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js') |
||||
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal') |
||||
const CustomizeGasModal = require('../customize-gas-modal') |
||||
|
||||
const accountModalStyle = { |
||||
mobileModalStyle: { |
||||
width: '95%', |
||||
// top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
|
||||
boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', |
||||
borderRadius: '4px', |
||||
top: '10%', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
laptopModalStyle: { |
||||
width: '360px', |
||||
// top: 'calc(33% + 45px)',
|
||||
boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', |
||||
borderRadius: '4px', |
||||
top: '10%', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
contentStyle: { |
||||
borderRadius: '4px', |
||||
}, |
||||
} |
||||
|
||||
const MODALS = { |
||||
BUY: { |
||||
contents: [ |
||||
h(BuyOptions, {}, []), |
||||
], |
||||
mobileModalStyle: { |
||||
width: '95%', |
||||
// top: isPopupOrNotification() === 'popup' ? '48vh' : '36.5vh',
|
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)', |
||||
top: '10%', |
||||
}, |
||||
laptopModalStyle: { |
||||
width: '66%', |
||||
maxWidth: '550px', |
||||
top: 'calc(10% + 10px)', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)', |
||||
transform: 'none', |
||||
}, |
||||
}, |
||||
|
||||
EDIT_ACCOUNT_NAME: { |
||||
contents: [ |
||||
h(EditAccountNameModal, {}, []), |
||||
], |
||||
mobileModalStyle: { |
||||
width: '95%', |
||||
// top: isPopupOrNotification() === 'popup' ? '48vh' : '36.5vh',
|
||||
top: '10%', |
||||
boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
laptopModalStyle: { |
||||
width: '375px', |
||||
// top: 'calc(30% + 10px)',
|
||||
top: '10%', |
||||
boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
}, |
||||
|
||||
ACCOUNT_DETAILS: { |
||||
contents: [ |
||||
h(AccountDetailsModal, {}, []), |
||||
], |
||||
...accountModalStyle, |
||||
}, |
||||
|
||||
EXPORT_PRIVATE_KEY: { |
||||
contents: [ |
||||
h(ExportPrivateKeyModal, {}, []), |
||||
], |
||||
...accountModalStyle, |
||||
}, |
||||
|
||||
SHAPESHIFT_DEPOSIT_TX: { |
||||
contents: [ |
||||
h(ShapeshiftDepositTxModal), |
||||
], |
||||
...accountModalStyle, |
||||
}, |
||||
|
||||
HIDE_TOKEN_CONFIRMATION: { |
||||
contents: [ |
||||
h(HideTokenConfirmationModal, {}, []), |
||||
], |
||||
mobileModalStyle: { |
||||
width: '95%', |
||||
top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh', |
||||
}, |
||||
laptopModalStyle: { |
||||
width: '449px', |
||||
top: 'calc(33% + 45px)', |
||||
}, |
||||
}, |
||||
|
||||
NEW_ACCOUNT: { |
||||
contents: [ |
||||
h(NewAccountModal, {}, []), |
||||
], |
||||
mobileModalStyle: { |
||||
width: '95%', |
||||
// top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
|
||||
top: '10%', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
laptopModalStyle: { |
||||
width: '449px', |
||||
// top: 'calc(33% + 45px)',
|
||||
top: '10%', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
}, |
||||
|
||||
CUSTOMIZE_GAS: { |
||||
contents: [ |
||||
h(CustomizeGasModal, {}, []), |
||||
], |
||||
mobileModalStyle: { |
||||
width: '355px', |
||||
height: '598px', |
||||
// top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
|
||||
top: '5%', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
laptopModalStyle: { |
||||
width: '720px', |
||||
height: '377px', |
||||
top: '80px', |
||||
transform: 'none', |
||||
left: '0', |
||||
right: '0', |
||||
margin: '0 auto', |
||||
}, |
||||
}, |
||||
|
||||
DEFAULT: { |
||||
contents: [], |
||||
mobileModalStyle: {}, |
||||
laptopModalStyle: {}, |
||||
}, |
||||
} |
||||
|
||||
const BACKDROPSTYLE = { |
||||
backgroundColor: 'rgba(245, 245, 245, 0.85)', |
||||
} |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
active: state.appState.modal.open, |
||||
modalState: state.appState.modal.modalState, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// Global Modal Component
|
||||
inherits(Modal, Component) |
||||
function Modal () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(Modal) |
||||
|
||||
Modal.prototype.render = function () { |
||||
const modal = MODALS[this.props.modalState.name || 'DEFAULT'] |
||||
|
||||
const children = modal.contents |
||||
const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle'] |
||||
const contentStyle = modal.contentStyle || {}; |
||||
|
||||
return h(FadeModal, |
||||
{ |
||||
className: 'modal', |
||||
keyboard: false, |
||||
onHide: () => { this.onHide() }, |
||||
ref: (ref) => { |
||||
this.modalRef = ref |
||||
}, |
||||
modalStyle, |
||||
contentStyle, |
||||
backdropStyle: BACKDROPSTYLE, |
||||
}, |
||||
children, |
||||
) |
||||
} |
||||
|
||||
Modal.prototype.componentWillReceiveProps = function (nextProps) { |
||||
if (nextProps.active) { |
||||
this.show() |
||||
} else if (this.props.active) { |
||||
this.hide() |
||||
} |
||||
} |
||||
|
||||
Modal.prototype.onHide = function () { |
||||
if (this.props.onHideCallback) { |
||||
this.props.onHideCallback() |
||||
} |
||||
this.props.hideModal() |
||||
} |
||||
|
||||
Modal.prototype.hide = function () { |
||||
this.modalRef.hide() |
||||
} |
||||
|
||||
Modal.prototype.show = function () { |
||||
this.modalRef.show() |
||||
} |
@ -0,0 +1,87 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
network: state.metamask.network, |
||||
address: state.metamask.selectedAddress, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
toCoinbase: (address) => { |
||||
dispatch(actions.buyEth({ network: '1', address, amount: 0 })) |
||||
}, |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
createAccount: (newAccountName) => { |
||||
dispatch(actions.addNewAccount()) |
||||
.then((newAccountAddress) => { |
||||
if (newAccountName) { |
||||
dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName)) |
||||
} |
||||
dispatch(actions.hideModal()) |
||||
}) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
inherits(NewAccountModal, Component) |
||||
function NewAccountModal () { |
||||
Component.call(this) |
||||
|
||||
this.state = { |
||||
newAccountName: '' |
||||
} |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(NewAccountModal) |
||||
|
||||
NewAccountModal.prototype.render = function () { |
||||
const { newAccountName } = this.state |
||||
|
||||
return h('div', {}, [ |
||||
h('div.new-account-modal-wrapper', { |
||||
}, [ |
||||
h('div.new-account-modal-header', {}, [ |
||||
'New Account', |
||||
]), |
||||
|
||||
h('div.modal-close-x', { |
||||
onClick: this.props.hideModal, |
||||
}), |
||||
|
||||
h('div.new-account-modal-content', {}, [ |
||||
'Account Name', |
||||
]), |
||||
|
||||
h('div.new-account-input-wrapper', {}, [ |
||||
h('input.new-account-input', { |
||||
placeholder: 'E.g. My new account', |
||||
onChange: (event) => this.setState({ newAccountName: event.target.value }) |
||||
}, []), |
||||
]), |
||||
|
||||
h('div.new-account-modal-content.after-input', {}, [ |
||||
'or', |
||||
]), |
||||
|
||||
h('div.new-account-modal-content.after-input', {}, [ |
||||
'Import an account', |
||||
]), |
||||
|
||||
h('div.new-account-modal-content.button', {}, [ |
||||
h('button.btn-clear', { |
||||
onClick: () => this.props.createAccount(newAccountName) |
||||
}, [ |
||||
'SAVE', |
||||
]), |
||||
]), |
||||
]), |
||||
]) |
||||
} |
@ -0,0 +1,40 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../actions') |
||||
const QrView = require('../qr-code') |
||||
const AccountModalContainer = require('./account-modal-container') |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
Qr: state.appState.modal.modalState.Qr, |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
hideModal: () => { |
||||
dispatch(actions.hideModal()) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
inherits(ShapeshiftDepositTxModal, Component) |
||||
function ShapeshiftDepositTxModal () { |
||||
Component.call(this) |
||||
|
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftDepositTxModal) |
||||
|
||||
ShapeshiftDepositTxModal.prototype.render = function () { |
||||
const { Qr } = this.props |
||||
|
||||
return h(AccountModalContainer, { |
||||
}, [ |
||||
h('div', {}, [ |
||||
h(QrView, {key: 'qr', Qr}), |
||||
]) |
||||
]) |
||||
} |