From 34d373cd01884729d0ce6bb0c3c304fb1790b83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Thu, 25 Apr 2024 15:17:52 +0200 Subject: [PATCH] Start unload Script --- .../src/hooks/useCaptchaScript.ts | 76 +++++++++++++++++++ .../src/hooks/useInsertCaptchaScript.ts | 40 ---------- .../auth/sign-in-up/hooks/useSignInUp.tsx | 11 +-- packages/twenty-front/src/utils/captcha.ts | 9 +-- 4 files changed, 86 insertions(+), 50 deletions(-) create mode 100644 packages/twenty-front/src/hooks/useCaptchaScript.ts delete mode 100644 packages/twenty-front/src/hooks/useInsertCaptchaScript.ts diff --git a/packages/twenty-front/src/hooks/useCaptchaScript.ts b/packages/twenty-front/src/hooks/useCaptchaScript.ts new file mode 100644 index 000000000000..c187a87c1eb5 --- /dev/null +++ b/packages/twenty-front/src/hooks/useCaptchaScript.ts @@ -0,0 +1,76 @@ +import { useRecoilState, useRecoilValue } from 'recoil'; + +import { isCaptchaLoadedState } from '@/auth/states/isCaptchaLoadedState'; +import { captchaProviderState } from '@/client-config/states/captchaProviderState'; +import { getCaptchaUrlByProvider } from '~/utils/captcha'; +import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; + +export const useCaptchaScript = () => { + const captchaProvider = useRecoilValue(captchaProviderState); + const [isCaptchaLoaded, setIsCaptchaLoaded] = + useRecoilState(isCaptchaLoadedState); + + const scriptElementId = 'captcha-script'; + let scriptElement: HTMLScriptElement | null = document.getElementById( + scriptElementId, + ) as HTMLScriptElement | null; + + const loadCaptchaScript = () => { + if ( + isUndefinedOrNull(captchaProvider) || + isUndefinedOrNull(captchaProvider.provider) || + isUndefinedOrNull(captchaProvider.siteKey) + ) { + return false; + } + const scriptUrl = getCaptchaUrlByProvider( + captchaProvider.provider, + captchaProvider.siteKey, + ); + if (!scriptUrl) { + return false; + } + + if (isUndefinedOrNull(scriptElement)) { + scriptElement = document.createElement('script'); + scriptElement.id = scriptElementId; + scriptElement.src = scriptUrl; + scriptElement.onload = () => { + setIsCaptchaLoaded(true); + }; + document.body.appendChild(scriptElement); + } + return isCaptchaLoaded; + }; + + const unloadCaptchaScript = () => { + if (!isUndefinedOrNull(scriptElement)) { + scriptElement.remove(); + } + + try { + if (!isUndefinedOrNull(window.grecaptcha)) { + window.grecaptcha.reset(scriptElementId); + } + if (!isUndefinedOrNull(window.turnstile)) { + window.turnstile.reset('#' + scriptElementId); + } + if (!isUndefinedOrNull(window.hcaptcha)) { + window.hcaptcha.reset(scriptElementId); + } + } catch (error) { + // There is no official method to unload + // so the closest is to call a reset that will fail + } + + delete window.grecaptcha; + delete window.turnstile; + delete window.hcaptcha; + + setIsCaptchaLoaded(false); + + return isCaptchaLoaded; + }; + + return { loadCaptchaScript, unloadCaptchaScript }; +}; diff --git a/packages/twenty-front/src/hooks/useInsertCaptchaScript.ts b/packages/twenty-front/src/hooks/useInsertCaptchaScript.ts deleted file mode 100644 index da5f4a89e0e4..000000000000 --- a/packages/twenty-front/src/hooks/useInsertCaptchaScript.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useEffect } from 'react'; -import { useRecoilState, useRecoilValue } from 'recoil'; - -import { isCaptchaLoadedState } from '@/auth/states/isCaptchaLoadedState'; -import { captchaProviderState } from '@/client-config/states/captchaProviderState'; -import { getCaptchaUrlByProvider } from '~/utils/captcha'; - -export const useInsertCaptchaScript = () => { - const captchaProvider = useRecoilValue(captchaProviderState); - const [isCaptchaLoaded, setIsCaptchaLoaded] = - useRecoilState(isCaptchaLoadedState); - - useEffect(() => { - if (!captchaProvider?.provider || !captchaProvider.siteKey) { - return; - } - - const scriptUrl = getCaptchaUrlByProvider( - captchaProvider.provider, - captchaProvider.siteKey, - ); - if (!scriptUrl) { - return; - } - - let scriptElement: HTMLScriptElement | null = document.querySelector( - `script[src="${scriptUrl}"]`, - ); - if (!scriptElement) { - scriptElement = document.createElement('script'); - scriptElement.src = scriptUrl; - scriptElement.onload = () => { - setIsCaptchaLoaded(true); - }; - document.body.appendChild(scriptElement); - } - }, [captchaProvider?.provider, captchaProvider?.siteKey, setIsCaptchaLoaded]); - - return isCaptchaLoaded; -}; diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx index 9c9bf4a7293c..5482ed69149b 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx @@ -7,7 +7,7 @@ import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAft import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm'; import { AppPath } from '@/types/AppPath'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { useInsertCaptchaScript } from '~/hooks/useInsertCaptchaScript'; +import { useCaptchaScript } from '~/hooks/useCaptchaScript'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; @@ -51,7 +51,7 @@ export const useSignInUp = (form: UseFormReturn
) => { checkUserExists: { checkUserExistsQuery }, } = useAuth(); - const isCaptchaScriptLoaded = useInsertCaptchaScript(); + const { loadCaptchaScript, unloadCaptchaScript } = useCaptchaScript(); const { generateCaptchaToken } = useGenerateCaptchaToken(); const [isGeneratingCaptchaToken, setIsGeneratingCaptchaToken] = @@ -60,9 +60,8 @@ export const useSignInUp = (form: UseFormReturn) => { const getCaptchaToken = useCallback(async () => { setIsGeneratingCaptchaToken(true); try { - console.log(isCaptchaScriptLoaded); + const isCaptchaScriptLoaded = loadCaptchaScript(); const captchaToken = await generateCaptchaToken(isCaptchaScriptLoaded); - console.log(captchaToken); if (!isUndefinedOrNull(captchaToken)) { form.setValue('captchaToken', captchaToken); } @@ -80,7 +79,7 @@ export const useSignInUp = (form: UseFormReturn) => { } finally { setIsGeneratingCaptchaToken(false); } - }, [form, generateCaptchaToken, isCaptchaScriptLoaded, enqueueSnackBar]); + }, [form, generateCaptchaToken, loadCaptchaScript, enqueueSnackBar]); const continueWithEmail = useCallback(() => { setSignInUpStep(SignInUpStep.Email); @@ -140,6 +139,7 @@ export const useSignInUp = (form: UseFormReturn) => { data.captchaToken, ); + unloadCaptchaScript(); navigateAfterSignInUp(currentWorkspace, currentWorkspaceMember); } catch (err: any) { enqueueSnackBar(err?.message, { @@ -155,6 +155,7 @@ export const useSignInUp = (form: UseFormReturn) => { workspaceInviteHash, navigateAfterSignInUp, enqueueSnackBar, + unloadCaptchaScript, ], ); diff --git a/packages/twenty-front/src/utils/captcha.ts b/packages/twenty-front/src/utils/captcha.ts index 95921ee162ec..6cc9b2bbd095 100644 --- a/packages/twenty-front/src/utils/captcha.ts +++ b/packages/twenty-front/src/utils/captcha.ts @@ -1,10 +1,9 @@ import { CaptchaDriverType } from '~/generated-metadata/graphql'; -export const getCaptchaUrlByProvider = (name: string, siteKey: string) => { - if (!name) { - return ''; - } - +export const getCaptchaUrlByProvider = ( + name: CaptchaDriverType, + siteKey: string, +) => { switch (name) { case CaptchaDriverType.GoogleRecatpcha: return `https://www.google.com/recaptcha/api.js?render=${siteKey}`;