aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/scripts/metamask-controller.js36
-rw-r--r--ui/app/account-detail.js32
-rw-r--r--ui/app/actions.js169
-rw-r--r--ui/app/app.js17
-rw-r--r--ui/app/components/buy-button-subview.js74
-rw-r--r--ui/app/components/coinbase-form.js186
-rw-r--r--ui/app/components/shapeshift-form.js335
-rw-r--r--ui/app/css/index.css110
-rw-r--r--ui/app/reducers.js8
-rw-r--r--ui/app/store.js8
10 files changed, 966 insertions, 9 deletions
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 63970799d..dd43ac2fc 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -6,6 +6,7 @@ const messageManager = require('./lib/message-manager')
const HostStore = require('./lib/remote-store.js').HostStore
const Web3 = require('web3')
const ConfigManager = require('./lib/config-manager')
+const extension = require('./lib/extension')
module.exports = class MetamaskController {
@@ -39,6 +40,7 @@ module.exports = class MetamaskController {
setProviderType: this.setProviderType.bind(this),
useEtherscanProvider: this.useEtherscanProvider.bind(this),
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
+ agreeToEthWarning: this.agreeToEthWarning.bind(this),
// forward directly to idStore
createNewVault: idStore.createNewVault.bind(idStore),
recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
@@ -55,6 +57,8 @@ module.exports = class MetamaskController {
saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
tryPassword: idStore.tryPassword.bind(idStore),
recoverSeed: idStore.recoverSeed.bind(idStore),
+ // coinbase
+ buyEth: this.buyEth.bind(this),
}
}
@@ -161,6 +165,7 @@ module.exports = class MetamaskController {
function configToPublic (state) {
return {
provider: state.provider,
+ selectedAddress: state.selectedAccount,
}
}
// dump obj into store
@@ -236,23 +241,48 @@ module.exports = class MetamaskController {
}
}
+ agreeToEthWarning (cb) {
+ try {
+ this.configManager.setShouldntShowWarning(true)
+ cb()
+ } catch (e) {
+ cb(e)
+ }
+ }
+
// called from popup
setRpcTarget (rpcTarget) {
this.configManager.setRpcTarget(rpcTarget)
- chrome.runtime.reload()
+ extension.runtime.reload()
this.idStore.getNetwork()
}
setProviderType (type) {
this.configManager.setProviderType(type)
- chrome.runtime.reload()
+ extension.runtime.reload()
this.idStore.getNetwork()
}
useEtherscanProvider () {
this.configManager.useEtherscanProvider()
- chrome.runtime.reload()
+ extension.runtime.reload()
}
+
+ buyEth (address, amount) {
+ if (!amount) amount = '5'
+
+ var network = this.idStore._currentState.network
+ var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
+
+ if (network === '2') {
+ url = 'https://testfaucet.metamask.io/'
+ }
+
+ extension.tabs.create({
+ url,
+ })
+ }
+
}
function noop () {}
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index 6d50dbd71..cf65cbb7d 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -15,7 +15,7 @@ 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) {
@@ -28,6 +28,7 @@ function mapStateToProps (state) {
network: state.metamask.network,
unconfTxs: valuesFor(state.metamask.unconfTxs),
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
+ isEthWarningConfirmed: state.metamask.isEthConfirmed,
}
}
@@ -171,6 +172,21 @@ AccountDetailScreen.prototype.render = function () {
}),
h('button', {
+ onClick: this.buyButtonDeligator.bind(this),
+ style: {
+ marginBottom: '20px',
+ marginRight: '8px',
+ position: 'absolute',
+ left: '219px',
+ },
+ }, props.accountDetail.subview === 'buyForm' ? [h('i.fa.fa-arrow-left', {
+ style: {
+ width: '22.641px',
+ height: '14px',
+ },
+ })] : 'BUY'),
+
+ h('button', {
onClick: () => props.dispatch(actions.showSendPage()),
style: {
marginBottom: '20px',
@@ -181,7 +197,7 @@ AccountDetailScreen.prototype.render = function () {
]),
]),
- // subview (tx history, pk export confirm)
+ // subview (tx history, pk export confirm, buy eth warning)
h(ReactCSSTransitionGroup, {
className: 'css-transition-group',
transitionName: 'main',
@@ -209,6 +225,8 @@ 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()
}
@@ -239,3 +257,13 @@ AccountDetailScreen.prototype.transactionList = function () {
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.buyEthSubview())
+ }
+}
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 3e0fe55c0..754ffced8 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -66,6 +66,10 @@ var actions = {
showPrivateKey: showPrivateKey,
SAVE_ACCOUNT_LABEL: 'SAVE_ACCOUNT_LABEL',
saveAccountLabel: saveAccountLabel,
+ AGREE_TO_ETH_WARNING: 'AGREE_TO_ETH_WARNING',
+ agreeToEthWarning: agreeToEthWarning,
+ SHOW_ETH_WARNING: 'SHOW_ETH_WARNING',
+ showEthWarning: showEthWarning,
// tx conf screen
COMPLETED_TX: 'COMPLETED_TX',
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
@@ -106,6 +110,28 @@ var actions = {
HIDE_LOADING: 'HIDE_LOADING_INDICATION',
showLoadingIndication: showLoadingIndication,
hideLoadingIndication: hideLoadingIndication,
+ // buy Eth with coinbase
+ BUY_ETH: 'BUY_ETH',
+ buyEth: buyEth,
+ buyEthSubview: buyEthSubview,
+ BUY_ETH_SUBVIEW: 'BUY_ETH_SUBVIEW',
+ 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,
+ COIN_SHIFT_REQUEST: 'COIN_SHIFT_REQUEST',
+ coinShiftRquest: coinShiftRquest,
+ SHOW_SUB_LOADING_INDICATION: 'SHOW_SUB_LOADING_INDICATION',
+ showSubLoadingIndication: showSubLoadingIndication,
+ HIDE_SUB_LOADING_INDICATION: 'HIDE_SUB_LOADING_INDICATION',
+ hideSubLoadingIndication: hideSubLoadingIndication,
+
}
module.exports = actions
@@ -489,6 +515,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)
}
@@ -559,3 +597,134 @@ function showSendPage () {
type: actions.SHOW_SEND_PAGE,
}
}
+
+function agreeToEthWarning () {
+ return (dispatch) => {
+ _accountManager.agreeToEthWarning((err) => {
+ if (err) {
+ return dispatch(actions.showEthWarning(err.message))
+ }
+ dispatch({
+ type: actions.AGREE_TO_ETH_WARNING,
+ })
+ })
+ }
+}
+
+function showEthWarning () {
+ return {
+ type: actions.SHOW_ETH_WARNING,
+ }
+}
+
+function buyEth (address, amount) {
+ return (dispatch) => {
+ _accountManager.buyEth(address, amount)
+ dispatch({
+ type: actions.BUY_ETH,
+ })
+ }
+}
+
+function buyEthSubview () {
+ return {
+ type: actions.BUY_ETH_SUBVIEW,
+ }
+}
+
+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
+ network === 'classic' ? pair = 'btc_etc' : 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) {
+ return (dispatch) => {
+ dispatch(actions.showSubLoadingIndication())
+ shapeShiftRequest('shift', { method: 'POST', data}, (response) => {
+ dispatch(actions.hideSubLoadingIndication())
+ if (response.error) return dispatch(actions.showWarning(response.error))
+ dispatch({
+ type: actions.COIN_SHIFT_REQUEST,
+ value: {
+ response: response,
+ },
+ })
+ })
+ }
+}
+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()
+ }
+}
diff --git a/ui/app/app.js b/ui/app/app.js
index 536b871b3..b8deedc13 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -27,6 +27,7 @@ const MenuDroppo = require('menu-droppo')
const DropMenuItem = require('./components/drop-menu-item')
const NetworkIndicator = require('./components/network')
const Tooltip = require('./components/tooltip')
+const EthStoreWarning = require('./eth-store-warning')
module.exports = connect(mapStateToProps)(App)
@@ -37,6 +38,7 @@ function mapStateToProps (state) {
return {
// state from plugin
isConfirmed: state.metamask.isConfirmed,
+ isEthConfirmed: state.metamask.isEthConfirmed,
isInitialized: state.metamask.isInitialized,
isUnlocked: state.metamask.isUnlocked,
currentView: state.appState.currentView,
@@ -129,6 +131,7 @@ App.prototype.renderAppBar = function () {
h(NetworkIndicator, {
network: this.props.network,
+ provider: this.props.provider,
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
@@ -201,6 +204,7 @@ App.prototype.renderNetworkDropdown = function () {
style: {
position: 'absolute',
left: 0,
+ top: '36px',
},
innerStyle: {
background: 'white',
@@ -218,6 +222,16 @@ App.prototype.renderNetworkDropdown = function () {
action: () => props.dispatch(actions.setProviderType('mainnet')),
icon: h('.menu-icon.diamond'),
activeNetworkRender: props.network,
+ provider: props.provider,
+ }),
+
+ 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, {
@@ -235,6 +249,7 @@ App.prototype.renderNetworkDropdown = function () {
icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
activeNetworkRender: props.provider.rpcTarget,
}),
+
this.renderCustomOption(props.provider.rpcTarget),
])
}
@@ -324,6 +339,8 @@ App.prototype.renderPrimary = function () {
// show current view
switch (props.currentView.name) {
+ case 'EthStoreWarning':
+ return h(EthStoreWarning, {key: 'ethWarning'})
case 'accounts':
return h(AccountsScreen, {key: 'accounts'})
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
new file mode 100644
index 000000000..b410630a9
--- /dev/null
+++ b/ui/app/components/buy-button-subview.js
@@ -0,0 +1,74 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const connect = require('react-redux').connect
+const actions = require('../actions')
+const CoinbaseForm = require('./coinbase-form')
+const ShapeshiftForm = require('./shapeshift-form')
+
+module.exports = connect(mapStateToProps)(BuyButtonSubview)
+
+function mapStateToProps (state) {
+ return {
+ selectedAccount: state.selectedAccount,
+ warning: state.appState.warning,
+ 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.accountDetail.formView
+
+ return (
+ h('span', {key: 'buyForm'}, [
+ h('h3.flex-row.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ paddingTop: '4px',
+ justifyContent: 'space-around',
+ },
+ }, [
+ h(currentForm.coinbase ? '.activeForm' : '.inactiveForm', {
+ onClick: () => props.dispatch(actions.coinBaseSubview()),
+ }, 'Coinbase'),
+ h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm', {
+ onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)),
+ }, 'Shapeshift'),
+ ]),
+ this.formVersionSubview(),
+ ])
+ )
+}
+
+BuyButtonSubview.prototype.formVersionSubview = function () {
+ if (this.props.network === '1') {
+ if (this.props.accountDetail.formView.coinbase) {
+ return h(CoinbaseForm, this.props)
+ } else if (this.props.accountDetail.formView.shapeshift) {
+ return h(ShapeshiftForm, this.props)
+ }
+ } else {
+ console.log(this.props.network)
+ 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'),
+ ])
+ }
+}
+
diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js
new file mode 100644
index 000000000..5ab6b507a
--- /dev/null
+++ b/ui/app/components/coinbase-form.js
@@ -0,0 +1,186 @@
+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.accountDetail.amount
+ var address = props.accountDetail.buyAddress
+
+ return h('.flex-column', {
+ style: {
+ margin: '10px',
+ },
+ }, [
+ h('.flex-column', {
+ style: {
+ alignItems: 'flex-start',
+ },
+ }, [
+ h('.flex-column', [
+ h('div', 'Address:'),
+ h('.input-container', {
+ style: {},
+ }, [
+ h('input.buy-inputs', {
+ type: 'text',
+ style: {
+ boxSizing: 'border-box',
+ width: '317px',
+ height: '20px',
+ padding: ' 12px 0px 12px 1px ',
+ },
+ defaultValue: address,
+ onChange: this.handleAddress.bind(this),
+ }),
+ h('i.fa.fa-pencil-square-o.edit-text', {
+ style: {
+ fontSize: '12px',
+ color: '#F7861C',
+ position: 'relative',
+ bottom: '8px',
+ right: '11px',
+ },
+ }),
+
+ ]),
+ ]),
+
+ 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.backToAccountDetail(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.accountDetail.amount
+ var address = props.accountDetail.buyAddress
+ var message
+
+ if (isValidAddress(address) && isValidAmountforCoinBase(amount).valid) {
+ props.dispatch(actions.buyEth(address, props.accountDetail.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/shapeshift-form.js b/ui/app/components/shapeshift-form.js
new file mode 100644
index 000000000..fea9539b0
--- /dev/null
+++ b/ui/app/components/shapeshift-form.js
@@ -0,0 +1,335 @@
+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 CopyButton = require('./CopyButton')
+
+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,
+ }
+}
+
+inherits(ShapeshiftForm, Component)
+
+function ShapeshiftForm() {
+ Component.call(this)
+}
+
+ShapeshiftForm.prototype.render = function () {
+ const marketinfo = this.props.accountDetail.formView.marketinfo
+ const coinOptions = this.props.accountDetail.formView.coinOptions
+ var coin = marketinfo.pair.split('_')[0].toUpperCase()
+
+ return h('.flex-column', {
+ style: {
+ margin: '10px',
+ 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',
+ 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: '23px',
+ right: '11px',
+ },
+ }),
+ ]),
+
+ h('.icon-control', [
+ h('i.fa.fa-refresh.fa-4.orange', {
+ style: {
+ position: 'relative',
+ bottom: '5px',
+ right: '5px',
+ color: '#F7861C',
+ },
+ onClick: this.updateCoin.bind(this),
+ }),
+ h('i.fa.fa-chevron-right.fa-4.orange', {
+ style: {
+ position: 'relative',
+ bottom: '5px',
+ right: '15px',
+ 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(this.activeToggle('.input-container'), {
+ style: {
+ width: '100%',
+ marginTop: '19px',
+ },
+ }, [
+ h('div', 'Receiving address:'),
+
+ h('input.buy-inputs', {
+ type: 'text',
+ value: this.props.accountDetail.buyAddress,
+ onChange: this.handleAddress.bind(this),
+ style: {
+ boxSizing: 'border-box',
+ width: '325px',
+ 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(this.activeToggle('.input-container'), {
+ style: {
+ width: '100%',
+ },
+ }, [
+ h('div', `${coin} Address:`),
+
+ h('input#fromCoinAddress.buy-inputs', {
+ type: 'text',
+ placeholder: `Your ${coin} Refund Address`,
+ style: {
+ boxSizing: 'border-box',
+ width: '235px',
+ 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('button', {
+ onClick: this.shift.bind(this),
+ },
+ 'Submit'),
+ ]),
+ ])
+}
+
+ShapeshiftForm.prototype.shift = function () {
+ var withdrawal = this.props.accountDetail.buyAddress
+ var returnAddress = document.getElementById('fromCoinAddress').value
+ var pair = this.props.accountDetail.formView.marketinfo.pair
+ var data = {
+ 'withdrawal': withdrawal,
+ 'pair': pair,
+ 'returnAddress': returnAddress,
+ }
+
+ if (isValidAddress(withdrawal)) {
+ this.props.dispatch(actions.coinShiftRquest(data))
+ }
+}
+
+ShapeshiftForm.prototype.renderCoinList = function () {
+ var list = Object.keys(this.props.accountDetail.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.accountDetail.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.accountDetail.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.accountDetail.formView.marketinfo
+ const coinOptions = this.props.accountDetail.formView.coinOptions
+ var coin = marketinfo.pair.split('_')[0].toUpperCase()
+ const request = this.props.accountDetail.formView.response
+
+ if (!request) {
+ return h('span', [
+ 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}`]),
+ ])
+ } else {
+ return h('.flex-column', {
+ style: {
+ width: '229px',
+ height: '82px',
+ },
+ }, [
+ h('.marketinfo', ['Limit: ', `${marketinfo.limit}`]),
+ h('.marketinfo', ['Minimum : ', `${marketinfo.minimum}`]),
+ h('div', {
+ style: {
+ fontSize: '12px',
+ lineHeight: '16px',
+ marginTop: '4px',
+ color: '#F7861C',
+ },
+ }, `Deposit your ${request.depositType} to the address bellow:`),
+ h('.flex-row', {
+ style: {
+ position: 'relative',
+ right: '38px',
+ },
+ }, [
+ h('div', {
+ style: {
+ fontSize: '13px',
+ },
+ }, request.deposit),
+ h(CopyButton, {
+ value: request.deposit,
+ }),
+ ]),
+ ])
+ }
+}
+
+ShapeshiftForm.prototype.handleAddress = function (event) {
+ this.props.dispatch(actions.updateBuyAddress(event.target.value))
+}
+
+ShapeshiftForm.prototype.activeToggle = function (elementType) {
+ if (!this.props.accountDetail.formView.response || this.props.warning) return elementType
+ return `${elementType}.inactive`
+}
+
+ShapeshiftForm.prototype.renderLoading = function () {
+ return h('span', {
+ style: {
+ position: 'absolute',
+ left: '70px',
+ bottom: '138px',
+ background: 'transparent',
+ width: '229px',
+ height: '82px',
+ display: 'flex',
+ justifyContent: 'center',
+ },
+ }, [
+ h('img', {
+ style: {
+ width: '60px',
+ },
+ src: 'images/loading.svg',
+ }),
+ ])
+}
diff --git a/ui/app/css/index.css b/ui/app/css/index.css
index 3f52b6ed4..f31bf5aaa 100644
--- a/ui/app/css/index.css
+++ b/ui/app/css/index.css
@@ -151,12 +151,14 @@ textarea.twelve-word-phrase {
.network-name {
position: absolute;
top: 8px;
+ left: 60px;
width: 5.2em;
line-height: 9px;
text-rendering: geometricPrecision;
}
.check {
+ margin-left: 7px;
color: #F7861C;
flex: 1 0 auto;
display: flex;
@@ -463,3 +465,111 @@ input.large-input {
display: inline-block;
padding-left: 5px;
}
+
+/* buy eth warning screen */
+
+.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;
+}
diff --git a/ui/app/reducers.js b/ui/app/reducers.js
index 9243ddba4..a691cf614 100644
--- a/ui/app/reducers.js
+++ b/ui/app/reducers.js
@@ -7,6 +7,8 @@ const reduceIdentities = require('./reducers/identities')
const reduceMetamask = require('./reducers/metamask')
const reduceApp = require('./reducers/app')
+window.METAMASK_CACHED_LOG_STATE = null
+
module.exports = rootReducer
function rootReducer (state, action) {
@@ -35,5 +37,11 @@ function rootReducer (state, action) {
state.appState = reduceApp(state, action)
+ window.METAMASK_CACHED_LOG_STATE = state
return state
}
+
+window.logState = function() {
+ var stateString = JSON.stringify(window.METAMASK_CACHED_LOG_STATE, null, 2)
+ console.log(stateString)
+}
diff --git a/ui/app/store.js b/ui/app/store.js
index ab6422e73..363a51cea 100644
--- a/ui/app/store.js
+++ b/ui/app/store.js
@@ -1,16 +1,16 @@
const createStore = require('redux').createStore
const applyMiddleware = require('redux').applyMiddleware
const thunkMiddleware = require('redux-thunk')
-const createLogger = require('redux-logger')
+// const createLogger = require('redux-logger')
const rootReducer = require('./reducers')
module.exports = configureStore
-const loggerMiddleware = createLogger()
+// const loggerMiddleware = createLogger()
const createStoreWithMiddleware = applyMiddleware(
- thunkMiddleware,
- loggerMiddleware
+ thunkMiddleware
+ // // loggerMiddleware
)(createStore)
function configureStore (initialState) {