aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components
diff options
context:
space:
mode:
authorAlexander Tseung <alextsg@gmail.com>2018-07-31 13:03:20 +0800
committerAlexander Tseung <alextsg@gmail.com>2018-08-24 07:44:44 +0800
commit5ee40675b9f986a9ff2e5d15a271d7de2145d0e9 (patch)
tree80f4b3e0a88a5621724a05efeb320596e0bcedad /ui/app/components
parentd733bd34fbd356bca640b3a50582208c0284be40 (diff)
downloadtangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.tar
tangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.tar.gz
tangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.tar.bz2
tangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.tar.lz
tangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.tar.xz
tangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.tar.zst
tangerine-wallet-browser-5ee40675b9f986a9ff2e5d15a271d7de2145d0e9.zip
Refactor transactions list views. Add redesign components
Diffstat (limited to 'ui/app/components')
-rw-r--r--ui/app/components/index.scss24
-rw-r--r--ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js4
-rw-r--r--ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js3
-rw-r--r--ui/app/components/pages/home/home.component.js21
-rw-r--r--ui/app/components/pages/home/home.container.js10
-rw-r--r--ui/app/components/token-balance.js120
-rw-r--r--ui/app/components/token-view-balance/index.js1
-rw-r--r--ui/app/components/token-view-balance/index.scss66
-rw-r--r--ui/app/components/token-view-balance/token-view-balance.component.js92
-rw-r--r--ui/app/components/token-view-balance/token-view-balance.container.js42
-rw-r--r--ui/app/components/token-view/index.js1
-rw-r--r--ui/app/components/token-view/index.scss27
-rw-r--r--ui/app/components/token-view/token-view.component.js28
-rw-r--r--ui/app/components/transaction-action/index.js1
-rw-r--r--ui/app/components/transaction-action/transaction-action.component.js52
-rw-r--r--ui/app/components/transaction-action/transaction-action.container.js4
-rw-r--r--ui/app/components/transaction-list-item/index.js1
-rw-r--r--ui/app/components/transaction-list-item/index.scss71
-rw-r--r--ui/app/components/transaction-list-item/transaction-list-item.component.js82
-rw-r--r--ui/app/components/transaction-list-item/transaction-list-item.container.js28
-rw-r--r--ui/app/components/transaction-list/index.js1
-rw-r--r--ui/app/components/transaction-list/index.scss40
-rw-r--r--ui/app/components/transaction-list/transaction-list.component.js90
-rw-r--r--ui/app/components/transaction-list/transaction-list.container.js20
-rw-r--r--ui/app/components/transaction-status/index.scss6
-rw-r--r--ui/app/components/tx-view.js51
26 files changed, 689 insertions, 197 deletions
diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss
index 35d38e2a3..261d917f6 100644
--- a/ui/app/components/index.scss
+++ b/ui/app/components/index.scss
@@ -1,23 +1,35 @@
+@import './app-header/index';
+
@import './button-group/index';
-@import './export-text-container/index';
+@import './confirm-page-container/index';
-@import './selected-account/index';
+@import './export-text-container/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 './pages/index';
-@import './modals/index';
+@import './selected-account/index';
@import './sender-to-recipient/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';
diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js
index d494977cd..2c44b6094 100644
--- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js
+++ b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js
@@ -12,12 +12,12 @@ import {
CONFIRM_TOKEN_METHOD_PATH,
SIGNATURE_REQUEST_PATH,
} from '../../../routes'
-import { isConfirmDeployContract } from './confirm-transaction-switch.util'
+import { isConfirmDeployContract } from '../../../helpers/transactions.util'
import {
TOKEN_METHOD_TRANSFER,
TOKEN_METHOD_APPROVE,
TOKEN_METHOD_TRANSFER_FROM,
-} from './confirm-transaction-switch.constants'
+} from '../../../constants/transactions'
export default class ConfirmTransactionSwitch extends Component {
static propTypes = {
diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js
deleted file mode 100644
index 9db4a2f96..000000000
--- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const TOKEN_METHOD_TRANSFER = 'transfer'
-export const TOKEN_METHOD_APPROVE = 'approve'
-export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom'
diff --git a/ui/app/components/pages/home/home.component.js b/ui/app/components/pages/home/home.component.js
index 20ba44484..dae9790de 100644
--- a/ui/app/components/pages/home/home.component.js
+++ b/ui/app/components/pages/home/home.component.js
@@ -4,6 +4,7 @@ import Media from 'react-media'
import { Redirect } from 'react-router-dom'
import WalletView from '../../wallet-view'
import TxView from '../../tx-view'
+import TokenView from '../../token-view'
import {
INITIALIZE_BACKUP_PHRASE_ROUTE,
RESTORE_VAULT_ROUTE,
@@ -14,28 +15,17 @@ import {
export default class Home extends PureComponent {
static propTypes = {
history: PropTypes.object,
- unapprovedTxs: PropTypes.object,
- unapprovedMsgCount: PropTypes.number,
- unapprovedPersonalMsgCount: PropTypes.number,
- unapprovedTypedMessagesCount: PropTypes.number,
noActiveNotices: PropTypes.bool,
lostAccounts: PropTypes.array,
forgottenPassword: PropTypes.bool,
seedWords: PropTypes.string,
+ unconfirmedTransactionsCount: PropTypes.number,
}
componentDidMount () {
- const {
- history,
- unapprovedTxs = {},
- unapprovedMsgCount = 0,
- unapprovedPersonalMsgCount = 0,
- unapprovedTypedMessagesCount = 0,
- } = this.props
+ const { history, unconfirmedTransactionsCount = 0 } = this.props
- // unapprovedTxs and unapproved messages
- if (Object.keys(unapprovedTxs).length ||
- unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) {
+ if (unconfirmedTransactionsCount > 0) {
history.push(CONFIRM_TRANSACTION_ROUTE)
}
}
@@ -69,7 +59,8 @@ export default class Home extends PureComponent {
query="(min-width: 576px)"
render={() => <WalletView />}
/>
- <TxView />
+ <TokenView />
+ {/* <TxView /> */}
</div>
</div>
)
diff --git a/ui/app/components/pages/home/home.container.js b/ui/app/components/pages/home/home.container.js
index 96a45a69b..b0e34f832 100644
--- a/ui/app/components/pages/home/home.container.js
+++ b/ui/app/components/pages/home/home.container.js
@@ -2,14 +2,11 @@ import Home from './home.component'
import { compose } from 'recompose'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
+import { unconfirmedTransactionsCountSelector } from '../../../selectors/confirm-transaction'
const mapStateToProps = state => {
const { metamask, appState } = state
const {
- unapprovedTxs = {},
- unapprovedMsgCount = 0,
- unapprovedPersonalMsgCount = 0,
- unapprovedTypedMessagesCount = 0,
noActiveNotices,
lostAccounts,
seedWords,
@@ -17,14 +14,11 @@ const mapStateToProps = state => {
const { forgottenPassword } = appState
return {
- unapprovedTxs,
- unapprovedMsgCount,
- unapprovedPersonalMsgCount,
- unapprovedTypedMessagesCount,
noActiveNotices,
lostAccounts,
forgottenPassword,
seedWords,
+ unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
}
}
diff --git a/ui/app/components/token-balance.js b/ui/app/components/token-balance.js
deleted file mode 100644
index 99ca7335c..000000000
--- a/ui/app/components/token-balance.js
+++ /dev/null
@@ -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)
-}
-
diff --git a/ui/app/components/token-view-balance/index.js b/ui/app/components/token-view-balance/index.js
new file mode 100644
index 000000000..e0509096a
--- /dev/null
+++ b/ui/app/components/token-view-balance/index.js
@@ -0,0 +1 @@
+export { default } from './token-view-balance.container'
diff --git a/ui/app/components/token-view-balance/index.scss b/ui/app/components/token-view-balance/index.scss
new file mode 100644
index 000000000..6a89e125b
--- /dev/null
+++ b/ui/app/components/token-view-balance/index.scss
@@ -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
+ }
+}
diff --git a/ui/app/components/token-view-balance/token-view-balance.component.js b/ui/app/components/token-view-balance/token-view-balance.component.js
new file mode 100644
index 000000000..6b8140a22
--- /dev/null
+++ b/ui/app/components/token-view-balance/token-view-balance.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/token-view-balance/token-view-balance.container.js b/ui/app/components/token-view-balance/token-view-balance.container.js
new file mode 100644
index 000000000..692e6e32f
--- /dev/null
+++ b/ui/app/components/token-view-balance/token-view-balance.container.js
@@ -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)
diff --git a/ui/app/components/token-view/index.js b/ui/app/components/token-view/index.js
new file mode 100644
index 000000000..f49cb034f
--- /dev/null
+++ b/ui/app/components/token-view/index.js
@@ -0,0 +1 @@
+export { default } from './token-view.component'
diff --git a/ui/app/components/token-view/index.scss b/ui/app/components/token-view/index.scss
new file mode 100644
index 000000000..438147ad9
--- /dev/null
+++ b/ui/app/components/token-view/index.scss
@@ -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;
+ }
+ }
+}
diff --git a/ui/app/components/token-view/token-view.component.js b/ui/app/components/token-view/token-view.component.js
new file mode 100644
index 000000000..3e1a4a0c3
--- /dev/null
+++ b/ui/app/components/token-view/token-view.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/transaction-action/index.js b/ui/app/components/transaction-action/index.js
new file mode 100644
index 000000000..5882443b6
--- /dev/null
+++ b/ui/app/components/transaction-action/index.js
@@ -0,0 +1 @@
+export { default } from './transaction-action.container'
diff --git a/ui/app/components/transaction-action/transaction-action.component.js b/ui/app/components/transaction-action/transaction-action.component.js
new file mode 100644
index 000000000..b608615d0
--- /dev/null
+++ b/ui/app/components/transaction-action/transaction-action.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/transaction-action/transaction-action.container.js b/ui/app/components/transaction-action/transaction-action.container.js
new file mode 100644
index 000000000..56efbdc26
--- /dev/null
+++ b/ui/app/components/transaction-action/transaction-action.container.js
@@ -0,0 +1,4 @@
+import withMethodData from '../../higher-order-components/with-method-data'
+import TransactionAction from './transaction-action.component'
+
+export default withMethodData(TransactionAction)
diff --git a/ui/app/components/transaction-list-item/index.js b/ui/app/components/transaction-list-item/index.js
new file mode 100644
index 000000000..697cc55e9
--- /dev/null
+++ b/ui/app/components/transaction-list-item/index.js
@@ -0,0 +1 @@
+export { default } from './transaction-list-item.container'
diff --git a/ui/app/components/transaction-list-item/index.scss b/ui/app/components/transaction-list-item/index.scss
new file mode 100644
index 000000000..8a3973f92
--- /dev/null
+++ b/ui/app/components/transaction-list-item/index.scss
@@ -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);
+ }
+}
diff --git a/ui/app/components/transaction-list-item/transaction-list-item.component.js b/ui/app/components/transaction-list-item/transaction-list-item.component.js
new file mode 100644
index 000000000..e334cd938
--- /dev/null
+++ b/ui/app/components/transaction-list-item/transaction-list-item.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/transaction-list-item/transaction-list-item.container.js b/ui/app/components/transaction-list-item/transaction-list-item.container.js
new file mode 100644
index 000000000..bc47f20aa
--- /dev/null
+++ b/ui/app/components/transaction-list-item/transaction-list-item.container.js
@@ -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)
diff --git a/ui/app/components/transaction-list/index.js b/ui/app/components/transaction-list/index.js
new file mode 100644
index 000000000..688994367
--- /dev/null
+++ b/ui/app/components/transaction-list/index.js
@@ -0,0 +1 @@
+export { default } from './transaction-list.container'
diff --git a/ui/app/components/transaction-list/index.scss b/ui/app/components/transaction-list/index.scss
new file mode 100644
index 000000000..f6f209831
--- /dev/null
+++ b/ui/app/components/transaction-list/index.scss
@@ -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;
+ }
+}
diff --git a/ui/app/components/transaction-list/transaction-list.component.js b/ui/app/components/transaction-list/transaction-list.component.js
new file mode 100644
index 000000000..63d171127
--- /dev/null
+++ b/ui/app/components/transaction-list/transaction-list.component.js
@@ -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>
+ )
+ }
+}
diff --git a/ui/app/components/transaction-list/transaction-list.container.js b/ui/app/components/transaction-list/transaction-list.container.js
new file mode 100644
index 000000000..b1c2c04c9
--- /dev/null
+++ b/ui/app/components/transaction-list/transaction-list.container.js
@@ -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)
diff --git a/ui/app/components/transaction-status/index.scss b/ui/app/components/transaction-status/index.scss
index dd9bf5877..03a378b4e 100644
--- a/ui/app/components/transaction-status/index.scss
+++ b/ui/app/components/transaction-status/index.scss
@@ -10,6 +10,12 @@
justify-content: center;
align-items: center;
+ @media screen and (max-width: $break-small) {
+ height: 24px;
+ width: 74px;
+ font-size: .5rem;
+ }
+
&--confirmed {
background-color: #eafad7;
color: #609a1c;
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 654090da6..aa540249f 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -15,6 +15,9 @@ const Tooltip = require('./tooltip')
const TxList = require('./tx-list')
const SelectedAccount = require('./selected-account')
+import Media from 'react-media'
+import MenuBar from './menu-bar'
+
module.exports = compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
@@ -104,49 +107,11 @@ TxView.prototype.renderButtons = function () {
}
TxView.prototype.render = function () {
- const { hideSidebar, isMascara, showSidebar, sidebarOpen } = this.props
- const { t } = this.context
-
- return h('div.tx-view.flex-column', {
- 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' })]),
- ]),
- ]),
+ return h('div.tx-view.flex-column', [
+ h(Media, {
+ query: '(max-width: 575px)',
+ render: () => h(MenuBar),
+ }),
this.renderHeroBalance(),