diff --git a/web/cashtab/extension/public/manifest.json b/web/cashtab/extension/public/manifest.json
index d1a1e2886..5edd3d900 100644
--- a/web/cashtab/extension/public/manifest.json
+++ b/web/cashtab/extension/public/manifest.json
@@ -1,30 +1,30 @@
{
"manifest_version": 2,
"name": "Cashtab",
"description": "A browser-integrated eCash wallet from Bitcoin ABC",
- "version": "1.0.3",
+ "version": "1.0.4",
"content_scripts": [
{
"matches": ["file://*/*", "http://*/*", "https://*/*"],
"js": ["contentscript.js"],
"run_at": "document_idle",
"all_frames": true
}
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "index.html",
"default_title": "Cashtab"
},
"icons": {
"16": "ecash16.png",
"48": "ecash48.png",
"128": "ecash128.png",
"192": "ecash192.png",
"512": "ecash512.png"
}
}
diff --git a/web/cashtab/extension/src/components/App.js b/web/cashtab/extension/src/components/App.js
index b48dacc22..6c916a94b 100644
--- a/web/cashtab/extension/src/components/App.js
+++ b/web/cashtab/extension/src/components/App.js
@@ -1,316 +1,325 @@
import React, { useState } from 'react';
import 'antd/dist/antd.less';
import { Spin } from 'antd';
import { CashLoadingIcon } from '@components/Common/CustomIcons';
import '../index.css';
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
import { theme } from '@assets/styles/theme';
import {
FolderOpenFilled,
CaretRightOutlined,
SettingFilled,
AppstoreAddOutlined,
} from '@ant-design/icons';
import Wallet from '@components/Wallet/Wallet';
import Tokens from '@components/Tokens/Tokens';
import Send from '@components/Send/Send';
import SendToken from '@components/Send/SendToken';
import Configure from '@components/Configure/Configure';
import NotFound from '@components/NotFound';
import CashTab from '@assets/cashtab_xec.png';
import './App.css';
import { WalletContext } from '@utils/context';
import { isValidStoredWallet } from '@utils/cashMethods';
import WalletLabel from '@components/Common/WalletLabel.js';
import {
Route,
Redirect,
Switch,
useLocation,
useHistory,
} from 'react-router-dom';
// Extension-only import used for open in new tab link
import PopOut from '@assets/popout.svg';
const GlobalStyle = createGlobalStyle`
- .ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button, .ant-modal > button, .ant-modal-confirm-btns > button, .ant-modal-footer > button {
+ .ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button, .ant-modal > button, .ant-modal-confirm-btns > button, .ant-modal-footer > button, #cropControlsConfirm{
border-radius: 8px;
background-color: ${props => props.theme.modals.buttons.background};
color: ${props => props.theme.wallet.text.secondary};
font-weight: bold;
}
- .ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button:hover,.ant-modal-confirm-btns > button:hover, .ant-modal-footer > button:hover {
+ .ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button:hover,.ant-modal-confirm-btns > button:hover, .ant-modal-footer > button:hover, #cropControlsConfirm:hover {
color: ${props => props.theme.primary};
transition: color 0.3s;
background-color: ${props => props.theme.modals.buttons.background};
}
.selectedCurrencyOption {
text-align: left;
color: ${props => props.theme.wallet.text.secondary} !important;
background-color: ${props => props.theme.contrast} !important;
}
.cashLoadingIcon {
color: ${props => props.theme.primary} !important;
font-size: 48px !important;
}
.selectedCurrencyOption:hover {
color: ${props => props.theme.contrast} !important;
background-color: ${props => props.theme.primary} !important;
}
- #addrSwitch {
+ #addrSwitch, #cropSwitch {
.ant-switch-checked {
background-color: white !important;
}
}
- #addrSwitch.ant-switch-checked {
+ #addrSwitch.ant-switch-checked, #cropSwitch.ant-switch-checked {
background-image: ${props =>
props.theme.buttons.primary.backgroundImage} !important;
}
+
+ .ant-slider-rail {
+ background-color: ${props => props.theme.forms.border} !important;
+ }
+ .ant-slider-track {
+ background-color: ${props => props.theme.primary} !important;
+ }
`;
const CustomApp = styled.div`
text-align: center;
font-family: 'Gilroy', sans-serif;
background-color: ${props => props.theme.app.background};
`;
const Footer = styled.div`
z-index: 2;
background-color: ${props => props.theme.footer.background};
border-radius: 20px;
position: fixed;
bottom: 0;
width: 500px;
@media (max-width: 768px) {
width: 100%;
}
border-top: 1px solid ${props => props.theme.wallet.borders.color};
`;
export const NavButton = styled.button`
:focus,
:active {
outline: none;
}
cursor: pointer;
padding: 24px 12px 12px 12px;
margin: 0 28px;
@media (max-width: 475px) {
margin: 0 20px;
}
@media (max-width: 420px) {
margin: 0 12px;
}
@media (max-width: 350px) {
margin: 0 8px;
}
background-color: ${props => props.theme.footer.background};
border: none;
font-size: 12px;
font-weight: bold;
.anticon {
display: block;
color: ${props => props.theme.footer.navIconInactive};
font-size: 24px;
margin-bottom: 6px;
}
${({ active, ...props }) =>
active &&
`
color: ${props.theme.primary};
.anticon {
color: ${props.theme.primary};
}
`}
`;
export const WalletBody = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 100vh;
background-image: ${props => props.theme.app.sidebars};
background-attachment: fixed;
`;
export const WalletCtn = styled.div`
position: relative;
width: 500px;
background-color: ${props => props.theme.footerBackground};
min-height: 100vh;
padding: 10px 30px 120px 30px;
background: ${props => props.theme.wallet.background};
-webkit-box-shadow: 0px 0px 24px 1px ${props => props.theme.wallet.shadow};
-moz-box-shadow: 0px 0px 24px 1px ${props => props.theme.wallet.shadow};
box-shadow: 0px 0px 24px 1px ${props => props.theme.wallet.shadow};
@media (max-width: 768px) {
width: 100%;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
`;
export const HeaderCtn = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 20px 0 30px;
margin-bottom: 20px;
justify-content: space-between;
border-bottom: 1px solid ${props => props.theme.wallet.borders.color};
a {
color: ${props => props.theme.wallet.text.secondary};
:hover {
color: ${props => props.theme.primary};
}
}
@media (max-width: 768px) {
a {
font-size: 12px;
}
padding: 10px 0 20px;
}
`;
export const CashTabLogo = styled.img`
width: 120px;
@media (max-width: 768px) {
width: 110px;
}
`;
// Extension only styled components
const OpenInTabBtn = styled.button`
background: none;
border: none;
`;
const ExtTabImg = styled.img`
max-width: 20px;
`;
const App = () => {
const ContextValue = React.useContext(WalletContext);
const { wallet, loading } = ContextValue;
const [loadingUtxosAfterSend, setLoadingUtxosAfterSend] = useState(false);
// If wallet is unmigrated, do not show page until it has migrated
// An invalid wallet will be validated/populated after the next API call, ETA 10s
const validWallet = isValidStoredWallet(wallet);
const location = useLocation();
const history = useHistory();
const selectedKey =
location && location.pathname ? location.pathname.substr(1) : '';
// openInTab is an extension-only method
const openInTab = () => {
window.open(`index.html#/${selectedKey}`);
};
return (
{/*Begin extension-only components*/}
openInTab()}
>
{/*End extension-only components*/}
+ {/*Note that the extension does not support biometric security*/}
+ {/*Hence is not pulled in*/}
(
)}
/>
{wallet ? (
history.push('/wallet')}
>
Wallet
history.push('/tokens')}
>
eTokens
history.push('/send')}
>
Send
history.push('/configure')}
>
Settings
) : null}
);
};
export default App;
diff --git a/web/cashtab/src/components/App.js b/web/cashtab/src/components/App.js
index 07814eb80..a4a9023cc 100644
--- a/web/cashtab/src/components/App.js
+++ b/web/cashtab/src/components/App.js
@@ -1,393 +1,394 @@
import React, { useState, useEffect } from 'react';
import 'antd/dist/antd.less';
import { Modal, Spin } from 'antd';
import { CashLoadingIcon } from '@components/Common/CustomIcons';
import '../index.css';
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
import { theme } from '@assets/styles/theme';
import {
FolderOpenFilled,
CaretRightOutlined,
SettingFilled,
AppstoreAddOutlined,
} from '@ant-design/icons';
import Wallet from '@components/Wallet/Wallet';
import Tokens from '@components/Tokens/Tokens';
import Send from '@components/Send/Send';
import SendToken from '@components/Send/SendToken';
import Configure from '@components/Configure/Configure';
import NotFound from '@components/NotFound';
import CashTab from '@assets/cashtab_xec.png';
import './App.css';
import { WalletContext } from '@utils/context';
import { isValidStoredWallet } from '@utils/cashMethods';
import WalletLabel from '@components/Common/WalletLabel.js';
import {
Route,
Redirect,
Switch,
useLocation,
useHistory,
} from 'react-router-dom';
// Easter egg imports not used in extension/src/components/App.js
import TabCash from '@assets/tabcash.png';
import ABC from '@assets/logo_topright.png';
import { checkForTokenById } from '@utils/tokenMethods.js';
import { currency } from './Common/Ticker';
+// Biometric security import not used in extension/src/components/App.js
import ProtectableComponentWrapper from './Authentication/ProtectableComponentWrapper';
const GlobalStyle = createGlobalStyle`
.ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button, .ant-modal > button, .ant-modal-confirm-btns > button, .ant-modal-footer > button, #cropControlsConfirm {
border-radius: 8px;
background-color: ${props => props.theme.modals.buttons.background};
color: ${props => props.theme.wallet.text.secondary};
font-weight: bold;
}
.ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button:hover,.ant-modal-confirm-btns > button:hover, .ant-modal-footer > button:hover, #cropControlsConfirm:hover {
color: ${props => props.theme.primary};
transition: color 0.3s;
background-color: ${props => props.theme.modals.buttons.background};
}
.selectedCurrencyOption {
text-align: left;
color: ${props => props.theme.wallet.text.secondary} !important;
background-color: ${props => props.theme.contrast} !important;
}
.cashLoadingIcon {
color: ${props => props.theme.primary} !important;
font-size: 48px !important;
}
.selectedCurrencyOption:hover {
color: ${props => props.theme.contrast} !important;
background-color: ${props => props.theme.primary} !important;
}
#addrSwitch, #cropSwitch {
.ant-switch-checked {
background-color: white !important;
}
}
#addrSwitch.ant-switch-checked, #cropSwitch.ant-switch-checked {
background-image: ${props =>
props.theme.buttons.primary.backgroundImage} !important;
}
.ant-slider-rail {
background-color: ${props => props.theme.forms.border} !important;
}
.ant-slider-track {
background-color: ${props => props.theme.primary} !important;
}
`;
const CustomApp = styled.div`
text-align: center;
font-family: 'Gilroy', sans-serif;
background-color: ${props => props.theme.app.background};
`;
const Footer = styled.div`
z-index: 2;
background-color: ${props => props.theme.footer.background};
border-radius: 20px 20px 0 0;
position: fixed;
bottom: 0;
width: 500px;
box-shadow: rgb(136 172 243 / 25%) 0px 10px 30px,
rgb(0 0 0 / 3%) 0px 1px 1px, rgb(0 51 167 / 10%) 0px 10px 20px;
@media (max-width: 768px) {
width: 100%;
}
`;
export const NavButton = styled.button`
:focus,
:active {
outline: none;
}
cursor: pointer;
padding: 24px 12px 12px 12px;
margin: 0 28px;
@media (max-width: 475px) {
margin: 0 20px;
}
@media (max-width: 420px) {
margin: 0 12px;
}
@media (max-width: 350px) {
margin: 0 8px;
}
background-color: ${props => props.theme.footer.background};
border: none;
font-size: 10.5px;
font-weight: bold;
.anticon {
display: block;
color: ${props => props.theme.footer.navIconInactive};
font-size: 24px;
margin-bottom: 6px;
}
${({ active, ...props }) =>
active &&
`
color: ${props.theme.primary};
.anticon {
color: ${props.theme.primary};
}
`}
`;
export const WalletBody = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 100vh;
background-image: ${props => props.theme.app.sidebars};
background-attachment: fixed;
`;
export const WalletCtn = styled.div`
position: relative;
width: 500px;
background-color: ${props => props.theme.footerBackground};
min-height: 100vh;
padding: 10px 30px 120px 30px;
background: ${props => props.theme.wallet.background};
-webkit-box-shadow: 0px 0px 24px 1px ${props => props.theme.wallet.shadow};
-moz-box-shadow: 0px 0px 24px 1px ${props => props.theme.wallet.shadow};
box-shadow: 0px 0px 24px 1px ${props => props.theme.wallet.shadow};
@media (max-width: 768px) {
width: 100%;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
`;
export const HeaderCtn = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 20px 0 30px;
margin-bottom: 20px;
justify-content: space-between;
a {
color: ${props => props.theme.wallet.text.secondary};
:hover {
color: ${props => props.theme.primary};
}
}
@media (max-width: 768px) {
a {
font-size: 12px;
}
padding: 10px 0 20px;
}
`;
export const CashTabLogo = styled.img`
width: 120px;
@media (max-width: 768px) {
width: 110px;
}
`;
// AbcLogo styled component not included in extension, replaced by open in new tab link
export const AbcLogo = styled.img`
width: 150px;
@media (max-width: 768px) {
width: 120px;
}
`;
// Easter egg styled component not used in extension/src/components/App.js
export const EasterEgg = styled.img`
position: fixed;
bottom: -195px;
margin: 0;
right: 10%;
transition-property: bottom;
transition-duration: 1.5s;
transition-timing-function: ease-out;
:hover {
bottom: 0;
}
@media screen and (max-width: 1250px) {
display: none;
}
`;
const App = () => {
const ContextValue = React.useContext(WalletContext);
const { wallet, loading } = ContextValue;
const [loadingUtxosAfterSend, setLoadingUtxosAfterSend] = useState(false);
// If wallet is unmigrated, do not show page until it has migrated
// An invalid wallet will be validated/populated after the next API call, ETA 10s
const validWallet = isValidStoredWallet(wallet);
const location = useLocation();
const history = useHistory();
const selectedKey =
location && location.pathname ? location.pathname.substr(1) : '';
// Easter egg boolean not used in extension/src/components/App.js
const hasTab = validWallet
? checkForTokenById(
wallet.state.tokens,
'50d8292c6255cda7afc6c8566fed3cf42a2794e9619740fe8f4c95431271410e',
)
: false;
useEffect(() => {
// If URL is not as specified in currency.appURL in Ticker.js, show a popup
const currentUrl = window.location.hostname;
if (currentUrl !== currency.appUrl) {
console.log(
`Loaded URL ${currentUrl} does not match app URL ${currency.appUrl}!`,
);
Modal.warning({
title: 'Cashtab is moving!',
content: (
Cashtab is moving to a new home at{' '}
Cashtab.com
Please write down your wallet 12-word seed and
import it at the new domain.
At the end of the month, cashtabapp.com will
auto-fwd to cashtab.com after one minute.
),
});
}
}, []);
return (
{/*Begin component not included in extension as desktop only*/}
{hasTab && (
)}
{/*End component not included in extension as desktop only*/}
{/*Begin component not included in extension as replaced by open in tab link*/}
{/*Begin component not included in extension as replaced by open in tab link*/}
(
)}
/>
{wallet ? (
history.push('/wallet')}
>
Wallet
history.push('/tokens')}
>
eTokens
history.push('/send')}
>
Send
history.push('/configure')}
>
Settings
) : null}
);
};
export default App;
diff --git a/web/cashtab/src/components/Send/__tests__/__snapshots__/Send.test.js.snap b/web/cashtab/src/components/Send/__tests__/__snapshots__/Send.test.js.snap
index 4236ef6b6..99244db08 100644
--- a/web/cashtab/src/components/Send/__tests__/__snapshots__/Send.test.js.snap
+++ b/web/cashtab/src/components/Send/__tests__/__snapshots__/Send.test.js.snap
@@ -1,2199 +1,2199 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Jest Snapshot v1, https://goo.gl/fbAQLP @generated
exports[`Wallet with BCH balances 1`] = `
Array [
You currently have 0
XEC
Deposit some funds to use this feature
,
,
Signatures
,
,
]
`;
exports[`Wallet with BCH balances and tokens 1`] = `
Array [
You currently have 0
XEC
Deposit some funds to use this feature
,
,
Signatures
,
,
]
`;
exports[`Wallet with BCH balances and tokens and state field 1`] = `
Array [
0.06
XEC
,
$
NaN
USD
,
,
Signatures
,
,
]
`;
exports[`Wallet without BCH balance 1`] = `
Array [
You currently have 0
XEC
Deposit some funds to use this feature
,
,
Signatures
,
,
]
`;
exports[`Without wallet defined 1`] = `
Array [
You currently have 0
XEC
Deposit some funds to use this feature
,
,
Signatures
,
,
]
`;
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 774608643..32aade75e 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,622 +1,622 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Jest Snapshot v1, https://goo.gl/fbAQLP @generated
exports[`Wallet with BCH balances 1`] = `
Array [
🎉
Congratulations on your new wallet!
🎉
Start using the wallet immediately to receive
XEC
payments, or load it up with
XEC
to send to others
,
0
XEC
,
,
,
]
`;
exports[`Wallet with BCH balances and tokens 1`] = `
Array [
🎉
Congratulations on your new wallet!
🎉
Start using the wallet immediately to receive
XEC
payments, or load it up with
XEC
to send to others
,
0
XEC
,
,
,
]
`;
exports[`Wallet with BCH balances and tokens and state field 1`] = `
Array [
0.06
XEC
,
$
NaN
USD
,
,
,
]
`;
exports[`Wallet without BCH balance 1`] = `
Array [
🎉
Congratulations on your new wallet!
🎉
Start using the wallet immediately to receive
XEC
payments, or load it up with
XEC
to send to others
,
0
XEC
,
,
,
]
`;
exports[`Without wallet defined 1`] = `
Array [
Welcome to Cashtab!
,
Cashtab is an
open source,
non-custodial web wallet for
eCash
.
Want to learn more?
Check out the Cashtab documentation.
,
New Wallet
,
Import Wallet
,
]
`;