diff options
author | Dan Finlay <dan@danfinlay.com> | 2016-08-30 07:14:51 +0800 |
---|---|---|
committer | Dan Finlay <dan@danfinlay.com> | 2016-08-30 07:14:51 +0800 |
commit | e85418b11ad14bf6f0cc3fdbcdbb5669330c4778 (patch) | |
tree | df71d73fa1533cea8b36fb24db90e02a07766607 /ui/app | |
parent | 40d5b446cfbea436a012d1799d0d7710caca752c (diff) | |
parent | a9c738d4d3226f61942641a41c399c75a3e9eb3e (diff) | |
download | tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.gz tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.bz2 tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.lz tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.xz tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.tar.zst tangerine-wallet-browser-e85418b11ad14bf6f0cc3fdbcdbb5669330c4778.zip |
Merge branch 'master' into EdgeCompatibility
Diffstat (limited to 'ui/app')
31 files changed, 1924 insertions, 155 deletions
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index b1b0495eb..486a1a633 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -4,22 +4,24 @@ const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const CopyButton = require('./components/copyButton') +const AccountInfoLink = require('./components/account-info-link') const actions = require('./actions') const ReactCSSTransitionGroup = require('react-addons-css-transition-group') const valuesFor = require('./util').valuesFor const Identicon = require('./components/identicon') -const EtherBalance = require('./components/eth-balance') +const AccountEtherBalance = require('./components/account-eth-balance') const TransactionList = require('./components/transaction-list') const ExportAccountView = require('./components/account-export') const ethUtil = require('ethereumjs-util') const EditableLabel = require('./components/editable-label') const Tooltip = require('./components/tooltip') - +const BuyButtonSubview = require('./components/buy-button-subview') module.exports = connect(mapStateToProps)(AccountDetailScreen) function mapStateToProps (state) { return { + metamask: state.metamask, identities: state.metamask.identities, accounts: state.metamask.accounts, address: state.metamask.selectedAccount, @@ -29,6 +31,7 @@ function mapStateToProps (state) { unconfTxs: valuesFor(state.metamask.unconfTxs), unconfMsgs: valuesFor(state.metamask.unconfMsgs), isEthWarningConfirmed: state.metamask.isEthConfirmed, + shapeShiftTxList: state.metamask.shapeShiftTxList, } } @@ -42,6 +45,7 @@ AccountDetailScreen.prototype.render = function () { var selected = props.address || Object.keys(props.accounts)[0] var identity = props.identities[selected] var account = props.accounts[selected] + const { network } = props return ( @@ -125,6 +129,9 @@ AccountDetailScreen.prototype.render = function () { bottom: '15px', }, }, [ + + h(AccountInfoLink, { selected, network }), + h(CopyButton, { value: ethUtil.toChecksumAddress(selected), }), @@ -134,16 +141,15 @@ AccountDetailScreen.prototype.render = function () { }, [ h('div', { style: { - margin: '5px', + display: 'flex', + alignItems: 'center', }, }, [ h('img.cursor-pointer.color-orange', { src: 'images/key-32.png', onClick: () => this.requestAccountExport(selected), style: { - margin: '0px 5px', - width: '20px', - height: '20px', + height: '19px', }, }), ]), @@ -162,9 +168,8 @@ AccountDetailScreen.prototype.render = function () { }, }, [ - h(EtherBalance, { + h(AccountEtherBalance, { value: account && account.balance, - mainBalance: true, style: { lineHeight: '7px', marginTop: '10px', @@ -172,7 +177,7 @@ AccountDetailScreen.prototype.render = function () { }), h('button', { - onClick: () => props.dispatch(actions.buyEth(selected)), + onClick: () => props.dispatch(actions.buyEthView(selected)), style: { marginBottom: '20px', marginRight: '8px', @@ -220,13 +225,15 @@ AccountDetailScreen.prototype.subview = function () { case 'export': var state = extend({key: 'export'}, this.props) return h(ExportAccountView, state) + case 'buyForm': + return h(BuyButtonSubview, extend({key: 'buyForm'}, this.props)) default: return this.transactionList() } } AccountDetailScreen.prototype.transactionList = function () { - const { transactions, unconfTxs, unconfMsgs, address, network } = this.props + const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props var txsToRender = transactions // only transactions that are from the current address @@ -241,6 +248,8 @@ AccountDetailScreen.prototype.transactionList = function () { network, unconfTxs, unconfMsgs, + address, + shapeShiftTxList, viewPendingTx: (txId) => { this.props.dispatch(actions.viewPendingTx(txId)) }, @@ -251,3 +260,13 @@ AccountDetailScreen.prototype.requestAccountExport = function () { this.props.dispatch(actions.requestExportAccount()) } + +AccountDetailScreen.prototype.buyButtonDeligator = function () { + var props = this.props + + if (this.props.accountDetail.subview === 'buyForm') { + props.dispatch(actions.backToAccountDetail(props.address)) + } else { + props.dispatch(actions.buyEthView()) + } +} diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js index 1010516e2..aa80e0e23 100644 --- a/ui/app/accounts/account-list-item.js +++ b/ui/app/accounts/account-list-item.js @@ -3,7 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const ethUtil = require('ethereumjs-util') -const EtherBalance = require('../components/eth-balance') +const AccountEtherBalance = require('../components/account-eth-balance') const CopyButton = require('../components/copyButton') const Identicon = require('../components/identicon') @@ -50,8 +50,12 @@ NewComponent.prototype.render = function () { textOverflow: 'ellipsis', }, }, ethUtil.toChecksumAddress(identity.address)), - h(EtherBalance, { + h(AccountEtherBalance, { value: account.balance, + style: { + lineHeight: '7px', + marginTop: '10px', + }, }), ]), diff --git a/ui/app/actions.js b/ui/app/actions.js index 82a319907..c92138f68 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -55,6 +55,8 @@ var actions = { SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE', REVEAL_ACCOUNT: 'REVEAL_ACCOUNT', revealAccount: revealAccount, + SET_CURRENT_FIAT: 'SET_CURRENT_FIAT', + setCurrentFiat: setCurrentFiat, // account detail screen SHOW_SEND_PAGE: 'SHOW_SEND_PAGE', showSendPage: showSendPage, @@ -113,6 +115,34 @@ var actions = { // buy Eth with coinbase BUY_ETH: 'BUY_ETH', buyEth: buyEth, + buyEthView: buyEthView, + BUY_ETH_VIEW: 'BUY_ETH_VIEW', + UPDATE_COINBASE_AMOUNT: 'UPDATE_COIBASE_AMOUNT', + updateCoinBaseAmount: updateCoinBaseAmount, + UPDATE_BUY_ADDRESS: 'UPDATE_BUY_ADDRESS', + updateBuyAddress: updateBuyAddress, + COINBASE_SUBVIEW: 'COINBASE_SUBVIEW', + coinBaseSubview: coinBaseSubview, + SHAPESHIFT_SUBVIEW: 'SHAPESHIFT_SUBVIEW', + shapeShiftSubview: shapeShiftSubview, + PAIR_UPDATE: 'PAIR_UPDATE', + pairUpdate: pairUpdate, + coinShiftRquest: coinShiftRquest, + SHOW_SUB_LOADING_INDICATION: 'SHOW_SUB_LOADING_INDICATION', + showSubLoadingIndication: showSubLoadingIndication, + HIDE_SUB_LOADING_INDICATION: 'HIDE_SUB_LOADING_INDICATION', + hideSubLoadingIndication: hideSubLoadingIndication, +// QR STUFF: + SHOW_QR: 'SHOW_QR', + getQr: getQr, + reshowQrCode: reshowQrCode, + SHOW_QR_VIEW: 'SHOW_QR_VIEW', +// FORGOT PASSWORD: + BACK_TO_INIT_MENU: 'BACK_TO_INIT_MENU', + goBackToInitView: goBackToInitView, + RECOVERY_IN_PROGRESS: 'RECOVERY_IN_PROGRESS', + BACK_TO_UNLOCK_VIEW: 'BACK_TO_UNLOCK_VIEW', + backToUnlockView: backToUnlockView, } module.exports = actions @@ -214,6 +244,23 @@ function revealAccount () { } } +function setCurrentFiat (fiat) { + return (dispatch) => { + dispatch(this.showLoadingIndication()) + _accountManager.setCurrentFiat(fiat, (data, err) => { + dispatch(this.hideLoadingIndication()) + dispatch({ + type: this.SET_CURRENT_FIAT, + value: { + currentFiat: data.currentFiat, + conversionRate: data.conversionRate, + conversionDate: data.conversionDate, + }, + }) + }) + } +} + function signMsg (msgData) { return (dispatch) => { dispatch(actions.showLoadingIndication()) @@ -329,6 +376,12 @@ function showNewVaultSeed (seed) { } } +function backToUnlockView () { + return { + type: actions.BACK_TO_UNLOCK_VIEW, + } +} + // // unlock screen // @@ -457,6 +510,12 @@ function showConfigPage (transitionForward = true) { } } +function goBackToInitView () { + return { + type: actions.BACK_TO_INIT_MENU, + } +} + // // config // @@ -496,6 +555,18 @@ function hideLoadingIndication () { } } +function showSubLoadingIndication () { + return { + type: actions.SHOW_SUB_LOADING_INDICATION, + } +} + +function hideSubLoadingIndication () { + return { + type: actions.HIDE_SUB_LOADING_INDICATION, + } +} + function showWarning (text) { return this.displayWarning(text) } @@ -594,3 +665,159 @@ function buyEth (address, amount) { }) } } + +function buyEthView (address) { + return { + type: actions.BUY_ETH_VIEW, + value: address, + } +} + +function updateCoinBaseAmount (value) { + return { + type: actions.UPDATE_COINBASE_AMOUNT, + value, + } +} + +function updateBuyAddress (value) { + return { + type: actions.UPDATE_BUY_ADDRESS, + value, + } +} + +function coinBaseSubview () { + return { + type: actions.COINBASE_SUBVIEW, + } +} + +function pairUpdate (coin) { + return (dispatch) => { + dispatch(actions.showSubLoadingIndication()) + dispatch(actions.hideWarning()) + shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => { + dispatch(actions.hideSubLoadingIndication()) + dispatch({ + type: actions.PAIR_UPDATE, + value: { + marketinfo: mktResponse, + }, + }) + }) + } +} + +function shapeShiftSubview (network) { + var pair = 'btc_eth' + + return (dispatch) => { + dispatch(actions.showSubLoadingIndication()) + shapeShiftRequest('marketinfo', {pair}, (mktResponse) => { + shapeShiftRequest('getcoins', {}, (response) => { + dispatch(actions.hideSubLoadingIndication()) + if (mktResponse.error) return dispatch(actions.showWarning(mktResponse.error)) + dispatch({ + type: actions.SHAPESHIFT_SUBVIEW, + value: { + marketinfo: mktResponse, + coinOptions: response, + }, + }) + }) + }) + } +} + +function coinShiftRquest (data, marketData) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + shapeShiftRequest('shift', { method: 'POST', data}, (response) => { + if (response.error) return dispatch(actions.showWarning(response.error)) + var message = ` + Deposit your ${response.depositType} to the address bellow:` + _accountManager.createShapeShiftTx(response.deposit, response.depositType) + dispatch(actions.getQr(response.deposit, '125x125', [message].concat(marketData))) + }) + } +} + +function getQr (data, size, message) { + return (dispatch) => { + qrRequest(data, size, (response) => { + dispatch(actions.hideLoadingIndication()) + if (response.error) return dispatch(actions.showWarning(response.error)) + dispatch({ + type: actions.SHOW_QR, + value: { + qr: response, + message: message, + data: data, + }, + }) + }) + } +} +function reshowQrCode (data, coin) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => { + if (mktResponse.error) return dispatch(actions.showWarning(mktResponse.error)) + + var message = [ + `Deposit your ${coin} to the address bellow:`, + `Deposit Limit: ${mktResponse.limit}`, + `Deposit Minimum:${mktResponse.minimum}`, + ] + + qrRequest(data, '125x125', (response) => { + dispatch(actions.hideLoadingIndication()) + dispatch({ + type: actions.SHOW_QR_VIEW, + value: { + qr: response, + message: message, + data: data, + }, + }) + }) + }) + } +} + +function shapeShiftRequest (query, options, cb) { + var queryResponse, method + !options ? options = {} : null + options.method ? method = options.method : method = 'GET' + + var requestListner = function (request) { + queryResponse = JSON.parse(this.responseText) + cb ? cb(queryResponse) : null + return queryResponse + } + + var shapShiftReq = new XMLHttpRequest() + shapShiftReq.addEventListener('load', requestListner) + shapShiftReq.open(method, `https://shapeshift.io/${query}/${options.pair ? options.pair : ''}`, true) + + if (options.method === 'POST') { + var jsonObj = JSON.stringify(options.data) + shapShiftReq.setRequestHeader('Content-Type', 'application/json') + return shapShiftReq.send(jsonObj) + } else { + return shapShiftReq.send() + } +} + +function qrRequest (data, size, cb) { + var requestListner = function (request) { + cb ? cb(this.responseText) : null + return this.responseText + } + + var qrReq = new XMLHttpRequest() + qrReq.addEventListener('load', requestListner) + qrReq.open('GET', `https://api.qrserver.com/v1/create-qr-code/?size=${size}&format=svg&data=${data}`, true) + qrReq.send() +} diff --git a/ui/app/app.js b/ui/app/app.js index cc616fb7c..b674efff1 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -28,7 +28,8 @@ const DropMenuItem = require('./components/drop-menu-item') const NetworkIndicator = require('./components/network') const Tooltip = require('./components/tooltip') const EthStoreWarning = require('./eth-store-warning') - +const BuyView = require('./components/buy-button-subview') +const QrView = require('./components/qr-code') module.exports = connect(mapStateToProps)(App) inherits(App, Component) @@ -50,6 +51,7 @@ function mapStateToProps (state) { menuOpen: state.appState.menuOpen, network: state.metamask.network, provider: state.metamask.provider, + forgottenPassword: state.appState.forgottenPassword, } } @@ -88,6 +90,7 @@ App.prototype.render = function () { transitionLeaveTimeout: 300, }, [ this.renderPrimary(), + this.renderBackToInitButton(), ]), ]), ]) @@ -95,6 +98,11 @@ App.prototype.render = function () { } App.prototype.renderAppBar = function () { + + if (window.METAMASK_UI_TYPE === 'notification') { + return null + } + const props = this.props const state = this.state || {} const isNetworkMenuOpen = state.isNetworkMenuOpen || false @@ -226,15 +234,6 @@ App.prototype.renderNetworkDropdown = function () { }), h(DropMenuItem, { - label: 'Ethereum Classic Network', - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setProviderType('classic')), - icon: h('.menu-icon.hollow-diamond'), - activeNetworkRender: props.network, - provider: props.provider, - }), - - h(DropMenuItem, { label: 'Morden Test Network', closeMenu: () => this.setState({ isNetworkMenuOpen: false }), action: () => props.dispatch(actions.setProviderType('testnet')), @@ -250,6 +249,13 @@ App.prototype.renderNetworkDropdown = function () { activeNetworkRender: props.provider.rpcTarget, }), + h(DropMenuItem, { + label: 'Custom RPC', + closeMenu: () => this.setState({ isNetworkMenuOpen: false }), + action: () => this.props.dispatch(actions.showConfigPage()), + icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }), + }), + this.renderCustomOption(props.provider.rpcTarget), ]) } @@ -301,6 +307,92 @@ App.prototype.renderDropdown = function () { }), ]) } +App.prototype.renderBackButton = function (style, justArrow = false) { + var props = this.props + return ( + h('.flex-row', { + key: 'leftArrow', + transForward: false, + style: style, + onClick: () => props.dispatch(actions.goBackToInitView()), + }, [ + h('i.fa.fa-arrow-left.cursor-pointer'), + justArrow ? null : h('div.cursor-pointer', { + style: { + marginLeft: '3px', + }, + onClick: () => props.dispatch(actions.goBackToInitView()), + }, 'BACK'), + ]) + ) + +} +App.prototype.renderBackToInitButton = function () { + var props = this.props + var button = null + if (!props.isUnlocked) { + if (props.currentView.name === 'InitMenu') { + button = props.forgottenPassword ? h('.flex-row', { + key: 'rightArrow', + style: { + position: 'absolute', + bottom: '10px', + right: '15px', + fontSize: '21px', + fontFamily: 'Montserrat Light', + color: '#7F8082', + width: '77.578px', + alignItems: 'flex-end', + }, + }, [ + h('div.cursor-pointer', { + style: { + marginRight: '3px', + }, + onClick: () => props.dispatch(actions.backToUnlockView()), + }, 'LOGIN'), + h('i.fa.fa-arrow-right.cursor-pointer'), + ]) : null + } else if (props.isInitialized) { + var style + switch (props.currentView.name) { + case 'createVault': + style = { + position: 'absolute', + top: '41px', + left: '80px', + fontSize: '21px', + fontFamily: 'Montserrat Bold', + color: 'rgb(174, 174, 174)', + } + return this.renderBackButton(style, true) + case 'restoreVault': + style = { + position: 'absolute', + top: '41px', + left: '70px', + fontSize: '21px', + fontFamily: 'Montserrat Bold', + color: 'rgb(174, 174, 174)', + } + return this.renderBackButton(style, true) + default: + style = { + position: 'absolute', + bottom: '10px', + left: '15px', + fontSize: '21px', + fontFamily: 'Montserrat Light', + color: '#7F8082', + width: '71.969px', + alignItems: 'flex-end', + } + return this.renderBackButton(style) + } + } + } + return button +} App.prototype.renderPrimary = function () { var props = this.props @@ -314,7 +406,7 @@ App.prototype.renderPrimary = function () { } // show initialize screen - if (!props.isInitialized) { + if (!props.isInitialized || props.forgottenPassword) { // show current view switch (props.currentView.name) { @@ -366,6 +458,35 @@ App.prototype.renderPrimary = function () { case 'createVault': return h(CreateVaultScreen, {key: 'createVault'}) + case 'buyEth': + return h(BuyView, {key: 'buyEthView'}) + case 'qr': + return h('div', { + style: { + position: 'absolute', + height: '100%', + top: '0px', + left: '0px', + }, + }, [ + h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { + onClick: () => props.dispatch(actions.backToAccountDetail(props.activeAddress)), + style: { + marginLeft: '10px', + marginTop: '50px', + }, + }), + h('div', { + style: { + position: 'absolute', + bottom: '115px', + left: '44px', + width: '285px', + }, + }, [ + h(QrView, {key: 'qr'}), + ]), + ]) default: return h(AccountDetailScreen, {key: 'account-detail'}) @@ -387,12 +508,8 @@ App.prototype.toggleMetamaskActive = function () { App.prototype.renderCustomOption = function (rpcTarget) { switch (rpcTarget) { case undefined: - return h(DropMenuItem, { - label: 'Custom RPC', - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => this.props.dispatch(actions.showConfigPage()), - icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }), - }) + return null + case 'http://localhost:8545': return h(DropMenuItem, { label: 'Custom RPC', diff --git a/ui/app/components/account-eth-balance.js b/ui/app/components/account-eth-balance.js new file mode 100644 index 000000000..8d693685f --- /dev/null +++ b/ui/app/components/account-eth-balance.js @@ -0,0 +1,140 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const formatBalance = require('../util').formatBalance +const generateBalanceObject = require('../util').generateBalanceObject +const Tooltip = require('./tooltip.js') + +module.exports = connect(mapStateToProps)(EthBalanceComponent) + +function mapStateToProps (state) { + return { + conversionRate: state.metamask.conversionRate, + conversionDate: state.metamask.conversionDate, + currentFiat: state.metamask.currentFiat, + } +} + +inherits(EthBalanceComponent, Component) +function EthBalanceComponent () { + Component.call(this) +} + +EthBalanceComponent.prototype.render = function () { + var state = this.props + var style = state.style + + const value = formatBalance(state.value, 6) + var width = state.width + + return ( + + h('.ether-balance', { + style: style, + }, [ + h('.ether-balance-amount', { + style: { + display: 'inline', + width: width, + }, + }, this.renderBalance(value, state)), + ]) + + ) +} +EthBalanceComponent.prototype.renderBalance = function (value, state) { + if (value === 'None') return value + var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3) + var balance, fiatDisplayNumber, fiatTooltipNumber + var splitBalance = value.split(' ') + var ethNumber = splitBalance[0] + var ethSuffix = splitBalance[1] + + + if (state.conversionRate !== 0) { + fiatTooltipNumber = Number(splitBalance[0]) * state.conversionRate + fiatDisplayNumber = fiatTooltipNumber.toFixed(2) + } else { + fiatDisplayNumber = 'N/A' + } + + var fiatSuffix = state.currentFiat + + if (state.shorten) { + balance = balanceObj.shortBalance + } else { + balance = balanceObj.balance + } + + var label = balanceObj.label + + return ( + h('.flex-column', [ + h(Tooltip, { + position: 'bottom', + title: `${ethNumber} ${ethSuffix}`, + }, [ + h('.flex-row', { + style: { + alignItems: 'flex-end', + lineHeight: '13px', + fontFamily: 'Montserrat Light', + textRendering: 'geometricPrecision', + marginBottom: '5px', + }, + }, [ + h('div', { + style: { + width: '100%', + textAlign: 'right', + }, + }, balance), + h('div', { + style: { + color: '#AEAEAE', + marginLeft: '5px', + }, + }, label), + ]), + ]), + h(Tooltip, { + position: 'bottom', + title: `${fiatTooltipNumber} ${fiatSuffix}`, + }, [ + fiatDisplay(fiatDisplayNumber, fiatSuffix), + ]), + ]) + ) +} + +function fiatDisplay (fiatDisplayNumber, fiatSuffix) { + if (fiatDisplayNumber !== 'N/A') { + return h('.flex-row', { + style: { + alignItems: 'flex-end', + lineHeight: '13px', + fontFamily: 'Montserrat Light', + textRendering: 'geometricPrecision', + }, + }, [ + h('div', { + style: { + width: '100%', + textAlign: 'right', + fontSize: '12px', + color: '#333333', + }, + }, fiatDisplayNumber), + h('div', { + style: { + color: '#AEAEAE', + marginLeft: '5px', + fontSize: '12px', + }, + }, fiatSuffix), + ]) + } else { + return h('div') + } +} diff --git a/ui/app/components/account-info-link.js b/ui/app/components/account-info-link.js new file mode 100644 index 000000000..4fe3b8b5d --- /dev/null +++ b/ui/app/components/account-info-link.js @@ -0,0 +1,42 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const Tooltip = require('./tooltip') +const genAccountLink = require('../../lib/account-link') +const extension = require('../../../app/scripts/lib/extension') + +module.exports = AccountInfoLink + +inherits(AccountInfoLink, Component) +function AccountInfoLink () { + Component.call(this) +} + +AccountInfoLink.prototype.render = function () { + const { selected, network } = this.props + const title = 'View account on etherscan' + const url = genAccountLink(selected, network) + + if (!url) { + return null + } + + return h('.account-info-link', { + style: { + display: 'flex', + alignItems: 'center', + }, + }, [ + + h(Tooltip, { + title, + }, [ + h('i.fa.fa-info-circle.cursor-pointer.color-orange', { + style: { + margin: '5px', + }, + onClick () { extension.tabs.create({ url }) }, + }), + ]), + ]) +} diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js new file mode 100644 index 000000000..742241e5b --- /dev/null +++ b/ui/app/components/buy-button-subview.js @@ -0,0 +1,123 @@ +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 CoinbaseForm = require('./coinbase-form') +const ShapeshiftForm = require('./shapeshift-form') +const extension = require('../../../app/scripts/lib/extension') + +module.exports = connect(mapStateToProps)(BuyButtonSubview) + +function mapStateToProps (state) { + return { + selectedAccount: state.selectedAccount, + warning: state.appState.warning, + buyView: state.appState.buyView, + network: state.metamask.network, + provider: state.metamask.provider, + } +} + +inherits(BuyButtonSubview, Component) +function BuyButtonSubview () { + Component.call(this) +} + +BuyButtonSubview.prototype.render = function () { + const props = this.props + const currentForm = props.buyView.formView + + return ( + h('.buy-eth-section', [ + // back button + h('.flex-row', { + style: { + alignItems: 'center', + justifyContent: 'center', + }, + }, [ + h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { + onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAccount)), + style: { + position: 'absolute', + left: '10px', + }, + }), + h('h2.page-subtitle', 'Buy Eth'), + ]), + h('h3.flex-row.text-transform-uppercase', { + style: { + background: '#EBEBEB', + color: '#AEAEAE', + paddingTop: '4px', + justifyContent: 'space-around', + }, + }, [ + h(currentForm.coinbase ? '.activeForm' : '.inactiveForm.pointer', { + onClick: () => props.dispatch(actions.coinBaseSubview()), + }, 'Coinbase'), + h('a', { + onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'), + }, [ + h('i.fa.fa-question-circle', { + style: { + position: 'relative', + right: '33px', + }, + }), + ]), + h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm.pointer', { + onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)), + }, 'Shapeshift'), + + h('a', { + href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md', + onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'), + }, [ + h('i.fa.fa-question-circle', { + style: { + position: 'relative', + right: '28px', + }, + }), + ]), + ]), + this.formVersionSubview(), + ]) + ) +} + +BuyButtonSubview.prototype.formVersionSubview = function () { + if (this.props.network === '1') { + if (this.props.buyView.formView.coinbase) { + return h(CoinbaseForm, this.props) + } else if (this.props.buyView.formView.shapeshift) { + return h(ShapeshiftForm, this.props) + } + } else { + return h('div.flex-column', { + style: { + alignItems: 'center', + margin: '50px', + }, + }, [ + h('h3.text-transform-uppercase', { + style: { + width: '225px', + }, + }, 'In order to access this feature please switch too the Main Network'), + h('h3.text-transform-uppercase', 'or:'), + this.props.network === '2' ? h('button.text-transform-uppercase', { + onClick: () => this.props.dispatch(actions.buyEth()), + style: { + marginTop: '15px', + }, + }, 'Go To Test Faucet') : null, + ]) + } +} + +BuyButtonSubview.prototype.navigateTo = function (url) { + extension.tabs.create({ url }) +} diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js new file mode 100644 index 000000000..efd05ec96 --- /dev/null +++ b/ui/app/components/coinbase-form.js @@ -0,0 +1,162 @@ +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 isValidAddress = require('../util').isValidAddress +module.exports = connect(mapStateToProps)(CoinbaseForm) + +function mapStateToProps(state) { + return { + selectedAccount: state.selectedAccount, + warning: state.appState.warning, + } +} + +inherits(CoinbaseForm, Component) + +function CoinbaseForm() { + Component.call(this) +} + +CoinbaseForm.prototype.render = function () { + var props = this.props + var amount = props.buyView.amount + var address = props.buyView.buyAddress + + return h('.flex-column', { + style: { + // margin: '10px', + padding: '25px', + }, + }, [ + h('.flex-column', { + style: { + alignItems: 'flex-start', + }, + }, [ + h('.flex-row', [ + h('div', 'Address:'), + h('.ellip-address', address), + ]), + h('.flex-row', [ + h('div', 'Amount: $'), + h('.input-container', [ + h('input.buy-inputs', { + style: { + width: '3em', + boxSizing: 'border-box', + }, + defaultValue: amount, + onChange: this.handleAmount.bind(this), + }), + h('i.fa.fa-pencil-square-o.edit-text', { + style: { + fontSize: '12px', + color: '#F7861C', + position: 'relative', + bottom: '5px', + right: '11px', + }, + }), + ]), + ]), + ]), + + h('.info-gray', { + style: { + fontSize: '10px', + fontFamily: 'Montserrat Light', + margin: '15px', + lineHeight: '13px', + }, + }, + `there is a USD$ 5 a day max and a USD$ 50 + dollar limit per the life time of an account without a + coinbase account. A fee of 3.75% will be aplied to debit/credit cards.`), + + !props.warning ? h('div', { + style: { + width: '340px', + height: '22px', + }, + }) : props.warning && h('span.error.flex-center', props.warning), + + + h('.flex-row', { + style: { + justifyContent: 'space-around', + margin: '33px', + }, + }, [ + h('button', { + onClick: this.toCoinbase.bind(this), + }, 'Continue to Coinbase'), + + h('button', { + onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)), + }, 'Cancel'), + ]), + ]) +} +CoinbaseForm.prototype.handleAmount = function (event) { + this.props.dispatch(actions.updateCoinBaseAmount(event.target.value)) +} +CoinbaseForm.prototype.handleAddress = function (event) { + this.props.dispatch(actions.updateBuyAddress(event.target.value)) +} +CoinbaseForm.prototype.toCoinbase = function () { + var props = this.props + var amount = props.buyView.amount + var address = props.buyView.buyAddress + var message + + if (isValidAddress(address) && isValidAmountforCoinBase(amount).valid) { + props.dispatch(actions.buyEth(address, props.buyView.amount)) + } else if (!isValidAmountforCoinBase(amount).valid) { + message = isValidAmountforCoinBase(amount).message + return props.dispatch(actions.showWarning(message)) + } else { + message = 'Receiving address is invalid.' + return props.dispatch(actions.showWarning(message)) + } +} + +CoinbaseForm.prototype.renderLoading = function () { + + return h('img', { + style: { + width: '27px', + marginRight: '-27px', + }, + src: 'images/loading.svg', + }) +} + +function isValidAmountforCoinBase(amount) { + amount = parseFloat(amount) + + if (amount) { + if (amount <= 5 && amount > 0) { + return { + valid: true, + } + } else if (amount > 5) { + return { + valid: false, + message: 'The amount can not be greater then $5', + } + } else { + return { + valid: false, + message: 'Can not buy amounts less then $0', + } + } + } else { + return { + valid: false, + message: 'The amount entered is not a number', + } + } +} diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js index 1a0d6cbd5..0ca1988c6 100644 --- a/ui/app/components/drop-menu-item.js +++ b/ui/app/components/drop-menu-item.js @@ -41,9 +41,6 @@ DropMenuItem.prototype.activeNetworkRender = function () { case 'Main Ethereum Network': if (providerType === 'mainnet') return h('.check', '✓') break - case 'Ethereum Classic Network': - if (providerType === 'classic') return h('.check', '✓') - break case 'Morden Test Network': if (activeNetwork === '2') return h('.check', '✓') break diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 612ef7779..498873faa 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -4,6 +4,7 @@ const inherits = require('util').inherits const formatBalance = require('../util').formatBalance const generateBalanceObject = require('../util').generateBalanceObject const Tooltip = require('./tooltip.js') + module.exports = EthBalanceComponent inherits(EthBalanceComponent, Component) @@ -14,29 +15,33 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { var state = this.props var style = state.style - - const value = formatBalance(state.value, 6) + var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true + const value = formatBalance(state.value, 6, needsParse) var width = state.width return ( - h('.ether-balance', { + h('.ether-balance.ether-balance-amount', { style: style, }, [ - h('.ether-balance-amount', { + h('div', { style: { display: 'inline', width: width, }, - }, this.renderBalance(value, state)), + }, this.renderBalance(value)), ]) ) } -EthBalanceComponent.prototype.renderBalance = function (value, state) { +EthBalanceComponent.prototype.renderBalance = function (value) { + var state = this.props if (value === 'None') return value var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3) var balance + var splitBalance = value.split(' ') + var ethNumber = splitBalance[0] + var ethSuffix = splitBalance[1] if (state.shorten) { balance = balanceObj.shortBalance @@ -49,7 +54,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) { return ( h(Tooltip, { position: 'bottom', - title: value.split(' ')[0], + title: `${ethNumber} ${ethSuffix}`, }, [ h('.flex-column', { style: { @@ -64,7 +69,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) { width: '100%', textAlign: 'right', }, - }, balance), + }, this.props.incoming ? `+${balance}` : balance), h('div', { style: { color: ' #AEAEAE', diff --git a/ui/app/components/mascot.js b/ui/app/components/mascot.js index ddd51f8ba..f2b00262b 100644 --- a/ui/app/components/mascot.js +++ b/ui/app/components/mascot.js @@ -14,8 +14,9 @@ function Mascot () { pxNotRatio: true, width: 200, height: 200, + staticImage: './images/icon-512.png', }) - if (!this.logo) return + if (!this.logo.webGLSupport) return this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000) this.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false) } @@ -34,19 +35,24 @@ Mascot.prototype.render = function () { } Mascot.prototype.componentDidMount = function () { - if (!this.logo) return var targetDivId = 'metamask-mascot-container' var container = document.getElementById(targetDivId) - container.appendChild(this.logo.canvas) + if (!this.logo.webGLSupport) { + var staticLogo = this.logo.staticLogo + staticLogo.style.marginBottom = '40px' + container.appendChild(staticLogo) + } else { + container.appendChild(this.logo.canvas) + } } Mascot.prototype.componentWillUnmount = function () { - if (!this.logo) return + if (!this.logo.webGLSupport) return this.logo.canvas.remove() } Mascot.prototype.handleAnimationEvents = function () { - if (!this.logo) return + if (!this.logo.webGLSupport) return // only setup listeners once if (this.animations) return this.animations = this.props.animationEventEmitter @@ -55,7 +61,7 @@ Mascot.prototype.handleAnimationEvents = function () { } Mascot.prototype.lookAt = function (target) { - if (!this.logo) return + if (!this.logo.webGLSupport) return this.unfollowMouse() this.logo.lookAt(target) this.refollowMouse() diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 95901fe70..6826e0685 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -23,7 +23,7 @@ Network.prototype.render = function () { if (networkNumber === 'loading') { - return h('img', { + return h('img.network-indicator', { title: 'Attempting to connect to blockchain.', onClick: (event) => this.props.onClick(event), style: { @@ -36,9 +36,6 @@ Network.prototype.render = function () { } else if (providerName === 'mainnet') { hoverText = 'Main Ethereum Network' iconName = 'ethereum-network' - } else if (providerName === 'classic') { - hoverText = 'Ethereum Classic Network' - iconName = 'classic-network' } else if (parseInt(networkNumber) === 2) { hoverText = 'Morden Test Network' iconName = 'morden-test-network' @@ -64,16 +61,7 @@ Network.prototype.render = function () { style: { color: '#039396', }}, - 'Etherum Main Net'), - ]) - case 'classic-network': - return h('.network-indicator', [ - h('.menu-icon.hollow-diamond'), - h('.network-name', { - style: { - color: '#039396', - }}, - 'Etherum Classic'), + 'Ethereum Main Net'), ]) case 'morden-test-network': return h('.network-indicator', [ diff --git a/ui/app/components/pending-msg-details.js b/ui/app/components/pending-msg-details.js index adcec596e..16308d121 100644 --- a/ui/app/components/pending-msg-details.js +++ b/ui/app/components/pending-msg-details.js @@ -3,7 +3,6 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const AccountPanel = require('./account-panel') -const readableDate = require('../util').readableDate module.exports = PendingMsgDetails @@ -24,6 +23,9 @@ PendingMsgDetails.prototype.render = function () { return ( h('div', { key: msgData.id, + style: { + margin: '10px 20px', + }, }, [ // account that will sign @@ -37,11 +39,6 @@ PendingMsgDetails.prototype.render = function () { // message data h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ h('.flex-row.flex-space-between', [ - h('label.font-small', 'DATE'), - h('span.font-small', readableDate(msgData.time)), - ]), - - h('.flex-row.flex-space-between', [ h('label.font-small', 'MESSAGE'), h('span.font-small', msgParams.data), ]), diff --git a/ui/app/components/qr-code.js b/ui/app/components/qr-code.js new file mode 100644 index 000000000..c26b02b94 --- /dev/null +++ b/ui/app/components/qr-code.js @@ -0,0 +1,71 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const CopyButton = require('./copyButton') + +module.exports = connect(mapStateToProps)(QrCodeView) + +function mapStateToProps (state) { + return { + Qr: state.appState.Qr, + buyView: state.appState.buyView, + warning: state.appState.warning, + } +} + +inherits(QrCodeView, Component) + +function QrCodeView () { + Component.call(this) +} + +QrCodeView.prototype.render = function () { + var props = this.props + var Qr = props.Qr + return h('.main-container.flex-column', { + key: 'qr', + style: { + justifyContent: 'center', + padding: '45px', + alignItems: 'center', + }, + }, [ + Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('h3', Qr.message), + + this.props.warning ? this.props.warning && h('span.error.flex-center', { + style: { + textAlign: 'center', + width: '229px', + height: '82px', + }, + }, + this.props.warning) : null, + + h('#qr-container.flex-column', { + style: { + marginTop: '25px', + marginBottom: '15px', + }, + dangerouslySetInnerHTML: { + __html: Qr.image, + }, + }), + h('.flex-row', [ + h('h3.ellip-address', { + style: { + width: '247px', + }, + }, Qr.data), + h(CopyButton, { + value: Qr.data, + }), + ]), + ]) +} + +QrCodeView.prototype.renderMultiMessage = function () { + var Qr = this.props.Qr + var multiMessage = Qr.message.map((message) => h('.qr-message', message)) + return multiMessage +} diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js new file mode 100644 index 000000000..58b7942c3 --- /dev/null +++ b/ui/app/components/shapeshift-form.js @@ -0,0 +1,322 @@ +const PersistentForm = require('../../lib/persistent-form') +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const ReactCSSTransitionGroup = require('react-addons-css-transition-group') +const actions = require('../actions') +const Qr = require('./qr-code') +const isValidAddress = require('../util').isValidAddress +module.exports = connect(mapStateToProps)(ShapeshiftForm) + +function mapStateToProps(state) { + return { + selectedAccount: state.selectedAccount, + warning: state.appState.warning, + isSubLoading: state.appState.isSubLoading, + qrRequested: state.appState.qrRequested, + } +} + +inherits(ShapeshiftForm, PersistentForm) + +function ShapeshiftForm () { + PersistentForm.call(this) + this.persistentFormParentId = 'shapeshift-buy-form' +} + +ShapeshiftForm.prototype.render = function () { + + return h(ReactCSSTransitionGroup, { + className: 'css-transition-group', + transitionName: 'main', + transitionEnterTimeout: 300, + transitionLeaveTimeout: 300, + }, [ + this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain(), + ]) + +} + +ShapeshiftForm.prototype.renderMain = function () { + const marketinfo = this.props.buyView.formView.marketinfo + const coinOptions = this.props.buyView.formView.coinOptions + var coin = marketinfo.pair.split('_')[0].toUpperCase() + + return h('.flex-column', { + style: { + // marginTop: '10px', + padding: '25px', + width: '100%', + alignItems: 'center', + }, + }, [ + h('.flex-row', { + style: { + justifyContent: 'center', + alignItems: 'baseline', + }, + }, [ + h('img', { + src: coinOptions[coin].image, + width: '25px', + height: '25px', + style: { + marginRight: '5px', + }, + }), + + h('.input-container', [ + h('input#fromCoin.buy-inputs.ex-coins', { + type: 'text', + list: 'coinList', + dataset: { + persistentFormId: 'input-coin', + }, + style: { + boxSizing: 'border-box', + }, + onChange: this.handleLiveInput.bind(this), + defaultValue: 'BTC', + }), + + this.renderCoinList(), + + h('i.fa.fa-pencil-square-o.edit-text', { + style: { + fontSize: '12px', + color: '#F7861C', + position: 'relative', + bottom: '48px', + left: '106px', + }, + }), + ]), + + h('.icon-control', [ + h('i.fa.fa-refresh.fa-4.orange', { + style: { + position: 'relative', + bottom: '5px', + left: '5px', + color: '#F7861C', + }, + onClick: this.updateCoin.bind(this), + }), + h('i.fa.fa-chevron-right.fa-4.orange', { + style: { + position: 'relative', + bottom: '26px', + left: '10px', + color: '#F7861C', + }, + onClick: this.updateCoin.bind(this), + }), + ]), + + h('#toCoin.ex-coins', marketinfo.pair.split('_')[1].toUpperCase()), + + h('img', { + src: coinOptions[marketinfo.pair.split('_')[1].toUpperCase()].image, + width: '25px', + height: '25px', + style: { + marginLeft: '5px', + }, + }), + ]), + + this.props.isSubLoading ? this.renderLoading() : null, + h('.flex-column', { + style: { + width: '235px', + alignItems: 'flex-start', + }, + }, [ + this.props.warning ? this.props.warning && h('span.error.flex-center', { + style: { + textAlign: 'center', + width: '229px', + height: '82px', + }, + }, + this.props.warning) : this.renderInfo(), + ]), + + h('.flex-row', { + style: { + padding: '10px', + paddingBottom: '2px', + width: '100%', + }, + }, [ + h('div', 'Receiving address:'), + h('.ellip-address', this.props.buyView.buyAddress), + ]), + + h(this.activeToggle('.input-container'), { + style: { + padding: '10px', + paddingTop: '0px', + width: '100%', + }, + }, [ + h('div', `${coin} Address:`), + + h('input#fromCoinAddress.buy-inputs', { + type: 'text', + placeholder: `Your ${coin} Refund Address`, + dataset: { + persistentFormId: 'refund-address', + }, + style: { + boxSizing: 'border-box', + width: '278px', + height: '20px', + padding: ' 5px ', + }, + }), + + h('i.fa.fa-pencil-square-o.edit-text', { + style: { + fontSize: '12px', + color: '#F7861C', + position: 'relative', + bottom: '5px', + right: '11px', + }, + }), + h('.flex-row', { + style: { + justifyContent: 'flex-end', + }, + }, [ + h('button', { + onClick: this.shift.bind(this), + style: { + marginTop: '10px', + }, + }, + 'Submit'), + ]), + ]), + ]) +} + +ShapeshiftForm.prototype.shift = function () { + var props = this.props + var withdrawal = this.props.buyView.buyAddress + var returnAddress = document.getElementById('fromCoinAddress').value + var pair = this.props.buyView.formView.marketinfo.pair + var data = { + 'withdrawal': withdrawal, + 'pair': pair, + 'returnAddress': returnAddress, + // Public api key + 'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6', + } + var message = [ + `Deposit Limit: ${props.buyView.formView.marketinfo.limit}`, + `Deposit Minimum:${props.buyView.formView.marketinfo.minimum}`, + ] + if (isValidAddress(withdrawal)) { + this.props.dispatch(actions.coinShiftRquest(data, message)) + } +} + +ShapeshiftForm.prototype.renderCoinList = function () { + var list = Object.keys(this.props.buyView.formView.coinOptions).map((item) => { + return h('option', { + value: item, + }, item) + }) + + return h('datalist#coinList', { + onClick: (event) => { + event.preventDefault() + }, + }, list) +} + +ShapeshiftForm.prototype.updateCoin = function (event) { + event.preventDefault() + const props = this.props + var coinOptions = this.props.buyView.formView.coinOptions + var coin = document.getElementById('fromCoin').value + + if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') { + var message = 'Not a valid coin' + return props.dispatch(actions.showWarning(message)) + } else { + return props.dispatch(actions.pairUpdate(coin)) + } +} + +ShapeshiftForm.prototype.handleLiveInput = function () { + const props = this.props + var coinOptions = this.props.buyView.formView.coinOptions + var coin = document.getElementById('fromCoin').value + + if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') { + return null + } else { + return props.dispatch(actions.pairUpdate(coin)) + } +} + +ShapeshiftForm.prototype.renderInfo = function () { + const marketinfo = this.props.buyView.formView.marketinfo + const coinOptions = this.props.buyView.formView.coinOptions + var coin = marketinfo.pair.split('_')[0].toUpperCase() + + return h('span', { + style: { + marginTop: '15px', + marginBottom: '15px', + }, + }, [ + h('h3.flex-row.text-transform-uppercase', { + style: { + color: '#AEAEAE', + paddingTop: '4px', + justifyContent: 'space-around', + textAlign: 'center', + fontSize: '14px', + }, + }, `Market Info for ${marketinfo.pair.replace('_', ' to ').toUpperCase()}:`), + h('.marketinfo', ['Status : ', `${coinOptions[coin].status}`]), + h('.marketinfo', ['Exchange Rate: ', `${marketinfo.rate}`]), + h('.marketinfo', ['Limit: ', `${marketinfo.limit}`]), + h('.marketinfo', ['Minimum : ', `${marketinfo.minimum}`]), + ]) +} + +ShapeshiftForm.prototype.handleAddress = function (event) { + this.props.dispatch(actions.updateBuyAddress(event.target.value)) +} + +ShapeshiftForm.prototype.activeToggle = function (elementType) { + if (!this.props.buyView.formView.response || this.props.warning) return elementType + return `${elementType}.inactive` +} + +ShapeshiftForm.prototype.renderLoading = function () { + return h('span', { + style: { + position: 'absolute', + left: '70px', + bottom: '194px', + background: 'transparent', + width: '229px', + height: '82px', + display: 'flex', + justifyContent: 'center', + }, + }, [ + h('img', { + style: { + width: '60px', + }, + src: 'images/loading.svg', + }), + ]) +} diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js new file mode 100644 index 000000000..38c19eb28 --- /dev/null +++ b/ui/app/components/shift-list-item.js @@ -0,0 +1,202 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const vreme = new (require('vreme')) +const explorerLink = require('../../lib/explorer-link') +const extension = require('../../../app/scripts/lib/extension') +const actions = require('../actions') +const addressSummary = require('../util').addressSummary + +const CopyButton = require('./copyButton') +const EtherBalance = require('./eth-balance') +const Tooltip = require('./tooltip') + + +module.exports = connect(mapStateToProps)(ShiftListItem) + +function mapStateToProps (state) { + return {} +} + +inherits(ShiftListItem, Component) + +function ShiftListItem () { + Component.call(this) +} + +ShiftListItem.prototype.render = function () { + + return ( + h('.transaction-list-item.flex-row', { + style: { + paddingTop: '20px', + paddingBottom: '20px', + justifyContent: 'space-around', + alignItems: 'center', + }, + }, [ + h('div', { + style: { + width: '0px', + position: 'relative', + bottom: '19px', + }, + }, [ + h('img', { + src: 'https://info.shapeshift.io/sites/default/files/logo.png', + style: { + height: '35px', + width: '132px', + position: 'absolute', + clip: 'rect(0px,23px,34px,0px)', + }, + }), + ]), + + this.renderInfo(), + this.renderUtilComponents(), + ]) + ) +} + +function formatDate (date) { + return vreme.format(new Date(date), 'March 16 2014 14:30') +} + +ShiftListItem.prototype.renderUtilComponents = function () { + var props = this.props + + switch (props.response.status) { + case 'no_deposits': + return h('.flex-row', [ + h(CopyButton, { + value: this.props.depositAddress, + }), + h(Tooltip, { + title: 'QR Code', + }, [ + h('i.fa.fa-qrcode.pointer.pop-hover', { + onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)), + style: { + margin: '5px', + marginLeft: '23px', + marginRight: '12px', + fontSize: '20px', + color: '#F7861C', + }, + }), + ]), + ]) + case 'received': + return h('.flex-row') + + case 'complete': + return h('.flex-row', [ + h(CopyButton, { + value: this.props.response.transaction, + }), + h(EtherBalance, { + value: `${props.response.outgoingCoin}`, + width: '55px', + shorten: true, + needsParse: false, + incoming: true, + style: { + fontSize: '15px', + color: '#01888C', + }, + }), + ]) + + case 'failed': + return '' + default: + return '' + } +} + +ShiftListItem.prototype.renderInfo = function () { + var props = this.props + switch (props.response.status) { + case 'no_deposits': + return h('.flex-column', { + style: { + width: '200px', + overflow: 'hidden', + }, + }, [ + h('div', { + style: { + fontSize: 'x-small', + color: '#ABA9AA', + width: '100%', + }, + }, `${props.depositType} to ETH via ShapeShift`), + h('div', 'No deposits received'), + h('div', { + style: { + fontSize: 'x-small', + color: '#ABA9AA', + width: '100%', + }, + }, formatDate(props.time)), + ]) + case 'received': + return h('.flex-column', { + style: { + width: '200px', + overflow: 'hidden', + }, + }, [ + h('div', { + style: { + fontSize: 'x-small', + color: '#ABA9AA', + width: '100%', + }, + }, `${props.depositType} to ETH via ShapeShift`), + h('div', 'Conversion in progress'), + h('div', { + style: { + fontSize: 'x-small', + color: '#ABA9AA', + width: '100%', + }, + }, formatDate(props.time)), + ]) + case 'complete': + var url = explorerLink(props.response.transaction, parseInt('1')) + + return h('.flex-column.pointer', { + style: { + width: '200px', + overflow: 'hidden', + }, + onClick: () => extension.tabs.create({ + url, + }), + }, [ + h('div', { + style: { + fontSize: 'x-small', + color: '#ABA9AA', + width: '100%', + }, + }, 'From ShapeShift'), + h('div', formatDate(props.time)), + h('div', { + style: { + fontSize: 'x-small', + color: '#ABA9AA', + width: '100%', + }, + }, addressSummary(props.response.transaction)), + ]) + + case 'failed': + return h('span.error', '(Failed)') + default: + return '' + } +} diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index 796ba61ae..1b85464e1 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -10,7 +10,7 @@ const vreme = new (require('vreme')) const extension = require('../../../app/scripts/lib/extension') const TransactionIcon = require('./transaction-list-item-icon') - +const ShiftListItem = require('./shift-list-item') module.exports = TransactionListItem inherits(TransactionListItem, Component) @@ -19,8 +19,10 @@ function TransactionListItem () { } TransactionListItem.prototype.render = function () { - const { transaction, i, network } = this.props - + const { transaction, network } = this.props + if (transaction.key === 'shapeshift') { + if (network === '1') return h(ShiftListItem, transaction) + } var date = formatDate(transaction.time) let isLinkable = false @@ -42,7 +44,6 @@ TransactionListItem.prototype.render = function () { return ( h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, { - key: `tx-${transaction.id + i}`, onClick: (event) => { if (isPending) { this.props.showTx(transaction.id) diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 886aa7c00..7e1bedb05 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -14,7 +14,11 @@ function TransactionList () { TransactionList.prototype.render = function () { const { txsToRender, network, unconfMsgs } = this.props - const transactions = txsToRender.concat(unconfMsgs) + var shapeShiftTxList + if (network === '1') { + shapeShiftTxList = this.props.shapeShiftTxList + } + const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList) .sort((a, b) => b.time - a.time) return ( @@ -39,33 +43,46 @@ TransactionList.prototype.render = function () { paddingBottom: '4px', }, }, [ - 'Transactions', + 'History', ]), h('.tx-list', { style: { overflowY: 'auto', - height: '305px', + height: '300px', padding: '0 20px', textAlign: 'center', }, - }, ( + }, [ transactions.length ? transactions.map((transaction, i) => { + let key + switch (transaction.key) { + case 'shapeshift': + const { depositAddress, time } = transaction + key = `shift-tx-${depositAddress}-${time}-${i}` + break + default: + key = `tx-${transaction.id}-${i}` + } return h(TransactionListItem, { - transaction, i, network, + transaction, i, network, key, showTx: (txId) => { this.props.viewPendingTx(txId) }, }) }) - : [h('.flex-center', { + : h('.flex-center', { style: { + flexDirection: 'column', height: '100%', }, - }, 'No transaction history...')] - )), + }, [ + 'No transaction history.', + ]), + ]), ]) ) } + diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index db876dd9b..22d29383f 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -5,6 +5,7 @@ const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('./actions') const txHelper = require('../lib/tx-helper') +const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification') const PendingTx = require('./components/pending-tx') const PendingMsg = require('./components/pending-msg') @@ -35,7 +36,8 @@ ConfirmTxScreen.prototype.render = function () { var unconfMsgs = state.unconfMsgs var unconfTxList = txHelper(unconfTxs, unconfMsgs) var index = state.index !== undefined ? state.index : 0 - var txData = unconfTxList[index] || {} + var txData = unconfTxList[index] || unconfTxList[0] || {} + var isNotification = isPopupOrNotification() === 'notification' return ( @@ -43,9 +45,9 @@ ConfirmTxScreen.prototype.render = function () { // subtitle and nav h('.section-title.flex-row.flex-center', [ - h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { + !isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { onClick: this.goHome.bind(this), - }), + }) : null, h('h2.page-subtitle', 'Confirm Transaction'), ]), diff --git a/ui/app/config.js b/ui/app/config.js index c88e7ac6e..b043a47d6 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -3,7 +3,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('./actions') - +const currencies = require('./conversion-util').availableCurrencies.rows module.exports = connect(mapStateToProps)(ConfigScreen) function mapStateToProps (state) { @@ -74,6 +74,8 @@ ConfigScreen.prototype.render = function () { }, 'Save'), ]), h('hr.horizontal-line'), + currentConversionInformation(metamaskState, state), + h('hr.horizontal-line'), h('div', { style: { @@ -97,6 +99,27 @@ ConfigScreen.prototype.render = function () { ) } +function currentConversionInformation (metamaskState, state) { + var currentFiat = metamaskState.currentFiat + var conversionDate = metamaskState.conversionDate + return h('div', [ + h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'), + h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`), + h('select#currentFiat', { + onChange (event) { + event.preventDefault() + var element = document.getElementById('currentFiat') + var newFiat = element.value + state.dispatch(actions.setCurrentFiat(newFiat)) + }, + defaultValue: currentFiat, + }, currencies.map((currency) => { + return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`) + }) + ), + ]) +} + function currentProviderDisplay (metamaskState) { var provider = metamaskState.provider var title, value diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js new file mode 100644 index 000000000..19259602a --- /dev/null +++ b/ui/app/conversion-util.js @@ -0,0 +1,5 @@ +var availableCurrencies = {"rows":[{"code":"007","name":"007","statuses":["primary"]},{"code":"1337","name":"1337","statuses":["primary"]},{"code":"1CR","name":"1CR","statuses":["primary"]},{"code":"256","name":"256","statuses":["primary"]},{"code":"2FLAV","name":"2FLAV","statuses":["primary"]},{"code":"2GIVE","name":"2GIVE","statuses":["primary"]},{"code":"404","name":"404","statuses":["primary"]},{"code":"611","name":"611","statuses":["primary"]},{"code":"888","name":"888","statuses":["primary"]},{"code":"8BIT","name":"8Bit","statuses":["primary"]},{"code":"ACLR","name":"ACLR","statuses":["primary"]},{"code":"ACOIN","name":"ACOIN","statuses":["primary"]},{"code":"ACP","name":"ACP","statuses":["primary"]},{"code":"ADC","name":"ADC","statuses":["primary"]},{"code":"ADZ","name":"Adzcoin","statuses":["primary"]},{"code":"AEC","name":"AEC","statuses":["primary"]},{"code":"AEON","name":"Aeon","statuses":["primary"]},{"code":"AGRS","name":"Agoras Tokens","statuses":["primary"]},{"code":"AIB","name":"AIB","statuses":["primary"]},{"code":"ADN","name":"Aiden","statuses":["primary"]},{"code":"AIR","name":"AIR","statuses":["primary"]},{"code":"ALC","name":"ALC","statuses":["primary"]},{"code":"ALTC","name":"ALTC","statuses":["primary"]},{"code":"AM","name":"AM","statuses":["primary"]},{"code":"AMBER","name":"AMBER","statuses":["primary"]},{"code":"AMS","name":"AMS","statuses":["primary"]},{"code":"ANAL","name":"ANAL","statuses":["primary"]},{"code":"AND","name":"AND","statuses":["primary"]},{"code":"ANI","name":"ANI","statuses":["primary"]},{"code":"ANC","name":"Anoncoin","statuses":["primary"]},{"code":"ANTI","name":"AntiBitcoin","statuses":["primary"]},{"code":"APEX","name":"APEX","statuses":["primary"]},{"code":"APC","name":"Applecoin","statuses":["primary"]},{"code":"APT","name":"APT","statuses":["primary"]},{"code":"AR2","name":"AR2","statuses":["primary"]},{"code":"ARB","name":"ARB","statuses":["primary"]},{"code":"ARC","name":"ARC","statuses":["primary"]},{"code":"ARCH","name":"ARCH","statuses":["primary"]},{"code":"ABY","name":"ArtByte","statuses":["primary"]},{"code":"ARTC","name":"ARTC","statuses":["primary"]},{"code":"ADCN","name":"Asiadigicoin","statuses":["primary"]},{"code":"ATEN","name":"ATEN","statuses":["primary"]},{"code":"REP","name":"Augur","statuses":["primary"]},{"code":"AUR","name":"Auroracoin","statuses":["primary"]},{"code":"AUD","name":"Australian Dollar","statuses":["secondary"]},{"code":"AV","name":"AV","statuses":["primary"]},{"code":"BA","name":"BA","statuses":["primary"]},{"code":"BAC","name":"BAC","statuses":["primary"]},{"code":"BTA","name":"Bata","statuses":["primary"]},{"code":"BAY","name":"BAY","statuses":["primary"]},{"code":"BBCC","name":"BBCC","statuses":["primary"]},{"code":"BQC","name":"BBQCoin","statuses":["primary"]},{"code":"BDC","name":"BDC","statuses":["primary"]},{"code":"BEC","name":"BEC","statuses":["primary"]},{"code":"BEEZ","name":"BEEZ","statuses":["primary"]},{"code":"BELA","name":"BellaCoin","statuses":["primary"]},{"code":"BERN","name":"BERNcash","statuses":["primary"]},{"code":"BILL","name":"BILL","statuses":["primary"]},{"code":"BILS","name":"BILS","statuses":["primary"]},{"code":"BIOS","name":"BiosCrypto","statuses":["primary"]},{"code":"BIT","name":"BIT","statuses":["primary"]},{"code":"BIT16","name":"BIT16","statuses":["primary"]},{"code":"BITB","name":"BitBean","statuses":["primary"]},{"code":"BTC","name":"Bitcoin","statuses":["primary","secondary"]},{"code":"XBC","name":"Bitcoin Plus","statuses":["primary"]},{"code":"BTCD","name":"BitcoinDark","statuses":["primary"]},{"code":"BCY","name":"Bitcrystals","statuses":["primary"]},{"code":"BTM","name":"Bitmark","statuses":["primary"]},{"code":"BTQ","name":"BitQuark","statuses":["primary"]},{"code":"BITS","name":"BITS","statuses":["primary"]},{"code":"BSD","name":"BitSend","statuses":["primary"]},{"code":"BTS","name":"BitShares","statuses":["primary"]},{"code":"PTS","name":"BitShares PTS","statuses":["primary"]},{"code":"SWIFT","name":"BitSwift","statuses":["primary"]},{"code":"BITZ","name":"Bitz","statuses":["primary"]},{"code":"BLK","name":"Blackcoin","statuses":["primary"]},{"code":"JACK","name":"BlackJack","statuses":["primary"]},{"code":"BLC","name":"Blakecoin","statuses":["primary"]},{"code":"BLEU","name":"BLEU","statuses":["primary"]},{"code":"BLITZ","name":"Blitzcoin","statuses":["primary"]},{"code":"BLOCK","name":"Blocknet","statuses":["primary"]},{"code":"BLRY","name":"BLRY","statuses":["primary"]},{"code":"BLU","name":"BLU","statuses":["primary"]},{"code":"BM","name":"BM","statuses":["primary"]},{"code":"BNT","name":"BNT","statuses":["primary"]},{"code":"BOB","name":"BOB","statuses":["primary"]},{"code":"BON","name":"BON","statuses":["primary"]},{"code":"BBR","name":"Boolberry","statuses":["primary"]},{"code":"BOST","name":"BoostCoin","statuses":["primary"]},{"code":"BOSS","name":"BOSS","statuses":["primary"]},{"code":"BPOK","name":"BPOK","statuses":["primary"]},{"code":"BRAIN","name":"BRAIN","statuses":["primary"]},{"code":"BRC","name":"BRC","statuses":["primary"]},{"code":"BRDD","name":"BRDD","statuses":["primary"]},{"code":"BRIT","name":"BRIT","statuses":["primary"]},{"code":"GBP","name":"British Pound Sterling","statuses":["secondary"]},{"code":"BRK","name":"BRK","statuses":["primary"]},{"code":"BRX","name":"BRX","statuses":["primary"]},{"code":"BSC","name":"BSC","statuses":["primary"]},{"code":"BST","name":"BST","statuses":["primary"]},{"code":"BTCHC","name":"BTCHC","statuses":["primary"]},{"code":"BTCR","name":"BTCR","statuses":["primary"]},{"code":"BTCS","name":"BTCS","statuses":["primary"]},{"code":"BTCU","name":"BTCU","statuses":["primary"]},{"code":"BTTF","name":"BTTF","statuses":["primary"]},{"code":"BTX","name":"BTX","statuses":["primary"]},{"code":"BUCKS","name":"BUCKS","statuses":["primary"]},{"code":"BUN","name":"BUN","statuses":["primary"]},{"code":"BURST","name":"Burst","statuses":["primary"]},{"code":"BUZZ","name":"BUZZ","statuses":["primary"]},{"code":"BVC","name":"BVC","statuses":["primary"]},{"code":"BYC","name":"Bytecent","statuses":["primary"]},{"code":"BCN","name":"Bytecoin","statuses":["primary"]},{"code":"XCT","name":"C-Bit","statuses":["primary"]},{"code":"C0C0","name":"C0C0","statuses":["primary"]},{"code":"CAB","name":"Cabbage Unit","statuses":["primary"]},{"code":"CAD","name":"CAD","statuses":["primary","secondary"]},{"code":"CAGE","name":"CAGE","statuses":["primary"]},{"code":"CANN","name":"CannabisCoin","statuses":["primary"]},{"code":"CCN","name":"Cannacoin","statuses":["primary"]},{"code":"CPC","name":"Capricoin","statuses":["primary"]},{"code":"DIEM","name":"CarpeDiemCoin","statuses":["primary"]},{"code":"CASH","name":"CASH","statuses":["primary"]},{"code":"CBIT","name":"CBIT","statuses":["primary"]},{"code":"CC","name":"CC","statuses":["primary"]},{"code":"CCB","name":"CCB","statuses":["primary"]},{"code":"CD","name":"CD","statuses":["primary"]},{"code":"CDN","name":"CDN","statuses":["primary"]},{"code":"CF","name":"CF","statuses":["primary"]},{"code":"CFC","name":"CFC","statuses":["primary"]},{"code":"CGA","name":"CGA","statuses":["primary"]},{"code":"CHC","name":"CHC","statuses":["primary"]},{"code":"CKC","name":"Checkcoin","statuses":["primary"]},{"code":"CHEMX","name":"CHEMX","statuses":["primary"]},{"code":"CHESS","name":"CHESS","statuses":["primary"]},{"code":"CHF","name":"CHF","statuses":["primary","secondary"]},{"code":"CNY","name":"Chinese Yuan","statuses":["secondary"]},{"code":"CHRG","name":"CHRG","statuses":["primary"]},{"code":"CJ","name":"CJ","statuses":["primary"]},{"code":"CLAM","name":"Clams","statuses":["primary"]},{"code":"CLICK","name":"CLICK","statuses":["primary"]},{"code":"CLINT","name":"CLINT","statuses":["primary"]},{"code":"CLOAK","name":"Cloakcoin","statuses":["primary"]},{"code":"CLR","name":"CLR","statuses":["primary"]},{"code":"CLUB","name":"CLUB","statuses":["primary"]},{"code":"CLUD","name":"CLUD","statuses":["primary"]},{"code":"CMT","name":"CMT","statuses":["primary"]},{"code":"CNC","name":"CNC","statuses":["primary"]},{"code":"COXST","name":"CoExistCoin","statuses":["primary"]},{"code":"COIN","name":"COIN","statuses":["primary"]},{"code":"C2","name":"Coin2.1","statuses":["primary"]},{"code":"CNMT","name":"Coinomat","statuses":["primary"]},{"code":"CV2","name":"Colossuscoin2.0","statuses":["primary"]},{"code":"CON","name":"CON","statuses":["primary"]},{"code":"XCP","name":"Counterparty","statuses":["primary"]},{"code":"COV","name":"COV","statuses":["primary"]},{"code":"CRAFT","name":"CRAFT","statuses":["primary"]},{"code":"CRAVE","name":"CRAVE","statuses":["primary"]},{"code":"CRC","name":"CRC","statuses":["primary"]},{"code":"CRE","name":"CRE","statuses":["primary"]},{"code":"CRBIT","name":"Creditbit","statuses":["primary"]},{"code":"CREVA","name":"CrevaCoin","statuses":["primary"]},{"code":"CRIME","name":"CRIME","statuses":["primary"]},{"code":"CRT","name":"CRT","statuses":["primary"]},{"code":"CRW","name":"CRW","statuses":["primary"]},{"code":"CRY","name":"CRY","statuses":["primary"]},{"code":"XCR","name":"Crypti","statuses":["primary"]},{"code":"CBX","name":"Crypto Bullion","statuses":["primary"]},{"code":"CESC","name":"CryptoEscudo","statuses":["primary"]},{"code":"XCN","name":"Cryptonite","statuses":["primary"]},{"code":"CSMIC","name":"CSMIC","statuses":["primary"]},{"code":"CST","name":"CST","statuses":["primary"]},{"code":"CTC","name":"CTC","statuses":["primary"]},{"code":"CTO","name":"CTO","statuses":["primary"]},{"code":"CURE","name":"Curecoin","statuses":["primary"]},{"code":"CYP","name":"Cypher","statuses":["primary"]},{"code":"CZC","name":"CZC","statuses":["primary"]},{"code":"CZECO","name":"CZECO","statuses":["primary"]},{"code":"CZR","name":"CZR","statuses":["primary"]},{"code":"DAO","name":"DAO","statuses":["primary"]},{"code":"DGD","name":"DarkGoldCoin","statuses":["primary"]},{"code":"DNET","name":"Darknet","statuses":["primary"]},{"code":"DASH","name":"Dash","statuses":["primary"]},{"code":"DTC","name":"Datacoin","statuses":["primary"]},{"code":"DBG","name":"DBG","statuses":["primary"]},{"code":"DBLK","name":"DBLK","statuses":["primary"]},{"code":"DBTC","name":"DBTC","statuses":["primary"]},{"code":"DCK","name":"DCK","statuses":["primary"]},{"code":"DCR","name":"Decred","statuses":["primary"]},{"code":"DES","name":"Destiny","statuses":["primary"]},{"code":"DETH","name":"DETH","statuses":["primary"]},{"code":"DEUR","name":"DEUR","statuses":["primary"]},{"code":"DEM","name":"Deutsche eMark","statuses":["primary"]},{"code":"DVC","name":"Devcoin","statuses":["primary"]},{"code":"DGCS","name":"DGCS","statuses":["primary"]},{"code":"DGMS","name":"DGMS","statuses":["primary"]},{"code":"DGORE","name":"DGORE","statuses":["primary"]},{"code":"DMD","name":"Diamond","statuses":["primary"]},{"code":"DGB","name":"Digibyte","statuses":["primary"]},{"code":"CUBE","name":"DigiCube","statuses":["primary"]},{"code":"DGC","name":"Digitalcoin","statuses":["primary"]},{"code":"XDN","name":"DigitalNote","statuses":["primary"]},{"code":"DP","name":"DigitalPrice","statuses":["primary"]},{"code":"DIGS","name":"DIGS","statuses":["primary"]},{"code":"DIME","name":"Dimecoin","statuses":["primary"]},{"code":"DISK","name":"DISK","statuses":["primary"]},{"code":"DLISK","name":"DLISK","statuses":["primary"]},{"code":"NOTE","name":"DNotes","statuses":["primary"]},{"code":"DOGE","name":"Dogecoin","statuses":["primary","secondary"]},{"code":"DON","name":"DON","statuses":["primary"]},{"code":"DOPE","name":"DopeCoin","statuses":["primary"]},{"code":"DOX","name":"DOX","statuses":["primary"]},{"code":"DRACO","name":"DRACO","statuses":["primary"]},{"code":"DRM","name":"DRM","statuses":["primary"]},{"code":"DROP","name":"DROP","statuses":["primary"]},{"code":"DRZ","name":"DRZ","statuses":["primary"]},{"code":"DSH","name":"DSH","statuses":["primary"]},{"code":"DBIC","name":"DubaiCoin","statuses":["primary"]},{"code":"DUO","name":"DUO","statuses":["primary"]},{"code":"DUST","name":"DUST","statuses":["primary"]},{"code":"EAC","name":"Earthcoin","statuses":["primary"]},{"code":"ECCHI","name":"ECCHI","statuses":["primary"]},{"code":"ECC","name":"ECCoin","statuses":["primary"]},{"code":"ECOS","name":"ECOS","statuses":["primary"]},{"code":"EDC","name":"EDC","statuses":["primary"]},{"code":"EDRC","name":"EDRC","statuses":["primary"]},{"code":"EGG","name":"EGG","statuses":["primary"]},{"code":"EMC2","name":"Einsteinium","statuses":["primary"]},{"code":"EKO","name":"EKO","statuses":["primary"]},{"code":"EL","name":"EL","statuses":["primary"]},{"code":"ELCO","name":"ELcoin","statuses":["primary"]},{"code":"ELE","name":"ELE","statuses":["primary"]},{"code":"EFL","name":"Electronic Gulden","statuses":["primary"]},{"code":"EMC","name":"Emercoin","statuses":["primary"]},{"code":"EMIRG","name":"EMIRG","statuses":["primary"]},{"code":"ENE","name":"ENE","statuses":["primary"]},{"code":"ENRG","name":"Energycoin","statuses":["primary"]},{"code":"EPC","name":"EPC","statuses":["primary"]},{"code":"EPY","name":"EPY","statuses":["primary"]},{"code":"ERC","name":"ERC","statuses":["primary"]},{"code":"ERC3","name":"ERC3","statuses":["primary"]},{"code":"ESC","name":"ESC","statuses":["primary"]},{"code":"ETC","name":"Ethereum Classic","statuses":["primary"]},{"code":"ETHS","name":"ETHS","statuses":["primary"]},{"code":"EURC","name":"EURC","statuses":["primary"]},{"code":"EUR","name":"Euro","statuses":["primary","secondary"]},{"code":"EGC","name":"EvergreenCoin","statuses":["primary"]},{"code":"EVIL","name":"EVIL","statuses":["primary"]},{"code":"EVO","name":"EVO","statuses":["primary"]},{"code":"EXCL","name":"EXCL","statuses":["primary"]},{"code":"EXIT","name":"EXIT","statuses":["primary"]},{"code":"EXP","name":"Expanse","statuses":["primary"]},{"code":"FCT","name":"Factom","statuses":["primary"]},{"code":"FAIR","name":"Faircoin","statuses":["primary"]},{"code":"FC2","name":"FC2","statuses":["primary"]},{"code":"FCN","name":"FCN","statuses":["primary"]},{"code":"FTC","name":"Feathercoin","statuses":["primary"]},{"code":"TIPS","name":"Fedoracoin","statuses":["primary"]},{"code":"FFC","name":"FFC","statuses":["primary"]},{"code":"FIBRE","name":"Fibre","statuses":["primary"]},{"code":"FIT","name":"FIT","statuses":["primary"]},{"code":"FJC","name":"FJC","statuses":["primary"]},{"code":"FLO","name":"Florincoin","statuses":["primary"]},{"code":"FLOZ","name":"FLOZ","statuses":["primary"]},{"code":"FLT","name":"FlutterCoin","statuses":["primary"]},{"code":"FLX","name":"FLX","statuses":["primary"]},{"code":"FLY","name":"Flycoin","statuses":["primary"]},{"code":"FLDC","name":"FoldingCoin","statuses":["primary"]},{"code":"FONZ","name":"FONZ","statuses":["primary"]},{"code":"FRK","name":"Franko","statuses":["primary"]},{"code":"FRC","name":"Freicoin","statuses":["primary"]},{"code":"FRN","name":"FRN","statuses":["primary"]},{"code":"FRWC","name":"FRWC","statuses":["primary"]},{"code":"FSC2","name":"FSC2","statuses":["primary"]},{"code":"FST","name":"FST","statuses":["primary"]},{"code":"FTP","name":"FTP","statuses":["primary"]},{"code":"FUN","name":"FUN","statuses":["primary"]},{"code":"FUTC","name":"FUTC","statuses":["primary"]},{"code":"FUZZ","name":"FUZZ","statuses":["primary"]},{"code":"GAIA","name":"GAIA","statuses":["primary"]},{"code":"GAIN","name":"GAIN","statuses":["primary"]},{"code":"GAKH","name":"GAKH","statuses":["primary"]},{"code":"GAM","name":"GAM","statuses":["primary"]},{"code":"GBT","name":"GameBet Coin","statuses":["primary"]},{"code":"GAME","name":"GameCredits","statuses":["primary"]},{"code":"GAP","name":"Gapcoin","statuses":["primary"]},{"code":"GARY","name":"GARY","statuses":["primary"]},{"code":"GB","name":"GB","statuses":["primary"]},{"code":"GBC","name":"GBC","statuses":["primary"]},{"code":"GBIT","name":"GBIT","statuses":["primary"]},{"code":"GCC","name":"GCC","statuses":["primary"]},{"code":"GCN","name":"GCN","statuses":["primary"]},{"code":"GEO","name":"GeoCoin","statuses":["primary"]},{"code":"GEMZ","name":"GetGems","statuses":["primary"]},{"code":"GHOST","name":"GHOST","statuses":["primary"]},{"code":"GHS","name":"GHS","statuses":["primary"]},{"code":"GIFT","name":"GIFT","statuses":["primary"]},{"code":"GIG","name":"GIG","statuses":["primary"]},{"code":"GLC","name":"GLC","statuses":["primary"]},{"code":"BSTY","name":"GlobalBoost-Y","statuses":["primary"]},{"code":"GML","name":"GML","statuses":["primary"]},{"code":"GMX","name":"GMX","statuses":["primary"]},{"code":"GCR","name":"GoCoineR","statuses":["primary"]},{"code":"GLD","name":"GoldCoin","statuses":["primary"]},{"code":"GOON","name":"GOON","statuses":["primary"]},{"code":"GP","name":"GP","statuses":["primary"]},{"code":"GPU","name":"GPU","statuses":["primary"]},{"code":"GRAM","name":"GRAM","statuses":["primary"]},{"code":"GRT","name":"Grantcoin","statuses":["primary"]},{"code":"GRE","name":"GRE","statuses":["primary"]},{"code":"GRC","name":"Gridcoin","statuses":["primary"]},{"code":"GRN","name":"GRN","statuses":["primary"]},{"code":"GRS","name":"Groestlcoin","statuses":["primary"]},{"code":"GRW","name":"GRW","statuses":["primary"]},{"code":"GSM","name":"GSM","statuses":["primary"]},{"code":"GSX","name":"GSX","statuses":["primary"]},{"code":"GUA","name":"GUA","statuses":["primary"]},{"code":"NLG","name":"Gulden","statuses":["primary"]},{"code":"GUN","name":"GUN","statuses":["primary"]},{"code":"HAM","name":"HAM","statuses":["primary"]},{"code":"HAWK","name":"HAWK","statuses":["primary"]},{"code":"HCC","name":"HCC","statuses":["primary"]},{"code":"HEAT","name":"HEAT","statuses":["primary"]},{"code":"HMP","name":"HempCoin","statuses":["primary"]},{"code":"XHI","name":"HiCoin","statuses":["primary"]},{"code":"HIFUN","name":"HIFUN","statuses":["primary"]},{"code":"HILL","name":"HILL","statuses":["primary"]},{"code":"HIRE","name":"HIRE","statuses":["primary"]},{"code":"HNC","name":"HNC","statuses":["primary"]},{"code":"HODL","name":"HOdlcoin","statuses":["primary"]},{"code":"HKD","name":"Hong Kong Dollar","statuses":["secondary"]},{"code":"HZ","name":"Horizon","statuses":["primary"]},{"code":"HTC","name":"HTC","statuses":["primary"]},{"code":"HTML5","name":"HTMLCOIN","statuses":["primary"]},{"code":"HUC","name":"HUC","statuses":["primary"]},{"code":"HVCO","name":"HVCO","statuses":["primary"]},{"code":"HYPER","name":"Hyper","statuses":["primary"]},{"code":"HYP","name":"HyperStake","statuses":["primary"]},{"code":"I0C","name":"I0C","statuses":["primary"]},{"code":"IBANK","name":"IBANK","statuses":["primary"]},{"code":"ICASH","name":"iCash","statuses":["primary"]},{"code":"ICN","name":"ICN","statuses":["primary"]},{"code":"IEC","name":"IEC","statuses":["primary"]},{"code":"IFC","name":"Infinitecoin","statuses":["primary"]},{"code":"INFX","name":"Influxcoin","statuses":["primary"]},{"code":"INV","name":"INV","statuses":["primary"]},{"code":"IOC","name":"IO Coin","statuses":["primary"]},{"code":"ION","name":"ION","statuses":["primary"]},{"code":"IRL","name":"IRL","statuses":["primary"]},{"code":"ISL","name":"IslaCoin","statuses":["primary"]},{"code":"IVZ","name":"IVZ","statuses":["primary"]},{"code":"IXC","name":"IXC","statuses":["primary"]},{"code":"JIF","name":"JIF","statuses":["primary"]},{"code":"JPC","name":"JPC","statuses":["primary"]},{"code":"JPY","name":"JPY","statuses":["primary","secondary"]},{"code":"JBS","name":"Jumbucks","statuses":["primary"]},{"code":"KAT","name":"KAT","statuses":["primary"]},{"code":"KGC","name":"KGC","statuses":["primary"]},{"code":"KNC","name":"KhanCoin","statuses":["primary"]},{"code":"KLC","name":"KLC","statuses":["primary"]},{"code":"KOBO","name":"KOBO","statuses":["primary"]},{"code":"KORE","name":"KoreCoin","statuses":["primary"]},{"code":"KRAK","name":"KRAK","statuses":["primary"]},{"code":"KRYP","name":"KRYP","statuses":["primary"]},{"code":"KR","name":"Krypton","statuses":["primary"]},{"code":"KTK","name":"KTK","statuses":["primary"]},{"code":"KUBO","name":"KUBO","statuses":["primary"]},{"code":"LANA","name":"LANA","statuses":["primary"]},{"code":"LBC","name":"LBC","statuses":["primary"]},{"code":"LC","name":"LC","statuses":["primary"]},{"code":"LEA","name":"LeaCoin","statuses":["primary"]},{"code":"LEMON","name":"LEMON","statuses":["primary"]},{"code":"LEO","name":"LEO","statuses":["primary"]},{"code":"LFC","name":"LFC","statuses":["primary"]},{"code":"LFO","name":"LFO","statuses":["primary"]},{"code":"LFTC","name":"LFTC","statuses":["primary"]},{"code":"LQD","name":"LIQUID","statuses":["primary"]},{"code":"LIR","name":"LIR","statuses":["primary"]},{"code":"LSK","name":"Lisk","statuses":["primary"]},{"code":"LTC","name":"Litecoin","statuses":["primary","secondary"]},{"code":"LTCR","name":"Litecred","statuses":["primary"]},{"code":"LDOGE","name":"LiteDoge","statuses":["primary"]},{"code":"LKC","name":"LKC","statuses":["primary"]},{"code":"LOC","name":"LOC","statuses":["primary"]},{"code":"LOOT","name":"LOOT","statuses":["primary"]},{"code":"LTBC","name":"LTBcoin","statuses":["primary"]},{"code":"LTH","name":"LTH","statuses":["primary"]},{"code":"LTS","name":"LTS","statuses":["primary"]},{"code":"LUN","name":"LUN","statuses":["primary"]},{"code":"LXC","name":"LXC","statuses":["primary"]},{"code":"LYB","name":"LYB","statuses":["primary"]},{"code":"M1","name":"M1","statuses":["primary"]},{"code":"MAD","name":"MAD","statuses":["primary"]},{"code":"XMG","name":"Magi","statuses":["primary"]},{"code":"MAID","name":"MaidSafeCoin","statuses":["primary"]},{"code":"MXT","name":"MarteXcoin","statuses":["primary"]},{"code":"MARV","name":"MARV","statuses":["primary"]},{"code":"MARYJ","name":"MARYJ","statuses":["primary"]},{"code":"OMNI","name":"Mastercoin (Omni)","statuses":["primary"]},{"code":"MTR","name":"MasterTraderCoin","statuses":["primary"]},{"code":"MAX","name":"Maxcoin","statuses":["primary"]},{"code":"MZC","name":"Mazacoin","statuses":["primary"]},{"code":"MBL","name":"MBL","statuses":["primary"]},{"code":"MCAR","name":"MCAR","statuses":["primary"]},{"code":"MCN","name":"MCN","statuses":["primary"]},{"code":"MCZ","name":"MCZ","statuses":["primary"]},{"code":"MED","name":"MediterraneanCoin","statuses":["primary"]},{"code":"MEC","name":"Megacoin","statuses":["primary"]},{"code":"MEME","name":"Memetic","statuses":["primary"]},{"code":"METAL","name":"METAL","statuses":["primary"]},{"code":"MND","name":"MindCoin","statuses":["primary"]},{"code":"MINT","name":"Mintcoin","statuses":["primary"]},{"code":"MIS","name":"MIS","statuses":["primary"]},{"code":"MM","name":"MM","statuses":["primary"]},{"code":"MMC","name":"MMC","statuses":["primary"]},{"code":"MMNXT","name":"MMNXT","statuses":["primary"]},{"code":"MMXVI","name":"MMXVI","statuses":["primary"]},{"code":"MNM","name":"MNM","statuses":["primary"]},{"code":"MOIN","name":"MOIN","statuses":["primary"]},{"code":"MOJO","name":"MojoCoin","statuses":["primary"]},{"code":"MONA","name":"MonaCoin","statuses":["primary"]},{"code":"XMR","name":"Monero","statuses":["primary","secondary"]},{"code":"MNTA","name":"Moneta","statuses":["primary"]},{"code":"MUE","name":"MonetaryUnit","statuses":["primary"]},{"code":"MOON","name":"Mooncoin","statuses":["primary"]},{"code":"MOOND","name":"MOOND","statuses":["primary"]},{"code":"MOTO","name":"MOTO","statuses":["primary"]},{"code":"MPRO","name":"MPRO","statuses":["primary"]},{"code":"MRB","name":"MRB","statuses":["primary"]},{"code":"MRP","name":"MRP","statuses":["primary"]},{"code":"MSC","name":"MSC","statuses":["primary"]},{"code":"MYR","name":"Myriadcoin","statuses":["primary"]},{"code":"NMC","name":"Namecoin","statuses":["primary"]},{"code":"NAUT","name":"Nautiluscoin","statuses":["primary"]},{"code":"NAV","name":"NAV Coin","statuses":["primary"]},{"code":"NCS","name":"NCS","statuses":["primary"]},{"code":"XEM","name":"NEM","statuses":["primary"]},{"code":"NEOS","name":"NeosCoin","statuses":["primary"]},{"code":"NETC","name":"NETC","statuses":["primary"]},{"code":"NET","name":"NetCoin","statuses":["primary"]},{"code":"NEU","name":"NeuCoin","statuses":["primary"]},{"code":"NTRN","name":"Neutron","statuses":["primary"]},{"code":"NEVA","name":"NevaCoin","statuses":["primary"]},{"code":"NEWB","name":"NEWB","statuses":["primary"]},{"code":"NIRO","name":"Nexus","statuses":["primary"]},{"code":"NIC","name":"NIC","statuses":["primary"]},{"code":"NKA","name":"NKA","statuses":["primary"]},{"code":"NKC","name":"NKC","statuses":["primary"]},{"code":"NOBL","name":"NobleCoin","statuses":["primary"]},{"code":"NODE","name":"NODE","statuses":["primary"]},{"code":"NODES","name":"NODES","statuses":["primary"]},{"code":"NOO","name":"NOO","statuses":["primary"]},{"code":"NVC","name":"Novacoin","statuses":["primary"]},{"code":"NRC","name":"NRC","statuses":["primary"]},{"code":"NRS","name":"NRS","statuses":["primary"]},{"code":"NUBIS","name":"NUBIS","statuses":["primary"]},{"code":"NBT","name":"NuBits","statuses":["primary"]},{"code":"NUM","name":"NUM","statuses":["primary"]},{"code":"NSR","name":"NuShares","statuses":["primary"]},{"code":"NXE","name":"NXE","statuses":["primary"]},{"code":"NXT","name":"NXT","statuses":["primary"]},{"code":"NXTTY","name":"Nxttycoin","statuses":["primary"]},{"code":"NYC","name":"NYC","statuses":["primary"]},{"code":"NZC","name":"NZC","statuses":["primary"]},{"code":"NZD","name":"NZD","statuses":["primary","secondary"]},{"code":"OC","name":"OC","statuses":["primary"]},{"code":"OCOW","name":"OCOW","statuses":["primary"]},{"code":"OK","name":"OKCash","statuses":["primary"]},{"code":"OMA","name":"OMA","statuses":["primary"]},{"code":"ONE","name":"ONE","statuses":["primary"]},{"code":"ONEC","name":"ONEC","statuses":["primary"]},{"code":"OP","name":"OP","statuses":["primary"]},{"code":"OPAL","name":"OPAL","statuses":["primary"]},{"code":"OPES","name":"OPES","statuses":["primary"]},{"code":"ORB","name":"Orbitcoin","statuses":["primary"]},{"code":"ORLY","name":"Orlycoin","statuses":["primary"]},{"code":"OS76","name":"OS76","statuses":["primary"]},{"code":"OZC","name":"OZC","statuses":["primary"]},{"code":"PAC","name":"PAC","statuses":["primary"]},{"code":"PAK","name":"PAK","statuses":["primary"]},{"code":"PND","name":"Pandacoin","statuses":["primary"]},{"code":"PAPAF","name":"PAPAF","statuses":["primary"]},{"code":"XPY","name":"Paycoin","statuses":["primary"]},{"code":"PBC","name":"PBC","statuses":["primary"]},{"code":"PDC","name":"PDC","statuses":["primary"]},{"code":"XPB","name":"Pebblecoin","statuses":["primary"]},{"code":"PPC","name":"Peercoin","statuses":["primary"]},{"code":"PEN","name":"PEN","statuses":["primary"]},{"code":"PHR","name":"PHR","statuses":["primary"]},{"code":"PIGGY","name":"Piggycoin","statuses":["primary"]},{"code":"PC","name":"Pinkcoin","statuses":["primary"]},{"code":"PKB","name":"PKB","statuses":["primary"]},{"code":"PLN","name":"PLN","statuses":["primary","secondary"]},{"code":"PLNC","name":"PLNC","statuses":["primary"]},{"code":"PNC","name":"PNC","statuses":["primary"]},{"code":"PNK","name":"PNK","statuses":["primary"]},{"code":"POKE","name":"POKE","statuses":["primary"]},{"code":"PONZ2","name":"PONZ2","statuses":["primary"]},{"code":"PONZI","name":"PONZI","statuses":["primary"]},{"code":"PEX","name":"PosEx","statuses":["primary"]},{"code":"POST","name":"POST","statuses":["primary"]},{"code":"POT","name":"Potcoin","statuses":["primary"]},{"code":"PRES","name":"PRES","statuses":["primary"]},{"code":"PXI","name":"Prime-XI","statuses":["primary"]},{"code":"PRIME","name":"PrimeChain","statuses":["primary"]},{"code":"XPM","name":"Primecoin","statuses":["primary"]},{"code":"PRM","name":"PRM","statuses":["primary"]},{"code":"PRT","name":"PRT","statuses":["primary"]},{"code":"PSP","name":"PSP","statuses":["primary"]},{"code":"PTC","name":"PTC","statuses":["primary"]},{"code":"PULSE","name":"PULSE","statuses":["primary"]},{"code":"PURE","name":"PURE","statuses":["primary"]},{"code":"PUTIN","name":"PUTIN","statuses":["primary"]},{"code":"PWR","name":"PWR","statuses":["primary"]},{"code":"PXL","name":"PXL","statuses":["primary"]},{"code":"QBC","name":"QBC","statuses":["primary"]},{"code":"QBK","name":"QBK","statuses":["primary"]},{"code":"QCN","name":"QCN","statuses":["primary"]},{"code":"QORA","name":"Qora","statuses":["primary"]},{"code":"QTZ","name":"QTZ","statuses":["primary"]},{"code":"QRK","name":"Quark","statuses":["primary"]},{"code":"QTL","name":"Quatloo","statuses":["primary"]},{"code":"RADI","name":"RADI","statuses":["primary"]},{"code":"RADS","name":"Radium","statuses":["primary"]},{"code":"RED","name":"RED","statuses":["primary"]},{"code":"RDD","name":"Reddcoin","statuses":["primary"]},{"code":"REE","name":"REE","statuses":["primary"]},{"code":"REV","name":"Revenu","statuses":["primary"]},{"code":"RBR","name":"RibbitRewards","statuses":["primary"]},{"code":"RICHX","name":"RICHX","statuses":["primary"]},{"code":"RIC","name":"Riecoin","statuses":["primary"]},{"code":"RBT","name":"Rimbit","statuses":["primary"]},{"code":"RIO","name":"RIO","statuses":["primary"]},{"code":"XRP","name":"Ripple","statuses":["primary"]},{"code":"RISE","name":"RISE","statuses":["primary"]},{"code":"RMS","name":"RMS","statuses":["primary"]},{"code":"RONIN","name":"RONIN","statuses":["primary"]},{"code":"ROOT","name":"ROOT","statuses":["primary"]},{"code":"ROS","name":"RosCoin","statuses":["primary"]},{"code":"RPC","name":"RPC","statuses":["primary"]},{"code":"RBIES","name":"Rubies","statuses":["primary"]},{"code":"RUBIT","name":"RUBIT","statuses":["primary"]},{"code":"RUR","name":"Ruble","statuses":["secondary"]},{"code":"RBY","name":"Rubycoin","statuses":["primary"]},{"code":"RUST","name":"RUST","statuses":["primary"]},{"code":"SEC","name":"Safe Exchange Coin","statuses":["primary"]},{"code":"SAK","name":"SAK","statuses":["primary"]},{"code":"SAR","name":"SAR","statuses":["primary"]},{"code":"SBD","name":"SBD","statuses":["primary"]},{"code":"SBIT","name":"SBIT","statuses":["primary"]},{"code":"SCAN","name":"SCAN","statuses":["primary"]},{"code":"SCOT","name":"Scotcoin","statuses":["primary"]},{"code":"SCRPT","name":"SCRPT","statuses":["primary"]},{"code":"SCRT","name":"SCRT","statuses":["primary"]},{"code":"SRC","name":"SecureCoin","statuses":["primary"]},{"code":"SXC","name":"Sexcoin","statuses":["primary"]},{"code":"SFE","name":"SFE","statuses":["primary"]},{"code":"SFR","name":"SFR","statuses":["primary"]},{"code":"SGD","name":"SGD","statuses":["primary","secondary"]},{"code":"SDC","name":"ShadowCash","statuses":["primary"]},{"code":"SHELL","name":"SHELL","statuses":["primary"]},{"code":"SHF","name":"SHF","statuses":["primary"]},{"code":"SHI","name":"SHI","statuses":["primary"]},{"code":"SHIFT","name":"Shift","statuses":["primary"]},{"code":"SHREK","name":"SHREK","statuses":["primary"]},{"code":"SC","name":"Siacoin","statuses":["primary"]},{"code":"SIB","name":"Siberian chervonets","statuses":["primary"]},{"code":"SIC","name":"SIC","statuses":["primary"]},{"code":"SIGU","name":"SIGU","statuses":["primary"]},{"code":"SILK","name":"Silkcoin","statuses":["primary"]},{"code":"SIX","name":"SIX","statuses":["primary"]},{"code":"SLING","name":"Sling","statuses":["primary"]},{"code":"SLS","name":"SLS","statuses":["primary"]},{"code":"SMBR","name":"SMBR","statuses":["primary"]},{"code":"SMC","name":"SMC","statuses":["primary"]},{"code":"SMLY","name":"SmileyCoin","statuses":["primary"]},{"code":"SNRG","name":"SNRG","statuses":["primary"]},{"code":"SOIL","name":"SOILcoin","statuses":["primary"]},{"code":"SLR","name":"Solarcoin","statuses":["primary"]},{"code":"SOLO","name":"SOLO","statuses":["primary"]},{"code":"SONG","name":"SongCoin","statuses":["primary"]},{"code":"SOON","name":"SOON","statuses":["primary"]},{"code":"SPC","name":"SPC","statuses":["primary"]},{"code":"SPEX","name":"SPEX","statuses":["primary"]},{"code":"SPHR","name":"Sphere","statuses":["primary"]},{"code":"SPM","name":"SPM","statuses":["primary"]},{"code":"SPN","name":"SPN","statuses":["primary"]},{"code":"SPOTS","name":"SPOTS","statuses":["primary"]},{"code":"SPR","name":"SpreadCoin","statuses":["primary"]},{"code":"SPRTS","name":"Sprouts","statuses":["primary"]},{"code":"SQC","name":"SQC","statuses":["primary"]},{"code":"SSC","name":"SSC","statuses":["primary"]},{"code":"SSTC","name":"SSTC","statuses":["primary"]},{"code":"STA","name":"STA","statuses":["primary"]},{"code":"START","name":"Startcoin","statuses":["primary"]},{"code":"XST","name":"Stealthcoin","statuses":["primary"]},{"code":"STEEM","name":"Steem","statuses":["primary"]},{"code":"XLM","name":"Stellar","statuses":["primary"]},{"code":"STR","name":"Stellar","statuses":["primary"]},{"code":"STEPS","name":"Steps","statuses":["primary"]},{"code":"SLG","name":"Sterlingcoin","statuses":["primary"]},{"code":"STL","name":"STL","statuses":["primary"]},{"code":"SJCX","name":"Storjcoin X","statuses":["primary"]},{"code":"STP","name":"STP","statuses":["primary"]},{"code":"STRB","name":"STRB","statuses":["primary"]},{"code":"STS","name":"Stress","statuses":["primary"]},{"code":"STRP","name":"STRP","statuses":["primary"]},{"code":"STV","name":"STV","statuses":["primary"]},{"code":"SUB","name":"Subcriptio","statuses":["primary"]},{"code":"SUPER","name":"SUPER","statuses":["primary"]},{"code":"UNITY","name":"SuperNET","statuses":["primary"]},{"code":"SWARM","name":"Swarm","statuses":["primary"]},{"code":"SWING","name":"SWING","statuses":["primary"]},{"code":"SDP","name":"SydPak Coin","statuses":["primary"]},{"code":"SYNC","name":"SYNC","statuses":["primary"]},{"code":"AMP","name":"Synereo","statuses":["primary"]},{"code":"SYS","name":"Syscoin","statuses":["primary"]},{"code":"TAG","name":"TagCoin","statuses":["primary"]},{"code":"TAJ","name":"TAJ","statuses":["primary"]},{"code":"TAK","name":"TAK","statuses":["primary"]},{"code":"TAM","name":"TAM","statuses":["primary"]},{"code":"TAO","name":"TAO","statuses":["primary"]},{"code":"TBC","name":"TBC","statuses":["primary"]},{"code":"TBCX","name":"TBCX","statuses":["primary"]},{"code":"TCR","name":"TCR","statuses":["primary"]},{"code":"TDFB","name":"TDFB","statuses":["primary"]},{"code":"TDY","name":"TDY","statuses":["primary"]},{"code":"TEK","name":"TEKcoin","statuses":["primary"]},{"code":"TRC","name":"Terracoin","statuses":["primary"]},{"code":"TESLA","name":"TESLA","statuses":["primary"]},{"code":"TES","name":"TeslaCoin","statuses":["primary"]},{"code":"TET","name":"TET","statuses":["primary"]},{"code":"USDT","name":"Tether","statuses":["primary","secondary"]},{"code":"THC","name":"THC","statuses":["primary"]},{"code":"THS","name":"THS","statuses":["primary"]},{"code":"TIX","name":"Tickets","statuses":["primary"]},{"code":"XTC","name":"TileCoin","statuses":["primary"]},{"code":"TIT","name":"Titcoin","statuses":["primary"]},{"code":"TTC","name":"TittieCoin","statuses":["primary"]},{"code":"TMC","name":"TMC","statuses":["primary"]},{"code":"TODAY","name":"TODAY","statuses":["primary"]},{"code":"TOKEN","name":"TOKEN","statuses":["primary"]},{"code":"TP1","name":"TP1","statuses":["primary"]},{"code":"TPC","name":"TPC","statuses":["primary"]},{"code":"TPG","name":"TPG","statuses":["primary"]},{"code":"TX","name":"Transfercoin","statuses":["primary"]},{"code":"TRAP","name":"TRAP","statuses":["primary"]},{"code":"TRICK","name":"TRICK","statuses":["primary"]},{"code":"TROLL","name":"TROLL","statuses":["primary"]},{"code":"TRK","name":"Truckcoin","statuses":["primary"]},{"code":"TRUMP","name":"TrumpCoin","statuses":["primary"]},{"code":"TRUST","name":"TRUST","statuses":["primary"]},{"code":"UAE","name":"UAE","statuses":["primary"]},{"code":"UFO","name":"UFO Coin","statuses":["primary"]},{"code":"UIS","name":"UIS","statuses":["primary"]},{"code":"UTC","name":"UltraCoin","statuses":["primary"]},{"code":"UNC","name":"UNC","statuses":["primary"]},{"code":"UNIQ","name":"UNIQ","statuses":["primary"]},{"code":"UNIT","name":"Universal Currency","statuses":["primary"]},{"code":"UNO","name":"Unobtanium","statuses":["primary"]},{"code":"URO","name":"Uro","statuses":["primary"]},{"code":"USD","name":"US Dollar","statuses":["primary","secondary"]},{"code":"USDE","name":"USDE","statuses":["primary"]},{"code":"UTH","name":"UTH","statuses":["primary"]},{"code":"VAL","name":"VAL","statuses":["primary"]},{"code":"XVC","name":"Vcash","statuses":["primary"]},{"code":"VCN","name":"VCN","statuses":["primary"]},{"code":"VEG","name":"VEG","statuses":["primary"]},{"code":"VENE","name":"VENE","statuses":["primary"]},{"code":"XVG","name":"Verge","statuses":["primary"]},{"code":"VRC","name":"VeriCoin","statuses":["primary"]},{"code":"VTC","name":"Vertcoin","statuses":["primary"]},{"code":"VIA","name":"Viacoin","statuses":["primary"]},{"code":"VIOR","name":"Viorcoin","statuses":["primary"]},{"code":"VIP","name":"VIP Tokens","statuses":["primary"]},{"code":"VIRAL","name":"Viral","statuses":["primary"]},{"code":"VOOT","name":"VootCoin","statuses":["primary"]},{"code":"VOX","name":"Voxels","statuses":["primary"]},{"code":"VOYA","name":"VOYA","statuses":["primary"]},{"code":"VPN","name":"VPNCoin","statuses":["primary"]},{"code":"VPRC","name":"VPRC","statuses":["primary"]},{"code":"VTA","name":"VTA","statuses":["primary"]},{"code":"VTN","name":"VTN","statuses":["primary"]},{"code":"VTR","name":"VTR","statuses":["primary"]},{"code":"WAC","name":"WAC","statuses":["primary"]},{"code":"WARP","name":"WARP","statuses":["primary"]},{"code":"WAVES","name":"WAVES","statuses":["primary"]},{"code":"WGC","name":"WGC","statuses":["primary"]},{"code":"XWC","name":"Whitecoin","statuses":["primary"]},{"code":"WBB","name":"Wild Beast Block","statuses":["primary"]},{"code":"WLC","name":"WLC","statuses":["primary"]},{"code":"WMC","name":"WMC","statuses":["primary"]},{"code":"LOG","name":"Woodcoin","statuses":["primary"]},{"code":"WOP","name":"WOP","statuses":["primary"]},{"code":"WDC","name":"Worldcoin","statuses":["primary"]},{"code":"XAB","name":"XAB","statuses":["primary"]},{"code":"XAI","name":"XAI","statuses":["primary"]},{"code":"XAU","name":"Xaurum","statuses":["primary"]},{"code":"XBS","name":"XBS","statuses":["primary"]},{"code":"XBU","name":"XBU","statuses":["primary"]},{"code":"XCO","name":"XCO","statuses":["primary"]},{"code":"XC","name":"XCurrency","statuses":["primary"]},{"code":"XDB","name":"XDB","statuses":["primary"]},{"code":"XEMP","name":"XEMP","statuses":["primary"]},{"code":"XFC","name":"XFC","statuses":["primary"]},{"code":"MI","name":"Xiaomicoin","statuses":["primary"]},{"code":"XID","name":"XID","statuses":["primary"]},{"code":"XJO","name":"XJO","statuses":["primary"]},{"code":"XLTCG","name":"XLTCG","statuses":["primary"]},{"code":"XMS","name":"XMS","statuses":["primary"]},{"code":"XNX","name":"XNX","statuses":["primary"]},{"code":"XPD","name":"XPD","statuses":["primary"]},{"code":"XPOKE","name":"XPOKE","statuses":["primary"]},{"code":"XPRO","name":"XPRO","statuses":["primary"]},{"code":"XQN","name":"XQN","statuses":["primary"]},{"code":"XSEED","name":"XSEED","statuses":["primary"]},{"code":"XSP","name":"XSP","statuses":["primary"]},{"code":"XT","name":"XT","statuses":["primary"]},{"code":"XTP","name":"XTP","statuses":["primary"]},{"code":"XUSD","name":"XUSD","statuses":["primary"]},{"code":"YACC","name":"YACC","statuses":["primary"]},{"code":"YAC","name":"Yacoin","statuses":["primary"]},{"code":"YAY","name":"YAY","statuses":["primary"]},{"code":"YBC","name":"Ybcoin","statuses":["primary"]},{"code":"YOC","name":"YOC","statuses":["primary"]},{"code":"YOVI","name":"YOVI","statuses":["primary"]},{"code":"YUM","name":"YUM","statuses":["primary"]},{"code":"ZCC","name":"ZCC","statuses":["primary"]},{"code":"ZEIT","name":"Zeitcoin","statuses":["primary"]},{"code":"ZET","name":"Zetacoin","statuses":["primary"]},{"code":"ZRC","name":"ZiftrCOIN","statuses":["primary"]},{"code":"ZMC","name":"ZMC","statuses":["primary"]},{"code":"ZNY","name":"ZNY","statuses":["primary"]},{"code":"ZS","name":"ZS","statuses":["primary"]}]} + +module.exports = { + availableCurrencies +} diff --git a/ui/app/css/index.css b/ui/app/css/index.css index 612dc9d9a..e2be0abd9 100644 --- a/ui/app/css/index.css +++ b/ui/app/css/index.css @@ -471,3 +471,128 @@ input.large-input { .eth-warning{ transition: opacity 400ms ease-in, transform 400ms ease-in; } + +.buy-subview{ + transition: opacity 400ms ease-in, transform 400ms ease-in; +} + +.input-container:hover .edit-text{ + visibility: visible; +} + +.buy-inputs{ + font-family: 'Montserrat Light'; + font-size: 13px; + height: 20px; + background: transparent; + box-sizing: border-box; + border: solid; + border-color: transparent; + border-width: 0.5px; + border-radius: 2px; + +} +.input-container:hover .buy-inputs{ + box-sizing: inherit; + border: solid; + border-color: #F7861C; + border-width: 0.5px; + border-radius: 2px; +} + +.buy-inputs:focus{ + border: solid; + border-color: #F7861C; + border-width: 0.5px; + border-radius: 2px; +} + +.activeForm { + background: #F7F7F7; + border: none; + border-radius: 8px 8px 0px 0px; + width: 50%; + text-align: center; + padding-bottom: 4px; + +} + +.inactiveForm { + border: none; + border-radius: 8px 8px 0px 0px; + width: 50%; + text-align: center; + padding-bottom: 4px; +} + +.ex-coins { + font-family: 'Montserrat Regular'; + text-transform: uppercase; + text-align: center; + font-size: 33px; + width: 118px; + height: 42px; + padding: 1px; + color: #4D4D4D; +} + +.marketinfo{ + font-family: 'Montserrat light'; + color: #AEAEAE; + font-size: 12px; + line-height: 14px; +} + +#fromCoin::-webkit-calendar-picker-indicator { + display: none; +} + +#coinList { + width: 400px; + height: 500px; + overflow: scroll; +} + +.icon-control .fa-refresh{ + visibility: hidden; +} + +.icon-control:hover .fa-refresh{ + visibility: visible; +} + +.icon-control:hover .fa-chevron-right{ + visibility: hidden; +} + +.inactive { + color: #AEAEAE; +} + +.inactive button{ + background: #AEAEAE; + color: white; +} + +.ellip-address { + overflow: hidden; + text-overflow: ellipsis; + width: 5em; + font-size: 14px; + font-family: "Montserrat Light"; + margin-left: 5px; +} + +.qr-message { + font-size: 12px; + color: #F7861C; +} + +div.message-container > div:first-child { + font-size: 15px; + color: #4D4D4D; +} + +.pop-hover:hover { + transform: scale(1.1); +} diff --git a/ui/app/eth-store-warning.js b/ui/app/eth-store-warning.js index 7fe54a309..55274996b 100644 --- a/ui/app/eth-store-warning.js +++ b/ui/app/eth-store-warning.js @@ -35,9 +35,10 @@ EthStoreWarning.prototype.render = function () { margin: '10px 10px 10px 10px', }, }, - `MetaMask is currently in beta - - exercise caution while handling - and storing your ether. + `The MetaMask team would like to + remind you that MetaMask is currently in beta - so + don't store large + amounts of ether in MetaMask. `), h('i.fa.fa-exclamation-triangle.fa-4', { diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index e63c30fc5..94a9d3df6 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -73,9 +73,7 @@ InitializeMenuScreen.prototype.renderMenu = function () { margin: 12, }, }, 'Restore Existing Vault'), - ]) - ) } diff --git a/ui/app/first-time/restore-vault.js b/ui/app/first-time/restore-vault.js index 684781e50..4c1f21008 100644 --- a/ui/app/first-time/restore-vault.js +++ b/ui/app/first-time/restore-vault.js @@ -1,14 +1,14 @@ const inherits = require('util').inherits -const Component = require('react').Component +const PersistentForm = require('../../lib/persistent-form') const connect = require('react-redux').connect const h = require('react-hyperscript') const actions = require('../actions') module.exports = connect(mapStateToProps)(RestoreVaultScreen) -inherits(RestoreVaultScreen, Component) +inherits(RestoreVaultScreen, PersistentForm) function RestoreVaultScreen () { - Component.call(this) + PersistentForm.call(this) } function mapStateToProps (state) { @@ -19,6 +19,8 @@ function mapStateToProps (state) { RestoreVaultScreen.prototype.render = function () { var state = this.props + this.persistentFormParentId = 'restore-vault-form' + return ( h('.initialize-screen.flex-column.flex-center.flex-grow', [ @@ -39,6 +41,9 @@ RestoreVaultScreen.prototype.render = function () { // wallet seed entry h('h3', 'Wallet Seed'), h('textarea.twelve-word-phrase.letter-spacey', { + dataset: { + persistentFormId: 'wallet-seed', + }, placeholder: 'Enter your secret twelve word phrase here to restore your vault.', }), @@ -47,6 +52,9 @@ RestoreVaultScreen.prototype.render = function () { type: 'password', id: 'password-box', placeholder: 'New Password (min 8 chars)', + dataset: { + persistentFormId: 'password', + }, style: { width: 260, marginTop: 12, @@ -59,6 +67,9 @@ RestoreVaultScreen.prototype.render = function () { id: 'password-box-confirm', placeholder: 'Confirm Password', onKeyPress: this.onMaybeCreate.bind(this), + dataset: { + persistentFormId: 'password-confirmation', + }, style: { width: 260, marginTop: 16, diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index a9d6e4ff0..bad11113a 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -1,6 +1,7 @@ const extend = require('xtend') const actions = require('../actions') const txHelper = require('../../lib/tx-helper') +const notification = require('../../../app/scripts/lib/notifications') module.exports = reduceApp @@ -123,6 +124,7 @@ function reduceApp (state, action) { case actions.UNLOCK_METAMASK: return extend(appState, { + forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null, detailView: {}, transForward: true, isLoading: false, @@ -136,6 +138,25 @@ function reduceApp (state, action) { warning: null, }) + case actions.BACK_TO_INIT_MENU: + return extend(appState, { + warning: null, + transForward: false, + forgottenPassword: true, + currentView: { + name: 'InitMenu', + }, + }) + + case actions.BACK_TO_UNLOCK_VIEW: + return extend(appState, { + warning: null, + transForward: true, + forgottenPassword: !appState.forgottenPassword, + currentView: { + name: 'UnlockScreen', + }, + }) // reveal seed words case actions.REVEAL_SEED_CONFIRMATION: @@ -170,6 +191,7 @@ function reduceApp (state, action) { case actions.SHOW_ACCOUNT_DETAIL: return extend(appState, { + forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null, currentView: { name: 'accountDetail', context: action.value, @@ -250,6 +272,9 @@ function reduceApp (state, action) { warning: null, }) } else { + + notification.closePopup() + return extend(appState, { transForward: false, warning: null, @@ -317,6 +342,15 @@ function reduceApp (state, action) { isLoading: false, }) + case actions.SHOW_SUB_LOADING_INDICATION: + return extend(appState, { + isSubLoading: true, + }) + + case actions.HIDE_SUB_LOADING_INDICATION: + return extend(appState, { + isSubLoading: false, + }) case actions.CLEAR_SEED_WORD_CACHE: return extend(appState, { transForward: true, @@ -369,15 +403,116 @@ function reduceApp (state, action) { }, }) - case actions.SHOW_ETH_WARNING: + case actions.BUY_ETH_VIEW: return extend(appState, { transForward: true, currentView: { - name: 'accountDetail', + name: 'buyEth', context: appState.currentView.context, }, - accountDetail: { - subview: 'buy-eth-warning', + buyView: { + subview: 'buyForm', + amount: '5.00', + buyAddress: action.value, + formView: { + coinbase: true, + shapeshift: false, + }, + }, + }) + + case actions.UPDATE_BUY_ADDRESS: + return extend(appState, { + buyView: { + subview: 'buyForm', + formView: { + coinbase: appState.buyView.formView.coinbase, + shapeshift: appState.buyView.formView.shapeshift, + }, + buyAddress: action.value, + amount: appState.buyView.amount, + }, + }) + + case actions.UPDATE_COINBASE_AMOUNT: + return extend(appState, { + buyView: { + subview: 'buyForm', + formView: { + coinbase: true, + shapeshift: false, + }, + buyAddress: appState.buyView.buyAddress, + amount: action.value, + }, + }) + + case actions.COINBASE_SUBVIEW: + return extend(appState, { + buyView: { + subview: 'buyForm', + formView: { + coinbase: true, + shapeshift: false, + }, + buyAddress: appState.buyView.buyAddress, + amount: appState.buyView.amount, + }, + }) + + case actions.SHAPESHIFT_SUBVIEW: + return extend(appState, { + buyView: { + subview: 'buyForm', + formView: { + coinbase: false, + shapeshift: true, + marketinfo: action.value.marketinfo, + coinOptions: action.value.coinOptions, + }, + buyAddress: appState.buyView.buyAddress, + amount: appState.buyView.amount, + }, + }) + + case actions.PAIR_UPDATE: + return extend(appState, { + buyView: { + subview: 'buyForm', + formView: { + coinbase: false, + shapeshift: true, + marketinfo: action.value.marketinfo, + coinOptions: appState.buyView.formView.coinOptions, + }, + buyAddress: appState.buyView.buyAddress, + amount: appState.buyView.amount, + warning: null, + }, + }) + + case actions.SHOW_QR: + return extend(appState, { + qrRequested: true, + transForward: true, + Qr: { + message: action.value.message, + image: action.value.qr, + data: action.value.data, + }, + }) + + case actions.SHOW_QR_VIEW: + return extend(appState, { + currentView: { + name: 'qr', + context: appState.currentView.context, + }, + transForward: true, + Qr: { + message: action.value.message, + image: action.value.qr, + data: action.value.data, }, }) default: @@ -405,4 +540,3 @@ function indexForPending (state, txId) { return idx } - diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 8d4520fb1..7f18480cb 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -10,10 +10,14 @@ function reduceMetamask (state, action) { var metamaskState = extend({ isInitialized: false, isUnlocked: false, + isEthConfirmed: false, currentDomain: 'example.com', rpcTarget: 'https://rawtestrpc.metamask.io/', identities: {}, unconfTxs: {}, + currentFiat: 'USD', + conversionRate: 0, + conversionDate: 'N/A', }, state.metamask) switch (action.type) { @@ -33,7 +37,7 @@ function reduceMetamask (state, action) { case actions.AGREE_TO_ETH_WARNING: return extend(metamaskState, { - isEthConfirmed: true, + isEthConfirmed: !metamaskState.isEthConfirmed, }) case actions.UNLOCK_METAMASK: @@ -114,6 +118,13 @@ function reduceMetamask (state, action) { var identities = extend(metamaskState.identities, id) return extend(metamaskState, { identities }) + case actions.SET_CURRENT_FIAT: + return extend(metamaskState, { + currentFiat: action.value.currentFiat, + conversionRate: action.value.conversionRate, + conversionDate: action.value.conversionDate, + }) + default: return metamaskState diff --git a/ui/app/send.js b/ui/app/send.js index 06ea199f4..0cc3a032f 100644 --- a/ui/app/send.js +++ b/ui/app/send.js @@ -1,5 +1,5 @@ const inherits = require('util').inherits -const Component = require('react').Component +const PersistentForm = require('../lib/persistent-form') const h = require('react-hyperscript') const connect = require('react-redux').connect const Identicon = require('./components/identicon') @@ -29,12 +29,14 @@ function mapStateToProps (state) { return result } -inherits(SendTransactionScreen, Component) +inherits(SendTransactionScreen, PersistentForm) function SendTransactionScreen () { - Component.call(this) + PersistentForm.call(this) } SendTransactionScreen.prototype.render = function () { + this.persistentFormParentId = 'send-tx-form' + var state = this.props var address = state.address var account = state.account @@ -137,6 +139,9 @@ SendTransactionScreen.prototype.render = function () { h('input.large-input', { name: 'address', placeholder: 'Recipient Address', + dataset: { + persistentFormId: 'recipient-address', + }, }), ]), @@ -150,6 +155,9 @@ SendTransactionScreen.prototype.render = function () { style: { marginRight: 6, }, + dataset: { + persistentFormId: 'tx-amount', + }, }), h('button.primary', { @@ -185,11 +193,12 @@ SendTransactionScreen.prototype.render = function () { width: '100%', resize: 'none', }, + dataset: { + persistentFormId: 'tx-data', + }, }), ]), - ]) - ) } diff --git a/ui/app/store.js b/ui/app/store.js index ab6422e73..8d891bdc9 100644 --- a/ui/app/store.js +++ b/ui/app/store.js @@ -1,17 +1,20 @@ const createStore = require('redux').createStore const applyMiddleware = require('redux').applyMiddleware const thunkMiddleware = require('redux-thunk') -const createLogger = require('redux-logger') const rootReducer = require('./reducers') +const createLogger = require('redux-logger') + +global.METAMASK_DEBUG = false module.exports = configureStore -const loggerMiddleware = createLogger() +const loggerMiddleware = createLogger({ + predicate: () => global.METAMASK_DEBUG, +}) + +const middlewares = [thunkMiddleware, loggerMiddleware] -const createStoreWithMiddleware = applyMiddleware( - thunkMiddleware, - loggerMiddleware -)(createStore) +const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore) function configureStore (initialState) { return createStoreWithMiddleware(rootReducer, initialState) diff --git a/ui/app/unlock.js b/ui/app/unlock.js index a7896d640..b82e46d02 100644 --- a/ui/app/unlock.js +++ b/ui/app/unlock.js @@ -3,10 +3,11 @@ const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const actions = require('./actions') -const Mascot = require('./components/mascot') const getCaretCoordinates = require('textarea-caret') const EventEmitter = require('events').EventEmitter +const Mascot = require('./components/mascot') + module.exports = connect(mapStateToProps)(UnlockScreen) inherits(UnlockScreen, Component) @@ -25,47 +26,46 @@ UnlockScreen.prototype.render = function () { const state = this.props const warning = state.warning return ( - - h('.unlock-screen.flex-column.flex-center.flex-grow', [ - - h(Mascot, { - animationEventEmitter: this.animationEventEmitter, - }), - - h('h1', { - style: { - fontSize: '1.4em', - textTransform: 'uppercase', - color: '#7F8082', - }, - }, 'MetaMask'), - - h('input.large-input', { - type: 'password', - id: 'password-box', - placeholder: 'enter password', - style: { - - }, - onKeyPress: this.onKeyPress.bind(this), - onInput: this.inputChanged.bind(this), - }), - - h('.error', { - style: { - display: warning ? 'block' : 'none', - }, - }, warning), - - h('button.primary.cursor-pointer', { - onClick: this.onSubmit.bind(this), - style: { - margin: 10, - }, - }, 'Unlock'), - + h('.flex-column.hey-im-here', [ + h('.unlock-screen.flex-column.flex-center.flex-grow', [ + + h(Mascot, { + animationEventEmitter: this.animationEventEmitter, + }), + + h('h1', { + style: { + fontSize: '1.4em', + textTransform: 'uppercase', + color: '#7F8082', + }, + }, 'MetaMask'), + + h('input.large-input', { + type: 'password', + id: 'password-box', + placeholder: 'enter password', + style: { + + }, + onKeyPress: this.onKeyPress.bind(this), + onInput: this.inputChanged.bind(this), + }), + + h('.error', { + style: { + display: warning ? 'block' : 'none', + }, + }, warning), + + h('button.primary.cursor-pointer', { + onClick: this.onSubmit.bind(this), + style: { + margin: 10, + }, + }, 'Unlock'), + ]), ]) - ) } diff --git a/ui/app/util.js b/ui/app/util.js index 04ebcecdb..e4b77e2bc 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -92,8 +92,8 @@ function parseBalance (balance) { // Takes wei hex, returns an object with three properties. // Its "formatted" property is what we generally use to render values. -function formatBalance (balance, decimalsToKeep) { - var parsed = parseBalance(balance) +function formatBalance (balance, decimalsToKeep, needsParse = true) { + var parsed = needsParse ? parseBalance(balance) : balance.split('.') var beforeDecimal = parsed[0] var afterDecimal = parsed[1] var formatted = 'None' @@ -141,14 +141,21 @@ function shortenBalance (balance, decimalsToKeep = 1) { var convertedBalance = parseFloat(balance) if (convertedBalance > 1000000) { truncatedValue = (balance / 1000000).toFixed(decimalsToKeep) - return `>${truncatedValue}m` + return `${truncatedValue}m` } else if (convertedBalance > 1000) { truncatedValue = (balance / 1000).toFixed(decimalsToKeep) - return `>${truncatedValue}k` + return `${truncatedValue}k` + } else if (convertedBalance === 0) { + return '0' + } else if (convertedBalance < 0.001) { + return '<0.001' } else if (convertedBalance < 1) { - var exponent = balance.match(/\.0*/)[0].length - truncatedValue = (convertedBalance * Math.pow(10, exponent)).toFixed(decimalsToKeep) - return `<${truncatedValue}e-${exponent}` + var stringBalance = convertedBalance.toString() + if (stringBalance.split('.')[1].length > 3) { + return convertedBalance.toFixed(3) + } else { + return stringBalance + } } else { return convertedBalance.toFixed(decimalsToKeep) } |