Private
Public Access
1
0
Files
MineSeeker/assets/js/mine-seeker/components/CaptchaOverlay.jsx

170 lines
4.4 KiB
React
Raw Normal View History

/**
* This file is part of the SplendidBear Websites' projects.
*
* Copyright (c) 2026 @ www.splendidbear.org
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { useEffect, useState } from 'react';
const CAPTCHA_STORAGE_KEY = 'mineseeker_captcha_verified';
const CAPTCHA_TOKEN_KEY = 'mineseeker_captcha_token';
const RECAPTCHA_ACTION = 'mineseeker_play';
const CaptchaOverlay = ({ siteKey, onVerified, children }) => {
const [verified, setVerified] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const storedToken = sessionStorage.getItem(CAPTCHA_TOKEN_KEY);
const storedTime = sessionStorage.getItem(CAPTCHA_STORAGE_KEY);
if (storedToken && storedTime) {
const elapsed = (Date.now() - parseInt(storedTime)) / 1000;
if (110 > elapsed) {
const wrapper = document.getElementById('mine-wrapper');
if (wrapper) {
wrapper.dataset.captchaToken = storedToken;
}
setVerified(true);
onVerified?.();
return;
}
}
if (window.grecaptcha) {
window.grecaptcha.ready(() => {
window.grecaptcha
.execute(siteKey, { action: RECAPTCHA_ACTION })
.then(token => {
handleToken(token);
})
.catch(() => {
setError(true);
});
});
}
}, [siteKey, onVerified]);
const handleToken = token => {
const wrapper = document.getElementById('mine-wrapper');
if (wrapper) {
wrapper.dataset.captchaToken = token;
}
sessionStorage.setItem(CAPTCHA_TOKEN_KEY, token);
sessionStorage.setItem(CAPTCHA_STORAGE_KEY, Date.now().toString());
setVerified(true);
onVerified?.();
};
const handleClick = () => {
setLoading(true);
setError(false);
window.grecaptcha.ready(() => {
window.grecaptcha
.execute(siteKey, { action: RECAPTCHA_ACTION })
.then(token => {
handleToken(token);
setLoading(false);
})
.catch(() => {
setLoading(false);
setError(true);
setTimeout(() => setError(false), 2000);
});
});
};
if (verified) {
return <>{children}</>;
}
const overlayStyles = {
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: 'rgba(7, 9, 13, 0.95)',
backdropFilter: 'blur(8px)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000,
};
const contentStyles = {
textAlign: 'center',
color: '#fff',
maxWidth: '400px',
padding: '40px',
};
const iconStyles = {
fontSize: '64px',
color: '#236f87',
marginBottom: '24px',
};
const h1Styles = {
font: '800 32px Rajdhani, sans-serif',
margin: '0 0 16px',
letterSpacing: '1px',
};
const pStyles = {
color: 'rgba(149, 207, 245, 0.7)',
font: '400 16px Rajdhani, sans-serif',
margin: '0 0 32px',
letterSpacing: '0.5px',
};
const buttonStyles = {
background: error
? 'linear-gradient(#8a2323 0%, #681a1a 100%)'
: loading
? 'linear-gradient(#236f87 0%, #1a5068 100%)'
: 'linear-gradient(#236f87 0%, #1a5068 100%)',
border: `2px solid ${error ? '#9a2e2e' : loading ? '#2e7a9a' : '#2e7a9a'}`,
borderRadius: '8px',
color: '#e0f4ff',
cursor: loading ? 'wait' : 'pointer',
font: '800 18px Rajdhani, sans-serif',
letterSpacing: '2px',
padding: '16px 40px',
textTransform: 'uppercase',
transition: 'all 0.3s ease',
display: 'inline-flex',
alignItems: 'center',
gap: '12px',
opacity: loading ? 0.7 : 1,
};
return (
<div style={overlayStyles}>
<div style={contentStyles}>
<div style={iconStyles}>
<i className="fa fa-shield-halved" />
</div>
<h1 style={h1Styles}>Ready to Play?</h1>
<p style={pStyles}>
Click below to verify you&apos;re human and start playing.
</p>
<button
style={buttonStyles}
onClick={handleClick}
disabled={loading}
>
<i className={`fa ${loading ? 'fa-spinner fa-spin' : error ? 'fa-exclamation-circle' : 'fa-play'}`} />
{loading ? 'Verifying...' : error ? 'Try Again' : 'Start Playing'}
</button>
</div>
</div>
);
};
export default CaptchaOverlay;