mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Refactor transactions list views. Add redesign components
This commit is contained in:
parent
d733bd34fb
commit
5ee40675b9
@ -451,6 +451,9 @@
|
|||||||
"hideTokenPrompt": {
|
"hideTokenPrompt": {
|
||||||
"message": "Hide Token?"
|
"message": "Hide Token?"
|
||||||
},
|
},
|
||||||
|
"history": {
|
||||||
|
"message": "History"
|
||||||
|
},
|
||||||
"howToDeposit": {
|
"howToDeposit": {
|
||||||
"message": "How would you like to deposit Ether?"
|
"message": "How would you like to deposit Ether?"
|
||||||
},
|
},
|
||||||
@ -651,7 +654,7 @@
|
|||||||
"message": "No transaction history."
|
"message": "No transaction history."
|
||||||
},
|
},
|
||||||
"noTransactions": {
|
"noTransactions": {
|
||||||
"message": "No Transactions"
|
"message": "You have no transactions"
|
||||||
},
|
},
|
||||||
"notFound": {
|
"notFound": {
|
||||||
"message": "Not Found"
|
"message": "Not Found"
|
||||||
@ -702,6 +705,9 @@
|
|||||||
"pasteSeed": {
|
"pasteSeed": {
|
||||||
"message": "Paste your seed phrase here!"
|
"message": "Paste your seed phrase here!"
|
||||||
},
|
},
|
||||||
|
"pending": {
|
||||||
|
"message": "Pending"
|
||||||
|
},
|
||||||
"personalAddressDetected": {
|
"personalAddressDetected": {
|
||||||
"message": "Personal address detected. Input the token contract address."
|
"message": "Personal address detected. Input the token contract address."
|
||||||
},
|
},
|
||||||
@ -894,6 +900,9 @@
|
|||||||
"sendETH": {
|
"sendETH": {
|
||||||
"message": "Send ETH"
|
"message": "Send ETH"
|
||||||
},
|
},
|
||||||
|
"sendEther": {
|
||||||
|
"message": "Send Ether"
|
||||||
|
},
|
||||||
"sendTokens": {
|
"sendTokens": {
|
||||||
"message": "Send Tokens"
|
"message": "Send Tokens"
|
||||||
},
|
},
|
||||||
|
@ -1,23 +1,35 @@
|
|||||||
|
@import './app-header/index';
|
||||||
|
|
||||||
@import './button-group/index';
|
@import './button-group/index';
|
||||||
|
|
||||||
|
@import './confirm-page-container/index';
|
||||||
|
|
||||||
@import './export-text-container/index';
|
@import './export-text-container/index';
|
||||||
|
|
||||||
@import './selected-account/index';
|
|
||||||
|
|
||||||
@import './info-box/index';
|
@import './info-box/index';
|
||||||
|
|
||||||
@import './network-display/index';
|
@import './menu-bar/index';
|
||||||
|
|
||||||
@import './confirm-page-container/index';
|
@import './modals/index';
|
||||||
|
|
||||||
|
@import './network-display/index';
|
||||||
|
|
||||||
@import './page-container/index';
|
@import './page-container/index';
|
||||||
|
|
||||||
@import './pages/index';
|
@import './pages/index';
|
||||||
|
|
||||||
@import './modals/index';
|
@import './selected-account/index';
|
||||||
|
|
||||||
@import './sender-to-recipient/index';
|
@import './sender-to-recipient/index';
|
||||||
|
|
||||||
@import './tabs/index';
|
@import './tabs/index';
|
||||||
|
|
||||||
@import './app-header/index';
|
@import './token-view/index';
|
||||||
|
|
||||||
|
@import './token-view-balance/index';
|
||||||
|
|
||||||
|
@import './transaction-list/index';
|
||||||
|
|
||||||
|
@import './transaction-list-item/index';
|
||||||
|
|
||||||
|
@import './transaction-status/index';
|
||||||
|
@ -12,12 +12,12 @@ import {
|
|||||||
CONFIRM_TOKEN_METHOD_PATH,
|
CONFIRM_TOKEN_METHOD_PATH,
|
||||||
SIGNATURE_REQUEST_PATH,
|
SIGNATURE_REQUEST_PATH,
|
||||||
} from '../../../routes'
|
} from '../../../routes'
|
||||||
import { isConfirmDeployContract } from './confirm-transaction-switch.util'
|
import { isConfirmDeployContract } from '../../../helpers/transactions.util'
|
||||||
import {
|
import {
|
||||||
TOKEN_METHOD_TRANSFER,
|
TOKEN_METHOD_TRANSFER,
|
||||||
TOKEN_METHOD_APPROVE,
|
TOKEN_METHOD_APPROVE,
|
||||||
TOKEN_METHOD_TRANSFER_FROM,
|
TOKEN_METHOD_TRANSFER_FROM,
|
||||||
} from './confirm-transaction-switch.constants'
|
} from '../../../constants/transactions'
|
||||||
|
|
||||||
export default class ConfirmTransactionSwitch extends Component {
|
export default class ConfirmTransactionSwitch extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export const TOKEN_METHOD_TRANSFER = 'transfer'
|
|
||||||
export const TOKEN_METHOD_APPROVE = 'approve'
|
|
||||||
export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom'
|
|
@ -4,6 +4,7 @@ import Media from 'react-media'
|
|||||||
import { Redirect } from 'react-router-dom'
|
import { Redirect } from 'react-router-dom'
|
||||||
import WalletView from '../../wallet-view'
|
import WalletView from '../../wallet-view'
|
||||||
import TxView from '../../tx-view'
|
import TxView from '../../tx-view'
|
||||||
|
import TokenView from '../../token-view'
|
||||||
import {
|
import {
|
||||||
INITIALIZE_BACKUP_PHRASE_ROUTE,
|
INITIALIZE_BACKUP_PHRASE_ROUTE,
|
||||||
RESTORE_VAULT_ROUTE,
|
RESTORE_VAULT_ROUTE,
|
||||||
@ -14,28 +15,17 @@ import {
|
|||||||
export default class Home extends PureComponent {
|
export default class Home extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
unapprovedTxs: PropTypes.object,
|
|
||||||
unapprovedMsgCount: PropTypes.number,
|
|
||||||
unapprovedPersonalMsgCount: PropTypes.number,
|
|
||||||
unapprovedTypedMessagesCount: PropTypes.number,
|
|
||||||
noActiveNotices: PropTypes.bool,
|
noActiveNotices: PropTypes.bool,
|
||||||
lostAccounts: PropTypes.array,
|
lostAccounts: PropTypes.array,
|
||||||
forgottenPassword: PropTypes.bool,
|
forgottenPassword: PropTypes.bool,
|
||||||
seedWords: PropTypes.string,
|
seedWords: PropTypes.string,
|
||||||
|
unconfirmedTransactionsCount: PropTypes.number,
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const {
|
const { history, unconfirmedTransactionsCount = 0 } = this.props
|
||||||
history,
|
|
||||||
unapprovedTxs = {},
|
|
||||||
unapprovedMsgCount = 0,
|
|
||||||
unapprovedPersonalMsgCount = 0,
|
|
||||||
unapprovedTypedMessagesCount = 0,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
// unapprovedTxs and unapproved messages
|
if (unconfirmedTransactionsCount > 0) {
|
||||||
if (Object.keys(unapprovedTxs).length ||
|
|
||||||
unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) {
|
|
||||||
history.push(CONFIRM_TRANSACTION_ROUTE)
|
history.push(CONFIRM_TRANSACTION_ROUTE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,7 +59,8 @@ export default class Home extends PureComponent {
|
|||||||
query="(min-width: 576px)"
|
query="(min-width: 576px)"
|
||||||
render={() => <WalletView />}
|
render={() => <WalletView />}
|
||||||
/>
|
/>
|
||||||
<TxView />
|
<TokenView />
|
||||||
|
{/* <TxView /> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -2,14 +2,11 @@ import Home from './home.component'
|
|||||||
import { compose } from 'recompose'
|
import { compose } from 'recompose'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { withRouter } from 'react-router-dom'
|
import { withRouter } from 'react-router-dom'
|
||||||
|
import { unconfirmedTransactionsCountSelector } from '../../../selectors/confirm-transaction'
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const { metamask, appState } = state
|
const { metamask, appState } = state
|
||||||
const {
|
const {
|
||||||
unapprovedTxs = {},
|
|
||||||
unapprovedMsgCount = 0,
|
|
||||||
unapprovedPersonalMsgCount = 0,
|
|
||||||
unapprovedTypedMessagesCount = 0,
|
|
||||||
noActiveNotices,
|
noActiveNotices,
|
||||||
lostAccounts,
|
lostAccounts,
|
||||||
seedWords,
|
seedWords,
|
||||||
@ -17,14 +14,11 @@ const mapStateToProps = state => {
|
|||||||
const { forgottenPassword } = appState
|
const { forgottenPassword } = appState
|
||||||
|
|
||||||
return {
|
return {
|
||||||
unapprovedTxs,
|
|
||||||
unapprovedMsgCount,
|
|
||||||
unapprovedPersonalMsgCount,
|
|
||||||
unapprovedTypedMessagesCount,
|
|
||||||
noActiveNotices,
|
noActiveNotices,
|
||||||
lostAccounts,
|
lostAccounts,
|
||||||
forgottenPassword,
|
forgottenPassword,
|
||||||
seedWords,
|
seedWords,
|
||||||
|
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const TokenTracker = require('eth-token-tracker')
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const selectors = require('../selectors')
|
|
||||||
const log = require('loglevel')
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
userAddress: selectors.getSelectedAddress(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(TokenBalance)
|
|
||||||
|
|
||||||
|
|
||||||
inherits(TokenBalance, Component)
|
|
||||||
function TokenBalance () {
|
|
||||||
this.state = {
|
|
||||||
string: '',
|
|
||||||
symbol: '',
|
|
||||||
isLoading: true,
|
|
||||||
error: null,
|
|
||||||
}
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenBalance.prototype.render = function () {
|
|
||||||
const state = this.state
|
|
||||||
const { symbol, string, isLoading } = state
|
|
||||||
const { balanceOnly } = this.props
|
|
||||||
|
|
||||||
return isLoading
|
|
||||||
? h('span', '')
|
|
||||||
: h('span.token-balance', [
|
|
||||||
h('span.hide-text-overflow.token-balance__amount', string),
|
|
||||||
!balanceOnly && h('span.token-balance__symbol', symbol),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenBalance.prototype.componentDidMount = function () {
|
|
||||||
this.createFreshTokenTracker()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenBalance.prototype.createFreshTokenTracker = function () {
|
|
||||||
if (this.tracker) {
|
|
||||||
// Clean up old trackers when refreshing:
|
|
||||||
this.tracker.stop()
|
|
||||||
this.tracker.removeListener('update', this.balanceUpdater)
|
|
||||||
this.tracker.removeListener('error', this.showError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!global.ethereumProvider) return
|
|
||||||
const { userAddress, token } = this.props
|
|
||||||
|
|
||||||
this.tracker = new TokenTracker({
|
|
||||||
userAddress,
|
|
||||||
provider: global.ethereumProvider,
|
|
||||||
tokens: [token],
|
|
||||||
pollingInterval: 8000,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// Set up listener instances for cleaning up
|
|
||||||
this.balanceUpdater = this.updateBalance.bind(this)
|
|
||||||
this.showError = error => {
|
|
||||||
this.setState({ error, isLoading: false })
|
|
||||||
}
|
|
||||||
this.tracker.on('update', this.balanceUpdater)
|
|
||||||
this.tracker.on('error', this.showError)
|
|
||||||
|
|
||||||
this.tracker.updateBalances()
|
|
||||||
.then(() => {
|
|
||||||
this.updateBalance(this.tracker.serialize())
|
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
log.error(`Problem updating balances`, reason)
|
|
||||||
this.setState({ isLoading: false })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenBalance.prototype.componentDidUpdate = function (nextProps) {
|
|
||||||
const {
|
|
||||||
userAddress: oldAddress,
|
|
||||||
token: { address: oldTokenAddress },
|
|
||||||
} = this.props
|
|
||||||
const {
|
|
||||||
userAddress: newAddress,
|
|
||||||
token: { address: newTokenAddress },
|
|
||||||
} = nextProps
|
|
||||||
|
|
||||||
if ((!oldAddress || !newAddress) && (!oldTokenAddress || !newTokenAddress)) return
|
|
||||||
if ((oldAddress === newAddress) && (oldTokenAddress === newTokenAddress)) return
|
|
||||||
|
|
||||||
this.setState({ isLoading: true })
|
|
||||||
this.createFreshTokenTracker()
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenBalance.prototype.updateBalance = function (tokens = []) {
|
|
||||||
if (!this.tracker.running) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const [{ string, symbol }] = tokens
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
string,
|
|
||||||
symbol,
|
|
||||||
isLoading: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenBalance.prototype.componentWillUnmount = function () {
|
|
||||||
if (!this.tracker) return
|
|
||||||
this.tracker.stop()
|
|
||||||
this.tracker.removeListener('update', this.balanceUpdater)
|
|
||||||
this.tracker.removeListener('error', this.showError)
|
|
||||||
}
|
|
||||||
|
|
1
ui/app/components/token-view-balance/index.js
Normal file
1
ui/app/components/token-view-balance/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './token-view-balance.container'
|
66
ui/app/components/token-view-balance/index.scss
Normal file
66
ui/app/components/token-view-balance/index.scss
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
.token-view-balance {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
height: 54px;
|
||||||
|
|
||||||
|
&__balance {
|
||||||
|
margin-left: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
align-items: center;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__primary-balance {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__secondary-balance {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
color: #a0a0a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__balance-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
min-width: initial;
|
||||||
|
width: 100px;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
flex-direction: column;
|
||||||
|
height: initial
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Button from '../button'
|
||||||
|
import Identicon from '../identicon'
|
||||||
|
import TokenBalance from '../token-balance'
|
||||||
|
import { SEND_ROUTE } from '../../routes'
|
||||||
|
import { formatCurrency } from '../../helpers/confirm-transaction/util'
|
||||||
|
|
||||||
|
export default class TokenViewBalance extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
showDepositModal: PropTypes.func,
|
||||||
|
selectedToken: PropTypes.object,
|
||||||
|
history: PropTypes.object,
|
||||||
|
network: PropTypes.string,
|
||||||
|
ethBalance: PropTypes.string,
|
||||||
|
fiatBalance: PropTypes.string,
|
||||||
|
currentCurrency: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBalance () {
|
||||||
|
const { selectedToken, ethBalance, fiatBalance, currentCurrency } = this.props
|
||||||
|
const formattedFiatBalance = formatCurrency(fiatBalance, currentCurrency)
|
||||||
|
|
||||||
|
return selectedToken
|
||||||
|
? (
|
||||||
|
<TokenBalance
|
||||||
|
token={selectedToken}
|
||||||
|
withSymbol
|
||||||
|
className="token-view-balance__primary-balance"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="token-view-balance__balance">
|
||||||
|
<div className="token-view-balance__primary-balance">
|
||||||
|
{ `${ethBalance} ETH` }
|
||||||
|
</div>
|
||||||
|
<div className="token-view-balance__secondary-balance">
|
||||||
|
{ formattedFiatBalance }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButtons () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { selectedToken, showDepositModal, history } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="token-view-balance__buttons">
|
||||||
|
{
|
||||||
|
!selectedToken && (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="token-view-balance__button"
|
||||||
|
onClick={() => showDepositModal()}
|
||||||
|
>
|
||||||
|
{ t('deposit') }
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="token-view-balance__button"
|
||||||
|
onClick={() => history.push(SEND_ROUTE)}
|
||||||
|
>
|
||||||
|
{ t('send') }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { network, selectedToken } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="token-view-balance">
|
||||||
|
<div className="token-view-balance__balance-container">
|
||||||
|
<Identicon
|
||||||
|
diameter={50}
|
||||||
|
address={selectedToken && selectedToken.address}
|
||||||
|
network={network}
|
||||||
|
/>
|
||||||
|
{ this.renderBalance() }
|
||||||
|
</div>
|
||||||
|
{ this.renderButtons() }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import TokenViewBalance from './token-view-balance.component'
|
||||||
|
import { getSelectedToken, getSelectedAddress } from '../../selectors'
|
||||||
|
import { showModal } from '../../actions'
|
||||||
|
import { getValueFromWeiHex } from '../../helpers/confirm-transaction/util'
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
const selectedAddress = getSelectedAddress(state)
|
||||||
|
const { metamask } = state
|
||||||
|
const { network, accounts, currentCurrency, conversionRate } = metamask
|
||||||
|
const account = accounts[selectedAddress]
|
||||||
|
const { balance: value } = account
|
||||||
|
|
||||||
|
const ethBalance = getValueFromWeiHex({
|
||||||
|
value, toCurrency: 'ETH', conversionRate, numberOfDecimals: 3,
|
||||||
|
})
|
||||||
|
|
||||||
|
const fiatBalance = getValueFromWeiHex({
|
||||||
|
value, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedToken: getSelectedToken(state),
|
||||||
|
network,
|
||||||
|
ethBalance,
|
||||||
|
fiatBalance,
|
||||||
|
currentCurrency,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
showDepositModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
)(TokenViewBalance)
|
1
ui/app/components/token-view/index.js
Normal file
1
ui/app/components/token-view/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './token-view.component'
|
27
ui/app/components/token-view/index.scss
Normal file
27
ui/app/components/token-view/index.scss
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
.token-view {
|
||||||
|
flex: 1 1 66.5%;
|
||||||
|
background: $white;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&__balance-wrapper {
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $break-large) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin: 2.3em 2.37em .8em;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
ui/app/components/token-view/token-view.component.js
Normal file
28
ui/app/components/token-view/token-view.component.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Media from 'react-media'
|
||||||
|
import MenuBar from '../menu-bar'
|
||||||
|
import TokenViewBalance from '../token-view-balance'
|
||||||
|
// import TransactionList from '../tx-list'
|
||||||
|
import TransactionList from '../transaction-list'
|
||||||
|
|
||||||
|
export default class TokenView extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className="token-view">
|
||||||
|
<Media
|
||||||
|
query="(max-width: 575px)"
|
||||||
|
render={() => <MenuBar />}
|
||||||
|
/>
|
||||||
|
<div className="token-view__balance-wrapper">
|
||||||
|
<TokenViewBalance />
|
||||||
|
</div>
|
||||||
|
<TransactionList />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
1
ui/app/components/transaction-action/index.js
Normal file
1
ui/app/components/transaction-action/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './transaction-action.container'
|
@ -0,0 +1,52 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { getTransactionActionKey } from '../../helpers/transactions.util'
|
||||||
|
|
||||||
|
export default class TransactionAction extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
tOrDefault: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
transaction: PropTypes.object,
|
||||||
|
methodData: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
transactionAction: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.getTransactionAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate () {
|
||||||
|
this.getTransactionAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
getTransactionAction () {
|
||||||
|
const { transactionAction } = this.state
|
||||||
|
const { transaction, methodData } = this.props
|
||||||
|
const { data, isFetching } = methodData
|
||||||
|
|
||||||
|
if (isFetching || transactionAction) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionKey = getTransactionActionKey(transaction, data)
|
||||||
|
const action = actionKey && this.context.tOrDefault(actionKey)
|
||||||
|
this.setState({ transactionAction: action })
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { className } = this.props
|
||||||
|
const { transactionAction } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
{ transactionAction || '--' }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
import withMethodData from '../../higher-order-components/with-method-data'
|
||||||
|
import TransactionAction from './transaction-action.component'
|
||||||
|
|
||||||
|
export default withMethodData(TransactionAction)
|
1
ui/app/components/transaction-list-item/index.js
Normal file
1
ui/app/components/transaction-list-item/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './transaction-list-item.container'
|
71
ui/app/components/transaction-list-item/index.scss
Normal file
71
ui/app/components/transaction-list-item/index.scss
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
.transaction-list-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 74px;
|
||||||
|
padding: 0 21px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid $geyser;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__identicon-wrapper {
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action-block {
|
||||||
|
padding: 0 8px 0 12px;
|
||||||
|
width: 180px;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
padding: 0 8px;
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action {
|
||||||
|
text-transform: capitalize;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
padding-bottom: 0;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nonce {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: #5e6064;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__transaction-amounts {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__primary-transaction-amount {
|
||||||
|
text-align: end;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
font-size: .75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__secondary-transaction-amount {
|
||||||
|
text-align: end;
|
||||||
|
font-size: .75rem;
|
||||||
|
color: #5e6064;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($alto, .2);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Media from 'react-media'
|
||||||
|
import Identicon from '../identicon'
|
||||||
|
import TransactionStatus from '../transaction-status'
|
||||||
|
import TransactionAction from '../transaction-action'
|
||||||
|
import { formatDate } from '../../util'
|
||||||
|
import prefixForNetwork from '../../../lib/etherscan-prefix-for-network'
|
||||||
|
import { CONFIRM_TRANSACTION_ROUTE } from '../../routes'
|
||||||
|
import { UNAPPROVED_STATUS } from '../../constants/transactions'
|
||||||
|
import { hexToDecimal } from '../../helpers/conversions.util'
|
||||||
|
|
||||||
|
export default class TransactionListItem extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
history: PropTypes.object,
|
||||||
|
methodData: PropTypes.object,
|
||||||
|
transaction: PropTypes.object,
|
||||||
|
ethTransactionAmount: PropTypes.string,
|
||||||
|
fiatDisplayValue: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
const { transaction, history } = this.props
|
||||||
|
const { id, status, hash, metamaskNetworkId } = transaction
|
||||||
|
|
||||||
|
if (status === UNAPPROVED_STATUS) {
|
||||||
|
history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`)
|
||||||
|
} else if (hash) {
|
||||||
|
const prefix = prefixForNetwork(metamaskNetworkId)
|
||||||
|
const etherscanUrl = `https://${prefix}etherscan.io/tx/${hash}`
|
||||||
|
global.platform.openWindow({ url: etherscanUrl })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
transaction,
|
||||||
|
ethTransactionAmount,
|
||||||
|
fiatDisplayValue,
|
||||||
|
} = this.props
|
||||||
|
const { txParams = {} } = transaction
|
||||||
|
const nonce = hexToDecimal(txParams.nonce)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="transaction-list-item"
|
||||||
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
|
<div className="transaction-list-item__identicon-wrapper">
|
||||||
|
<Media query="(max-width: 575px)">
|
||||||
|
{
|
||||||
|
matches => (
|
||||||
|
<Identicon
|
||||||
|
address={txParams.to}
|
||||||
|
diameter={matches ? 26 : 34}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Media>
|
||||||
|
</div>
|
||||||
|
<div className="transaction-list-item__action-block">
|
||||||
|
<TransactionAction
|
||||||
|
transaction={transaction}
|
||||||
|
className="transaction-list-item__action"
|
||||||
|
/>
|
||||||
|
<div className="transaction-list-item__nonce">
|
||||||
|
{ `#${nonce} - ${formatDate(transaction.time)}` }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TransactionStatus status={transaction.status} />
|
||||||
|
<div className="transaction-list-item__transaction-amounts">
|
||||||
|
<div className="transaction-list-item__primary-transaction-amount">
|
||||||
|
{ `-${fiatDisplayValue}` }
|
||||||
|
</div>
|
||||||
|
<div className="transaction-list-item__secondary-transaction-amount">
|
||||||
|
{ `-${ethTransactionAmount} ETH` }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import TransactionListItem from './transaction-list-item.component'
|
||||||
|
import { getEthFromWeiHex, getValueFromWeiHex } from '../../helpers/conversions.util'
|
||||||
|
import { formatCurrency } from '../../helpers/confirm-transaction/util'
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const { metamask } = state
|
||||||
|
const { currentCurrency, conversionRate } = metamask
|
||||||
|
const { transaction: { txParams: { value } = {} } = {} } = ownProps
|
||||||
|
const ethTransactionAmount = getEthFromWeiHex({ value, conversionRate })
|
||||||
|
const fiatTransactionAmount = getValueFromWeiHex({
|
||||||
|
value, conversionRate, toCurrency: currentCurrency, numberOfDecimals: 2,
|
||||||
|
})
|
||||||
|
const fiatFormattedAmount = formatCurrency(fiatTransactionAmount, currentCurrency)
|
||||||
|
const fiatDisplayValue = `${fiatFormattedAmount} ${currentCurrency.toUpperCase()}`
|
||||||
|
|
||||||
|
return {
|
||||||
|
ethTransactionAmount,
|
||||||
|
fiatDisplayValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
connect(mapStateToProps),
|
||||||
|
)(TransactionListItem)
|
1
ui/app/components/transaction-list/index.js
Normal file
1
ui/app/components/transaction-list/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './transaction-list.container'
|
40
ui/app/components/transaction-list/index.scss
Normal file
40
ui/app/components/transaction-list/index.scss
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
.transaction-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: .875rem;
|
||||||
|
color: $dusty-gray;
|
||||||
|
border-bottom: 1px solid $geyser;
|
||||||
|
padding: 16px 0 8px 20px;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
padding: 8px 0 8px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__transactions {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__pending-transactions {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__empty {
|
||||||
|
flex: 1;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 35% 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__empty-text {
|
||||||
|
grid-row-start: 2;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
color: $silver;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import TransactionListItem from '../transaction-list-item'
|
||||||
|
|
||||||
|
export default class TransactionList extends PureComponent {
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
pendingTransactions: [],
|
||||||
|
completedTransactions: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
pendingTransactions: PropTypes.array,
|
||||||
|
completedTransactions: PropTypes.array,
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTransactions () {
|
||||||
|
const { t } = this.context
|
||||||
|
const { pendingTransactions, completedTransactions } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="transaction-list__transactions">
|
||||||
|
{
|
||||||
|
pendingTransactions.length > 0 && (
|
||||||
|
<div className="transaction-list__pending-transactions">
|
||||||
|
<div className="transaction-list__header">
|
||||||
|
{ `${t('pending')} (${pendingTransactions.length})` }
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
pendingTransactions.map(transaction => {
|
||||||
|
return (
|
||||||
|
<TransactionListItem
|
||||||
|
transaction={transaction}
|
||||||
|
key={transaction.id}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div className="transaction-list__completed-transactions">
|
||||||
|
<div className="transaction-list__header">
|
||||||
|
{ t('history') }
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
completedTransactions.length > 0
|
||||||
|
? (
|
||||||
|
completedTransactions.map(transaction => {
|
||||||
|
return (
|
||||||
|
<TransactionListItem
|
||||||
|
transaction={transaction}
|
||||||
|
key={transaction.id}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
: this.renderEmpty()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEmpty () {
|
||||||
|
return (
|
||||||
|
<div className="transaction-list__empty">
|
||||||
|
<div className="transaction-list__empty-text">
|
||||||
|
{ this.context.t('noTransactions') }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className="transaction-list">
|
||||||
|
{
|
||||||
|
this.renderTransactions()
|
||||||
|
// pendingTransactions.length + completedTransactions.length > 0
|
||||||
|
// ? this.renderTransactions()
|
||||||
|
// : this.renderEmpty()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
import { compose } from 'recompose'
|
||||||
|
import TransactionList from './transaction-list.component'
|
||||||
|
import {
|
||||||
|
pendingTransactionsSelector,
|
||||||
|
completedTransactionsSelector,
|
||||||
|
} from '../../selectors/transactions'
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
pendingTransactions: pendingTransactionsSelector(state),
|
||||||
|
completedTransactions: completedTransactionsSelector(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
connect(mapStateToProps)
|
||||||
|
)(TransactionList)
|
@ -10,6 +10,12 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
height: 24px;
|
||||||
|
width: 74px;
|
||||||
|
font-size: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
&--confirmed {
|
&--confirmed {
|
||||||
background-color: #eafad7;
|
background-color: #eafad7;
|
||||||
color: #609a1c;
|
color: #609a1c;
|
||||||
|
@ -15,6 +15,9 @@ const Tooltip = require('./tooltip')
|
|||||||
const TxList = require('./tx-list')
|
const TxList = require('./tx-list')
|
||||||
const SelectedAccount = require('./selected-account')
|
const SelectedAccount = require('./selected-account')
|
||||||
|
|
||||||
|
import Media from 'react-media'
|
||||||
|
import MenuBar from './menu-bar'
|
||||||
|
|
||||||
module.exports = compose(
|
module.exports = compose(
|
||||||
withRouter,
|
withRouter,
|
||||||
connect(mapStateToProps, mapDispatchToProps)
|
connect(mapStateToProps, mapDispatchToProps)
|
||||||
@ -104,49 +107,11 @@ TxView.prototype.renderButtons = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TxView.prototype.render = function () {
|
TxView.prototype.render = function () {
|
||||||
const { hideSidebar, isMascara, showSidebar, sidebarOpen } = this.props
|
return h('div.tx-view.flex-column', [
|
||||||
const { t } = this.context
|
h(Media, {
|
||||||
|
query: '(max-width: 575px)',
|
||||||
return h('div.tx-view.flex-column', {
|
render: () => h(MenuBar),
|
||||||
style: {},
|
}),
|
||||||
}, [
|
|
||||||
|
|
||||||
h('div.flex-row.phone-visible', {
|
|
||||||
style: {
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
flex: '0 0 auto',
|
|
||||||
marginBottom: '16px',
|
|
||||||
padding: '5px',
|
|
||||||
borderBottom: '1px solid #e5e5e5',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h(Tooltip, {
|
|
||||||
title: t('menu'),
|
|
||||||
position: 'bottom',
|
|
||||||
}, [
|
|
||||||
h('div.fa.fa-bars', {
|
|
||||||
style: {
|
|
||||||
fontSize: '1.3em',
|
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '10px',
|
|
||||||
},
|
|
||||||
onClick: () => sidebarOpen ? hideSidebar() : showSidebar(),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h(SelectedAccount),
|
|
||||||
|
|
||||||
!isMascara && h(Tooltip, {
|
|
||||||
title: t('openInTab'),
|
|
||||||
position: 'bottom',
|
|
||||||
}, [
|
|
||||||
h('div.open-in-browser', {
|
|
||||||
onClick: () => global.platform.openExtensionInBrowser(),
|
|
||||||
}, [h('img', { src: 'images/popout.svg' })]),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
this.renderHeroBalance(),
|
this.renderHeroBalance(),
|
||||||
|
|
||||||
|
18
ui/app/constants/transactions.js
Normal file
18
ui/app/constants/transactions.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const UNAPPROVED_STATUS = 'unapproved'
|
||||||
|
export const REJECTED_STATUS = 'rejected'
|
||||||
|
export const APPROVED_STATUS = 'approved'
|
||||||
|
export const SIGNED_STATUS = 'signed'
|
||||||
|
export const SUBMITTED_STATUS = 'submitted'
|
||||||
|
export const CONFIRMED_STATUS = 'confirmed'
|
||||||
|
export const FAILED_STATUS = 'failed'
|
||||||
|
export const DROPPED_STATUS = 'dropped'
|
||||||
|
|
||||||
|
export const TOKEN_METHOD_TRANSFER = 'transfer'
|
||||||
|
export const TOKEN_METHOD_APPROVE = 'approve'
|
||||||
|
export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom'
|
||||||
|
|
||||||
|
export const SEND_ETHER_ACTION_KEY = 'sendEther'
|
||||||
|
export const DEPLOY_CONTRACT_ACTION_KEY = 'contractDeployment'
|
||||||
|
export const APPROVE_ACTION_KEY = 'approve'
|
||||||
|
export const SEND_TOKEN_ACTION_KEY = 'sendToken'
|
||||||
|
export const TRANSFER_FROM_ACTION_KEY = 'transferFrom'
|
@ -1,130 +0,0 @@
|
|||||||
.hero-balance {
|
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
padding-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
margin: 2.3em 2.37em .8em;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-container {
|
|
||||||
display: flex;
|
|
||||||
margin: 0;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
|
||||||
flex-direction: row;
|
|
||||||
flex-grow: 3;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-display {
|
|
||||||
.token-amount {
|
|
||||||
color: $black;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
.token-balance {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
|
||||||
max-width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.token-amount {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
|
|
||||||
.token-balance {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fiat-amount {
|
|
||||||
font-size: 115%;
|
|
||||||
margin-top: 8.5%;
|
|
||||||
color: #a0a0a0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
|
||||||
margin: 0 .8em;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
.token-amount {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fiat-amount {
|
|
||||||
margin-top: .25%;
|
|
||||||
font-size: 105%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media #{$sub-mid-size-breakpoint-range} {
|
|
||||||
margin-left: .4em;
|
|
||||||
margin-right: .4em;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
.token-amount {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fiat-amount {
|
|
||||||
margin-top: .25%;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-balance-buttons {
|
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
|
||||||
width: 100%;
|
|
||||||
// height: 100px; // needed a round number to set the heights of the buttons inside
|
|
||||||
flex: 0 0 auto;
|
|
||||||
padding: 16px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: $break-large) {
|
|
||||||
flex-grow: 2;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-balance-button {
|
|
||||||
min-width: initial;
|
|
||||||
width: 6rem;
|
|
||||||
|
|
||||||
@media #{$sub-mid-size-breakpoint-range} {
|
|
||||||
padding: .4rem;
|
|
||||||
width: 4rem;
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,8 +19,6 @@
|
|||||||
@import './loading-overlay.scss';
|
@import './loading-overlay.scss';
|
||||||
|
|
||||||
// Balances
|
// Balances
|
||||||
@import './hero-balance.scss';
|
|
||||||
|
|
||||||
@import './wallet-balance.scss';
|
@import './wallet-balance.scss';
|
||||||
|
|
||||||
// Tx List and Sections
|
// Tx List and Sections
|
||||||
|
@ -49,13 +49,6 @@ $wallet-view-bg: $alabaster;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.open-in-browser {
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wallet view and sidebar
|
// wallet view and sidebar
|
||||||
|
|
||||||
.wallet-view {
|
.wallet-view {
|
||||||
|
@ -6,8 +6,7 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
getTokenData,
|
getTokenData,
|
||||||
getMethodData,
|
getValueFromWeiHex,
|
||||||
getTransactionAmount,
|
|
||||||
getTransactionFee,
|
getTransactionFee,
|
||||||
getHexGasTotal,
|
getHexGasTotal,
|
||||||
addFiat,
|
addFiat,
|
||||||
@ -17,6 +16,7 @@ import {
|
|||||||
isSmartContractAddress,
|
isSmartContractAddress,
|
||||||
} from '../helpers/confirm-transaction/util'
|
} from '../helpers/confirm-transaction/util'
|
||||||
|
|
||||||
|
import { getMethodData } from '../helpers/transactions.util'
|
||||||
import { getSymbolAndDecimals } from '../token-util'
|
import { getSymbolAndDecimals } from '../token-util'
|
||||||
import { conversionUtil } from '../conversion-util'
|
import { conversionUtil } from '../conversion-util'
|
||||||
|
|
||||||
@ -301,10 +301,10 @@ export function updateTxDataAndCalculate (txData) {
|
|||||||
|
|
||||||
const { txParams: { value, gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData
|
const { txParams: { value, gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData
|
||||||
|
|
||||||
const fiatTransactionAmount = getTransactionAmount({
|
const fiatTransactionAmount = getValueFromWeiHex({
|
||||||
value, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
|
value, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
|
||||||
})
|
})
|
||||||
const ethTransactionAmount = getTransactionAmount({
|
const ethTransactionAmount = getValueFromWeiHex({
|
||||||
value, toCurrency: 'ETH', conversionRate, numberOfDecimals: 6,
|
value, toCurrency: 'ETH', conversionRate, numberOfDecimals: 6,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -7,9 +7,6 @@ import BigNumber from 'bignumber.js'
|
|||||||
|
|
||||||
abiDecoder.addABI(abi)
|
abiDecoder.addABI(abi)
|
||||||
|
|
||||||
import MethodRegistry from 'eth-method-registry'
|
|
||||||
const registry = new MethodRegistry({ provider: global.ethereumProvider })
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
conversionUtil,
|
conversionUtil,
|
||||||
addCurrencies,
|
addCurrencies,
|
||||||
@ -23,18 +20,6 @@ export function getTokenData (data = {}) {
|
|||||||
return abiDecoder.decodeMethod(data)
|
return abiDecoder.decodeMethod(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMethodData (data = {}) {
|
|
||||||
const prefixedData = ethUtil.addHexPrefix(data)
|
|
||||||
const fourBytePrefix = prefixedData.slice(0, 10)
|
|
||||||
const sig = await registry.lookup(fourBytePrefix)
|
|
||||||
const parsedResult = registry.parse(sig)
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: parsedResult.name,
|
|
||||||
params: parsedResult.args,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function increaseLastGasPrice (lastGasPrice) {
|
export function increaseLastGasPrice (lastGasPrice) {
|
||||||
return ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
|
return ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
|
||||||
multiplicandBase: 16,
|
multiplicandBase: 16,
|
||||||
@ -76,7 +61,7 @@ export function addFiat (...args) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTransactionAmount ({
|
export function getValueFromWeiHex ({
|
||||||
value,
|
value,
|
||||||
toCurrency,
|
toCurrency,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
|
@ -92,9 +92,9 @@ describe('Confirm Transaction utils', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getTransactionAmount', () => {
|
describe('getValueFromWeiHex', () => {
|
||||||
it('should get the transaction amount in ETH', () => {
|
it('should get the transaction amount in ETH', () => {
|
||||||
const ethTransactionAmount = utils.getTransactionAmount({
|
const ethTransactionAmount = utils.getValueFromWeiHex({
|
||||||
value: '0xde0b6b3a7640000', toCurrency: 'ETH', conversionRate: 468.58, numberOfDecimals: 6,
|
value: '0xde0b6b3a7640000', toCurrency: 'ETH', conversionRate: 468.58, numberOfDecimals: 6,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ describe('Confirm Transaction utils', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should get the transaction amount in fiat', () => {
|
it('should get the transaction amount in fiat', () => {
|
||||||
const fiatTransactionAmount = utils.getTransactionAmount({
|
const fiatTransactionAmount = utils.getValueFromWeiHex({
|
||||||
value: '0xde0b6b3a7640000', toCurrency: 'usd', conversionRate: 468.58, numberOfDecimals: 2,
|
value: '0xde0b6b3a7640000', toCurrency: 'usd', conversionRate: 468.58, numberOfDecimals: 2,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
37
ui/app/helpers/conversions.util.js
Normal file
37
ui/app/helpers/conversions.util.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { conversionUtil } from '../conversion-util'
|
||||||
|
|
||||||
|
export function hexToDecimal (hexValue) {
|
||||||
|
return conversionUtil(hexValue, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
toNumericBase: 'dec',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEthFromWeiHex ({
|
||||||
|
value,
|
||||||
|
conversionRate,
|
||||||
|
}) {
|
||||||
|
return getValueFromWeiHex({
|
||||||
|
value,
|
||||||
|
conversionRate,
|
||||||
|
toCurrency: 'ETH',
|
||||||
|
numberOfDecimals: 6,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getValueFromWeiHex ({
|
||||||
|
value,
|
||||||
|
toCurrency,
|
||||||
|
conversionRate,
|
||||||
|
numberOfDecimals,
|
||||||
|
}) {
|
||||||
|
return conversionUtil(value, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
toNumericBase: 'dec',
|
||||||
|
fromCurrency: 'ETH',
|
||||||
|
toCurrency,
|
||||||
|
numberOfDecimals,
|
||||||
|
fromDenomination: 'WEI',
|
||||||
|
conversionRate,
|
||||||
|
})
|
||||||
|
}
|
57
ui/app/helpers/transactions.util.js
Normal file
57
ui/app/helpers/transactions.util.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import ethUtil from 'ethereumjs-util'
|
||||||
|
import MethodRegistry from 'eth-method-registry'
|
||||||
|
const registry = new MethodRegistry({ provider: global.ethereumProvider })
|
||||||
|
|
||||||
|
import {
|
||||||
|
TOKEN_METHOD_TRANSFER,
|
||||||
|
TOKEN_METHOD_APPROVE,
|
||||||
|
TOKEN_METHOD_TRANSFER_FROM,
|
||||||
|
SEND_ETHER_ACTION_KEY,
|
||||||
|
DEPLOY_CONTRACT_ACTION_KEY,
|
||||||
|
APPROVE_ACTION_KEY,
|
||||||
|
SEND_TOKEN_ACTION_KEY,
|
||||||
|
TRANSFER_FROM_ACTION_KEY,
|
||||||
|
} from '../constants/transactions'
|
||||||
|
|
||||||
|
export function isConfirmDeployContract (txData = {}) {
|
||||||
|
const { txParams = {} } = txData
|
||||||
|
return !txParams.to
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTransactionActionKey (transaction, methodData) {
|
||||||
|
const { txParams: { data } = {} } = transaction
|
||||||
|
|
||||||
|
if (isConfirmDeployContract(transaction)) {
|
||||||
|
return DEPLOY_CONTRACT_ACTION_KEY
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
const { name } = methodData
|
||||||
|
const methodName = name && name.toLowerCase()
|
||||||
|
|
||||||
|
switch (methodName) {
|
||||||
|
case TOKEN_METHOD_TRANSFER:
|
||||||
|
return SEND_TOKEN_ACTION_KEY
|
||||||
|
case TOKEN_METHOD_APPROVE:
|
||||||
|
return APPROVE_ACTION_KEY
|
||||||
|
case TOKEN_METHOD_TRANSFER_FROM:
|
||||||
|
return TRANSFER_FROM_ACTION_KEY
|
||||||
|
default:
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return SEND_ETHER_ACTION_KEY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMethodData (data = {}) {
|
||||||
|
const prefixedData = ethUtil.addHexPrefix(data)
|
||||||
|
const fourBytePrefix = prefixedData.slice(0, 10)
|
||||||
|
const sig = await registry.lookup(fourBytePrefix)
|
||||||
|
const parsedResult = registry.parse(sig)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: parsedResult.name,
|
||||||
|
params: parsedResult.args,
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { getMethodData } from '../../helpers/confirm-transaction/util'
|
import { getMethodData } from '../../helpers/transactions.util'
|
||||||
|
|
||||||
export default function withMethodData (WrappedComponent) {
|
export default function withMethodData (WrappedComponent) {
|
||||||
return class MethodDataWrappedComponent extends PureComponent {
|
return class MethodDataWrappedComponent extends PureComponent {
|
||||||
@ -13,7 +13,11 @@ export default function withMethodData (WrappedComponent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
methodData: {},
|
methodData: {
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
isFetching: false,
|
||||||
|
error: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
@ -25,18 +29,24 @@ export default function withMethodData (WrappedComponent) {
|
|||||||
const { txParams: { data = '' } = {} } = transaction
|
const { txParams: { data = '' } = {} } = transaction
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
const methodData = await getMethodData(data)
|
this.setState({ isFetching: true })
|
||||||
this.setState({ methodData })
|
|
||||||
|
try {
|
||||||
|
const methodData = await getMethodData(data)
|
||||||
|
this.setState({ methodData, isFetching: false })
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({ isFetching: false, error })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { methodData } = this.state
|
const { methodData, isFetching, error } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WrappedComponent
|
<WrappedComponent
|
||||||
{ ...this.props }
|
{ ...this.props }
|
||||||
methodData={methodData}
|
methodData={{ data: methodData, isFetching, error }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@ class I18nProvider extends Component {
|
|||||||
t (key, ...args) {
|
t (key, ...args) {
|
||||||
return t(current, key, ...args) || t(en, key, ...args) || `[${key}]`
|
return t(current, key, ...args) || t(en, key, ...args) || `[${key}]`
|
||||||
},
|
},
|
||||||
|
tOrDefault (key, ...args) {
|
||||||
|
return t(current, key, ...args) || t(en, key, ...args) || key
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ I18nProvider.propTypes = {
|
|||||||
|
|
||||||
I18nProvider.childContextTypes = {
|
I18nProvider.childContextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
|
tOrDefault: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
const valuesFor = require('./util').valuesFor
|
|
||||||
const abi = require('human-standard-token-abi')
|
const abi = require('human-standard-token-abi')
|
||||||
|
import { createSelector } from 'reselect'
|
||||||
|
|
||||||
|
import {
|
||||||
|
transactionsSelector,
|
||||||
|
} from './selectors/transactions'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
@ -101,21 +105,49 @@ function getCurrentAccountWithSendEtherInfo (state) {
|
|||||||
return accounts.find(({ address }) => address === currentAddress)
|
return accounts.find(({ address }) => address === currentAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
function transactionsSelector (state) {
|
// // function shapeShiftTxListSelector (state) {
|
||||||
const { network, selectedTokenAddress } = state.metamask
|
// // return state.metamask.shapeShiftTxList || []
|
||||||
const unapprovedMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
// // }
|
||||||
const shapeShiftTxList = (network === '1') ? state.metamask.shapeShiftTxList : undefined
|
|
||||||
const transactions = state.metamask.selectedAddressTxList || []
|
|
||||||
const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
|
|
||||||
|
|
||||||
// console.log({txsToRender, selectedTokenAddress})
|
// const transactionsSelector = createSelector(
|
||||||
return selectedTokenAddress
|
// selectedTokenAddressSelector,
|
||||||
? txsToRender
|
// unapprovedMsgsSelector,
|
||||||
.filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
|
// shapeShiftTxListSelector,
|
||||||
.sort((a, b) => b.time - a.time)
|
// selectedAddressTxListSelector,
|
||||||
: txsToRender
|
// (selectedTokenAddress, unapprovedMsgs = {}, shapeShiftTxList = [], transactions = []) => {
|
||||||
.sort((a, b) => b.time - a.time)
|
// const unapprovedMsgsList = valuesFor(unapprovedMsgs)
|
||||||
}
|
// const txsToRender = transactions.concat(unapprovedMsgsList, shapeShiftTxList)
|
||||||
|
|
||||||
|
// return selectedTokenAddress
|
||||||
|
// ? txsToRender
|
||||||
|
// .filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
|
||||||
|
// .sort((a, b) => b.time - a.time)
|
||||||
|
// : txsToRender
|
||||||
|
// .sort((a, b) => b.time - a.time)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // function transactionsSelector (state) {
|
||||||
|
// // const { selectedTokenAddress } = state.metamask
|
||||||
|
// // const unapprovedMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
||||||
|
// // const shapeShiftTxList = shapeShiftTxListSelector(state)
|
||||||
|
// // const transactions = state.metamask.selectedAddressTxList || []
|
||||||
|
// // const txsToRender = transactions.concat(unapprovedMsgs, shapeShiftTxList)
|
||||||
|
|
||||||
|
// // return selectedTokenAddress
|
||||||
|
// // ? txsToRender
|
||||||
|
// // .filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
|
||||||
|
// // .sort((a, b) => b.time - a.time)
|
||||||
|
// // : txsToRender
|
||||||
|
// // .sort((a, b) => b.time - a.time)
|
||||||
|
// // }
|
||||||
|
|
||||||
|
export const pendingTransactionsSelector = createSelector(
|
||||||
|
transactionsSelector,
|
||||||
|
transactions => {
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
function getGasIsLoading (state) {
|
function getGasIsLoading (state) {
|
||||||
return state.appState.gasIsLoading
|
return state.appState.gasIsLoading
|
||||||
|
50
ui/app/selectors/transactions.js
Normal file
50
ui/app/selectors/transactions.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { createSelector } from 'reselect'
|
||||||
|
import { valuesFor } from '../util'
|
||||||
|
import {
|
||||||
|
UNAPPROVED_STATUS,
|
||||||
|
APPROVED_STATUS,
|
||||||
|
SUBMITTED_STATUS,
|
||||||
|
} from '../constants/transactions'
|
||||||
|
|
||||||
|
export const shapeShiftTxListSelector = state => state.metamask.shapeShiftTxList
|
||||||
|
export const selectedTokenAddressSelector = state => state.metamask.selectedTokenAddress
|
||||||
|
export const unapprovedMsgsSelector = state => state.metamask.unapprovedMsgs
|
||||||
|
export const selectedAddressTxListSelector = state => state.metamask.selectedAddressTxList
|
||||||
|
|
||||||
|
const pendingStatusHash = {
|
||||||
|
[UNAPPROVED_STATUS]: true,
|
||||||
|
[APPROVED_STATUS]: true,
|
||||||
|
[SUBMITTED_STATUS]: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transactionsSelector = createSelector(
|
||||||
|
selectedTokenAddressSelector,
|
||||||
|
unapprovedMsgsSelector,
|
||||||
|
shapeShiftTxListSelector,
|
||||||
|
selectedAddressTxListSelector,
|
||||||
|
(selectedTokenAddress, unapprovedMsgs = {}, shapeShiftTxList = [], transactions = []) => {
|
||||||
|
const unapprovedMsgsList = valuesFor(unapprovedMsgs)
|
||||||
|
const txsToRender = transactions.concat(unapprovedMsgsList, shapeShiftTxList)
|
||||||
|
|
||||||
|
return selectedTokenAddress
|
||||||
|
? txsToRender
|
||||||
|
.filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
|
||||||
|
.sort((a, b) => b.time - a.time)
|
||||||
|
: txsToRender
|
||||||
|
.sort((a, b) => b.time - a.time)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const pendingTransactionsSelector = createSelector(
|
||||||
|
transactionsSelector,
|
||||||
|
(transactions = []) => (
|
||||||
|
transactions.filter(transaction => transaction.status in pendingStatusHash)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
export const completedTransactionsSelector = createSelector(
|
||||||
|
transactionsSelector,
|
||||||
|
(transactions = []) => (
|
||||||
|
transactions.filter(transaction => !(transaction.status in pendingStatusHash))
|
||||||
|
)
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user