@ -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}), |
||||||
|
]) |
||||||
|
]) |
||||||
|
} |