Commit ff311e56 authored by tom's avatar tom

refactor reCaptcha field

parent 5909d485
import { Button, chakra, Flex } from '@chakra-ui/react'; import { Alert, Button, chakra, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm, FormProvider } from 'react-hook-form'; import { useForm, FormProvider } from 'react-hook-form';
...@@ -11,9 +11,9 @@ import type { ResourceName } from 'lib/api/resources'; ...@@ -11,9 +11,9 @@ import type { ResourceName } from 'lib/api/resources';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import downloadBlob from 'lib/downloadBlob'; import downloadBlob from 'lib/downloadBlob';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import FormFieldReCaptcha from 'ui/shared/forms/fields/FormFieldReCaptcha';
import CsvExportFormField from './CsvExportFormField'; import CsvExportFormField from './CsvExportFormField';
import CsvExportFormReCaptcha from './CsvExportFormReCaptcha';
interface Props { interface Props {
hash: string; hash: string;
...@@ -76,6 +76,13 @@ const CsvExportForm = ({ hash, resource, filterType, filterValue, fileNameTempla ...@@ -76,6 +76,13 @@ const CsvExportForm = ({ hash, resource, filterType, filterValue, fileNameTempla
}, [ resource, hash, exportType, filterType, filterValue, fileNameTemplate, toast ]); }, [ resource, hash, exportType, filterType, filterValue, fileNameTemplate, toast ]);
const disabledFeatureMessage = (
<Alert status="error">
CSV export is not available at the moment since reCaptcha is not configured for this application.
Please contact the service maintainer to make necessary changes in the service configuration.
</Alert>
);
return ( return (
<FormProvider { ...formApi }> <FormProvider { ...formApi }>
<chakra.form <chakra.form
...@@ -85,7 +92,7 @@ const CsvExportForm = ({ hash, resource, filterType, filterValue, fileNameTempla ...@@ -85,7 +92,7 @@ const CsvExportForm = ({ hash, resource, filterType, filterValue, fileNameTempla
<Flex columnGap={ 5 } rowGap={ 3 } flexDir={{ base: 'column', lg: 'row' }} alignItems={{ base: 'flex-start', lg: 'center' }} flexWrap="wrap"> <Flex columnGap={ 5 } rowGap={ 3 } flexDir={{ base: 'column', lg: 'row' }} alignItems={{ base: 'flex-start', lg: 'center' }} flexWrap="wrap">
{ exportType !== 'holders' && <CsvExportFormField name="from" formApi={ formApi }/> } { exportType !== 'holders' && <CsvExportFormField name="from" formApi={ formApi }/> }
{ exportType !== 'holders' && <CsvExportFormField name="to" formApi={ formApi }/> } { exportType !== 'holders' && <CsvExportFormField name="to" formApi={ formApi }/> }
<CsvExportFormReCaptcha formApi={ formApi }/> <FormFieldReCaptcha disabledFeatureMessage={ disabledFeatureMessage }/>
</Flex> </Flex>
<Button <Button
variant="solid" variant="solid"
......
import { Alert } from '@chakra-ui/react';
import React from 'react';
import ReCaptcha from 'react-google-recaptcha';
import type { UseFormReturn } from 'react-hook-form';
import type { FormFields } from './types';
import config from 'configs/app';
interface Props {
formApi: UseFormReturn<FormFields>;
}
const CsvExportFormReCaptcha = ({ formApi }: Props) => {
const ref = React.useRef<ReCaptcha>(null);
React.useEffect(() => {
formApi.register('reCaptcha', { required: true, shouldUnregister: true });
return () => {
formApi.unregister('reCaptcha');
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
React.useEffect(() => {
ref.current?.reset();
formApi.trigger('reCaptcha');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ formApi.formState.submitCount ]);
const handleReCaptchaChange = React.useCallback((token: string | null) => {
if (token) {
formApi.clearErrors('reCaptcha');
formApi.setValue('reCaptcha', token, { shouldValidate: true });
}
}, [ formApi ]);
const handleReCaptchaExpire = React.useCallback(() => {
formApi.resetField('reCaptcha');
formApi.setError('reCaptcha', { type: 'required' });
}, [ formApi ]);
const feature = config.features.csvExport;
if (!feature.isEnabled) {
return (
<Alert status="error">
CSV export is not available at the moment since reCaptcha is not configured for this application.
Please contact the service maintainer to make necessary changes in the service configuration.
</Alert>
);
}
return (
<ReCaptcha
className="recaptcha"
ref={ ref }
sitekey={ feature.reCaptcha.siteKey }
onChange={ handleReCaptchaChange }
onExpired={ handleReCaptchaExpire }
/>
);
};
export default CsvExportFormReCaptcha;
...@@ -8,13 +8,13 @@ import type { PublicTagTypesResponse } from 'types/api/addressMetadata'; ...@@ -8,13 +8,13 @@ import type { PublicTagTypesResponse } from 'types/api/addressMetadata';
import delay from 'lib/delay'; import delay from 'lib/delay';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import FormFieldReCaptcha from 'ui/shared/forms/fields/FormFieldReCaptcha';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
import PublicTagsSubmitFieldAddresses from './fields/PublicTagsSubmitFieldAddresses'; import PublicTagsSubmitFieldAddresses from './fields/PublicTagsSubmitFieldAddresses';
import PublicTagsSubmitFieldCompanyName from './fields/PublicTagsSubmitFieldCompanyName'; import PublicTagsSubmitFieldCompanyName from './fields/PublicTagsSubmitFieldCompanyName';
import PublicTagsSubmitFieldCompanyWebsite from './fields/PublicTagsSubmitFieldCompanyWebsite'; import PublicTagsSubmitFieldCompanyWebsite from './fields/PublicTagsSubmitFieldCompanyWebsite';
import PublicTagsSubmitFieldDescription from './fields/PublicTagsSubmitFieldDescription'; import PublicTagsSubmitFieldDescription from './fields/PublicTagsSubmitFieldDescription';
import PublicTagsSubmitFieldReCaptcha from './fields/PublicTagsSubmitFieldReCaptcha';
import PublicTagsSubmitFieldRequesterEmail from './fields/PublicTagsSubmitFieldRequesterEmail'; import PublicTagsSubmitFieldRequesterEmail from './fields/PublicTagsSubmitFieldRequesterEmail';
import PublicTagsSubmitFieldRequesterName from './fields/PublicTagsSubmitFieldRequesterName'; import PublicTagsSubmitFieldRequesterName from './fields/PublicTagsSubmitFieldRequesterName';
import PublicTagsSubmitFieldTags from './fields/PublicTagsSubmitFieldTags'; import PublicTagsSubmitFieldTags from './fields/PublicTagsSubmitFieldTags';
...@@ -73,7 +73,7 @@ const PublicTagsSubmitForm = ({ config }: Props) => { ...@@ -73,7 +73,7 @@ const PublicTagsSubmitForm = ({ config }: Props) => {
</GridItem> </GridItem>
<GridItem colSpan={{ base: 1, lg: 3 }}> <GridItem colSpan={{ base: 1, lg: 3 }}>
<PublicTagsSubmitFieldReCaptcha formApi={ formApi }/> <FormFieldReCaptcha/>
</GridItem> </GridItem>
<Button <Button
......
import React from 'react'; import React from 'react';
import ReCaptcha from 'react-google-recaptcha'; import ReCaptcha from 'react-google-recaptcha';
import type { UseFormReturn } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import type { FormFields } from '../types';
import config from 'configs/app'; import config from 'configs/app';
interface Props { interface Props {
formApi: UseFormReturn<FormFields>; disabledFeatureMessage?: JSX.Element;
} }
const PublicTagsSubmitFieldReCaptcha = ({ formApi }: Props) => { const FormFieldReCaptcha = ({ disabledFeatureMessage }: Props) => {
const { register, unregister, trigger, clearErrors, setValue, resetField, setError, formState } = useFormContext();
const ref = React.useRef<ReCaptcha>(null); const ref = React.useRef<ReCaptcha>(null);
React.useEffect(() => { React.useEffect(() => {
formApi.register('reCaptcha', { required: true, shouldUnregister: true }); register('reCaptcha', { required: true, shouldUnregister: true });
return () => { return () => {
formApi.unregister('reCaptcha'); unregister('reCaptcha');
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
React.useEffect(() => { React.useEffect(() => {
ref.current?.reset(); ref.current?.reset();
formApi.trigger('reCaptcha'); trigger('reCaptcha');
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [ formApi.formState.submitCount ]); }, [ formState.submitCount ]);
const handleReCaptchaChange = React.useCallback((token: string | null) => { const handleReCaptchaChange = React.useCallback((token: string | null) => {
if (token) { if (token) {
formApi.clearErrors('reCaptcha'); clearErrors('reCaptcha');
formApi.setValue('reCaptcha', token, { shouldValidate: true }); setValue('reCaptcha', token, { shouldValidate: true });
} }
}, [ formApi ]); }, [ clearErrors, setValue ]);
const handleReCaptchaExpire = React.useCallback(() => { const handleReCaptchaExpire = React.useCallback(() => {
formApi.resetField('reCaptcha'); resetField('reCaptcha');
formApi.setError('reCaptcha', { type: 'required' }); setError('reCaptcha', { type: 'required' });
}, [ formApi ]); }, [ resetField, setError ]);
const feature = config.features.csvExport;
if (!feature.isEnabled) { if (!config.services.reCaptcha.siteKey) {
return null; return disabledFeatureMessage ?? null;
} }
return ( return (
<ReCaptcha <ReCaptcha
className="recaptcha" className="recaptcha"
ref={ ref } ref={ ref }
sitekey={ feature.reCaptcha.siteKey } sitekey={ config.services.reCaptcha.siteKey }
onChange={ handleReCaptchaChange } onChange={ handleReCaptchaChange }
onExpired={ handleReCaptchaExpire } onExpired={ handleReCaptchaExpire }
/> />
); );
}; };
export default PublicTagsSubmitFieldReCaptcha; export default FormFieldReCaptcha;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment