diff --git a/frontend/src/components/Captcha.jsx b/frontend/src/components/Captcha.jsx index c56e189..87626cf 100644 --- a/frontend/src/components/Captcha.jsx +++ b/frontend/src/components/Captcha.jsx @@ -1,19 +1,114 @@ -import { Turnstile } from '@marsidev/react-turnstile' -import { CAPTCHA_ENABLED, TURNSTILE_SITE_KEY } from '../config/captcha' +import { useEffect, useRef } from 'react' +import { TURNSTILE_SITE_KEY } from '../config/captcha' -// `ref` expose reset() : un jeton Turnstile n'est valable qu'une fois, il faut -// en redemander un après chaque envoi refusé. -export default function Captcha({ ref, onToken }) { - if (!CAPTCHA_ENABLED) return null +const TURNSTILE_SCRIPT_ID = 'cf-turnstile-script' +const TURNSTILE_SCRIPT_URL = + 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit' + +export default function Captcha({ onToken }) { + const containerRef = useRef(null) + const widgetIdRef = useRef(null) + + useEffect(() => { + if (!TURNSTILE_SITE_KEY || !containerRef.current) return + + let cancelled = false + + const renderWidget = () => { + if ( + cancelled || + !containerRef.current || + !window.turnstile + ) { + return + } + + if (widgetIdRef.current !== null) { + try { + window.turnstile.remove(widgetIdRef.current) + } catch { + // Rien à faire + } + + widgetIdRef.current = null + } + + containerRef.current.innerHTML = '' + + widgetIdRef.current = window.turnstile.render( + containerRef.current, + { + sitekey: TURNSTILE_SITE_KEY, + language: 'fr', + size: 'flexible', + + callback: (token) => { + onToken(token) + }, + + 'expired-callback': () => { + onToken('') + }, + + 'error-callback': () => { + onToken('') + }, + } + ) + } + + const existingScript = document.getElementById( + TURNSTILE_SCRIPT_ID + ) + + if (window.turnstile) { + renderWidget() + return + } + + const previousCallback = window.onloadTurnstileCallback + + window.onloadTurnstileCallback = () => { + previousCallback?.() + renderWidget() + } + + if (!existingScript) { + const script = document.createElement('script') + + script.id = TURNSTILE_SCRIPT_ID + script.src = `${TURNSTILE_SCRIPT_URL}&onload=onloadTurnstileCallback` + script.async = true + script.defer = true + + document.head.appendChild(script) + } + + return () => { + cancelled = true + + if ( + widgetIdRef.current !== null && + window.turnstile + ) { + try { + window.turnstile.remove(widgetIdRef.current) + } catch { + // Rien à faire + } + } + + widgetIdRef.current = null + } + }, [onToken]) + + if (!TURNSTILE_SITE_KEY) return null return ( - onToken('')} - onError={() => onToken('')} +
) }