diff --git a/cashtab/package-lock.json b/cashtab/package-lock.json --- a/cashtab/package-lock.json +++ b/cashtab/package-lock.json @@ -1,12 +1,12 @@ { "name": "cashtab", - "version": "2.9.0", + "version": "2.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cashtab", - "version": "2.9.0", + "version": "2.9.1", "dependencies": { "@ant-design/icons": "^5.3.0", "@bitgo/utxo-lib": "^9.33.0", diff --git a/cashtab/package.json b/cashtab/package.json --- a/cashtab/package.json +++ b/cashtab/package.json @@ -1,6 +1,6 @@ { "name": "cashtab", - "version": "2.9.0", + "version": "2.9.1", "private": true, "scripts": { "start": "node scripts/start.js", diff --git a/cashtab/src/assets/eye-invisible.png b/cashtab/src/assets/eye-invisible.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ \ No newline at end of file diff --git a/cashtab/src/assets/eye.png b/cashtab/src/assets/eye.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ \ No newline at end of file diff --git a/cashtab/src/components/Airdrop/Airdrop.js b/cashtab/src/components/Airdrop/Airdrop.js --- a/cashtab/src/components/Airdrop/Airdrop.js +++ b/cashtab/src/components/Airdrop/Airdrop.js @@ -8,14 +8,7 @@ import { BN } from 'slp-mdm'; import styled from 'styled-components'; import { WalletContext } from 'wallet/context'; -import { - AntdFormWrapper, - DestinationAddressMulti, - InputAmountSingle, -} from 'components/Common/EnhancedInputs'; -import { Form, Input } from 'antd'; -import { Switch } from 'antd'; -import PrimaryButton from 'components/Common/PrimaryButton'; +import PrimaryButton, { SecondaryLink } from 'components/Common/PrimaryButton'; import CopyToClipboard from 'components/Common/CopyToClipboard'; import { getMintAddress } from 'chronik'; import { @@ -24,53 +17,39 @@ isValidAirdropExclusionArray, } from 'validation'; import { SidePaddingCtn } from 'components/Common/Atoms'; -import { Link } from 'react-router-dom'; import { getAirdropTx, getEqualAirdropTx } from 'airdrop'; import Communist from 'assets/communist.png'; import { toast } from 'react-toastify'; +import CashtabSwitch from 'components/Common/Switch'; +import { Input, TextArea, InputFlex } from 'components/Common/Inputs'; +import { ThemedCopySolid } from 'components/Common/CustomIcons'; -const { TextArea } = Input; -const Gulag = styled.img` - width: 20px; - height: 20px; +const AirdropForm = styled.div` + display: flex; + flex-direction: column; + gap: 12px; `; -const SwitchContentHolder = styled.div` +const FormRow = styled.div` display: flex; - align-items: center; + flex-direction: column; + gap: 12px; + color: ${props => props.theme.contrast}; `; -const AirdropActions = styled.div` - text-align: center; - width: 100%; - padding: 10px; - border-radius: 5px; +const SwitchHolder = styled.div` display: flex; + align-content: center; + gap: 12px; +`; +const AirdropTitle = styled.div` + display: flex; + align-items: flex-start; + gap: 12px; + text-align: center; justify-content: center; - a { - color: ${props => props.theme.contrast}; - margin: 0; - font-size: 11px; - border: 1px solid ${props => props.theme.contrast}; - border-radius: 5px; - padding: 2px 10px; - opacity: 0.6; - } - a:hover { - opacity: 1; - border-color: ${props => props.theme.eCashBlue}; - color: ${props => props.theme.contrast}; - background: ${props => props.theme.eCashBlue}; - } - ${({ received, ...props }) => - received && - ` - text-align: left; - background: ${props.theme.receivedMessage}; - `} `; - -const AirdropOptions = styled.div` - text-align: left; +const SwitchLabel = styled.div` color: ${props => props.theme.contrast}; + font-size: 18px; `; const Airdrop = ({ passLoadingStatus }) => { @@ -110,7 +89,7 @@ const [ignoreCustomAddresses, setIgnoreCustomAddresses] = useState(false); // the exclusion list values const [ignoreCustomAddressesList, setIgnoreCustomAddressesList] = - useState(false); + useState(''); const [ ignoreCustomAddressesListIsValid, setIgnoreCustomAddressesListIsValid, @@ -178,6 +157,8 @@ } catch (err) { console.log(`Error getting token utxos from chronik`, err); toast.error('Error retrieving airdrop recipients'); + // Clear result field from earlier calc, if present, on any error + setAirdropRecipients(''); return passLoadingStatus(false); } @@ -195,6 +176,8 @@ toast.error( `Error determining mint address for ${formData.tokenId}`, ); + // Clear result field from earlier calc, if present, on any error + setAirdropRecipients(''); return passLoadingStatus(false); } } @@ -225,6 +208,8 @@ toast.error( `Error determining mint address for ${formData.tokenId}`, ); + // Clear result field from earlier calc, if present, on any error + setAirdropRecipients(''); return passLoadingStatus(false); } } else { @@ -255,6 +240,8 @@ // display the airdrop outputs TextArea setShowAirdropOutputs(true); } catch (err) { + // Clear result field from earlier calc, if present, on any error + setAirdropRecipients(''); toast.error(`${err}`); } return passLoadingStatus(false); @@ -286,9 +273,6 @@ let customAddressList = e.target.value; - // remove all whitespaces via regex - customAddressList = customAddressList.replace(/ /g, ''); - // validate the exclusion list input const addressListIsValid = isValidAirdropExclusionArray(customAddressList); @@ -296,7 +280,7 @@ if (!addressListIsValid) { setIgnoreCustomAddressListError( - 'Invalid address detected in ignore list', + 'Must be a comma-separated list of valid ecash-prefixed addresses with no spaces', ); } else { setIgnoreCustomAddressListError(false); // needs to be explicitly set in order to refresh the error state from prior invalidation @@ -334,248 +318,182 @@ <>
- -
- + + + handleTokenIdInput(e)} + handleInput={handleTokenIdInput} + error={ + tokenIdIsValid === false + ? 'Invalid eToken ID' + : false + } /> - - + + + + handleTotalAirdropInput(e)} + handleInput={handleTotalAirdropInput} + error={ + totalAirdropIsValid === false + ? 'Invalid total XEC airdrop' + : false + } /> - - - - - - "Equal"{' '} - - - - } - unCheckedChildren="Pro-Rata" - defaultunchecked="true" - checked={equalDistributionRatio} - onChange={() => { - setEqualDistributionRatio( - prev => !prev, - ); - }} - /> + + + + + + { + setEqualDistributionRatio(prev => !prev); + }} + /> + {equalDistributionRatio - ? ` Airdrop qty + ? ` Airdrop the same for everyone` - : ` Airdrop qty + : ` Airdrop scaled to token balance`} - - - - - - handleIgnoreOwnAddress(prev => !prev) - } - defaultunchecked="true" - checked={ignoreOwnAddress} - /> -  Ignore my own address - - - - - - handleIgnoreMintAddress(prev => !prev) - } - defaultunchecked="true" - checked={ignoreMintAddress} - /> -  Ignore eToken minter address - - - - - - handleIgnoreMinEtokenBalanceAmt( - prev => !prev, - ) - } - defaultunchecked="true" - checked={ignoreMinEtokenBalance} - style={{ - marginBottom: '5px', - }} - /> -  Minimum eToken holder balance - {ignoreMinEtokenBalance && ( - - handleMinEtokenBalanceChange(e), - value: ignoreMinEtokenBalanceAmount, - }} - /> - )} - - - - - - handleIgnoreCustomAddresses( - prev => !prev, - ) - } - defaultunchecked="true" - checked={ignoreCustomAddresses} - style={{ - marginBottom: '5px', - }} - /> -  Ignore custom addresses - {ignoreCustomAddresses && ( - - handleIgnoreCustomAddressesList( - e, - ), - required: ignoreCustomAddresses, - disabled: !ignoreCustomAddresses, - }} - /> - )} - - - - calculateXecAirdrop()} - disabled={ - !airdropCalcInputIsValid || !tokenIdIsValid + + + + + + + handleIgnoreOwnAddress(prev => !prev) } - > - Calculate Airdrop - - - {showAirdropOutputs && ( - <> - - One to Many Airdrop Payment Outputs -