"use client" import { useEffect, useRef } from "react" import { cn } from "@/lib/utils" interface TurnstileProps { siteKey: string onVerify: (token: string) => void onExpire?: () => void resetSignal?: number className?: string } export function Turnstile({ siteKey, onVerify, onExpire, resetSignal, className, }: TurnstileProps) { const containerRef = useRef(null) const widgetIdRef = useRef(null) useEffect(() => { if (!siteKey) return const renderWidget = () => { if (!containerRef.current || !window.turnstile) return if (widgetIdRef.current) { window.turnstile.reset(widgetIdRef.current) return } widgetIdRef.current = window.turnstile.render(containerRef.current, { sitekey: siteKey, theme: "auto", callback: (token: string) => onVerify(token), "error-callback": () => onVerify(""), "expired-callback": () => { onVerify("") onExpire?.() }, }) } const existingScript = document.querySelector('script[data-turnstile="true"]') if (window.turnstile) { renderWidget() } else if (existingScript) { const handleExistingScriptLoad = () => renderWidget() if (existingScript.dataset.loaded === "true") { renderWidget() } else { existingScript.addEventListener("load", handleExistingScriptLoad) } return () => { existingScript.removeEventListener("load", handleExistingScriptLoad) } } else { const script = document.createElement("script") script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" script.async = true script.defer = true script.dataset.turnstile = "true" const handleScriptLoad = () => { script.dataset.loaded = "true" renderWidget() } script.addEventListener("load", handleScriptLoad) document.head.appendChild(script) return () => { script.removeEventListener("load", handleScriptLoad) } } return () => { if (widgetIdRef.current && window.turnstile) { window.turnstile.remove(widgetIdRef.current) widgetIdRef.current = null } } }, [siteKey, onExpire, onVerify]) useEffect(() => { if (resetSignal === undefined) return if (widgetIdRef.current && window.turnstile) { window.turnstile.reset(widgetIdRef.current) } onVerify("") // eslint-disable-next-line react-hooks/exhaustive-deps }, [resetSignal]) return (
) }