api integrate of start-a-project form , captcha pending
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import React, { useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import React, {
|
||||
useEffect,
|
||||
useRef,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
} from "react";
|
||||
|
||||
interface CustomReCaptchaProps {
|
||||
siteKey: string;
|
||||
@@ -6,6 +11,8 @@ interface CustomReCaptchaProps {
|
||||
onExpired?: () => void;
|
||||
onError?: () => void;
|
||||
className?: string;
|
||||
theme?: "light" | "dark";
|
||||
size?: "normal" | "compact";
|
||||
}
|
||||
|
||||
export interface ReCaptchaRef {
|
||||
@@ -16,99 +23,127 @@ export interface ReCaptchaRef {
|
||||
declare global {
|
||||
interface Window {
|
||||
grecaptcha: any;
|
||||
onReCaptchaLoad?: () => void;
|
||||
}
|
||||
}
|
||||
|
||||
const CustomReCaptcha = forwardRef<ReCaptchaRef, CustomReCaptchaProps>(({
|
||||
siteKey,
|
||||
onVerify,
|
||||
onExpired,
|
||||
onError,
|
||||
className = ""
|
||||
}, ref) => {
|
||||
const captchaRef = useRef<HTMLDivElement>(null);
|
||||
const widgetId = useRef<number | null>(null);
|
||||
const isLoadedRef = useRef(false);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
reset: () => {
|
||||
if (window.grecaptcha && widgetId.current !== null) {
|
||||
window.grecaptcha.reset(widgetId.current);
|
||||
}
|
||||
const CustomReCaptcha = forwardRef<ReCaptchaRef, CustomReCaptchaProps>(
|
||||
(
|
||||
{
|
||||
siteKey,
|
||||
onVerify,
|
||||
onExpired,
|
||||
onError,
|
||||
className = "",
|
||||
theme = "dark",
|
||||
size = "normal",
|
||||
},
|
||||
execute: () => {
|
||||
if (window.grecaptcha && widgetId.current !== null) {
|
||||
window.grecaptcha.execute(widgetId.current);
|
||||
}
|
||||
}
|
||||
}));
|
||||
ref
|
||||
) => {
|
||||
const captchaRef = useRef<HTMLDivElement>(null);
|
||||
const widgetId = useRef<number | null>(null);
|
||||
const isLoadedRef = useRef(false);
|
||||
|
||||
const loadReCaptcha = () => {
|
||||
if (window.grecaptcha) {
|
||||
renderReCaptcha();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load reCAPTCHA script
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://www.google.com/recaptcha/api.js?onload=onReCaptchaLoad&render=explicit';
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
|
||||
// Set up callback for when script loads
|
||||
(window as any).onReCaptchaLoad = () => {
|
||||
renderReCaptcha();
|
||||
};
|
||||
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
const renderReCaptcha = () => {
|
||||
if (!captchaRef.current || isLoadedRef.current) return;
|
||||
|
||||
try {
|
||||
widgetId.current = window.grecaptcha.render(captchaRef.current, {
|
||||
sitekey: siteKey,
|
||||
callback: onVerify,
|
||||
'expired-callback': onExpired,
|
||||
'error-callback': onError,
|
||||
theme: 'dark',
|
||||
size: 'normal'
|
||||
});
|
||||
isLoadedRef.current = true;
|
||||
} catch (error) {
|
||||
console.error('Error rendering reCAPTCHA:', error);
|
||||
if (onError) onError();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadReCaptcha();
|
||||
|
||||
return () => {
|
||||
// Cleanup
|
||||
if (window.grecaptcha && widgetId.current !== null) {
|
||||
try {
|
||||
useImperativeHandle(ref, () => ({
|
||||
reset: () => {
|
||||
if (window.grecaptcha && widgetId.current !== null) {
|
||||
window.grecaptcha.reset(widgetId.current);
|
||||
} catch (error) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
},
|
||||
execute: () => {
|
||||
if (window.grecaptcha && widgetId.current !== null) {
|
||||
window.grecaptcha.execute(widgetId.current);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const loadReCaptcha = () => {
|
||||
if (window.grecaptcha) {
|
||||
renderReCaptcha();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if script is already loading
|
||||
if (
|
||||
document.querySelector(
|
||||
'script[src^="https://www.google.com/recaptcha/api.js"]'
|
||||
)
|
||||
) {
|
||||
// If already loading, set up a callback for when it loads
|
||||
window.onReCaptchaLoad = renderReCaptcha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load reCAPTCHA script
|
||||
const script = document.createElement("script");
|
||||
script.src =
|
||||
"https://www.google.com/recaptcha/api.js?onload=onReCaptchaLoad&render=explicit";
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
|
||||
// Set up callback for when script loads
|
||||
window.onReCaptchaLoad = () => {
|
||||
renderReCaptcha();
|
||||
window.onReCaptchaLoad = undefined; // Clean up
|
||||
};
|
||||
|
||||
script.onerror = () => {
|
||||
console.error("Failed to load reCAPTCHA script");
|
||||
if (onError) onError();
|
||||
window.onReCaptchaLoad = undefined; // Clean up
|
||||
};
|
||||
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
const renderReCaptcha = () => {
|
||||
if (!captchaRef.current || isLoadedRef.current) return;
|
||||
|
||||
try {
|
||||
widgetId.current = window.grecaptcha.render(captchaRef.current, {
|
||||
sitekey: siteKey,
|
||||
callback: onVerify,
|
||||
"expired-callback": onExpired,
|
||||
"error-callback": onError,
|
||||
theme: theme,
|
||||
size: size,
|
||||
});
|
||||
isLoadedRef.current = true;
|
||||
} catch (error) {
|
||||
console.error("Error rendering reCAPTCHA:", error);
|
||||
if (onError) onError();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Add styles to document head instead of using styled-jsx
|
||||
useEffect(() => {
|
||||
const styleId = 'custom-recaptcha-styles';
|
||||
|
||||
// Check if styles are already added
|
||||
if (document.getElementById(styleId)) {
|
||||
return;
|
||||
}
|
||||
useEffect(() => {
|
||||
loadReCaptcha();
|
||||
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.id = styleId;
|
||||
styleElement.textContent = `
|
||||
return () => {
|
||||
// Cleanup
|
||||
if (window.grecaptcha && widgetId.current !== null) {
|
||||
try {
|
||||
window.grecaptcha.reset(widgetId.current);
|
||||
} catch (error) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
// Clean up the global callback
|
||||
window.onReCaptchaLoad = undefined;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Add styles to document head instead of using styled-jsx
|
||||
useEffect(() => {
|
||||
const styleId = "custom-recaptcha-styles";
|
||||
|
||||
// Check if styles are already added
|
||||
if (document.getElementById(styleId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.id = styleId;
|
||||
styleElement.textContent = `
|
||||
.grecaptcha-badge {
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -129,32 +164,35 @@ const CustomReCaptcha = forwardRef<ReCaptchaRef, CustomReCaptchaProps>(({
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
`;
|
||||
|
||||
document.head.appendChild(styleElement);
|
||||
|
||||
// Cleanup function to remove styles when component unmounts
|
||||
return () => {
|
||||
const existingStyle = document.getElementById(styleId);
|
||||
if (existingStyle) {
|
||||
document.head.removeChild(existingStyle);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
document.head.appendChild(styleElement);
|
||||
|
||||
return (
|
||||
<div className={`flex justify-center ${className}`}>
|
||||
<div
|
||||
className="bg-gray-800/30 border border-gray-600/50 rounded-xl p-6 shadow-lg backdrop-blur-sm"
|
||||
style={{
|
||||
'--recaptcha-border-radius': '12px'
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div ref={captchaRef} />
|
||||
// Cleanup function to remove styles when component unmounts
|
||||
return () => {
|
||||
const existingStyle = document.getElementById(styleId);
|
||||
if (existingStyle) {
|
||||
document.head.removeChild(existingStyle);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`flex justify-center ${className}`}>
|
||||
<div
|
||||
className="bg-gray-800/30 border border-gray-600/50 rounded-xl p-6 shadow-lg backdrop-blur-sm"
|
||||
style={
|
||||
{
|
||||
"--recaptcha-border-radius": "12px",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<div ref={captchaRef} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
CustomReCaptcha.displayName = 'CustomReCaptcha';
|
||||
CustomReCaptcha.displayName = "CustomReCaptcha";
|
||||
|
||||
export default CustomReCaptcha;
|
||||
export default CustomReCaptcha;
|
||||
|
||||
Reference in New Issue
Block a user