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 !== '' ? ( {`identicon } /> ) : ( {`identicon )} {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
,
Copied
qzagy4 7mvh6qxkvcn3acjnz73rkhkc6y7cpt zgcqy6
,
BCHA
SLPA
, ] `; exports[`Wallet with BCH balances and tokens 1`] = ` Array [
0.06047469 BCHA
,
$ NaN USD
,
Copied
qzagy4 7mvh6qxkvcn3acjnz73rkhkc6y7cpt zgcqy6
,
BCHA
SLPA
, ] `; 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
,
Copied
qzagy4 7mvh6qxkvcn3acjnz73rkhkc6y7cpt zgcqy6
,
BCHA
SLPA
, ] `; 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.

, , , ] `;