"use client"; import { useCallback, useId, useLayoutEffect, useRef, useState, type KeyboardEvent } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faLock, faUnlock } from "@fortawesome/free-solid-svg-icons"; import type { CaptchaChallengeData, CaptchaVerifyPayload } from "../types/captcha"; type CaptchaChallengeProps = { isOpen: boolean; challenge?: CaptchaChallengeData | null; onVerify: (payload: CaptchaVerifyPayload) => Promise; onSuccess: (bypassToken: string) => void; onRetry: () => void | Promise; onClose: () => void; }; const getErrorMessage = (error: unknown): string => { if (error instanceof Error && error.message.trim().length > 0) { return error.message; } return "Verification failed. Please try again."; }; export default function CaptchaChallenge({ isOpen, challenge, onVerify, onSuccess, onRetry, onClose, }: CaptchaChallengeProps) { const [answer, setAnswer] = useState(""); const [error, setError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const titleId = useId(); const descriptionId = useId(); const inputRef = useRef(null); const answerRef = useRef(""); const modalRef = useRef(null); useLayoutEffect(() => { if (!isOpen) { return; } setAnswer(""); setError(null); setIsSubmitting(false); answerRef.current = ""; }, [isOpen, challenge?.challengeToken]); useLayoutEffect(() => { if (!isOpen) { return; } inputRef.current?.focus(); }, [isOpen, challenge?.challengeToken]); const commitAnswer = useCallback((value: string) => { answerRef.current = value; setAnswer(value); }, []); const handleSubmit = useCallback(async () => { if (!challenge || isSubmitting) { return; } setIsSubmitting(true); setError(null); try { const bypassToken = await onVerify({ challengeToken: challenge.challengeToken, answer: (inputRef.current?.value ?? answerRef.current ?? answer).trim(), }); onSuccess(bypassToken); await Promise.resolve(onRetry()); onClose(); } catch (err) { setError(getErrorMessage(err)); } finally { setIsSubmitting(false); } }, [answer, challenge, isSubmitting, onClose, onRetry, onSuccess, onVerify]); const handleKeyDown = useCallback( (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); onClose(); return; } if (event.key !== "Tab") { return; } const container = modalRef.current; if (!container) { return; } const focusableElements = Array.from( container.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ), ).filter( (element) => !element.hasAttribute("disabled") && element.getAttribute("aria-hidden") !== "true", ); if (focusableElements.length === 0) { return; } const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; const activeElement = document.activeElement; if (event.shiftKey && activeElement === firstElement) { event.preventDefault(); lastElement.focus(); return; } if (!event.shiftKey && activeElement === lastElement) { event.preventDefault(); firstElement.focus(); } }, [onClose], ); const handleInputKeyDown = useCallback( (event: KeyboardEvent) => { if (event.key === "Enter") { event.preventDefault(); void handleSubmit(); } }, [handleSubmit], ); if (!isOpen || !challenge) { return null; } return (

Security Check Required

Solve the CAPTCHA to continue analysis.

{/* eslint-disable-next-line @next/next/no-img-element */} CAPTCHA challenge
{error ? (

{error}

) : null}
); }