diff --git a/web/cashtab/src/assets/styles/theme.js b/web/cashtab/src/assets/styles/theme.js new file mode 100644 index 000000000..f887f4b48 --- /dev/null +++ b/web/cashtab/src/assets/styles/theme.js @@ -0,0 +1,3 @@ +export const theme = { + iconOutlined: '#3e3f42', +}; diff --git a/web/cashtab/src/components/App.css b/web/cashtab/src/components/App.css index ad7a48277..6a93f313f 100644 --- a/web/cashtab/src/components/App.css +++ b/web/cashtab/src/components/App.css @@ -1,152 +1,148 @@ @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 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*/ -/* Configure.js, CustomIcons.js */ -.anticon { - color: #3e3f42; -} /* EnhancedInputs.js */ .ant-input-group-addon { background-color: #f4f4f4 !important; border: 1px solid rgb(234, 237, 243); color: #3e3f42 !important; } /* useWallet.js, useBCH.js */ @media (max-width: 768px) { .ant-notification { width: 100%; top: 20px !important; max-width: unset; margin-right: 0; } } /* OK and Cancel button colors on Modals*/ .ant-card-actions > li > span:hover, .ant-btn:hover, .ant-btn:focus { color: #f59332; transition: color 0.3s; background-color: white; } .ant-btn { border-radius: 8px; background-color: #fff; color: rgb(62, 63, 66); font-weight: bold; } /*Custom Input Fields */ input.ant-input, .ant-select-selection { background-color: #fff !important; box-shadow: none !important; border-radius: 4px; font-weight: bold; color: rgb(62, 63, 66); opacity: 1; height: 50px; } .ant-select-selection:hover { border: 1px solid #eaedf3; } .ant-select-selection-selected-value { color: rgb(62, 63, 66); } .ant-select-dropdown-menu-item { color: #444; background-color: #fff; } .ant-select-dropdown-menu-item-active, .ant-select-dropdown-menu-item:hover { color: #fff; background-color: #ff8d00 !important; } .selectedCurrencyOption:hover { color: #fff !important; background-color: #ff8d00 !important; } .ant-input-affix-wrapper { background-color: #fff; border: 1px solid #eaedf3 !important; } input.ant-input, .ant-select-selection { border: none; } .ant-input::placeholder { text-align: left; } .ant-select-selector { height: 60px !important; border: 1px solid #eaedf3 !important; } /*Revs with updated Antd*/ .ant-select-single .ant-select-selector .ant-select-selection-item, .ant-select-single .ant-select-selector .ant-select-selection-placeholder { line-height: 60px; text-align: left; color: #3e3f42; font-weight: bold; } /* Handle new antd error formatting */ .ant-form-item-has-error > div > div.ant-form-item-control-input > div > span > span > span.ant-input-affix-wrapper { background-color: #fff; border-color: #f04134 !important; } .ant-form-item-has-error .ant-input, .ant-form-item-has-error .ant-input-affix-wrapper, .ant-form-item-has-error .ant-input:hover, .ant-form-item-has-error .ant-input-affix-wrapper:hover { background-color: #fff; border-color: #f04134 !important; } .ant-form-item-has-error .ant-select:not(.ant-select-disabled):not(.ant-select-customize-input) .ant-select-selector { background-color: #fff; border-color: #f04134 !important; } diff --git a/web/cashtab/src/components/App.js b/web/cashtab/src/components/App.js index 459599f54..6b857cf19 100644 --- a/web/cashtab/src/components/App.js +++ b/web/cashtab/src/components/App.js @@ -1,243 +1,248 @@ import React from 'react'; import 'antd/dist/antd.less'; import '../index.css'; -import styled from 'styled-components'; +import styled, { ThemeProvider } from 'styled-components'; +import { theme } from '@assets/styles/theme'; import { FolderOpenFilled, CaretRightOutlined, SettingFilled, } from '@ant-design/icons'; import Wallet from '@components/Wallet/Wallet'; 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.png'; import TabCash from '@assets/tabcash.png'; import ABC from '@assets/bitcoinabclogo.png'; import './App.css'; import { WalletContext } from '@utils/context'; import { checkForTokenById } from '@utils/tokenMethods.js'; import WalletLabel from '@components/Common/WalletLabel.js'; import { Route, Redirect, Switch, useLocation, useHistory, } from 'react-router-dom'; import fbt from 'fbt'; const CustomApp = styled.div` text-align: center; font-family: 'Gilroy', sans-serif; background-color: #fbfbfd; `; const Footer = styled.div` background-color: #fff; border-radius: 20px; position: fixed; bottom: 0; width: 500px; @media (max-width: 768px) { width: 100%; } border-top: 1px solid #e2e2e2; `; export const NavButton = styled.button` :focus, :active { outline: none; } cursor: pointer; padding: 24px 12px 12px 12px; margin: 0 28px; @media (max-width: 360px) { margin: 0 12px; } background-color: #fff; border: none; font-size: 12px; font-weight: bold; .anticon { display: block; color: rgb(148, 148, 148); font-size: 24px; margin-bottom: 6px; } ${({ active }) => active && ` color: #ff8d00; .anticon { color: #ff8d00; } `} `; export const WalletBody = styled.div` display: flex; align-items: center; justify-content: center; width: 100%; min-height: 100vh; background: linear-gradient(270deg, #040c3c, #212c6e); `; export const WalletCtn = styled.div` position: relative; width: 500px; background-color: #fff; min-height: 100vh; padding: 10px 30px 120px 30px; background: #fff; -webkit-box-shadow: 0px 0px 24px 1px rgba(0, 0, 0, 1); -moz-box-shadow: 0px 0px 24px 1px rgba(0, 0, 0, 1); box-shadow: 0px 0px 24px 1px rgba(0, 0, 0, 1); @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 #e2e2e2; a { color: #848484; :hover { color: #ff8d00; } } @media (max-width: 768px) { a { font-size: 12px; } padding: 10px 0 20px; } `; 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; } `; export const CashTabLogo = styled.img` width: 120px; @media (max-width: 768px) { width: 110px; } `; export const AbcLogo = styled.img` width: 150px; @media (max-width: 768px) { width: 120px; } `; const App = () => { const ContextValue = React.useContext(WalletContext); const { wallet, tokens } = ContextValue; const hasTab = checkForTokenById( tokens, '50d8292c6255cda7afc6c8566fed3cf42a2794e9619740fe8f4c95431271410e', ); const location = useLocation(); const history = useHistory(); const selectedKey = location && location.pathname ? location.pathname.substr(1) : ''; return ( - - - - - - {hasTab && } - - - - - - - - - - - - - ( - + + + + + + + {hasTab && ( + )} - /> - - - - - - - - {wallet ? ( - + ) : null} + + + ); }; export default App; diff --git a/web/cashtab/src/components/Common/CustomIcons.js b/web/cashtab/src/components/Common/CustomIcons.js index 2b123ba9f..eb5caf9c4 100644 --- a/web/cashtab/src/components/Common/CustomIcons.js +++ b/web/cashtab/src/components/Common/CustomIcons.js @@ -1,32 +1,51 @@ import * as React from 'react'; import styled from 'styled-components'; -import { LoadingOutlined } from '@ant-design/icons'; +import { + CopyOutlined, + DollarOutlined, + LoadingOutlined, + WalletOutlined, + QrcodeOutlined, +} from '@ant-design/icons'; export const CashLoadingIcon = ( ); +export const ThemedCopyOutlined = styled(CopyOutlined)` + color: ${props => props.theme.iconOutlined} !important; +`; +export const ThemedDollarOutlined = styled(DollarOutlined)` + color: ${props => props.theme.iconOutlined} !important; +`; +export const ThemedWalletOutlined = styled(WalletOutlined)` + color: ${props => props.theme.iconOutlined} !important; +`; +export const ThemedQrcodeOutlined = styled(QrcodeOutlined)` + color: ${props => props.theme.iconOutlined} !important; +`; + export const LoadingBlock = styled.div` width: 100%; display: flex; align-items: center; justify-content: center; padding: 24px; flex-direction: column; svg { width: 50px; height: 50px; fill: #ff8d00; } `; export const CashLoader = () => ( ); diff --git a/web/cashtab/src/components/Common/EnhancedInputs.js b/web/cashtab/src/components/Common/EnhancedInputs.js index 8dd181be8..d61b93d63 100644 --- a/web/cashtab/src/components/Common/EnhancedInputs.js +++ b/web/cashtab/src/components/Common/EnhancedInputs.js @@ -1,183 +1,186 @@ import * as React from 'react'; import { Form, Input, Select } from 'antd'; -import { DollarOutlined, WalletOutlined } from '@ant-design/icons'; +import { + ThemedDollarOutlined, + ThemedWalletOutlined, +} from '@components/Common/CustomIcons'; import styled from 'styled-components'; import { ScanQRCode } from './ScanQRCode'; import useBCH from '@hooks/useBCH'; import { currency } from '@components/Common/Ticker.js'; export const InputAddonText = styled.span` width: 100%; height: 100%; display: block; ${props => props.disabled ? ` cursor: not-allowed; ` : `cursor: pointer;`} `; export const InputNumberAddonText = styled.span` background-color: #f4f4f4 !important; border: 1px solid rgb(234, 237, 243); color: #3e3f42 !important; height: 50px; line-height: 47px; * { color: #3e3f42 !important; } ${props => props.disabled ? ` cursor: not-allowed; ` : `cursor: pointer;`} `; export const SendBchInput = ({ onMax, inputProps, selectProps, ...otherProps }) => { const { Option } = Select; const currencies = [ { value: currency.ticker, label: currency.ticker, }, { value: 'USD', label: 'USD' }, ]; const currencyOptions = currencies.map(currency => { return ( ); }); const CurrencySelect = ( ); return ( + ) : ( ) } {...inputProps} /> {CurrencySelect} max ); }; export const FormItemWithMaxAddon = ({ onMax, inputProps, ...otherProps }) => { return ( } addonAfter={ max } {...inputProps} /> ); }; // loadWithCameraOpen prop: if true, load page with camera scanning open export const FormItemWithQRCodeAddon = ({ onScan, loadWithCameraOpen, inputProps, ...otherProps }) => { return ( } + prefix={} autoComplete="off" addonAfter={ } {...inputProps} /> ); }; export const AddressValidators = () => { const { BCH } = useBCH(); return { safelyDetectAddressFormat: value => { try { return BCH.Address.detectAddressFormat(value); } catch (error) { return null; } }, isSLPAddress: value => AddressValidators.safelyDetectAddressFormat(value) === 'slpaddr', isBCHAddress: value => AddressValidators.safelyDetectAddressFormat(value) === 'cashaddr', isLegacyAddress: value => AddressValidators.safelyDetectAddressFormat(value) === 'legacy', }(); }; diff --git a/web/cashtab/src/components/Common/ScanQRCode.js b/web/cashtab/src/components/Common/ScanQRCode.js index f65010a1f..33eaf4acd 100644 --- a/web/cashtab/src/components/Common/ScanQRCode.js +++ b/web/cashtab/src/components/Common/ScanQRCode.js @@ -1,170 +1,170 @@ import React, { useState } from 'react'; import { Alert, Modal } from 'antd'; -import { QrcodeOutlined } from '@ant-design/icons'; +import { ThemedQrcodeOutlined } from '@components/Common/CustomIcons'; import styled from 'styled-components'; import { BrowserQRCodeReader } from '@zxing/library'; import { currency, isValidCashPrefix, isValidTokenPrefix, } from '@components/Common/Ticker.js'; import { Event } from '@utils/GoogleAnalytics'; const StyledScanQRCode = styled.span` display: block; `; const StyledModal = styled(Modal)` width: 400px !important; height: 400px !important; .ant-modal-close { top: 0 !important; right: 0 !important; } `; const QRPreview = styled.video` width: 100%; `; export const ScanQRCode = ({ width, loadWithCameraOpen, onScan = () => null, ...otherProps }) => { const [visible, setVisible] = useState(loadWithCameraOpen); const [error, setError] = useState(false); // Use these states to debug video errors on mobile // Note: iOS chrome/brave/firefox does not support accessing camera, will throw error // iOS users can use safari // todo only show scanner with safari //const [mobileError, setMobileError] = useState(false); //const [mobileErrorMsg, setMobileErrorMsg] = useState(false); const [activeCodeReader, setActiveCodeReader] = useState(null); const teardownCodeReader = codeReader => { if (codeReader !== null) { codeReader.reset(); codeReader.stop(); codeReader = null; setActiveCodeReader(codeReader); } }; const parseContent = content => { let type = 'unknown'; let values = {}; // If what scanner reads from QR code begins with 'bitcoincash:' or 'simpleledger:' or their successor prefixes if (isValidCashPrefix(content) || isValidTokenPrefix(content)) { type = 'address'; values = { address: content }; // Event("Category", "Action", "Label") // Track number of successful QR code scans // BCH or slp? let eventLabel = currency.ticker; const isToken = content.split(currency.tokenPrefix).length > 1; if (isToken) { eventLabel = currency.tokenTicker; } Event('ScanQRCode.js', 'Address Scanned', eventLabel); } return { type, values }; }; const scanForQrCode = async () => { const codeReader = new BrowserQRCodeReader(); setActiveCodeReader(codeReader); try { // Need to execute this before you can decode input // eslint-disable-next-line no-unused-vars const videoInputDevices = await codeReader.getVideoInputDevices(); //console.log(`videoInputDevices`, videoInputDevices); //setMobileError(JSON.stringify(videoInputDevices)); // choose your media device (webcam, frontal camera, back camera, etc.) // TODO implement if necessary //const selectedDeviceId = videoInputDevices[0].deviceId; //const previewElem = document.querySelector("#test-area-qr-code-webcam"); const content = await codeReader.decodeFromInputVideoDevice( undefined, 'test-area-qr-code-webcam', ); const result = parseContent(content.text); // stop scanning and fill form if it's an address if (result.type === 'address') { // Hide the scanner setVisible(false); onScan(result.values.address); return teardownCodeReader(codeReader); } } catch (err) { console.log(`Error in QR scanner:`); console.log(err); console.log(JSON.stringify(err.message)); //setMobileErrorMsg(JSON.stringify(err.message)); setError(err); teardownCodeReader(codeReader); } // stop scanning after 20s no matter what }; React.useEffect(() => { if (!visible) { setError(false); // Stop the camera if user closes modal if (activeCodeReader !== null) { teardownCodeReader(activeCodeReader); } } else { scanForQrCode(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [visible]); return ( <> setVisible(!visible)} > - + setVisible(false)} footer={null} > {visible ? (
{error ? ( <> {/*

{mobileError}

{mobileErrorMsg}

*/} ) : ( )}
) : null}
); }; diff --git a/web/cashtab/src/components/Configure/Configure.js b/web/cashtab/src/components/Configure/Configure.js index 8d4050ca2..64ad4e137 100644 --- a/web/cashtab/src/components/Configure/Configure.js +++ b/web/cashtab/src/components/Configure/Configure.js @@ -1,586 +1,589 @@ /* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import { Collapse, Form, Input, Modal, Spin, Alert } from 'antd'; import { PlusSquareOutlined, WalletFilled, - WalletOutlined, ImportOutlined, - CopyOutlined, LockOutlined, } from '@ant-design/icons'; import { WalletContext } from '@utils/context'; import { StyledCollapse } from '@components/Common/StyledCollapse'; import PrimaryButton, { SecondaryButton, SmartButton, } from '@components/Common/PrimaryButton'; -import { CashLoader, CashLoadingIcon } from '@components/Common/CustomIcons'; +import { + CashLoader, + CashLoadingIcon, + ThemedCopyOutlined, + ThemedWalletOutlined, +} from '@components/Common/CustomIcons'; import { ReactComponent as Trashcan } from '@assets/trashcan.svg'; import { ReactComponent as Edit } from '@assets/edit.svg'; import { Event } from '@utils/GoogleAnalytics'; const { Panel } = Collapse; const SettingsLink = styled.a` text-decoration: underline; color: #ff8d00; :visited { text-decoration: underline; color: #ff8d00; } `; const SWRow = styled.div` border-radius: 3px; padding: 10px 0; display: flex; align-items: center; justify-content: center; margin-bottom: 6px; @media (max-width: 500px) { flex-direction: column; margin-bottom: 12px; } `; const SWName = styled.div` width: 50%; display: flex; align-items: center; justify-content: space-between; word-wrap: break-word; hyphens: auto; @media (max-width: 500px) { width: 100%; justify-content: center; margin-bottom: 15px; } h3 { font-size: 16px; color: #444; margin: 0; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } `; const SWButtonCtn = styled.div` width: 50%; display: flex; align-items: center; justify-content: flex-end; @media (max-width: 500px) { width: 100%; justify-content: center; } button { cursor: pointer; @media (max-width: 768px) { font-size: 14px; } } svg { stroke: #444; fill: #444; width: 25px; height: 25px; margin-right: 20px; cursor: pointer; :first-child:hover { stroke: #ff8d00; fill: #ff8d00; } :hover { stroke: red; fill: red; } } `; const AWRow = styled.div` padding: 10px 0; display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; h3 { font-size: 16px; display: inline-block; color: #444; margin: 0; text-align: left; font-weight: bold; @media (max-width: 500px) { font-size: 14px; } } h4 { font-size: 16px; display: inline-block; color: #ff8d00 !important; margin: 0; text-align: right; } @media (max-width: 500px) { flex-direction: column; margin-bottom: 12px; } `; const StyledConfigure = styled.div` h2 { color: #444; font-size: 25px; } p { color: #444; } `; const StyledSpacer = styled.div` height: 1px; width: 100%; background-color: #e2e2e2; margin: 60px 0 50px; `; const Configure = () => { const ContextValue = React.useContext(WalletContext); const { wallet, loading, apiError } = ContextValue; const { addNewSavedWallet, activateWallet, renameWallet, deleteWallet, validateMnemonic, getSavedWallets, } = ContextValue; const [savedWallets, setSavedWallets] = useState([]); const [formData, setFormData] = useState({ dirty: true, mnemonic: '', }); const [showRenameWalletModal, setShowRenameWalletModal] = useState(false); const [showDeleteWalletModal, setShowDeleteWalletModal] = useState(false); const [walletToBeRenamed, setWalletToBeRenamed] = useState(null); const [walletToBeDeleted, setWalletToBeDeleted] = useState(null); const [newWalletName, setNewWalletName] = useState(''); const [ confirmationOfWalletToBeDeleted, setConfirmationOfWalletToBeDeleted, ] = useState(''); const [newWalletNameIsValid, setNewWalletNameIsValid] = useState(null); const [walletDeleteValid, setWalletDeleteValid] = useState(null); const [seedInput, openSeedInput] = useState(false); const showPopulatedDeleteWalletModal = walletInfo => { setWalletToBeDeleted(walletInfo); setShowDeleteWalletModal(true); }; const showPopulatedRenameWalletModal = walletInfo => { setWalletToBeRenamed(walletInfo); setShowRenameWalletModal(true); }; const cancelRenameWallet = () => { // Delete form value setNewWalletName(''); setShowRenameWalletModal(false); }; const cancelDeleteWallet = () => { setWalletToBeDeleted(null); setConfirmationOfWalletToBeDeleted(''); setShowDeleteWalletModal(false); }; const updateSavedWallets = async activeWallet => { if (activeWallet) { let savedWallets; try { savedWallets = await getSavedWallets(activeWallet); setSavedWallets(savedWallets); } catch (err) { console.log(`Error in getSavedWallets()`); console.log(err); } } }; const [isValidMnemonic, setIsValidMnemonic] = useState(false); useEffect(() => { // Update savedWallets every time the active wallet changes updateSavedWallets(wallet); }, [wallet]); // Need this function to ensure that savedWallets are updated on new wallet creation const updateSavedWalletsOnCreate = async importMnemonic => { // Event("Category", "Action", "Label") // Track number of times a different wallet is activated Event('Configure.js', 'Create Wallet', 'New'); const walletAdded = await addNewSavedWallet(importMnemonic); if (!walletAdded) { Modal.error({ title: 'This wallet already exists!', content: 'Wallet not added', }); } else { Modal.success({ content: 'Wallet added to your saved wallets', }); } await updateSavedWallets(wallet); }; // Same here // TODO you need to lock UI here until this is complete // Otherwise user may try to load an already-loading wallet, wreak havoc with indexedDB const updateSavedWalletsOnLoad = async walletToActivate => { // Event("Category", "Action", "Label") // Track number of times a different wallet is activated Event('Configure.js', 'Activate', ''); await activateWallet(walletToActivate); await updateSavedWallets(wallet); }; async function submit() { setFormData({ ...formData, dirty: false, }); // Exit if no user input if (!formData.mnemonic) { return; } // Exit if mnemonic is invalid if (!isValidMnemonic) { return; } // Event("Category", "Action", "Label") // Track number of times a different wallet is activated Event('Configure.js', 'Create Wallet', 'Imported'); updateSavedWalletsOnCreate(formData.mnemonic); } const handleChange = e => { const { value, name } = e.target; // Validate mnemonic on change // Import button should be disabled unless mnemonic is valid setIsValidMnemonic(validateMnemonic(value)); setFormData(p => ({ ...p, [name]: value })); }; const changeWalletName = async () => { if (newWalletName === '' || newWalletName.length > 24) { setNewWalletNameIsValid(false); return; } // Hide modal setShowRenameWalletModal(false); // Change wallet name console.log( `Changing wallet ${walletToBeRenamed.name} name to ${newWalletName}`, ); const renameSuccess = await renameWallet( walletToBeRenamed.name, newWalletName, ); if (renameSuccess) { Modal.success({ content: `Wallet "${walletToBeRenamed.name}" renamed to "${newWalletName}"`, }); } else { Modal.error({ content: `Rename failed. All wallets must have a unique name.`, }); } await updateSavedWallets(wallet); // Clear wallet name for form setNewWalletName(''); }; const deleteSelectedWallet = async () => { if (!walletDeleteValid) { return; } if ( confirmationOfWalletToBeDeleted !== `delete ${walletToBeDeleted.name}` ) { setWalletDeleteValid(false); return; } // Hide modal setShowDeleteWalletModal(false); // Change wallet name console.log(`Deleting wallet "${walletToBeDeleted.name}"`); const walletDeletedSuccess = await deleteWallet(walletToBeDeleted); if (walletDeletedSuccess) { Modal.success({ content: `Wallet "${walletToBeDeleted.name}" successfully deleted`, }); } else { Modal.error({ content: `Error deleting ${walletToBeDeleted.name}.`, }); } await updateSavedWallets(wallet); // Clear wallet delete confirmation from form setConfirmationOfWalletToBeDeleted(''); }; const handleWalletNameInput = e => { const { value } = e.target; // validation if (value && value.length && value.length < 24) { setNewWalletNameIsValid(true); } else { setNewWalletNameIsValid(false); } setNewWalletName(value); }; const handleWalletToDeleteInput = e => { const { value } = e.target; if (value && value === `delete ${walletToBeDeleted.name}`) { setWalletDeleteValid(true); } else { setWalletDeleteValid(false); } setConfirmationOfWalletToBeDeleted(value); }; return ( {walletToBeRenamed !== null && ( cancelRenameWallet()} >
} placeholder="Enter new wallet name" name="newName" value={newWalletName} onChange={e => handleWalletNameInput(e)} />
)} {walletToBeDeleted !== null && ( cancelDeleteWallet()} >
} placeholder={`Type "delete ${walletToBeDeleted.name}" to confirm`} name="walletToBeDeletedInput" value={confirmationOfWalletToBeDeleted} onChange={e => handleWalletToDeleteInput(e)} />
)}

- Backup your wallet + Backup your wallet

{wallet && wallet.mnemonic && (

{wallet && wallet.mnemonic ? wallet.mnemonic : ''}

)}

- Manage Wallets + Manage Wallets

{apiError ? ( <>

An error occured on our end. Reconnecting...

) : ( <> updateSavedWalletsOnCreate()} > New Wallet openSeedInput(!seedInput)} > Import Wallet {seedInput && ( <>

Copy and paste your mnemonic seed phrase below to import an existing wallet

} placeholder="mnemonic (seed phrase)" name="mnemonic" autoComplete="off" onChange={e => handleChange(e)} required /> submit()} > Import
)} )} {savedWallets && savedWallets.length > 0 && ( <>

{wallet.name}

Currently active

{savedWallets.map(sw => (

{sw.name}

showPopulatedRenameWalletModal( sw, ) } /> showPopulatedDeleteWalletModal( sw, ) } />
))}
)} [ Documentation ]
); }; export default Configure; diff --git a/web/cashtab/src/components/Configure/__tests__/__snapshots__/Configure.test.js.snap b/web/cashtab/src/components/Configure/__tests__/__snapshots__/Configure.test.js.snap index fad5b353e..945f92d06 100644 --- a/web/cashtab/src/components/Configure/__tests__/__snapshots__/Configure.test.js.snap +++ b/web/cashtab/src/components/Configure/__tests__/__snapshots__/Configure.test.js.snap @@ -1,449 +1,449 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Configure with a wallet 1`] = `

Backup your wallet

Your seed phrase is the only way to restore your wallet. Write it down. Keep it safe.
Click to reveal seed phrase

Manage Wallets

`; exports[`Configure without a wallet 1`] = `

Backup your wallet

Your seed phrase is the only way to restore your wallet. Write it down. Keep it safe.

Manage Wallets

`; 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 275638411..82db81ee8 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,508 +1,508 @@ // 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
, View Transactions , ] `; exports[`Wallet with BCH balances and tokens 1`] = ` Array [
0.06047469 BCHA
,
$ NaN USD
,
Copied
qzagy4 7mvh6qxkvcn3acjnz73rkhkc6y7cpt zgcqy6
,
BCHA
SLPA
, View Transactions ,

SLPA Tokens

identicon of tokenId bd1acc4c986de57af8d6d2a64aecad8c30ee80f37ae9d066d758923732ddc9ba
6.001 TBS
, ] `; 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.

, , , ] `;