import { Input, InputError, Loader, VegaIcon, VegaIconNames, } from '@vegaprotocol/ui-toolkit'; import { useForm } from 'react-hook-form'; import classNames from 'classnames'; import { Navigate, useNavigate, useSearchParams } from 'react-router-dom'; import type { ButtonHTMLAttributes, MouseEventHandler } from 'react'; import { useCallback } from 'react'; import { RainbowButton } from '../../components/rainbow-button'; import { useSimpleTransaction, useVegaWallet, useVegaWalletDialogStore, } from '@vegaprotocol/wallet'; import { useIsInReferralSet, useReferral } from './hooks/use-referral'; import { Routes } from '../../lib/links'; import { Statistics, useStats } from './referral-statistics'; import { useReferralProgram } from './hooks/use-referral-program'; import { ns, useT } from '../../lib/use-t'; import { useFundsAvailable } from './hooks/use-funds-available'; import { ViewType, useSidebar } from '../../components/sidebar'; import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; import { QUSDTooltip } from './qusd-tooltip'; import { Trans } from 'react-i18next'; const RELOAD_DELAY = 3000; const SPAM_PROTECTION_ERR = 'SPAM_PROTECTION_ERR'; const SpamProtectionErr = ({ requiredFunds, }: { requiredFunds?: string | number | bigint; }) => { if (!requiredFunds) return null; // eslint-disable-next-line react/jsx-no-undef return ( ]} ns={ns} /> ); }; const validateCode = (value: string, t: ReturnType) => { const number = +`0x${value}`; if (!value || value.length !== 64) { return t('Code must be 64 characters in length'); } else if (Number.isNaN(number)) { return t('Code must be be valid hex'); } return true; }; export const ApplyCodeFormContainer = ({ onSuccess, }: { onSuccess?: () => void; }) => { const { pubKey } = useVegaWallet(); const isInReferralSet = useIsInReferralSet(pubKey); // Navigate to the index page when already in the referral set. if (isInReferralSet) { return ; } return ; }; type FormFields = { code: string; }; export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => { const t = useT(); const program = useReferralProgram(); const navigate = useNavigate(); const openWalletDialog = useVegaWalletDialogStore( (store) => store.openVegaWalletDialog ); const { isReadOnly, pubKey } = useVegaWallet(); const { isEligible, requiredFunds } = useFundsAvailable(); const currentRouteId = useGetCurrentRouteId(); const setViews = useSidebar((s) => s.setViews); const [params] = useSearchParams(); const { register, handleSubmit, formState: { errors }, setError, watch, } = useForm({ defaultValues: { code: params.get('code') || '', }, }); const codeField = watch('code'); const { data: previewData, loading: previewLoading } = useReferral({ code: validateCode(codeField, t) ? codeField : undefined, }); const { send, status } = useSimpleTransaction({ onSuccess: () => { // go to main page when successfully applied setTimeout(() => { if (onSuccess) onSuccess(); navigate(Routes.REFERRALS); }, RELOAD_DELAY); }, onError: (msg) => { setError('code', { type: 'required', message: msg, }); }, }); /** * Validates if a connected party can apply a code (min funds span protection) */ const validateFundsAvailable = useCallback(() => { if (requiredFunds && !isEligible) { const err = SPAM_PROTECTION_ERR; return err; } return true; }, [isEligible, requiredFunds]); /** * Validates the set a user tries to apply to. */ const validateSet = useCallback(() => { if ( codeField && !previewLoading && previewData && !previewData.isEligible ) { return t('The code is no longer valid.'); } if (codeField && !previewLoading && !previewData) { return t('The code is invalid'); } return true; }, [codeField, previewData, previewLoading, t]); const noFunds = validateFundsAvailable() !== true ? true : false; const onSubmit = ({ code }: FormFields) => { if (isReadOnly || !pubKey || !code || code.length === 0) { return; } send({ applyReferralCode: { id: code as string, }, }); // sendTx(pubKey, { // applyReferralCode: { // id: code as string, // }, // }) // .then((res) => { // if (!res) { // setError('code', { // type: 'required', // message: t('The transaction could not be sent'), // }); // } // if (res) { // txHash.current = res.transactionHash.toLowerCase(); // } // }) // .catch((err) => { // if (err.message.includes('user rejected')) { // setStatus(null); // } else { // setStatus(null); // setError('code', { // type: 'required', // message: // err instanceof Error // ? err.message // : t('Your code has been rejected'), // }); // } // }); }; const { epochsValue, nextBenefitTierValue } = useStats({ program }); // show "code applied" message when successfully applied if (status === 'confirmed') { return (

{' '} {t('Code applied')}

); } const getButtonProps = () => { if (!pubKey) { return { disabled: false, children: t('Connect wallet'), type: 'button' as ButtonHTMLAttributes['type'], onClick: ((event) => { event.preventDefault(); openWalletDialog(); }) as MouseEventHandler, }; } if (isReadOnly) { return { disabled: true, children: t('Apply a code'), type: 'submit' as ButtonHTMLAttributes['type'], }; } if (noFunds) { return { disabled: false, children: t('Deposit funds'), type: 'button' as ButtonHTMLAttributes['type'], onClick: ((event) => { event.preventDefault(); setViews({ type: ViewType.Deposit }, currentRouteId); }) as MouseEventHandler, }; } if (status === 'requested') { return { disabled: true, children: t('Confirm in wallet...'), type: 'submit' as ButtonHTMLAttributes['type'], }; } return { disabled: false, children: t('Apply a code'), type: 'submit' as ButtonHTMLAttributes['type'], }; }; const nextBenefitTierEpochsValue = nextBenefitTierValue ? nextBenefitTierValue.epochs - epochsValue : 0; return ( <>

{t('Apply a referral code')}

{t( 'Apply a referral code to access the discount benefits of the current program.' )}

{noFunds ? ( ) : ( errors.code && ( {errors.code.message === SPAM_PROTECTION_ERR ? ( ) : ( errors.code.message?.toString() )} ) )}
{validateCode(codeField, t) === true && previewLoading && !previewData ? (
) : null} {/* TODO: Re-check plural forms once i18n is updated */} {previewData && previewData.isEligible ? (

{t( 'youAreJoiningTheGroup', 'You are joining the group shown, but will not have access to benefits until you have completed at least {{count}} epochs.', { count: nextBenefitTierEpochsValue } )}

) : null} ); };