diff --git a/web/cashtab/src/components/Wallet/Tx.js b/web/cashtab/src/components/Wallet/Tx.js
index 9af7eb142..5a7d9e24d 100644
--- a/web/cashtab/src/components/Wallet/Tx.js
+++ b/web/cashtab/src/components/Wallet/Tx.js
@@ -1,229 +1,258 @@
import React from 'react';
import styled from 'styled-components';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
import { currency } from '@components/Common/Ticker';
import makeBlockie from 'ethereum-blockies-base64';
import { Img } from 'react-image';
const SentTx = styled(ArrowUpOutlined)`
color: ${props => props.theme.secondary} !important;
`;
const ReceivedTx = styled(ArrowDownOutlined)`
color: ${props => props.theme.primary} !important;
`;
const DateType = styled.div`
text-align: left;
padding: 12px;
@media screen and (max-width: 500px) {
font-size: 0.8rem;
}
`;
const SentLabel = styled.span`
font-weight: bold;
color: ${props => props.theme.secondary} !important;
`;
const ReceivedLabel = styled.span`
font-weight: bold;
color: ${props => props.theme.primary} !important;
`;
const TxIcon = styled.div`
svg {
width: 32px;
height: 32px;
}
height: 32px;
width: 32px;
@media screen and (max-width: 500px) {
svg {
width: 24px;
height: 24px;
}
height: 24px;
width: 24px;
}
`;
const TxInfo = styled.div`
padding: 12px;
font-size: 1rem;
text-align: right;
color: ${props =>
props.outgoing ? props.theme.secondary : props.theme.primary};
@media screen and (max-width: 500px) {
font-size: 0.8rem;
}
`;
+const TxFiatPrice = styled.span`
+ font-size: 0.8rem;
+`;
const TokenInfo = styled.div`
display: grid;
grid-template-rows: 50%;
grid-template-columns: 24px auto;
padding: 12px;
font-size: 1rem;
color: ${props =>
props.outgoing ? props.theme.secondary : props.theme.primary};
@media screen and (max-width: 500px) {
font-size: 0.8rem;
grid-template-columns: 16px auto;
}
`;
const TxTokenIcon = styled.div`
img {
height: 24px;
width: 24px;
}
@media screen and (max-width: 500px) {
img {
height: 16px;
width: 16px;
}
}
grid-column-start: 1;
grid-column-end: span 1;
grid-row-start: 1;
grid-row-end: span 2;
align-self: center;
`;
const TokenTxAmt = styled.div`
padding-left: 12px;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const TokenName = styled.div`
padding-left: 12px;
font-size: 0.8rem;
@media screen and (max-width: 500px) {
font-size: 0.6rem;
}
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const TxWrapper = styled.div`
display: grid;
grid-template-columns: 36px 30% 50%;
justify-content: space-between;
align-items: center;
padding: 15px 25px;
border-radius: 3px;
background: ${props => props.theme.tokenListItem.background};
margin-bottom: 3px;
box-shadow: ${props => props.theme.tokenListItem.boxShadow};
border: 1px solid ${props => props.theme.tokenListItem.border};
:hover {
border-color: ${props => props.theme.primary};
}
@media screen and (max-width: 500px) {
grid-template-columns: 24px 30% 50%;
padding: 12px 12px;
}
`;
-const Tx = ({ data }) => {
+const Tx = ({ data, fiatPrice }) => {
const txDate =
typeof data.blocktime === 'undefined'
? new Date().toLocaleDateString()
: new Date(data.blocktime * 1000).toLocaleDateString();
return (
{data.outgoingTx ? : }
{data.outgoingTx ? (
Sent
) : (
Received
)}
{txDate}
{data.tokenTx ? (
{data.tokenTx && data.tokenInfo ? (
<>
{currency.tokenIconsUrl !== '' ? (
}
/>
) : (
)}
{data.outgoingTx ? (
<>
- {data.tokenInfo.qtySent.toString()}
{data.tokenInfo.tokenTicker}
{data.tokenInfo.tokenName}
>
) : (
<>
+{' '}
{data.tokenInfo.qtyReceived.toString()}
{data.tokenInfo.tokenTicker}
{data.tokenInfo.tokenName}
>
)}
>
) : (
Token Tx
)}
) : (
<>
- {data.outgoingTx
- ? `- ${data.amountSent.toFixed(8)} ${
- currency.ticker
- }`
- : `+ ${data.amountReceived.toFixed(8)} ${
- currency.ticker
- }`}
+ {data.outgoingTx ? (
+ <>
+ - {data.amountSent.toFixed(8)}
+ {currency.ticker}
+
+ {fiatPrice !== null &&
+ !isNaN(data.amountSent) && (
+
+ - $
+ {(
+ data.amountSent * fiatPrice
+ ).toFixed(2)}{' '}
+ USD
+
+ )}
+ >
+ ) : (
+ <>
+ + {data.amountReceived.toFixed(8)}
+ {currency.ticker}
+
+ {fiatPrice !== null &&
+ !isNaN(data.amountReceived) && (
+
+ + $
+ {(
+ data.amountReceived * fiatPrice
+ ).toFixed(2)}{' '}
+ USD
+
+ )}
+ >
+ )}
>
)}
);
};
export default Tx;
diff --git a/web/cashtab/src/components/Wallet/TxHistory.js b/web/cashtab/src/components/Wallet/TxHistory.js
index bf105c82e..972e6a61b 100644
--- a/web/cashtab/src/components/Wallet/TxHistory.js
+++ b/web/cashtab/src/components/Wallet/TxHistory.js
@@ -1,24 +1,24 @@
import React from 'react';
import styled from 'styled-components';
import Tx from './Tx';
export const TxLink = styled.a``;
-const TxHistory = ({ txs }) => {
+const TxHistory = ({ txs, fiatPrice }) => {
return (
{txs.map(tx => (
-
+
))}
);
};
export default TxHistory;
diff --git a/web/cashtab/src/components/Wallet/Wallet.js b/web/cashtab/src/components/Wallet/Wallet.js
index a53c47acb..16277b6b6 100644
--- a/web/cashtab/src/components/Wallet/Wallet.js
+++ b/web/cashtab/src/components/Wallet/Wallet.js
@@ -1,369 +1,372 @@
import React from 'react';
import styled from 'styled-components';
import { LinkOutlined, LoadingOutlined } from '@ant-design/icons';
import { WalletContext } from '@utils/context';
import { OnBoarding } from '@components/OnBoarding/OnBoarding';
import { QRCode } from '@components/Common/QRCode';
import { currency } from '@components/Common/Ticker.js';
import { Link } from 'react-router-dom';
import TokenList from './TokenList';
import TxHistory from './TxHistory';
import { CashLoader } from '@components/Common/CustomIcons';
import { formatBalance } from '@utils/cashMethods';
export const LoadingCtn = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 400px;
flex-direction: column;
svg {
width: 50px;
height: 50px;
fill: ${props => props.theme.primary};
}
`;
export const BalanceHeader = styled.div`
color: ${props => props.theme.wallet.text.primary};
width: 100%;
font-size: 30px;
font-weight: bold;
@media (max-width: 768px) {
font-size: 23px;
}
`;
export const BalanceHeaderFiat = styled.div`
color: ${props => props.theme.wallet.text.secondary};
width: 100%;
font-size: 18px;
margin-bottom: 20px;
font-weight: bold;
@media (max-width: 768px) {
font-size: 16px;
}
`;
export const ZeroBalanceHeader = styled.div`
color: ${props => props.theme.wallet.text.primary};
width: 100%;
font-size: 14px;
margin-bottom: 5px;
`;
export const Tabs = styled.div`
margin: auto;
margin-bottom: 12px;
display: inline-block;
text-align: center;
`;
export const TabLabel = styled.button`
:focus,
:active {
outline: none;
}
border: none;
background: none;
font-size: 20px;
cursor: pointer;
@media (max-width: 400px) {
font-size: 16px;
}
${({ active, ...props }) =>
active &&
`
color: ${props.theme.primary};
`}
`;
export const TabLine = styled.div`
margin: auto;
transition: margin-left 0.5s ease-in-out, width 0.5s 0.1s;
height: 4px;
border-radius: 5px;
background-color: ${props => props.theme.primary};
pointer-events: none;
margin-left: 72%;
width: 28%;
${({ left, ...props }) =>
left &&
`
margin-left: 1%
width: 69%;
`}
`;
export const TabPane = styled.div`
${({ active }) =>
!active &&
`
display: none;
`}
`;
export const SwitchBtnCtn = styled.div`
display: flex;
align-items: center;
justify-content: center;
align-content: space-between;
margin-bottom: 15px;
.nonactiveBtn {
color: ${props => props.theme.wallet.text.secondary};
background: ${props =>
props.theme.wallet.switch.inactive.background} !important;
box-shadow: none !important;
}
.slpActive {
background: ${props =>
props.theme.wallet.switch.activeToken.background} !important;
box-shadow: ${props =>
props.theme.wallet.switch.activeToken.shadow} !important;
}
`;
export const SwitchBtn = styled.div`
font-weight: bold;
display: inline-block;
cursor: pointer;
color: ${props => props.theme.contrast};
font-size: 14px;
padding: 6px 0;
width: 100px;
margin: 0 1px;
text-decoration: none;
background: ${props => props.theme.primary};
box-shadow: ${props => props.theme.wallet.switch.activeCash.shadow};
user-select: none;
:first-child {
border-radius: 100px 0 0 100px;
}
:nth-child(2) {
border-radius: 0 100px 100px 0;
}
`;
export const Links = styled(Link)`
color: ${props => props.theme.wallet.text.secondary};
width: 100%;
font-size: 16px;
margin: 10px 0 20px 0;
border: 1px solid ${props => props.theme.wallet.text.secondary};
padding: 14px 0;
display: inline-block;
border-radius: 3px;
transition: all 200ms ease-in-out;
svg {
fill: ${props => props.theme.wallet.text.secondary};
}
:hover {
color: ${props => props.theme.primary};
border-color: ${props => props.theme.primary};
svg {
fill: ${props => props.theme.primary};
}
}
@media (max-width: 768px) {
padding: 10px 0;
font-size: 14px;
}
`;
export const ExternalLink = styled.a`
color: ${props => props.theme.wallet.text.secondary};
width: 100%;
font-size: 16px;
margin: 0 0 20px 0;
border: 1px solid ${props => props.theme.wallet.text.secondary};
padding: 14px 0;
display: inline-block;
border-radius: 3px;
transition: all 200ms ease-in-out;
svg {
fill: ${props => props.theme.wallet.text.secondary};
transition: all 200ms ease-in-out;
}
:hover {
color: ${props => props.theme.primary};
border-color: ${props => props.theme.primary};
svg {
fill: ${props => props.theme.primary};
}
}
@media (max-width: 768px) {
padding: 10px 0;
font-size: 14px;
}
`;
const WalletInfo = () => {
const ContextValue = React.useContext(WalletContext);
const {
wallet,
fiatPrice,
balances,
tokens,
parsedTxHistory,
apiError,
} = ContextValue;
const [address, setAddress] = React.useState('cashAddress');
const [activeTab, setActiveTab] = React.useState('txHistory');
const hasHistory = parsedTxHistory && parsedTxHistory.length > 0;
const handleChangeAddress = () => {
setAddress(address === 'cashAddress' ? 'slpAddress' : 'cashAddress');
};
return (
<>
{!balances.totalBalance && !apiError && !hasHistory ? (
<>
🎉
Congratulations on your new wallet!{' '}
🎉
Start using the wallet immediately to receive{' '}
{currency.ticker} payments, or load it up with{' '}
{currency.ticker} to send to others
0 {currency.ticker}
>
) : (
<>
{formatBalance(balances.totalBalance)} {currency.ticker}
{fiatPrice !== null && !isNaN(balances.totalBalance) && (
${(balances.totalBalance * fiatPrice).toFixed(2)}{' '}
USD
)}
>
)}
{apiError && (
<>
An error occured on our end.
Re-establishing connection...
>
)}
{wallet &&
((wallet.Path245 && wallet.Path145) || wallet.Path1899) && (
<>
{wallet.Path1899 ? (
) : (
)}
>
)}
handleChangeAddress()}
className={
address !== 'cashAddress' ? 'nonactiveBtn' : null
}
>
{currency.ticker}
handleChangeAddress()}
className={
address === 'cashAddress' ? 'nonactiveBtn' : 'slpActive'
}
>
{currency.tokenTicker}
{hasHistory && parsedTxHistory && (
<>
setActiveTab('txHistory')}
>
Transaction History
setActiveTab('tokens')}
>
Tokens
-
+
More transactions
{tokens && tokens.length > 0 ? (
) : (
Tokens sent to your {currency.tokenTicker}{' '}
address will appear here
)}
>
)}
>
);
};
const Wallet = () => {
const ContextValue = React.useContext(WalletContext);
const { wallet, loading } = ContextValue;
return (
<>
{loading ? (
) : (
<>{wallet.Path1899 ? : }>
)}
>
);
};
export default Wallet;
diff --git a/web/cashtab/src/components/Wallet/__tests__/__snapshots__/Wallet.test.js.snap b/web/cashtab/src/components/Wallet/__tests__/__snapshots__/Wallet.test.js.snap
index 914f07776..ef86080e9 100644
--- a/web/cashtab/src/components/Wallet/__tests__/__snapshots__/Wallet.test.js.snap
+++ b/web/cashtab/src/components/Wallet/__tests__/__snapshots__/Wallet.test.js.snap
@@ -1,409 +1,409 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Wallet with BCH balances 1`] = `
Array [
0.06047469
BCHA
,
$
NaN
USD
,
,
,
]
`;
exports[`Wallet with BCH balances and tokens 1`] = `
Array [
0.06047469
BCHA
,
$
NaN
USD
,
,
,
]
`;
exports[`Wallet without BCH balance 1`] = `
Array [
🎉
Congratulations on your new wallet!
🎉
Start using the wallet immediately to receive
BCHA
payments, or load it up with
BCHA
to send to others
,
0
BCHA
,
,
,
]
`;
exports[`Without wallet defined 1`] = `
Array [
Welcome to Cashtab!
,
Cashtab is an
open source,
non-custodial web wallet for
Bitcoin ABC
.
Want to learn more?
Check out the Cashtab documentation.
,
,
,
]
`;