Skip to content

Commit

Permalink
Feature: Add destination tag field for xrp, add validation of some cu…
Browse files Browse the repository at this point in the history
…rrency addresses
  • Loading branch information
Oleksandr Khlopiachyi committed Jan 28, 2021
1 parent 3e73387 commit 058595c
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 48 deletions.
7 changes: 6 additions & 1 deletion src/components/Beneficiaries/Beneficiaries.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@
}

.beneficiaries-add-address-modal--coin .cr-email-form {
min-height: calc(var(--big-gap) * 6.6);
min-height: calc(var(--big-gap) * 7.2);

.pg-beneficiaries__error-text {
color: var(--system-red);
margin-left: 16px;
}
}

.beneficiaries-add-address-modal--fiat .cr-email-form {
Expand Down
111 changes: 64 additions & 47 deletions src/components/Beneficiaries/BeneficiariesAddModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
selectMobileDeviceState,
} from '../../modules';
import { CustomInput } from '../CustomInput';
import { validateBeneficiaryAddress } from '../../helpers/validateBeneficiaryAddress';

interface Props {
currency: string;
Expand All @@ -21,11 +22,14 @@ interface Props {

const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
const [coinAddress, setCoinAddress] = React.useState('');
const [coinAddressValid, setCoinAddressValid] = React.useState(false);
const [coinBeneficiaryName, setCoinBeneficiaryName] = React.useState('');
const [coinDescription, setCoinDescription] = React.useState('');
const [coinDestinationTag, setCoinDestinationTag] = React.useState('');
const [coinAddressFocused, setCoinAddressFocused] = React.useState(false);
const [coinBeneficiaryNameFocused, setCoinBeneficiaryNameFocused] = React.useState(false);
const [coinDescriptionFocused, setCoinDescriptionFocused] = React.useState(false);
const [coinDestinationTagFocused, setCoinDestinationTagFocused] = React.useState(false);

const [fiatName, setFiatName] = React.useState('');
const [fiatFullName, setFiatFullName] = React.useState('');
Expand All @@ -47,14 +51,18 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
const dispatch = useDispatch();

const isMobileDevice = useSelector(selectMobileDeviceState);
const isRipple = React.useMemo(() => currency === 'xrp' ? true : false, [currency]);

const handleClearModalsInputs = React.useCallback(() => {
setCoinAddress('');
setCoinBeneficiaryName('');
setCoinDescription('');
setCoinDestinationTag('');
setCoinAddressFocused(false);
setCoinBeneficiaryNameFocused(false);
setCoinDescriptionFocused(false);
setCoinDestinationTagFocused(false);
setCoinAddressValid(false);

setFiatAccountNumber('');
setFiatName('');
Expand All @@ -79,9 +87,9 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
if (clear) {
handleClearModalsInputs();
}
}, [handleClearModalsInputs, handleToggleAddAddressModal]);
}, [handleToggleAddAddressModal]);

const renderAddAddressModalHeader = React.useCallback(() => {
const renderAddAddressModalHeader = React.useMemo(() => {
return (
<div className="cr-email-form__options-group">
<div className="cr-email-form__option">
Expand All @@ -95,35 +103,30 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
</div>
</div>
);
}, [formatMessage, handleClickToggleAddAddressModal]);
}, [formatMessage]);

const handleSubmitAddAddressCoinModal = React.useCallback(() => {
// tslint:disable-next-line:no-any
let payload: any = {
const payload = {
currency: currency || '',
name: coinBeneficiaryName,
data: JSON.stringify({
address: coinAddress,
address: (isRipple && coinDestinationTag ? `${coinAddress}?dt=${coinDestinationTag}` : coinAddress),
}),
...(coinDescription && { description: coinDescription }),
};

if (coinDescription) {
payload = {
...payload,
description: coinDescription,
};
}

dispatch(beneficiariesCreate(payload));
handleClearModalsInputs();
}, [coinAddress, coinBeneficiaryName, coinDescription, dispatch, handleClearModalsInputs, currency]);
}, [coinAddress, coinBeneficiaryName, coinDescription, currency]);

