diff --git a/web/cashtab/src/components/App.css b/web/cashtab/src/components/App.css index 09680624d..63140c657 100644 --- a/web/cashtab/src/components/App.css +++ b/web/cashtab/src/components/App.css @@ -1,40 +1,51 @@ @import '~antd/dist/antd.less'; @import '~@fortawesome/fontawesome-free/css/all.css'; @import url('https://fonts.googleapis.com/css?family=Khula&display=swap&.css'); +/* Hide scrollbars but keep functionality*/ +/* Hide scrollbar for Chrome, Safari and Opera */ +body::-webkit-scrollbar { + display: none; +} + +/* Hide scrollbar for IE, Edge and Firefox */ +body { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} /* Hide up and down arros on input type="number" */ /* Chrome, Safari, Edge, Opera */ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } /* Hide up and down arros on input type="number" */ /* Firefox */ input[type='number'] { -moz-appearance: textfield; } html, body { max-width: 100%; overflow-x: hidden; } /* Hide scroll bars on antd modals*/ .ant-modal-wrap.ant-modal-centered::-webkit-scrollbar { display: none; } /* ITEMS BELOW TO BE MOVED TO STYLED COMPONENTS*/ /* useWallet.js, useBCH.js */ @media (max-width: 768px) { .ant-notification { width: 100%; top: 20px !important; max-width: unset; margin-right: 0; } } diff --git a/web/cashtab/src/components/Wallet/TokenList.js b/web/cashtab/src/components/Wallet/TokenList.js index bb0c7e2d2..3c1388ef6 100644 --- a/web/cashtab/src/components/Wallet/TokenList.js +++ b/web/cashtab/src/components/Wallet/TokenList.js @@ -1,29 +1,22 @@ import React from 'react'; -import styled from 'styled-components'; import TokenListItem from './TokenListItem'; import { Link } from 'react-router-dom'; -import { currency } from '@components/Common/Ticker.js'; import { formatBalance } from '@utils/cashMethods'; -export const TokenTitle = styled.h2` - color: ${props => props.theme.wallet.text.secondary}; -`; - const TokenList = ({ tokens }) => { return (
- {currency.tokenTicker} Tokens {tokens.map(token => ( ))}
); }; export default TokenList; diff --git a/web/cashtab/src/components/Wallet/Tx.js b/web/cashtab/src/components/Wallet/Tx.js index 417546f81..54a425e15 100644 --- a/web/cashtab/src/components/Wallet/Tx.js +++ b/web/cashtab/src/components/Wallet/Tx.js @@ -1,114 +1,114 @@ import React from 'react'; import styled from 'styled-components'; -import { UpCircleOutlined, DownCircleOutlined } from '@ant-design/icons'; +import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'; import { currency } from '@components/Common/Ticker'; -const SentTx = styled(UpCircleOutlined)` +const SentTx = styled(ArrowUpOutlined)` color: ${props => props.theme.secondary} !important; `; -const ReceivedTx = styled(DownCircleOutlined)` +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; color: ${props => props.outgoing ? props.theme.secondary : props.theme.primary}; @media screen and (max-width: 500px) { font-size: 0.8rem; } `; const TxWrapper = styled.div` display: grid; grid-template-columns: 36px 30% 50%; @media screen and (max-width: 500px) { grid-template-columns: 24px 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}; } `; const Tx = ({ data }) => { 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 ? ( Token Tx ) : ( <> {data.outgoingTx ? `- ${data.amountSent.toFixed(8)} ${ currency.ticker }` : `+ ${data.amountReceived.toFixed(8)} ${ currency.ticker }`} )}
); }; export default Tx; diff --git a/web/cashtab/src/components/Wallet/TxHistory.js b/web/cashtab/src/components/Wallet/TxHistory.js index 28bbd3965..bf105c82e 100644 --- a/web/cashtab/src/components/Wallet/TxHistory.js +++ b/web/cashtab/src/components/Wallet/TxHistory.js @@ -1,28 +1,24 @@ import React from 'react'; import styled from 'styled-components'; import Tx from './Tx'; -export const TxTitle = styled.h2` - color: ${props => props.theme.wallet.text.secondary}; -`; export const TxLink = styled.a``; const TxHistory = ({ txs }) => { return (
- Transactions {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 60e09fa01..a53c47acb 100644 --- a/web/cashtab/src/components/Wallet/Wallet.js +++ b/web/cashtab/src/components/Wallet/Wallet.js @@ -1,292 +1,369 @@ 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 && ( - - )} - {balances.totalBalance ? ( <> - - More transactions - + + setActiveTab('txHistory')} + > + Transaction History + + setActiveTab('tokens')} + > + Tokens + + + + + + + + More transactions + + + + {tokens && tokens.length > 0 ? ( + + ) : ( +

+ Tokens sent to your {currency.tokenTicker}{' '} + address will appear here +

+ )} +
- ) : null} + )} ); }; const Wallet = () => { const ContextValue = React.useContext(WalletContext); - const { wallet, tokens, loading } = ContextValue; + const { wallet, loading } = ContextValue; return ( <> - {loading && ( + {loading ? ( + ) : ( + <>{wallet.Path1899 ? : } )} - {!loading && wallet.Path245 && } - - {!loading && wallet.Path245 && tokens && tokens.length > 0 && ( - - )} - {!loading && !wallet.Path245 ? : null} ); }; 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 641205c3a..a8376bbce 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,514 +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
, - - - - - More transactions - , ] `; exports[`Wallet with BCH balances and tokens 1`] = ` Array [
0.06047469 BCHA
,
$ NaN USD
,
Copied
qzagy4 7mvh6qxkvcn3acjnz73rkhkc6y7cpt zgcqy6
,
BCHA
SLPA
, - - - - - More transactions - , - , ] `; 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.

, , , ] `;