diff options
Merge branch 'master' into EdgeCompatibility
Diffstat (limited to 'ui/app/components')
-rw-r--r-- | ui/app/components/account-eth-balance.js | 140 | ||||
-rw-r--r-- | ui/app/components/account-info-link.js | 42 | ||||
-rw-r--r-- | ui/app/components/buy-button-subview.js | 123 | ||||
-rw-r--r-- | ui/app/components/coinbase-form.js | 162 | ||||
-rw-r--r-- | ui/app/components/drop-menu-item.js | 3 | ||||
-rw-r--r-- | ui/app/components/eth-balance.js | 21 | ||||
-rw-r--r-- | ui/app/components/mascot.js | 18 | ||||
-rw-r--r-- | ui/app/components/network.js | 16 | ||||
-rw-r--r-- | ui/app/components/pending-msg-details.js | 9 | ||||
-rw-r--r-- | ui/app/components/qr-code.js | 71 | ||||
-rw-r--r-- | ui/app/components/shapeshift-form.js | 322 | ||||
-rw-r--r-- | ui/app/components/shift-list-item.js | 202 | ||||
-rw-r--r-- | ui/app/components/transaction-list-item.js | 9 | ||||
-rw-r--r-- | ui/app/components/transaction-list.js | 33 |
14 files changed, 1122 insertions, 49 deletions
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.', + ]), + ]), ]) ) } + |