aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components')
-rw-r--r--ui/app/components/account-menu/index.js28
-rw-r--r--ui/app/components/balance-component.js5
-rw-r--r--ui/app/components/coinbase-form.js2
-rw-r--r--ui/app/components/currency-input.js14
-rw-r--r--ui/app/components/dropdowns/components/account-dropdowns.js10
-rw-r--r--ui/app/components/mascot.js6
-rw-r--r--ui/app/components/modals/account-details-modal.js4
-rw-r--r--ui/app/components/modals/buy-options-modal.js2
-rw-r--r--ui/app/components/modals/deposit-ether-modal.js184
-rw-r--r--ui/app/components/modals/export-private-key-modal.js10
-rw-r--r--ui/app/components/modals/hide-token-confirmation-modal.js4
-rw-r--r--ui/app/components/modals/modal.js32
-rw-r--r--ui/app/components/network.js41
-rw-r--r--ui/app/components/pages/add-token.js30
-rw-r--r--ui/app/components/pages/import-account/json.js40
-rw-r--r--ui/app/components/pages/import-account/private-key.js61
-rw-r--r--ui/app/components/pages/keychains/restore-vault.js5
-rw-r--r--ui/app/components/pages/settings/settings.js14
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js28
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js43
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js3
-rw-r--r--ui/app/components/send/send-constants.js3
-rw-r--r--ui/app/components/send/send-v2-container.js4
-rw-r--r--ui/app/components/shapeshift-form.js468
-rw-r--r--ui/app/components/shift-list-item.js50
-rw-r--r--ui/app/components/token-cell.js6
-rw-r--r--ui/app/components/transaction-list-item.js129
-rw-r--r--ui/app/components/tx-list-item.js10
-rw-r--r--ui/app/components/tx-list.js19
-rw-r--r--ui/app/components/tx-view.js20
-rw-r--r--ui/app/components/wallet-view.js4
31 files changed, 731 insertions, 548 deletions
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index 0d3b43ae9..f031f2446 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -33,15 +33,28 @@ function mapDispatchToProps (dispatch) {
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
showAccountDetail: address => {
dispatch(actions.showAccountDetail(address))
+ dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
lockMetamask: () => {
dispatch(actions.lockMetamask())
- dispatch(actions.displayWarning(null))
+ dispatch(actions.hideWarning())
+ dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
- showNewAccountModal: () => {
- dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
+ showConfigPage: () => {
+ dispatch(actions.showConfigPage())
+ dispatch(actions.hideSidebar())
+ dispatch(actions.toggleAccountMenu())
+ },
+ showNewAccountPage: (formToSelect) => {
+ dispatch(actions.showNewAccountPage(formToSelect))
+ dispatch(actions.hideSidebar())
+ dispatch(actions.toggleAccountMenu())
+ },
+ showInfoPage: () => {
+ dispatch(actions.showInfoPage())
+ dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
}
@@ -51,7 +64,7 @@ AccountMenu.prototype.render = function () {
const {
isAccountMenuOpen,
toggleAccountMenu,
- showNewAccountModal,
+ showNewAccountPage,
lockMetamask,
history,
} = this.props
@@ -73,15 +86,12 @@ AccountMenu.prototype.render = function () {
h('div.account-menu__accounts', this.renderAccounts()),
h(Divider),
h(Item, {
- onClick: showNewAccountModal,
+ onClick: () => showNewAccountPage('CREATE'),
icon: h('img', { src: 'images/plus-btn-white.svg' }),
text: 'Create Account',
}),
h(Item, {
- onClick: () => {
- toggleAccountMenu()
- history.push(IMPORT_ACCOUNT_ROUTE)
- },
+ onClick: () => showNewAccountPage('IMPORT'),
icon: h('img', { src: 'images/import-account.svg' }),
text: 'Import Account',
}),
diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js
index d14aa675f..d591ab455 100644
--- a/ui/app/components/balance-component.js
+++ b/ui/app/components/balance-component.js
@@ -40,7 +40,7 @@ BalanceComponent.prototype.render = function () {
// style: {},
// }),
h(Identicon, {
- diameter: 45,
+ diameter: 50,
address: token && token.address,
network,
}),
@@ -94,7 +94,8 @@ BalanceComponent.prototype.renderFiatValue = function (formattedBalance) {
}
BalanceComponent.prototype.renderFiatAmount = function (fiatDisplayNumber, fiatSuffix, fiatPrefix) {
- if (fiatDisplayNumber === 'N/A') return null
+ const shouldNotRenderFiat = fiatDisplayNumber === 'N/A' || Number(fiatDisplayNumber) === 0
+ if (shouldNotRenderFiat) return null
return h('div.fiat-amount', {
style: {},
diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js
index f44d86045..f70208625 100644
--- a/ui/app/components/coinbase-form.js
+++ b/ui/app/components/coinbase-form.js
@@ -40,7 +40,7 @@ CoinbaseForm.prototype.render = function () {
}, 'Continue to Coinbase'),
h('button.btn-red', {
- onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)),
+ onClick: () => props.dispatch(actions.goHome()),
}, 'Cancel'),
]),
])
diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js
index 66880091f..6f7862e51 100644
--- a/ui/app/components/currency-input.js
+++ b/ui/app/components/currency-input.js
@@ -50,10 +50,18 @@ function sanitizeValue (value) {
CurrencyInput.prototype.handleChange = function (newValue) {
const { onInputChange } = this.props
+ const { value } = this.state
- this.setState({ value: sanitizeValue(newValue) })
+ let parsedValue = newValue
+ const newValueLastIndex = newValue.length - 1
- onInputChange(sanitizeValue(newValue))
+ if (value === '0' && newValue[newValueLastIndex] === '0') {
+ parsedValue = parsedValue.slice(0, newValueLastIndex)
+ }
+
+ const sanitizedValue = sanitizeValue(parsedValue)
+ this.setState({ value: sanitizedValue })
+ onInputChange(sanitizedValue)
}
// If state.value === props.value plus a decimal point, or at least one
@@ -90,6 +98,6 @@ CurrencyInput.prototype.render = function () {
size: valueToRender.length * inputSizeMultiplier,
readOnly,
onChange: e => this.handleChange(e.target.value),
- ref: inputRef,
+ ref: inputRef,
})
}
diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js
index 58326b13c..f97ac0691 100644
--- a/ui/app/components/dropdowns/components/account-dropdowns.js
+++ b/ui/app/components/dropdowns/components/account-dropdowns.js
@@ -199,7 +199,7 @@ class AccountDropdowns extends Component {
{},
menuItemStyles,
),
- onClick: () => actions.showNewAccountModal(),
+ onClick: () => actions.showNewAccountPageCreateForm(),
},
[
h(
@@ -228,7 +228,7 @@ class AccountDropdowns extends Component {
actions.hideSidebar()
}
},
- onClick: () => actions.showImportPage(),
+ onClick: () => actions.showNewAccountPageImportForm(),
style: Object.assign(
{},
menuItemStyles,
@@ -457,9 +457,7 @@ const mapDispatchToProps = (dispatch) => {
identity,
}))
},
- showNewAccountModal: () => {
- dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
- },
+ showNewAccountPageCreateForm: () => dispatch(actions.showNewAccountPage({ form: 'CREATE' })),
showExportPrivateKeyModal: () => {
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
},
@@ -467,7 +465,7 @@ const mapDispatchToProps = (dispatch) => {
dispatch(actions.showAddTokenPage())
},
addNewAccount: () => dispatch(actions.addNewAccount()),
- showImportPage: () => dispatch(actions.showImportPage()),
+ showNewAccountPageImportForm: () => dispatch(actions.showNewAccountPage({ form: 'IMPORT' })),
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
},
}
diff --git a/ui/app/components/mascot.js b/ui/app/components/mascot.js
index 973ec2cad..3b0d3e31b 100644
--- a/ui/app/components/mascot.js
+++ b/ui/app/components/mascot.js
@@ -7,13 +7,13 @@ const debounce = require('debounce')
module.exports = Mascot
inherits(Mascot, Component)
-function Mascot () {
+function Mascot ({width = '200', height = '200'}) {
Component.call(this)
this.logo = metamaskLogo({
followMouse: true,
pxNotRatio: true,
- width: 200,
- height: 200,
+ width,
+ height,
})
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js
index 4bf671834..c1f7a3236 100644
--- a/ui/app/components/modals/account-details-modal.js
+++ b/ui/app/components/modals/account-details-modal.js
@@ -62,12 +62,12 @@ AccountDetailsModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('button.btn-clear', {
+ h('button.btn-clear.account-modal__button', {
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
}, 'View account on Etherscan'),
// Holding on redesign for Export Private Key functionality
- h('button.btn-clear', {
+ h('button.btn-clear.account-modal__button', {
onClick: () => showExportPrivateKeyModal(),
}, 'Export private key'),
diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js
index d735983f9..74a7a847e 100644
--- a/ui/app/components/modals/buy-options-modal.js
+++ b/ui/app/components/modals/buy-options-modal.js
@@ -69,7 +69,7 @@ BuyOptions.prototype.render = function () {
// h('div.buy-modal-content-option', {}, [
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
- // ]),
+ // ]),,
this.renderModalContentOption(
'Direct Deposit',
diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js
new file mode 100644
index 000000000..532d66653
--- /dev/null
+++ b/ui/app/components/modals/deposit-ether-modal.js
@@ -0,0 +1,184 @@
+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 networkNames = require('../../../../app/scripts/config.js').networkNames
+const ShapeshiftForm = require('../shapeshift-form')
+
+const DIRECT_DEPOSIT_ROW_TITLE = 'Directly Deposit Ether'
+const DIRECT_DEPOSIT_ROW_TEXT = `If you already have some Ether, the quickest way to get Ether in
+your new wallet by direct deposit.`
+const COINBASE_ROW_TITLE = 'Buy on Coinbase'
+const COINBASE_ROW_TEXT = `Coinbase is the world’s most popular way to buy and sell bitcoin,
+ethereum, and litecoin.`
+const SHAPESHIFT_ROW_TITLE = 'Deposit with ShapeShift'
+const SHAPESHIFT_ROW_TEXT = `If you own other cryptocurrencies, you can trade and deposit Ether
+directly into your MetaMask wallet. No Account Needed.`
+const FAUCET_ROW_TITLE = 'Test Faucet'
+const facuetRowText = networkName => `Get Ether from a faucet for the ${networkName}`
+
+function mapStateToProps (state) {
+ return {
+ network: state.metamask.network,
+ address: state.metamask.selectedAddress,
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ toCoinbase: (address) => {
+ dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
+ },
+ hideModal: () => {
+ dispatch(actions.hideModal())
+ },
+ showAccountDetailModal: () => {
+ dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
+ },
+ toFaucet: network => dispatch(actions.buyEth({ network })),
+ }
+}
+
+inherits(DepositEtherModal, Component)
+function DepositEtherModal () {
+ Component.call(this)
+
+ this.state = {
+ buyingWithShapeshift: false,
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(DepositEtherModal)
+
+DepositEtherModal.prototype.renderRow = function ({
+ logo,
+ title,
+ text,
+ buttonLabel,
+ onButtonClick,
+ hide,
+ className,
+ hideButton,
+ hideTitle,
+ onBackClick,
+ showBackButton,
+}) {
+ if (hide) {
+ return null
+ }
+
+ return h('div', {
+ className: className || 'deposit-ether-modal__buy-row',
+ }, [
+
+ onBackClick && showBackButton && h('div.deposit-ether-modal__buy-row__back', {
+ onClick: onBackClick,
+ }, [
+
+ h('i.fa.fa-arrow-left.cursor-pointer'),
+
+ ]),
+
+ h('div.deposit-ether-modal__buy-row__logo', [logo]),
+
+ h('div.deposit-ether-modal__buy-row__description', [
+
+ !hideTitle && h('div.deposit-ether-modal__buy-row__description__title', [title]),
+
+ h('div.deposit-ether-modal__buy-row__description__text', [text]),
+
+ ]),
+
+ !hideButton && h('div.deposit-ether-modal__buy-row__button', [
+ h('button.deposit-ether-modal__deposit-button', {
+ onClick: onButtonClick,
+ }, [buttonLabel]),
+ ]),
+
+ ])
+}
+
+DepositEtherModal.prototype.render = function () {
+ const { network, toCoinbase, address, toFaucet } = this.props
+ const { buyingWithShapeshift } = this.state
+
+ const isTestNetwork = ['3', '4', '42'].find(n => n === network)
+ const networkName = networkNames[network]
+
+ return h('div.deposit-ether-modal', {}, [
+
+ h('div.deposit-ether-modal__header', [
+
+ h('div.deposit-ether-modal__header__title', ['Deposit Ether']),
+
+ h('div.deposit-ether-modal__header__description', [
+ 'To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet.',
+ ]),
+
+ h('div.deposit-ether-modal__header__close', {
+ onClick: () => {
+ this.setState({ buyingWithShapeshift: false })
+ this.props.hideModal()
+ },
+ }),
+
+ ]),
+
+ h('div.deposit-ether-modal__buy-rows', [
+
+ this.renderRow({
+ logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }),
+ title: DIRECT_DEPOSIT_ROW_TITLE,
+ text: DIRECT_DEPOSIT_ROW_TEXT,
+ buttonLabel: 'View Account',
+ onButtonClick: () => this.goToAccountDetailsModal(),
+ hide: buyingWithShapeshift,
+ }),
+
+ this.renderRow({
+ logo: h('i.fa.fa-tint.fa-2x'),
+ title: FAUCET_ROW_TITLE,
+ text: facuetRowText(networkName),
+ buttonLabel: 'Get Ether',
+ onButtonClick: () => toFaucet(network),
+ hide: !isTestNetwork || buyingWithShapeshift,
+ }),
+
+ this.renderRow({
+ logo: h('img.deposit-ether-modal__buy-row__coinbase-logo', {
+ src: '../../../images/coinbase logo.png',
+ }),
+ title: COINBASE_ROW_TITLE,
+ text: COINBASE_ROW_TEXT,
+ buttonLabel: 'Continue to Coinbase',
+ onButtonClick: () => toCoinbase(address),
+ hide: isTestNetwork || buyingWithShapeshift,
+ }),
+
+ this.renderRow({
+ logo: h('img.deposit-ether-modal__buy-row__shapeshift-logo', {
+ src: '../../../images/shapeshift logo.png',
+ }),
+ title: SHAPESHIFT_ROW_TITLE,
+ text: SHAPESHIFT_ROW_TEXT,
+ buttonLabel: 'Buy with Shapeshift',
+ onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
+ hide: isTestNetwork,
+ hideButton: buyingWithShapeshift,
+ hideTitle: buyingWithShapeshift,
+ onBackClick: () => this.setState({ buyingWithShapeshift: false }),
+ showBackButton: this.state.buyingWithShapeshift,
+ className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
+ }),
+
+ buyingWithShapeshift && h(ShapeshiftForm),
+
+ ]),
+ ])
+}
+
+DepositEtherModal.prototype.goToAccountDetailsModal = function () {
+ this.props.hideModal()
+ this.props.showAccountDetailModal()
+}
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 193755df5..422f23f44 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -79,11 +79,15 @@ ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, lab
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
return h('div.export-private-key-buttons', {}, [
- !privateKey && this.renderButton('btn-clear btn-cancel', () => hideModal(), 'Cancel'),
+ !privateKey && this.renderButton(
+ 'btn-cancel export-private-key__button export-private-key__button--cancel',
+ () => hideModal(),
+ 'Cancel'
+ ),
(privateKey
- ? this.renderButton('btn-clear', () => hideModal(), 'Done')
- : this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Show')
+ ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), 'Done')
+ : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Confirm')
),
])
diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js
index fa3ad0b1e..56c7ba299 100644
--- a/ui/app/components/modals/hide-token-confirmation-modal.js
+++ b/ui/app/components/modals/hide-token-confirmation-modal.js
@@ -58,12 +58,12 @@ HideTokenConfirmationModal.prototype.render = function () {
]),
h('div.hide-token-confirmation__buttons', {}, [
- h('button.btn-clear', {
+ h('button.btn-cancel.hide-token-confirmation__button', {
onClick: () => hideModal(),
}, [
'CANCEL',
]),
- h('button.btn-clear', {
+ h('button.btn-clear.hide-token-confirmation__button', {
onClick: () => hideToken(address),
}, [
'HIDE',
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 2ff6accaa..afb2a2175 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -9,6 +9,7 @@ const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-n
// Modal Components
const BuyOptions = require('./buy-options-modal')
+const DepositEtherModal = require('./deposit-ether-modal')
const AccountDetailsModal = require('./account-details-modal')
const EditAccountNameModal = require('./edit-account-name-modal')
const ExportPrivateKeyModal = require('./export-private-key-modal')
@@ -73,6 +74,37 @@ const MODALS = {
},
},
+ DEPOSIT_ETHER: {
+ contents: [
+ h(DepositEtherModal, {}, []),
+ ],
+ mobileModalStyle: {
+ width: '100%',
+ height: '100%',
+ transform: 'none',
+ left: '0',
+ right: '0',
+ margin: '0 auto',
+ boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)',
+ top: '0',
+ display: 'flex',
+ },
+ laptopModalStyle: {
+ width: '900px',
+ maxWidth: '900px',
+ top: 'calc(10% + 10px)',
+ left: '0',
+ right: '0',
+ margin: '0 auto',
+ boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)',
+ borderRadius: '8px',
+ transform: 'none',
+ },
+ contentStyle: {
+ borderRadius: '8px',
+ },
+ },
+
EDIT_ACCOUNT_NAME: {
contents: [
h(EditAccountNameModal, {}, []),
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 5a8d0763d..3f147159b 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -39,7 +39,6 @@ Network.prototype.render = function () {
},
src: 'images/loading.svg',
}),
- h('i.fa.fa-caret-down.network-caret'),
])
} else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network'
@@ -85,12 +84,8 @@ Network.prototype.render = function () {
backgroundColor: '#038789', // $blue-lagoon
nonSelectBackgroundColor: '#15afb2',
}),
- h('.network-name', {
- style: {
- color: '#039396',
- }},
- 'Main Network'),
- h('i.fa.fa-caret-down.fa-lg.network-caret'),
+ h('.network-name', 'Main Network'),
+ h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'ropsten-test-network':
return h('.network-indicator', [
@@ -98,12 +93,8 @@ Network.prototype.render = function () {
backgroundColor: '#e91550', // $crimson
nonSelectBackgroundColor: '#ec2c50',
}),
- h('.network-name', {
- style: {
- color: '#ff6666',
- }},
- 'Ropsten Test Net'),
- h('i.fa.fa-caret-down.fa-lg.network-caret'),
+ h('.network-name', 'Ropsten Test Net'),
+ h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'kovan-test-network':
return h('.network-indicator', [
@@ -111,12 +102,8 @@ Network.prototype.render = function () {
backgroundColor: '#690496', // $purple
nonSelectBackgroundColor: '#b039f3',
}),
- h('.network-name', {
- style: {
- color: '#690496',
- }},
- 'Kovan Test Net'),
- h('i.fa.fa-caret-down.fa-lg.network-caret'),
+ h('.network-name', 'Kovan Test Net'),
+ h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'rinkeby-test-network':
return h('.network-indicator', [
@@ -124,12 +111,8 @@ Network.prototype.render = function () {
backgroundColor: '#ebb33f', // $tulip-tree
nonSelectBackgroundColor: '#ecb23e',
}),
- h('.network-name', {
- style: {
- color: '#e7a218',
- }},
- 'Rinkeby Test Net'),
- h('i.fa.fa-caret-down.fa-lg.network-caret'),
+ h('.network-name', 'Rinkeby Test Net'),
+ h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
default:
return h('.network-indicator', [
@@ -140,12 +123,8 @@ Network.prototype.render = function () {
},
}),
- h('.network-name', {
- style: {
- color: '#AEAEAE',
- }},
- 'Private Network'),
- h('i.fa.fa-caret-down.fa-lg.network-caret'),
+ h('.network-name', 'Private Network'),
+ h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
}
})(),
diff --git a/ui/app/components/pages/add-token.js b/ui/app/components/pages/add-token.js
index 7f0c37475..0454ee2f1 100644
--- a/ui/app/components/pages/add-token.js
+++ b/ui/app/components/pages/add-token.js
@@ -3,6 +3,7 @@ const Component = require('react').Component
const classnames = require('classnames')
const h = require('react-hyperscript')
const connect = require('react-redux').connect
+const R = require('ramda')
const Fuse = require('fuse.js')
const contractMap = require('eth-contract-metadata')
const TokenBalance = require('../../components/token-balance')
@@ -17,7 +18,10 @@ const fuse = new Fuse(contractList, {
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
- keys: ['address', 'name', 'symbol'],
+ keys: [
+ { name: 'name', weight: 0.5 },
+ { name: 'symbol', weight: 0.5 },
+ ],
})
// const actions = require('./actions')
const actions = require('../../actions')
@@ -219,9 +223,11 @@ AddTokenScreen.prototype.renderCustomForm = function () {
AddTokenScreen.prototype.renderTokenList = function () {
const { searchQuery = '', selectedTokens } = this.state
- const results = searchQuery
- ? fuse.search(searchQuery) || []
- : contractList
+ const fuseSearchResult = fuse.search(searchQuery)
+ const addressSearchResult = contractList.filter(token => {
+ return token.address.toLowerCase() === searchQuery.toLowerCase()
+ })
+ const results = [...addressSearchResult, ...fuseSearchResult]
return Array(6).fill(undefined)
.map((_, i) => {
@@ -297,12 +303,12 @@ AddTokenScreen.prototype.renderConfirmation = function () {
]),
]),
h('div.add-token__buttons', [
- h('button.btn-secondary', {
- onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
- }, 'Add Tokens'),
- h('button.btn-tertiary', {
+ h('button.btn-cancel.add-token__button', {
onClick: () => this.setState({ isShowingConfirmation: false }),
}, 'Back'),
+ h('button.btn-clear.add-token__button', {
+ onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
+ }, 'Add Tokens'),
]),
])
)
@@ -347,12 +353,12 @@ AddTokenScreen.prototype.render = function () {
]),
]),
h('div.add-token__buttons', [
- h('button.btn-secondary', {
- onClick: this.onNext,
- }, 'Next'),
- h('button.btn-tertiary', {
+ h('button.btn-cancel.add-token__button', {
onClick: () => history.goBack(),
}, 'Cancel'),
+ h('button.btn-clear.add-token__button', {
+ onClick: this.onNext,
+ }, 'Next'),
]),
])
)
diff --git a/ui/app/components/pages/import-account/json.js b/ui/app/components/pages/import-account/json.js
index 63a44b4b7..c7d232d30 100644
--- a/ui/app/components/pages/import-account/json.js
+++ b/ui/app/components/pages/import-account/json.js
@@ -24,14 +24,7 @@ JsonImportSubview.prototype.render = function () {
const { error } = this.props
return (
- h('div', {
- style: {
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- padding: '5px 15px 0px 15px',
- },
- }, [
+ h('div.new-account-import-form__json', [
h('p', 'Used by a variety of different clients'),
h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'),
@@ -40,28 +33,35 @@ JsonImportSubview.prototype.render = function () {
readAs: 'text',
onLoad: this.onLoad.bind(this),
style: {
- margin: '20px 0px 12px 20px',
+ margin: '20px 0px 12px 34%',
fontSize: '15px',
+ display: 'flex',
+ justifyContent: 'center',
},
}),
- h('input.large-input.letter-spacey', {
+ h('input.new-account-import-form__input-password', {
type: 'password',
placeholder: 'Enter password',
id: 'json-password-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
- style: {
- width: 260,
- marginTop: 12,
- },
}),
- h('button.primary', {
- onClick: this.createNewKeychain.bind(this),
- style: {
- margin: 12,
- },
- }, 'Import'),
+ h('div.new-account-create-form__buttons', {}, [
+
+ h('button.new-account-create-form__button-cancel', {
+ onClick: () => this.props.goHome(),
+ }, [
+ 'CANCEL',
+ ]),
+
+ h('button.new-account-create-form__button-create', {
+ onClick: () => this.createNewKeychain.bind(this),
+ }, [
+ 'IMPORT',
+ ]),
+
+ ]),
error ? h('span.error', error) : null,
])
diff --git a/ui/app/components/pages/import-account/private-key.js b/ui/app/components/pages/import-account/private-key.js
index 217e6d9f9..a236d90ee 100644
--- a/ui/app/components/pages/import-account/private-key.js
+++ b/ui/app/components/pages/import-account/private-key.js
@@ -4,7 +4,7 @@ const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../../actions')
-module.exports = connect(mapStateToProps)(PrivateKeyImportView)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
function mapStateToProps (state) {
return {
@@ -12,45 +12,49 @@ function mapStateToProps (state) {
}
}
+function mapDispatchToProps (dispatch) {
+ return {
+ goHome: () => dispatch(actions.goHome()),
+ importNewAccount: (strategy, [ privateKey ]) => {
+ dispatch(actions.importNewAccount(strategy, [ privateKey ]))
+ },
+ displayWarning: () => dispatch(actions.displayWarning(null)),
+ }
+}
+
inherits(PrivateKeyImportView, Component)
function PrivateKeyImportView () {
Component.call(this)
}
-PrivateKeyImportView.prototype.componentWillUnmount = function () {
- this.props.dispatch(actions.displayWarning(null))
-}
-
PrivateKeyImportView.prototype.render = function () {
- const { error } = this.props
+ const { error, goHome } = this.props
return (
- h('div', {
- style: {
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- padding: '5px 15px 0px 15px',
- },
- }, [
- h('span', 'Paste your private key string here'),
+ h('div.new-account-import-form__private-key', [
+ h('span.new-account-create-form__instruction', 'Paste your private key string here:'),
- h('input.large-input.letter-spacey', {
+ h('input.new-account-import-form__input-password', {
type: 'password',
id: 'private-key-box',
- onKeyPress: this.createKeyringOnEnter.bind(this),
- style: {
- width: 260,
- marginTop: 12,
- },
+ onKeyPress: () => this.createKeyringOnEnter(),
}),
- h('button.primary', {
- onClick: this.createNewKeychain.bind(this),
- style: {
- margin: 12,
- },
- }, 'Import'),
+ h('div.new-account-create-form__buttons', {}, [
+
+ h('button.new-account-create-form__button-cancel', {
+ onClick: () => goHome(),
+ }, [
+ 'CANCEL',
+ ]),
+
+ h('button.new-account-create-form__button-create', {
+ onClick: () => this.createNewKeychain(),
+ }, [
+ 'IMPORT',
+ ]),
+
+ ]),
error ? h('span.error', error) : null,
])
@@ -67,5 +71,6 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
PrivateKeyImportView.prototype.createNewKeychain = function () {
const input = document.getElementById('private-key-box')
const privateKey = input.value
- this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
+
+ this.props.importNewAccount('Private Key', [ privateKey ])
}
diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js
index 5573f2dd2..749da9758 100644
--- a/ui/app/components/pages/keychains/restore-vault.js
+++ b/ui/app/components/pages/keychains/restore-vault.js
@@ -52,7 +52,10 @@ class RestoreVaultPage extends PersistentForm {
// submit
this.props.createNewVaultAndRestore(password, seed)
.then(() => history.push(DEFAULT_ROUTE))
- .catch(({ message }) => this.setState({ error: message }))
+ .catch(({ message }) => {
+ this.setState({ error: message })
+ log.error(message)
+ })
}
render () {
diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js
index 537270dee..fb3f20a95 100644
--- a/ui/app/components/pages/settings/settings.js
+++ b/ui/app/components/pages/settings/settings.js
@@ -11,6 +11,7 @@ const { exportAsFile } = require('../../../util')
const SimpleDropdown = require('../../dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button')
const { REVEAL_SEED_ROUTE } = require('../../../routes')
+const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
@@ -230,18 +231,18 @@ class Settings extends Component {
}
render () {
- const { warning } = this.props
+ const { warning, isMascara } = this.props
return (
h('div.settings__content', [
warning && h('div.settings__error', warning),
- this.renderBlockieOptIn(),
this.renderCurrentConversion(),
// this.renderCurrentProvider(),
this.renderNewRpcUrl(),
this.renderStateLogs(),
this.renderSeedWords(),
- this.renderOldUI(),
+ !isMascara && this.renderOldUI(),
+ this.renderBlockieOptIn(),
])
)
}
@@ -257,12 +258,14 @@ Settings.propTypes = {
setFeatureFlagToBeta: PropTypes.func,
warning: PropTypes.string,
history: PropTypes.object,
+ isMascara: PropTypes.bool,
}
const mapStateToProps = state => {
return {
metamask: state.metamask,
warning: state.appState.warning,
+ isMascara: state.metamask.isMascara,
}
}
@@ -273,7 +276,10 @@ const mapDispatchToProps = dispatch => {
displayWarning: warning => dispatch(actions.displayWarning(warning)),
revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()),
setUseBlockie: value => dispatch(actions.setUseBlockie(value)),
- setFeatureFlagToBeta: () => dispatch(actions.setFeatureFlag('betaUI', false)),
+ setFeatureFlagToBeta: () => {
+ return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
+ .then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
+ },
}
}
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 8a167f7cd..e63415194 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -231,8 +231,8 @@ ConfirmSendEther.prototype.render = function () {
// Main Send token Card
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
h('h3.flex-center.confirm-screen-header', [
- h('button.confirm-screen-back-button', {
- onClick: () => this.editTransaction(txMeta),
+ h('button.btn-clear.confirm-screen-back-button', {
+ onClick: () => editTransaction(txMeta),
}, 'EDIT'),
h('div.confirm-screen-title', 'Confirm Transaction'),
h('div.confirm-screen-header-tip'),
@@ -433,7 +433,9 @@ ConfirmSendEther.prototype.onSubmit = function (event) {
ConfirmSendEther.prototype.cancel = function (event, txMeta) {
event.preventDefault()
- this.props.cancelTransaction(txMeta)
+ const { cancelTransaction } = this.props
+
+ cancelTransaction(txMeta)
.then(() => this.props.history.push(DEFAULT_ROUTE))
}
@@ -458,26 +460,6 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
- if (props.send.editingTransactionId) {
- const {
- send: {
- memo,
- amount: value,
- gasLimit: gas,
- gasPrice,
- },
- } = props
- const { txParams: { from, to } } = txData
- txData.txParams = {
- from: ethUtil.addHexPrefix(from),
- to: ethUtil.addHexPrefix(to),
- memo: memo && ethUtil.addHexPrefix(memo),
- value: ethUtil.addHexPrefix(value),
- gas: ethUtil.addHexPrefix(gas),
- gasPrice: ethUtil.addHexPrefix(gasPrice),
- }
- }
-
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index 8087e431f..da3a92fa8 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -4,7 +4,6 @@ const { withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const ethAbi = require('ethereumjs-abi')
const tokenAbi = require('human-standard-token-abi')
const abiDecoder = require('abi-decoder')
abiDecoder.addABI(tokenAbi)
@@ -305,6 +304,7 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
}
ConfirmSendToken.prototype.render = function () {
+ const { editTransaction } = this.props
const txMeta = this.gatherTxMeta()
const {
from: {
@@ -326,8 +326,8 @@ ConfirmSendToken.prototype.render = function () {
// Main Send token Card
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
h('h3.flex-center.confirm-screen-header', [
- h('button.confirm-screen-back-button', {
- onClick: () => this.editTransaction(txMeta),
+ h('button.btn-clear.confirm-screen-back-button', {
+ onClick: () => editTransaction(txMeta),
}, 'EDIT'),
h('div.confirm-screen-title', 'Confirm Transaction'),
h('div.confirm-screen-header-tip'),
@@ -426,7 +426,9 @@ ConfirmSendToken.prototype.onSubmit = function (event) {
ConfirmSendToken.prototype.cancel = function (event, txMeta) {
event.preventDefault()
- this.props.cancelTransaction(txMeta)
+ const { cancelTransaction } = this.props
+
+ cancelTransaction(txMeta)
.then(() => this.props.history.push(DEFAULT_ROUTE))
}
@@ -451,39 +453,6 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
- if (props.send.editingTransactionId) {
- const {
- send: {
- memo,
- amount,
- gasLimit: gas,
- gasPrice,
- to,
- },
- } = props
-
- const { txParams: { from, to: tokenAddress } } = txData
-
- const tokenParams = {
- from: ethUtil.addHexPrefix(from),
- value: '0',
- gas: ethUtil.addHexPrefix(gas),
- gasPrice: ethUtil.addHexPrefix(gasPrice),
- }
-
- const data = '0xa9059cbb' + Array.prototype.map.call(
- ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
- x => ('00' + x.toString(16)).slice(-2)
- ).join('')
-
- txData.txParams = {
- ...tokenParams,
- to: ethUtil.addHexPrefix(tokenAddress),
- memo: memo && ethUtil.addHexPrefix(memo),
- data,
- }
- }
-
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
index 806c33f0a..0c4c3f7a9 100644
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ b/ui/app/components/send/gas-fee-display-v2.js
@@ -32,8 +32,9 @@ GasFeeDisplay.prototype.render = function () {
})
: h('div.currency-display', 'Loading...'),
- h('div.send-v2__sliders-icon-container', {
+ h('button.send-v2__sliders-icon-container', {
onClick,
+ disabled: !gasTotal,
}, [
h('i.fa.fa-sliders.send-v2__sliders-icon'),
]),
diff --git a/ui/app/components/send/send-constants.js b/ui/app/components/send/send-constants.js
index 9c240972f..b3ee0899a 100644
--- a/ui/app/components/send/send-constants.js
+++ b/ui/app/components/send/send-constants.js
@@ -20,6 +20,8 @@ const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
multiplierBase: 16,
})
+const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb'
+
module.exports = {
MIN_GAS_PRICE_GWEI,
MIN_GAS_PRICE_HEX,
@@ -27,4 +29,5 @@ module.exports = {
MIN_GAS_LIMIT_HEX,
MIN_GAS_LIMIT_DEC,
MIN_GAS_TOTAL,
+ TOKEN_TRANSFER_FUNCTION_SIGNATURE,
}
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index 5998bd252..1ebbe597c 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -55,6 +55,8 @@ function mapStateToProps (state) {
data,
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
tokenContract: getSelectedTokenContract(state),
+ unapprovedTxs: state.metamask.unapprovedTxs,
+ network: state.metamask.network,
}
}
@@ -69,6 +71,7 @@ function mapDispatchToProps (dispatch) {
),
signTx: txParams => dispatch(actions.signTx(txParams)),
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
+ updateTx: txData => dispatch(actions.updateTransaction(txData)),
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
@@ -82,7 +85,6 @@ function mapDispatchToProps (dispatch) {
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
goHome: () => dispatch(actions.goHome()),
clearSend: () => dispatch(actions.clearSend()),
- backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })),
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
}
}
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index c5993e3d3..2270b8236 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -1,308 +1,242 @@
-const PersistentForm = require('../../lib/persistent-form')
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const Component = require('react').Component
const connect = require('react-redux').connect
-const actions = require('../actions')
-const Qr = require('./qr-code')
-const isValidAddress = require('../util').isValidAddress
-module.exports = connect(mapStateToProps)(ShapeshiftForm)
+const classnames = require('classnames')
+const { qrcode } = require('qrcode-npm')
+const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
+const { isValidAddress } = require('../util')
+const SimpleDropdown = require('./dropdowns/simple-dropdown')
function mapStateToProps (state) {
+ const {
+ coinOptions,
+ tokenExchangeRates,
+ selectedAddress,
+ } = state.metamask
+
return {
- warning: state.appState.warning,
- isSubLoading: state.appState.isSubLoading,
- qrRequested: state.appState.qrRequested,
+ coinOptions,
+ tokenExchangeRates,
+ selectedAddress,
}
}
-inherits(ShapeshiftForm, PersistentForm)
+function mapDispatchToProps (dispatch) {
+ return {
+ shapeShiftSubview: () => dispatch(shapeShiftSubview()),
+ pairUpdate: coin => dispatch(pairUpdate(coin)),
+ buyWithShapeShift: data => dispatch(buyWithShapeShift(data)),
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftForm)
+inherits(ShapeshiftForm, Component)
function ShapeshiftForm () {
- PersistentForm.call(this)
- this.persistentFormParentId = 'shapeshift-buy-form'
+ Component.call(this)
+
+ this.state = {
+ depositCoin: 'btc',
+ refundAddress: '',
+ showQrCode: false,
+ depositAddress: '',
+ errorMessage: '',
+ isLoading: false,
+ bought: false,
+ }
}
-ShapeshiftForm.prototype.render = function () {
- return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
+ShapeshiftForm.prototype.componentWillMount = function () {
+ this.props.shapeShiftSubview()
}
-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: {
- position: 'relative',
- padding: '25px',
- paddingTop: '5px',
- width: '90%',
- minHeight: '215px',
- alignItems: 'center',
- overflowY: 'auto',
- },
- }, [
- h('.flex-row', {
- style: {
- justifyContent: 'center',
- alignItems: 'baseline',
- height: '42px',
- },
- }, [
- h('img', {
- src: coinOptions[coin].image,
- width: '25px',
- height: '25px',
- style: {
- marginRight: '5px',
- },
- }),
-
- h('.input-container', {
- position: 'relative',
- }, [
- h('input#fromCoin.buy-inputs.ex-coins', {
- type: 'text',
- list: 'coinList',
- autoFocus: true,
- dataset: {
- persistentFormId: 'input-coin',
- },
- style: {
- boxSizing: 'border-box',
- },
- onChange: this.handleLiveInput.bind(this),
- defaultValue: 'BTC',
- }),
+ShapeshiftForm.prototype.onCoinChange = function (e) {
+ const coin = e.target.value
+ this.setState({
+ depositCoin: coin,
+ errorMessage: '',
+ })
+ this.props.pairUpdate(coin)
+}
- this.renderCoinList(),
+ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
+ this.setState({
+ isLoading: true,
+ showQrCode: true,
+ })
- h('i.fa.fa-pencil-square-o.edit-text', {
- style: {
- fontSize: '12px',
- color: '#F7861C',
- position: 'absolute',
- },
- }),
- ]),
+ const {
+ buyWithShapeShift,
+ selectedAddress: withdrawal,
+ } = this.props
+ const {
+ refundAddress: returnAddress,
+ depositCoin,
+ } = this.state
+ const pair = `${depositCoin}_eth`
+ const data = {
+ withdrawal,
+ pair,
+ returnAddress,
+ // Public api key
+ 'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6',
+ }
- h('.icon-control', {
- style: {
- position: 'relative',
- },
- }, [
- // Not visible on the screen, can't see it on master.
-
- // h('i.fa.fa-refresh.fa-4.orange', {
- // style: {
- // bottom: '5px',
- // left: '5px',
- // color: '#F7861C',
- // },
- // onClick: this.updateCoin.bind(this),
- // }),
- h('i.fa.fa-chevron-right.fa-4.orange', {
- style: {
- position: 'absolute',
- bottom: '35%',
- left: '0%',
- color: '#F7861C',
- },
- onClick: this.updateCoin.bind(this),
- }),
- ]),
+ if (isValidAddress(withdrawal)) {
+ buyWithShapeShift(data)
+ .then(d => this.setState({
+ showQrCode: true,
+ depositAddress: d.deposit,
+ isLoading: false,
+ }))
+ .catch(() => this.setState({
+ showQrCode: false,
+ errorMessage: 'Invalid Request',
+ isLoading: false,
+ }))
+ }
+}
- h('#toCoin.ex-coins', marketinfo.pair.split('_')[1].toUpperCase()),
+ShapeshiftForm.prototype.renderMetadata = function (label, value) {
+ return h('div', {className: 'shapeshift-form__metadata-wrapper'}, [
- h('img', {
- src: coinOptions[marketinfo.pair.split('_')[1].toUpperCase()].image,
- width: '25px',
- height: '25px',
- style: {
- marginLeft: '5px',
- },
- }),
+ h('div.shapeshift-form__metadata-label', {}, [
+ h('span', `${label}:`),
]),
- h('.flex-column', {
- style: {
- marginTop: '1%',
- 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(),
-
- this.renderRefundAddressForCoin(coin),
+ h('div.shapeshift-form__metadata-value', {}, [
+ h('span', value),
]),
])
}
-ShapeshiftForm.prototype.renderRefundAddressForCoin = function (coin) {
- return h(this.activeToggle('.input-container'), {
- style: {
- marginTop: '1%',
- },
- }, [
-
- 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: '227px',
- height: '30px',
- padding: ' 5px ',
- },
- }),
-
- h('i.fa.fa-pencil-square-o.edit-text', {
- style: {
- fontSize: '12px',
- color: '#F7861C',
- position: 'absolute',
- },
- }),
- h('div.flex-row', {
- style: {
- justifyContent: 'flex-start',
- },
- }, [
- h('button', {
- onClick: this.shift.bind(this),
- style: {
- marginTop: '1%',
- },
- },
- 'Submit'),
- ]),
+ShapeshiftForm.prototype.renderMarketInfo = function () {
+ const { depositCoin } = this.state
+ const coinPair = `${depositCoin}_eth`
+ const { tokenExchangeRates } = this.props
+ const {
+ limit,
+ rate,
+ minimum,
+ } = tokenExchangeRates[coinPair] || {}
+
+ return h('div.shapeshift-form__metadata', {}, [
+
+ this.renderMetadata('Status', limit ? 'Available' : 'Unavailable'),
+ this.renderMetadata('Limit', limit),
+ this.renderMetadata('Exchange Rate', rate),
+ this.renderMetadata('Minimum', minimum),
+
])
}
-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.renderQrCode = function () {
+ const { depositAddress, isLoading } = this.state
+ const qrImage = qrcode(4, 'M')
+ qrImage.addData(depositAddress)
+ qrImage.make()
-ShapeshiftForm.prototype.renderCoinList = function () {
- var list = Object.keys(this.props.buyView.formView.coinOptions).map((item) => {
- return h('option', {
- value: item,
- }, item)
- })
+ return h('div.shapeshift-form', {}, [
- return h('datalist#coinList', {
- onClick: (event) => {
- event.preventDefault()
- },
- }, list)
-}
+ h('div.shapeshift-form__deposit-instruction', [
+ 'Deposit your BTC to the address below:',
+ ]),
-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.displayWarning(message))
- } else {
- return props.dispatch(actions.pairUpdate(coin))
- }
-}
+ h('div', depositAddress),
-ShapeshiftForm.prototype.handleLiveInput = function () {
- const props = this.props
- var coinOptions = this.props.buyView.formView.coinOptions
- var coin = document.getElementById('fromCoin').value
+ h('div.shapeshift-form__qr-code', [
+ isLoading
+ ? h('img', {
+ src: 'images/loading.svg',
+ style: { width: '60px'},
+ })
+ : h('div', {
+ dangerouslySetInnerHTML: { __html: qrImage.createTableTag(4) },
+ }),
+ ]),
- if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') {
- return null
- } else {
- return props.dispatch(actions.pairUpdate(coin))
- }
-}
+ this.renderMarketInfo(),
-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: {
- },
- }, [
- h('h3.flex-row.text-transform-uppercase', {
- style: {
- color: '#868686',
- paddingTop: '4px',
- justifyContent: 'space-around',
- textAlign: 'center',
- fontSize: '17px',
- },
- }, `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.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',
- }),
- ])
+ShapeshiftForm.prototype.render = function () {
+ const { coinOptions, btnClass } = this.props
+ const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state
+ const coinPair = `${depositCoin}_eth`
+ const { tokenExchangeRates } = this.props
+ const token = tokenExchangeRates[coinPair]
+
+ return h('div.shapeshift-form-wrapper', [
+ showQrCode
+ ? this.renderQrCode()
+ : h('div.shapeshift-form', [
+ h('div.shapeshift-form__selectors', [
+
+ h('div.shapeshift-form__selector', [
+
+ h('div.shapeshift-form__selector-label', 'Deposit'),
+
+ h(SimpleDropdown, {
+ selectedOption: this.state.depositCoin,
+ onSelect: this.onCoinChange,
+ options: Object.entries(coinOptions).map(([coin]) => ({
+ value: coin.toLowerCase(),
+ displayValue: coin,
+ })),
+ }),
+
+ ]),
+
+ h('div.icon.shapeshift-form__caret', {
+ style: { backgroundImage: 'url(images/caret-right.svg)'},
+ }),
+
+ h('div.shapeshift-form__selector', [
+
+ h('div.shapeshift-form__selector-label', [
+ 'Receive',
+ ]),
+
+ h('div.shapeshift-form__selector-input', ['ETH']),
+
+ ]),
+
+ ]),
+
+ h('div', {
+ className: classnames('shapeshift-form__address-input-wrapper', {
+ 'shapeshift-form__address-input-wrapper--error': errorMessage,
+ }),
+ }, [
+
+ h('div.shapeshift-form__address-input-label', [
+ 'Your Refund Address',
+ ]),
+
+ h('input.shapeshift-form__address-input', {
+ type: 'text',
+ onChange: e => this.setState({
+ refundAddress: e.target.value,
+ errorMessage: '',
+ }),
+ }),
+
+ h('divshapeshift-form__address-input-error-message', [errorMessage]),
+ ]),
+
+ this.renderMarketInfo(),
+
+ ]),
+
+ !depositAddress && h('button.shapeshift-form__shapeshift-buy-btn', {
+ className: btnClass,
+ disabled: !token,
+ onClick: () => this.onBuyWithShapeShift(),
+ }, ['Buy']),
+
+ ])
}
diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js
index 43973de63..111a77df4 100644
--- a/ui/app/components/shift-list-item.js
+++ b/ui/app/components/shift-list-item.js
@@ -16,6 +16,7 @@ module.exports = connect(mapStateToProps)(ShiftListItem)
function mapStateToProps (state) {
return {
+ selectedAddress: state.metamask.selectedAddress,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
}
@@ -28,36 +29,39 @@ function ShiftListItem () {
}
ShiftListItem.prototype.render = function () {
+ const { selectedAddress, receivingAddress } = this.props
return (
- h('div.tx-list-item.tx-list-clickable', {
- style: {
- paddingTop: '20px',
- paddingBottom: '20px',
- justifyContent: 'space-around',
- alignItems: 'center',
- },
- }, [
- h('div', {
+ selectedAddress === receivingAddress
+ ? h('div.tx-list-item.tx-list-clickable', {
style: {
- width: '0px',
- position: 'relative',
- bottom: '19px',
+ paddingTop: '20px',
+ paddingBottom: '20px',
+ justifyContent: 'space-around',
+ alignItems: 'center',
},
}, [
- h('img', {
- src: 'https://info.shapeshift.io/sites/default/files/logo.png',
+ h('div', {
style: {
- height: '35px',
- width: '132px',
- position: 'absolute',
- clip: 'rect(0px,23px,34px,0px)',
+ 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(),
- ])
+ this.renderInfo(),
+ this.renderUtilComponents(),
+ ])
+ : null
)
}
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index b40c0ec0d..59552f4a0 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -86,7 +86,9 @@ TokenCell.prototype.render = function () {
numberOfDecimals: 2,
conversionRate: currentTokenToFiatRate,
})
- formattedFiat = `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
+ formattedFiat = currentTokenInFiat.toString() === '0'
+ ? ''
+ : `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
}
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
@@ -104,7 +106,7 @@ TokenCell.prototype.render = function () {
h(Identicon, {
className: 'token-list-item__identicon',
- diameter: 45,
+ diameter: 50,
address,
network,
}),
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
index 255f0e5eb..4e3d2cb93 100644
--- a/ui/app/components/transaction-list-item.js
+++ b/ui/app/components/transaction-list-item.js
@@ -1,6 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const connect = require('react-redux').connect
const EthBalance = require('./eth-balance')
const addressSummary = require('../util').addressSummary
@@ -9,18 +10,33 @@ const CopyButton = require('./copyButton')
const vreme = new (require('vreme'))()
const Tooltip = require('./tooltip')
const numberToBN = require('number-to-bn')
+const actions = require('../actions')
const TransactionIcon = require('./transaction-list-item-icon')
const ShiftListItem = require('./shift-list-item')
-module.exports = TransactionListItem
+
+const mapDispatchToProps = dispatch => {
+ return {
+ retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
+ }
+}
+
+module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
inherits(TransactionListItem, Component)
function TransactionListItem () {
Component.call(this)
}
+TransactionListItem.prototype.showRetryButton = function () {
+ const { transaction = {} } = this.props
+ const { status, time } = transaction
+ return status === 'submitted' && Date.now() - time > 30000
+}
+
TransactionListItem.prototype.render = function () {
const { transaction, network, conversionRate, currentCurrency } = this.props
+ const { status } = transaction
if (transaction.key === 'shapeshift') {
if (network === '1') return h(ShiftListItem, transaction)
}
@@ -32,7 +48,7 @@ TransactionListItem.prototype.render = function () {
var isMsg = ('msgParams' in transaction)
var isTx = ('txParams' in transaction)
- var isPending = transaction.status === 'unapproved'
+ var isPending = status === 'unapproved'
let txParams
if (isTx) {
txParams = transaction.txParams
@@ -44,7 +60,7 @@ TransactionListItem.prototype.render = function () {
const isClickable = ('hash' in transaction && isLinkable) || isPending
return (
- h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
+ h('.transaction-list-item.flex-column', {
onClick: (event) => {
if (isPending) {
this.props.showTx(transaction.id)
@@ -56,51 +72,92 @@ TransactionListItem.prototype.render = function () {
},
style: {
padding: '20px 0',
+ alignItems: 'center',
},
}, [
-
- h('.identicon-wrapper.flex-column.flex-center.select-none', [
- h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
+ h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
+ style: {
+ width: '100%',
+ },
+ }, [
+ h('.identicon-wrapper.flex-column.flex-center.select-none', [
+ h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
+ ]),
+
+ h(Tooltip, {
+ title: 'Transaction Number',
+ position: 'right',
+ }, [
+ h('span', {
+ style: {
+ display: 'flex',
+ cursor: 'normal',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: '10px',
+ },
+ }, nonce),
+ ]),
+
+ h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
+ domainField(txParams),
+ h('div', date),
+ recipientField(txParams, transaction, isTx, isMsg),
+ ]),
+
+ // Places a copy button if tx is successful, else places a placeholder empty div.
+ transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
+
+ isTx ? h(EthBalance, {
+ value: txParams.value,
+ conversionRate,
+ currentCurrency,
+ width: '55px',
+ shorten: true,
+ showFiat: false,
+ style: {fontSize: '15px'},
+ }) : h('.flex-column'),
]),
- h(Tooltip, {
- title: 'Transaction Number',
- position: 'right',
+ this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
+ onClick: event => {
+ event.stopPropagation()
+ this.resubmit()
+ },
+ style: {
+ height: '22px',
+ borderRadius: '22px',
+ color: '#F9881B',
+ padding: '0 20px',
+ backgroundColor: '#FFE3C9',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ fontSize: '8px',
+ cursor: 'pointer',
+ },
}, [
- h('span', {
+ h('div', {
style: {
- display: 'flex',
- cursor: 'normal',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- padding: '10px',
+ paddingRight: '2px',
},
- }, nonce),
- ]),
-
- h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
- domainField(txParams),
- h('div', date),
- recipientField(txParams, transaction, isTx, isMsg),
+ }, 'Taking too long?'),
+ h('div', {
+ style: {
+ textDecoration: 'underline',
+ },
+ }, 'Retry with a higher gas price here'),
]),
-
- // Places a copy button if tx is successful, else places a placeholder empty div.
- transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
-
- isTx ? h(EthBalance, {
- value: txParams.value,
- conversionRate,
- currentCurrency,
- width: '55px',
- shorten: true,
- showFiat: false,
- style: {fontSize: '15px'},
- }) : h('.flex-column'),
])
)
}
+TransactionListItem.prototype.resubmit = function () {
+ const { transaction } = this.props
+ this.props.retryTransaction(transaction.id)
+}
+
function domainField (txParams) {
return h('div', {
style: {
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 4e76147a1..7ccc5c315 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -170,6 +170,7 @@ TxListItem.prototype.getSendTokenTotal = async function () {
TxListItem.prototype.render = function () {
const {
transactionStatus,
+ transactionAmount,
onClick,
transActionId,
dateString,
@@ -177,6 +178,7 @@ TxListItem.prototype.render = function () {
className,
} = this.props
const { total, fiatTotal } = this.state
+ const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
return h(`div${className || ''}`, {
key: transActionId,
@@ -232,13 +234,9 @@ TxListItem.prototype.render = function () {
style: {},
}, [
- h('span', {
- className: classnames('tx-list-value', {
- 'tx-list-value--confirmed': transactionStatus === 'confirmed',
- }),
- }, total),
+ h('span.tx-list-value', total),
- fiatTotal && h('span.tx-list-fiat-value', fiatTotal),
+ showFiatTotal && h('span.tx-list-fiat-value', fiatTotal),
]),
]),
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index 2c4c3dd31..ca4fd81d6 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -42,23 +42,22 @@ TxList.prototype.componentWillMount = function () {
}
TxList.prototype.render = function () {
- return h('div.flex-column.tx-list-container', {}, [
-
+ return h('div.flex-column', [
h('div.flex-row.tx-list-header-wrapper', [
h('div.flex-row.tx-list-header', [
h('div', 'transactions'),
]),
]),
-
- this.renderTransaction(),
-
+ h('div.flex-column.tx-list-container', {}, [
+ this.renderTransaction(),
+ ]),
])
}
TxList.prototype.renderTransaction = function () {
const { txsToRender, conversionRate } = this.props
return txsToRender.length
- ? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate))
+ ? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate, i))
: [h(
'div.tx-list-item.tx-list-item--empty',
{ key: 'tx-list-none' },
@@ -67,12 +66,16 @@ TxList.prototype.renderTransaction = function () {
}
// TODO: Consider moving TxListItem into a separate component
-TxList.prototype.renderTransactionListItem = function (transaction, conversionRate) {
+TxList.prototype.renderTransactionListItem = function (transaction, conversionRate, index) {
// console.log({transaction})
// refer to transaction-list.js:line 58
if (transaction.key === 'shapeshift') {
- return h(ShiftListItem, transaction)
+ return h('div', {
+ key: `shapeshift${index}`,
+ }, [
+ h(ShiftListItem, transaction),
+ ])
}
const props = {
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 46029166e..60c108c36 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -74,18 +74,14 @@ TxView.prototype.renderButtons = function () {
return !selectedToken
? (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear', {
- style: {
- textAlign: 'center',
- },
+ h('button.btn-clear.hero-balance-button', {
onClick: () => showModal({
- name: 'BUY',
+ name: 'DEPOSIT_ETHER',
}),
}, 'DEPOSIT'),
- h('button.btn-clear', {
+ h('button.btn-clear.hero-balance-button', {
style: {
- textAlign: 'center',
marginLeft: '0.8em',
},
onClick: () => history.push(SEND_ROUTE),
@@ -94,11 +90,7 @@ TxView.prototype.renderButtons = function () {
)
: (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear', {
- style: {
- textAlign: 'center',
- marginLeft: '0.8em',
- },
+ h('button.btn-clear.hero-balance-button', {
onClick: () => history.push(SEND_ROUTE),
}, 'SEND'),
])
@@ -114,7 +106,7 @@ TxView.prototype.render = function () {
h('div.flex-row.phone-visible', {
style: {
- margin: '1em 0.9em',
+ margin: '1.5em 1.2em 0',
justifyContent: 'space-between',
alignItems: 'center',
},
@@ -150,7 +142,7 @@ TxView.prototype.render = function () {
!isMascara && h('div.open-in-browser', {
onClick: () => global.platform.openExtensionInBrowser(),
- }, [h('img', { src: 'images/open.svg' })]),
+ }, [h('img', { src: 'images/popout.svg' })]),
]),
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 72ffc5d65..c7f81a305 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -136,7 +136,7 @@ WalletView.prototype.render = function () {
selectedIdentity.name,
]),
- h('button.wallet-view__details-button', 'DETAILS'),
+ h('button.btn-clear.wallet-view__details-button', 'DETAILS'),
]),
]),
@@ -157,7 +157,7 @@ WalletView.prototype.render = function () {
h(TokenList),
- h('button.wallet-view__add-token-button', {
+ h('button.btn-clear.wallet-view__add-token-button', {
onClick: () => history.push(ADD_TOKEN_ROUTE),
}, 'Add Token'),
])