import React, { useEffect, useRef, useState } from 'react'; import ReCAPTCHA from 'react-google-recaptcha'; import { recaptchaService } from '../../features/system/services/systemSettingsService'; interface RecaptchaProps { onChange?: (token: string | null) => void; onError?: (error: string) => void; theme?: 'light' | 'dark'; size?: 'normal' | 'compact'; className?: string; } // Cache for reCAPTCHA settings to avoid multiple API calls interface RecaptchaSettingsCache { siteKey: string; enabled: boolean; timestamp: number; } const CACHE_KEY = 'recaptcha_settings_cache'; const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes let settingsCache: RecaptchaSettingsCache | null = null; let fetchPromise: Promise | null = null; const getCachedSettings = (): RecaptchaSettingsCache | null => { // Check in-memory cache first if (settingsCache) { const age = Date.now() - settingsCache.timestamp; if (age < CACHE_DURATION) { return settingsCache; } } // Check localStorage cache try { const cached = localStorage.getItem(CACHE_KEY); if (cached) { const parsed: RecaptchaSettingsCache = JSON.parse(cached); const age = Date.now() - parsed.timestamp; if (age < CACHE_DURATION) { settingsCache = parsed; return parsed; } } } catch (error) { // Ignore cache errors } return null; }; const fetchRecaptchaSettings = async (): Promise => { // If there's already a fetch in progress, return that promise if (fetchPromise) { return fetchPromise; } fetchPromise = (async () => { try { const response = await recaptchaService.getRecaptchaSettings(); if (response.status === 'success' && response.data) { const settings: RecaptchaSettingsCache = { siteKey: response.data.recaptcha_site_key || '', enabled: response.data.recaptcha_enabled || false, timestamp: Date.now(), }; // Update caches settingsCache = settings; try { localStorage.setItem(CACHE_KEY, JSON.stringify(settings)); } catch (error) { // Ignore localStorage errors } return settings; } return null; } catch (error) { console.error('Error fetching reCAPTCHA settings:', error); return null; } finally { fetchPromise = null; } })(); return fetchPromise; }; const Recaptcha: React.FC = ({ onChange, onError, theme = 'dark', size = 'normal', className = '', }) => { const recaptchaRef = useRef(null); const [siteKey, setSiteKey] = useState(''); const [enabled, setEnabled] = useState(false); const [loading, setLoading] = useState(true); useEffect(() => { const loadSettings = async () => { // Try to get from cache first const cached = getCachedSettings(); if (cached) { setSiteKey(cached.siteKey); setEnabled(cached.enabled); setLoading(false); return; } // Fetch from API if not cached const settings = await fetchRecaptchaSettings(); if (settings) { setSiteKey(settings.siteKey); setEnabled(settings.enabled); } else { if (onError) { onError('Failed to load reCAPTCHA settings'); } } setLoading(false); }; loadSettings(); }, [onError]); const handleChange = (token: string | null) => { if (onChange) { onChange(token); } }; const handleExpired = () => { if (onChange) { onChange(null); } }; const handleError = () => { if (onError) { onError('reCAPTCHA error occurred'); } if (onChange) { onChange(null); } }; if (loading) { return null; } if (!enabled || !siteKey) { return null; } return (
); }; export default Recaptcha;