Changeset View
Changeset View
Standalone View
Standalone View
web/cashtab/src/components/Send/Send.js
Show First 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | const ConvertAmount = styled.div` | ||||
font-size: 14px; | font-size: 14px; | ||||
margin-bottom: 10px; | margin-bottom: 10px; | ||||
font-weight: bold; | font-weight: bold; | ||||
@media (max-width: 768px) { | @media (max-width: 768px) { | ||||
font-size: 12px; | font-size: 12px; | ||||
} | } | ||||
`; | `; | ||||
const SendBCH = ({ filledAddress, callbackTxId }) => { | // Note jestBCH is only used for unit tests; BCHJS must be mocked for jest | ||||
const { | const SendBCH = ({ jestBCH, filledAddress, callbackTxId }) => { | ||||
wallet, | // use balance parameters from wallet.state object and not legacy balances parameter from walletState, if user has migrated wallet | ||||
fiatPrice, | // this handles edge case of user with old wallet who has not opened latest Cashtab version yet | ||||
balances, | |||||
slpBalancesAndUtxos, | // If the wallet object from ContextValue has a `state key`, then check which keys are in the wallet object | ||||
apiError, | // Else set it as blank | ||||
} = React.useContext(WalletContext); | const ContextValue = React.useContext(WalletContext); | ||||
const { wallet, fiatPrice, slpBalancesAndUtxos, apiError } = ContextValue; | |||||
let balances; | |||||
const paramsInWalletState = wallet.state ? Object.keys(wallet.state) : []; | |||||
// If wallet.state includes balances and parsedTxHistory params, use these | |||||
// These are saved in indexedDb in the latest version of the app, hence accessible more quickly | |||||
if (paramsInWalletState.includes('balances')) { | |||||
balances = wallet.state.balances; | |||||
} else { | |||||
// If balances and parsedTxHistory are not in the wallet.state object, load them from Context | |||||
// This is how the app used to work | |||||
balances = ContextValue.balances; | |||||
} | |||||
// Get device window width | // Get device window width | ||||
// If this is less than 769, the page will open with QR scanner open | // If this is less than 769, the page will open with QR scanner open | ||||
const { width } = useWindowDimensions(); | const { width } = useWindowDimensions(); | ||||
// Load with QR code open if device is mobile and NOT iOS + anything but safari | // Load with QR code open if device is mobile and NOT iOS + anything but safari | ||||
const scannerSupported = width < 769 && isMobile && !(isIOS && !isSafari); | const scannerSupported = width < 769 && isMobile && !(isIOS && !isSafari); | ||||
const [formData, setFormData] = useState({ | const [formData, setFormData] = useState({ | ||||
Show All 22 Lines | const handleOk = () => { | ||||
submit(); | submit(); | ||||
}; | }; | ||||
const handleCancel = () => { | const handleCancel = () => { | ||||
setIsModalVisible(false); | setIsModalVisible(false); | ||||
}; | }; | ||||
const { getBCH, getRestUrl, sendBch, calcFee } = useBCH(); | const { getBCH, getRestUrl, sendBch, calcFee } = useBCH(); | ||||
const BCH = getBCH(); | |||||
// jestBCH is only ever specified for unit tests, otherwise app will use getBCH(); | |||||
const BCH = jestBCH ? jestBCH : getBCH(); | |||||
// If the balance has changed, unlock the UI | // If the balance has changed, unlock the UI | ||||
// This is redundant, if backend has refreshed in 1.75s timeout below, UI will already be unlocked | // This is redundant, if backend has refreshed in 1.75s timeout below, UI will already be unlocked | ||||
useEffect(() => { | useEffect(() => { | ||||
setLoading(false); | setLoading(false); | ||||
}, [balances.totalBalance]); | }, [balances.totalBalance]); | ||||
useEffect(() => { | useEffect(() => { | ||||
Show All 20 Lines | useEffect(() => { | ||||
} | } | ||||
console.log(`txInfo from page params`, txInfo); | console.log(`txInfo from page params`, txInfo); | ||||
setTxInfoFromUrl(txInfo); | setTxInfoFromUrl(txInfo); | ||||
populateFormsFromUrl(txInfo); | populateFormsFromUrl(txInfo); | ||||
}, []); | }, []); | ||||
function populateFormsFromUrl(txInfo) { | function populateFormsFromUrl(txInfo) { | ||||
if (txInfo && txInfo.address && txInfo.value) { | if (txInfo && txInfo.address && txInfo.value) { | ||||
setFormData({ address: txInfo.address, value: txInfo.value }); | setFormData({ | ||||
address: txInfo.address, | |||||
value: txInfo.value, | |||||
}); | |||||
} | } | ||||
} | } | ||||
async function submit() { | async function submit() { | ||||
setFormData({ | setFormData({ | ||||
...formData, | ...formData, | ||||
dirty: false, | dirty: false, | ||||
}); | }); | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | const handleAddressChange = e => { | ||||
setSendBchAddressError(error); | setSendBchAddressError(error); | ||||
// Set amount if it's in the query string | // Set amount if it's in the query string | ||||
if (amount !== null) { | if (amount !== null) { | ||||
// Set currency to BCHA | // Set currency to BCHA | ||||
setSelectedCurrency(currency.ticker); | setSelectedCurrency(currency.ticker); | ||||
// Use this object to mimic user input and get validation for the value | // Use this object to mimic user input and get validation for the value | ||||
let amountObj = { target: { name: 'value', value: amount } }; | let amountObj = { | ||||
target: { | |||||
name: 'value', | |||||
value: amount, | |||||
}, | |||||
}; | |||||
handleBchAmountChange(amountObj); | handleBchAmountChange(amountObj); | ||||
setFormData({ | setFormData({ | ||||
...formData, | ...formData, | ||||
value: amount, | value: amount, | ||||
}); | }); | ||||
} | } | ||||
// Set address field to user input | // Set address field to user input | ||||
setFormData(p => ({ | setFormData(p => ({ | ||||
...p, | ...p, | ||||
[name]: value, | [name]: value, | ||||
})); | })); | ||||
}; | }; | ||||
const handleSelectedCurrencyChange = e => { | const handleSelectedCurrencyChange = e => { | ||||
setSelectedCurrency(e); | setSelectedCurrency(e); | ||||
// Clear input field to prevent accidentally sending 1 BCH instead of 1 USD | // Clear input field to prevent accidentally sending 1 BCH instead of 1 USD | ||||
setFormData(p => ({ ...p, value: '' })); | setFormData(p => ({ | ||||
...p, | |||||
value: '', | |||||
})); | |||||
}; | }; | ||||
const handleBchAmountChange = e => { | const handleBchAmountChange = e => { | ||||
const { value, name } = e.target; | const { value, name } = e.target; | ||||
let bchValue = value; | let bchValue = value; | ||||
const error = shouldRejectAmountInput( | const error = shouldRejectAmountInput( | ||||
bchValue, | bchValue, | ||||
selectedCurrency, | selectedCurrency, | ||||
fiatPrice, | fiatPrice, | ||||
balances.totalBalance, | balances.totalBalance, | ||||
); | ); | ||||
setSendBchAmountError(error); | setSendBchAmountError(error); | ||||
setFormData(p => ({ ...p, [name]: value })); | setFormData(p => ({ | ||||
...p, | |||||
[name]: value, | |||||
})); | |||||
}; | }; | ||||
const onMax = async () => { | const onMax = async () => { | ||||
// Clear amt error | // Clear amt error | ||||
setSendBchAmountError(false); | setSendBchAmountError(false); | ||||
// Set currency to BCH | // Set currency to BCH | ||||
setSelectedCurrency(currency.ticker); | setSelectedCurrency(currency.ticker); | ||||
try { | try { | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | return ( | ||||
</BalanceHeaderFiat> | </BalanceHeaderFiat> | ||||
)} | )} | ||||
</> | </> | ||||
)} | )} | ||||
<Row type="flex"> | <Row type="flex"> | ||||
<Col span={24}> | <Col span={24}> | ||||
<Spin spinning={loading} indicator={CashLoadingIcon}> | <Spin spinning={loading} indicator={CashLoadingIcon}> | ||||
<Form style={{ width: 'auto' }}> | <Form | ||||
style={{ | |||||
width: 'auto', | |||||
}} | |||||
> | |||||
<FormItemWithQRCodeAddon | <FormItemWithQRCodeAddon | ||||
loadWithCameraOpen={scannerSupported} | loadWithCameraOpen={scannerSupported} | ||||
disabled={Boolean(filledAddress)} | disabled={Boolean(filledAddress)} | ||||
validateStatus={ | validateStatus={ | ||||
sendBchAddressError ? 'error' : '' | sendBchAddressError ? 'error' : '' | ||||
} | } | ||||
help={ | help={ | ||||
sendBchAddressError | sendBchAddressError | ||||
Show All 36 Lines | return ( | ||||
selectProps={{ | selectProps={{ | ||||
value: selectedCurrency, | value: selectedCurrency, | ||||
disabled: queryStringText !== null, | disabled: queryStringText !== null, | ||||
onChange: e => | onChange: e => | ||||
handleSelectedCurrencyChange(e), | handleSelectedCurrencyChange(e), | ||||
}} | }} | ||||
></SendBchInput> | ></SendBchInput> | ||||
<ConvertAmount>= {fiatPriceString}</ConvertAmount> | <ConvertAmount>= {fiatPriceString}</ConvertAmount> | ||||
<div style={{ paddingTop: '12px' }}> | <div | ||||
style={{ | |||||
paddingTop: '12px', | |||||
}} | |||||
> | |||||
{!balances.totalBalance || | {!balances.totalBalance || | ||||
apiError || | apiError || | ||||
sendBchAmountError || | sendBchAmountError || | ||||
sendBchAddressError ? ( | sendBchAddressError ? ( | ||||
<SecondaryButton>Send</SecondaryButton> | <SecondaryButton>Send</SecondaryButton> | ||||
) : ( | ) : ( | ||||
<> | <> | ||||
{txInfoFromUrl ? ( | {txInfoFromUrl ? ( | ||||
Show All 16 Lines | return ( | ||||
<Alert | <Alert | ||||
message={`You are sending a transaction to an address including query parameters "${queryStringText}." Only the "amount" parameter, in units of ${currency.ticker} satoshis, is currently supported.`} | message={`You are sending a transaction to an address including query parameters "${queryStringText}." Only the "amount" parameter, in units of ${currency.ticker} satoshis, is currently supported.`} | ||||
type="warning" | type="warning" | ||||
/> | /> | ||||
)} | )} | ||||
{apiError && ( | {apiError && ( | ||||
<> | <> | ||||
<CashLoader /> | <CashLoader /> | ||||
<p style={{ color: 'red' }}> | <p | ||||
style={{ | |||||
color: 'red', | |||||
}} | |||||
> | |||||
<b> | <b> | ||||
An error occured on our end. | An error occured on our end. | ||||
Reconnecting... | Reconnecting... | ||||
</b> | </b> | ||||
</p> | </p> | ||||
</> | </> | ||||
)} | )} | ||||
</Form> | </Form> | ||||
</Spin> | </Spin> | ||||
</Col> | </Col> | ||||
</Row> | </Row> | ||||
</> | </> | ||||
); | ); | ||||
}; | }; | ||||
export default SendBCH; | export default SendBCH; |