mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
ReCaptcha: allow the user to display the widget if an error occurs. (#2669)
Fixes #2653
This commit is contained in:
@@ -8,6 +8,7 @@ import dayjs from 'lib/date/dayjs';
|
||||
import downloadBlob from 'lib/downloadBlob';
|
||||
import { Button } from 'toolkit/chakra/button';
|
||||
import { toaster } from 'toolkit/chakra/toaster';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
|
||||
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
|
||||
|
||||
@@ -63,16 +64,22 @@ const ExportCSV = ({ filters }: Props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={ handleExportCSV }
|
||||
variant="outline"
|
||||
loading={ isLoading }
|
||||
size="sm"
|
||||
mr={ 3 }
|
||||
<Tooltip
|
||||
content="This feature is not available due to a reCAPTCHA initialization error. Please contact the project team on Discord to report this issue."
|
||||
disabled={ !recaptcha.isInitError }
|
||||
>
|
||||
Export to CSV
|
||||
</Button>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<Button
|
||||
onClick={ handleExportCSV }
|
||||
variant="outline"
|
||||
loading={ isLoading }
|
||||
size="sm"
|
||||
mr={ 3 }
|
||||
disabled={ recaptcha.isInitError }
|
||||
>
|
||||
Export to CSV
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ReCaptcha { ...recaptcha } hideWarning/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -101,14 +101,14 @@ const CsvExportForm = ({ hash, resource, filterType, filterValue, fileNameTempla
|
||||
{ exportType !== 'holders' && <CsvExportFormField name="from" formApi={ formApi }/> }
|
||||
{ exportType !== 'holders' && <CsvExportFormField name="to" formApi={ formApi }/> }
|
||||
</Flex>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<ReCaptcha { ...recaptcha }/>
|
||||
<Button
|
||||
variant="solid"
|
||||
type="submit"
|
||||
mt={ 8 }
|
||||
loading={ formState.isSubmitting }
|
||||
loadingText="Download"
|
||||
disabled={ Boolean(formState.errors.from || formState.errors.to) }
|
||||
disabled={ Boolean(formState.errors.from || formState.errors.to || recaptcha.isInitError) }
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
|
||||
@@ -89,14 +89,14 @@ const MyProfileEmail = ({ profileQuery }: Props) => {
|
||||
isReadOnly={ !config.services.reCaptchaV2.siteKey || Boolean(profileQuery.data?.email) }
|
||||
defaultValue={ profileQuery.data?.email || undefined }
|
||||
/>
|
||||
{ config.services.reCaptchaV2.siteKey && !profileQuery.data?.email && <ReCaptcha ref={ recaptcha.ref }/> }
|
||||
{ config.services.reCaptchaV2.siteKey && !profileQuery.data?.email && <ReCaptcha { ...recaptcha }/> }
|
||||
{ config.services.reCaptchaV2.siteKey && !profileQuery.data?.email && (
|
||||
<Button
|
||||
mt={ 6 }
|
||||
size="sm"
|
||||
variant="outline"
|
||||
type="submit"
|
||||
disabled={ formApi.formState.isSubmitting || !hasDirtyFields }
|
||||
disabled={ formApi.formState.isSubmitting || !hasDirtyFields || recaptcha.isInitError }
|
||||
loading={ formApi.formState.isSubmitting }
|
||||
loadingText="Save changes"
|
||||
>
|
||||
|
||||
@@ -129,9 +129,10 @@ const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => {
|
||||
/>
|
||||
</GridItem>
|
||||
|
||||
<GridItem colSpan={{ base: 1, lg: 3 }}>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<GridItem colSpan={{ base: 1, lg: 2 }}>
|
||||
<ReCaptcha { ...recaptcha }/>
|
||||
</GridItem>
|
||||
{ !isMobile && <div/> }
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
@@ -140,6 +141,7 @@ const PublicTagsSubmitForm = ({ config, userInfo, onSubmitResult }: Props) => {
|
||||
loading={ formApi.formState.isSubmitting }
|
||||
loadingText="Send request"
|
||||
w="min-content"
|
||||
disabled={ recaptcha.isInitError }
|
||||
>
|
||||
Send request
|
||||
</Button>
|
||||
|
||||
@@ -51,8 +51,8 @@ const AppErrorTooManyRequests = () => {
|
||||
<Text color="text.secondary" mt={ 3 }>
|
||||
You have exceeded the request rate for a given time period. Please reduce the number of requests and try again soon.
|
||||
</Text>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<Button onClick={ handleSubmit } mt={ 8 }>Try again</Button>
|
||||
<ReCaptcha { ...recaptcha }/>
|
||||
<Button onClick={ handleSubmit } disabled={ recaptcha.isInitError } mt={ 8 }>Try again</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,30 +2,61 @@ import React from 'react';
|
||||
import ReCaptcha from 'react-google-recaptcha';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { Alert } from 'toolkit/chakra/alert';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
|
||||
interface Props {
|
||||
disabledFeatureMessage?: React.ReactNode;
|
||||
onInitError: () => void;
|
||||
hideWarning?: boolean;
|
||||
}
|
||||
|
||||
const ReCaptchaInvisible = ({ disabledFeatureMessage }: Props, ref: React.Ref<ReCaptcha>) => {
|
||||
const ReCaptchaInvisible = ({ onInitError, hideWarning = false }: Props, ref: React.Ref<ReCaptcha>) => {
|
||||
const [ attempt, setAttempt ] = React.useState(0);
|
||||
const [ isError, setIsError ] = React.useState(false);
|
||||
const [ , setIsVisible ] = React.useState(false);
|
||||
|
||||
const handleChange = React.useCallback(() => {
|
||||
setAttempt(attempt + 1);
|
||||
}, [ attempt ]);
|
||||
|
||||
const handleError = React.useCallback(() => {
|
||||
setIsError(true);
|
||||
onInitError();
|
||||
}, [ onInitError ]);
|
||||
|
||||
const handleClick = React.useCallback(() => {
|
||||
const badge = window.document.querySelector('.grecaptcha-badge');
|
||||
if (badge) {
|
||||
setIsVisible((prev) => {
|
||||
const nextValue = !prev;
|
||||
(badge as HTMLElement).style.visibility = nextValue ? 'visible' : 'hidden';
|
||||
(badge as HTMLElement).style.right = nextValue ? '14px' : '-1000px';
|
||||
return nextValue;
|
||||
});
|
||||
}
|
||||
}, [ ]);
|
||||
|
||||
if (!config.services.reCaptchaV2.siteKey) {
|
||||
return disabledFeatureMessage ?? null;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ReCaptcha
|
||||
ref={ ref }
|
||||
key={ attempt }
|
||||
sitekey={ config.services.reCaptchaV2.siteKey }
|
||||
size="invisible"
|
||||
onChange={ handleChange }
|
||||
/>
|
||||
<>
|
||||
<ReCaptcha
|
||||
ref={ ref }
|
||||
key={ attempt }
|
||||
sitekey={ config.services.reCaptchaV2.siteKey }
|
||||
size="invisible"
|
||||
onChange={ handleChange }
|
||||
onErrored={ handleError }
|
||||
/>
|
||||
{ isError && !hideWarning && (
|
||||
<Alert status="warning" whiteSpace="pre-wrap" w="fit-content" mt={ 3 } descriptionProps={{ display: 'block' }}>
|
||||
This feature is not available due to a reCAPTCHA initialization error. Please contact the project team on Discord to report this issue.
|
||||
Click <Link onClick={ handleClick } display="inline">here</Link> to show/hide reCAPTCHA widget content.
|
||||
</Alert>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export default function useReCaptcha() {
|
||||
const rejectCb = React.useRef<((error: Error) => void) | null>(null);
|
||||
|
||||
const [ isOpen, setIsOpen ] = React.useState(false);
|
||||
const [ isInitError, setIsInitError ] = React.useState(false);
|
||||
|
||||
const executeAsync: () => Promise<string | null> = React.useCallback(async() => {
|
||||
setIsOpen(true);
|
||||
@@ -22,6 +23,10 @@ export default function useReCaptcha() {
|
||||
rejectCb.current?.(new Error('ReCaptcha is not solved'));
|
||||
}, []);
|
||||
|
||||
const handleInitError = React.useCallback(() => {
|
||||
setIsInitError(true);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
@@ -35,5 +40,5 @@ export default function useReCaptcha() {
|
||||
};
|
||||
}, [ isOpen, handleContainerClick ]);
|
||||
|
||||
return React.useMemo(() => ({ ref, executeAsync }), [ ref, executeAsync ]);
|
||||
return React.useMemo(() => ({ ref, executeAsync, isInitError, onInitError: handleInitError }), [ ref, executeAsync, isInitError, handleInitError ]);
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ const AuthModalScreenConnectWallet = ({ onSuccess, onError, isAuth, source, logi
|
||||
}, [ start ]);
|
||||
|
||||
return (
|
||||
<Center h="100px">
|
||||
<Spinner size="xl"/>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<Center minH="100px" flexDir="column">
|
||||
{ !recaptcha.isInitError && <Spinner size="xl"/> }
|
||||
<ReCaptcha { ...recaptcha }/>
|
||||
</Center>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -85,16 +85,16 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
|
||||
bgColor="dialog.bg"
|
||||
mt={ 6 }
|
||||
/>
|
||||
<ReCaptcha { ...recaptcha }/>
|
||||
<Button
|
||||
mt={ 6 }
|
||||
type="submit"
|
||||
disabled={ formApi.formState.isSubmitting }
|
||||
disabled={ formApi.formState.isSubmitting || recaptcha.isInitError }
|
||||
loading={ formApi.formState.isSubmitting }
|
||||
loadingText="Send a code"
|
||||
>
|
||||
Send a code
|
||||
</Button>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
</chakra.form>
|
||||
</FormProvider>
|
||||
);
|
||||
|
||||
@@ -108,22 +108,22 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
|
||||
and enter your code below.
|
||||
</Text>
|
||||
<AuthModalFieldOtpCode isDisabled={ isCodeSending }/>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<Button
|
||||
variant="link"
|
||||
columnGap={ 2 }
|
||||
mt={ 3 }
|
||||
disabled={ isCodeSending }
|
||||
disabled={ isCodeSending || recaptcha.isInitError }
|
||||
onClick={ handleResendCodeClick }
|
||||
>
|
||||
<IconSvg name="repeat" boxSize={ 5 }/>
|
||||
<Box fontSize="sm">Resend code</Box>
|
||||
</Button>
|
||||
<ReCaptcha { ...recaptcha }/>
|
||||
<Button
|
||||
mt={ 6 }
|
||||
type="submit"
|
||||
loading={ formApi.formState.isSubmitting }
|
||||
disabled={ formApi.formState.isSubmitting || isCodeSending }
|
||||
disabled={ formApi.formState.isSubmitting || isCodeSending || recaptcha.isInitError }
|
||||
loadingText="Submit"
|
||||
onClick={ formApi.handleSubmit(onFormSubmit) }
|
||||
>
|
||||
|
||||
@@ -170,7 +170,7 @@ const TokenInstanceMetadataFetcher = ({ hash, id }: Props) => {
|
||||
<Center h="80px">
|
||||
<Spinner size="lg"/>
|
||||
</Center>
|
||||
<ReCaptcha ref={ recaptcha.ref }/>
|
||||
<ReCaptcha { ...recaptcha } hideWarning/>
|
||||
</>
|
||||
) : (
|
||||
<Alert status="error">
|
||||
|
||||
Reference in New Issue
Block a user