const getState = React.useCallback(key => {
switch (key) {
case 'coinAddress':
return coinAddress;
case 'coinBeneficiaryName':
return coinBeneficiaryName;
case 'coinDestinationTag':
return coinDestinationTag;
case 'coinDescription':
return coinDescription;
case 'coinAddressFocused':
Expand All @@ -132,6 +135,8 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
return coinBeneficiaryNameFocused;
case 'coinDescriptionFocused':
return coinDescriptionFocused;
case 'coinDestinationTagFocused':
return coinDestinationTagFocused;
case 'fiatName':
return fiatName;
case 'fiatFullName':
Expand Down Expand Up @@ -170,6 +175,8 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
coinBeneficiaryNameFocused,
coinDescription,
coinDescriptionFocused,
coinDestinationTag,
coinDestinationTagFocused,
fiatAccountNumber,
fiatAccountNumberFocused,
fiatBankName,
Expand All @@ -186,17 +193,27 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
fiatNameFocused,
]);

const validateCoinAddressFormat = React.useCallback((value: string) => {
const coinAddressValidator = validateBeneficiaryAddress.cryptocurrency(currency, true);

setCoinAddressValid(coinAddressValidator.test(value));
}, [currency]);

const handleChangeFieldValue = React.useCallback((key: string, value: string) => {
switch (key) {
case 'coinAddress':
setCoinAddress(value);
validateCoinAddressFormat(value.trim());
break;
case 'coinBeneficiaryName':
setCoinBeneficiaryName(value);
break;
case 'coinDescription':
setCoinDescription(value);
break;
case 'coinDestinationTag':
setCoinDestinationTag(value);
break;
case 'fiatName':
setFiatName(value);
break;
Expand Down Expand Up @@ -234,6 +251,9 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
case 'coinDescriptionFocused':
setCoinDescriptionFocused(v => !v);
break;
case 'coinDestinationTagFocused':
setCoinDestinationTagFocused(v => !v);
break;
case 'fiatNameFocused':
setFiatNameFocused(v => !v);
break;
Expand Down Expand Up @@ -283,16 +303,26 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
/>
</div>
);
}, [formatMessage, getState, handleChangeFieldFocus, handleChangeFieldValue]);
}, [formatMessage, getState]);

const renderAddAddressModalCryptoBody = React.useCallback(() => {
const isDisabled = !coinAddress || !coinBeneficiaryName;
const renderInvalidAddressMessage = React.useMemo(() => {
return (
<div className="cr-email-form__group">
<span className="pg-beneficiaries__error-text">{formatMessage({ id: 'page.body.wallets.beneficiaries.addAddressModal.body.invalidAddress' })}</span>
</div>
);
}, [coinAddress]);

const renderAddAddressModalCryptoBody = React.useMemo(() => {
const isDisabled = !coinAddress || !coinBeneficiaryName || !coinAddressValid;

return (
<div className="cr-email-form__form-content">
{renderAddAddressModalBodyItem('coinAddress')}
{!coinAddressValid && coinAddress && renderInvalidAddressMessage}
{renderAddAddressModalBodyItem('coinBeneficiaryName')}
{renderAddAddressModalBodyItem('coinDescription', true)}
{isRipple && renderAddAddressModalBodyItem('coinDestinationTag', true)}
<div className="cr-email-form__button-wrapper">
<Button
disabled={isDisabled}
Expand All @@ -305,36 +335,18 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
</div>
</div>
);
}, [coinAddress, coinBeneficiaryName, formatMessage, handleSubmitAddAddressCoinModal, renderAddAddressModalBodyItem]);
}, [coinAddress, coinBeneficiaryName, coinDescription, coinDestinationTag]);

const handleSubmitAddAddressFiatModal = React.useCallback(() => {
let data: BeneficiaryBank = {
full_name: fiatFullName,
account_number: fiatAccountNumber,
bank_name: fiatBankName,
...(fiatBankSwiftCode && { bank_swift_code: fiatBankSwiftCode }),
...(fiatIntermediaryBankName && { intermediary_bank_name: fiatIntermediaryBankName }),
...(fiatIntermediaryBankSwiftCode && { intermediary_bank_swift_code: fiatIntermediaryBankSwiftCode }),
};

if (fiatBankSwiftCode) {
data = {
...data,
bank_swift_code: fiatBankSwiftCode,
};
}

if (fiatIntermediaryBankName) {
data = {
...data,
intermediary_bank_name: fiatIntermediaryBankName,
};
}

if (fiatIntermediaryBankSwiftCode) {
data = {
...data,
intermediary_bank_swift_code: fiatIntermediaryBankSwiftCode,
};
}

const payload = {
currency: currency || '',
name: fiatName,
Expand All @@ -344,19 +356,16 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
dispatch(beneficiariesCreate(payload));
handleClearModalsInputs();
}, [
currency,
dispatch,
fiatAccountNumber,
fiatBankName,
fiatBankSwiftCode,
fiatFullName,
fiatIntermediaryBankName,
fiatIntermediaryBankSwiftCode,
fiatName,
handleClearModalsInputs,
]);

const renderAddAddressModalFiatBody = React.useCallback(() => {
const renderAddAddressModalFiatBody = React.useMemo(() => {
const isDisabled = !fiatName || !fiatFullName || !fiatAccountNumber || !fiatBankName;

return (
Expand All @@ -380,7 +389,15 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
</div>
</div>
);
}, [fiatAccountNumber, fiatBankName, fiatFullName, fiatName, formatMessage, handleSubmitAddAddressFiatModal, renderAddAddressModalBodyItem]);
}, [
fiatAccountNumber,
fiatBankName,
fiatFullName,
fiatName,
fiatBankSwiftCode,
fiatIntermediaryBankName,
fiatIntermediaryBankSwiftCode,
]);

const renderContent = React.useCallback(() => {
const addModalClass = classnames('beneficiaries-add-address-modal', {
Expand All @@ -392,12 +409,12 @@ const BeneficiariesAddModalComponent: React.FC<Props> = (props: Props) => {
return (
<div className={addModalClass}>
<div className="cr-email-form">
{renderAddAddressModalHeader()}
{type === 'coin' ? renderAddAddressModalCryptoBody() : renderAddAddressModalFiatBody()}
{renderAddAddressModalHeader}
{type === 'coin' ? renderAddAddressModalCryptoBody : renderAddAddressModalFiatBody}
</div>
</div>
);
}, [type, isMobileDevice, renderAddAddressModalCryptoBody, renderAddAddressModalFiatBody, renderAddAddressModalHeader]);
}, [type, isMobileDevice, getState]);

return (
isMobileDevice ?
Expand Down
2 changes: 2 additions & 0 deletions src/components/Markets/__snapshots__/Markets.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -866,13 +866,15 @@ exports[`Markets should render empty data 1`] = `
"page.body.wallets.beneficiaries.addAddressModal.body.coinAddress": "Blockchain Address",
"page.body.wallets.beneficiaries.addAddressModal.body.coinBeneficiaryName": "Beneficiary Name",
"page.body.wallets.beneficiaries.addAddressModal.body.coinDescription": "Description (optional)",
"page.body.wallets.beneficiaries.addAddressModal.body.coinDestinationTag": "Destination Tag (optional)",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatAccountNumber": "Account Number",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatBankName": "Bank Name",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatBankSwiftCode": "Bank Swift Code (optional)",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatFullName": "Full Name",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatIntermediaryBankName": "Intermediary Bank Name (optional)",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatIntermediaryBankSwiftCode": "Intermediary Bank Swift Code (optional)",
"page.body.wallets.beneficiaries.addAddressModal.body.fiatName": "Description",
"page.body.wallets.beneficiaries.addAddressModal.body.invalidAddress": "Invalid Address",
"page.body.wallets.beneficiaries.addAddressModal.header": "Add new withdrawal address",
"page.body.wallets.beneficiaries.confirmationModal.body.button": "Confirm",
"page.body.wallets.beneficiaries.confirmationModal.body.confirmationModalCode": "Pin code",
Expand Down
2 changes: 2 additions & 0 deletions src/custom/translations/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,10 @@ export const ru: LangType = {
'page.body.wallets.beneficiaries.addAddressModal.header': 'Add new withdrawal address',

'page.body.wallets.beneficiaries.addAddressModal.body.coinAddress': 'Blockchain Address',
'page.body.wallets.beneficiaries.addAddressModal.body.invalidAddress': 'Invalid Address',
'page.body.wallets.beneficiaries.addAddressModal.body.coinBeneficiaryName': 'Beneficiary Name',
'page.body.wallets.beneficiaries.addAddressModal.body.coinDescription': 'Description (optional)',
'page.body.wallets.beneficiaries.addAddressModal.body.coinDestinationTag': 'Destination Tag (optional)',

'page.body.wallets.beneficiaries.addAddressModal.body.fiatName': 'Description',
'page.body.wallets.beneficiaries.addAddressModal.body.fiatFullName': 'Full Name',
Expand Down

0 comments on commit 058595c

Please sign in to comment.