@safepassage/sdk
Version:
SafePassage SDK - Lightweight redirect-based age verification
197 lines (181 loc) • 7.22 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useSafePassage } from '../hooks/useSafePassage';
const SafePassageVerification = ({ wsUrl, mode = 'enhanced_verification', maxReconnectAttempts = 5, reconnectDelay = 3000, frameRate = 10, imageQuality = 0.8, config_overrides, onSuccess, onFailure, onStateChange, customMessages, className = '', style = {} }) => {
const { error, feedback, processingStep, zoneTransition, qualityScore, videoRef, canvasRef, isSessionStarted } = useSafePassage({
wsUrl,
mode,
maxReconnectAttempts,
reconnectDelay,
frameRate,
imageQuality,
config_overrides
}, {
onSuccess,
onFailure,
onStateChange
}, customMessages);
const getDisplayMessage = () => {
if (zoneTransition) {
return zoneTransition.message;
}
if (feedback && !processingStep) {
return feedback;
}
if (processingStep) {
return processingStep;
}
if (!isSessionStarted) {
return 'Starting camera...';
}
return 'Position your face in the camera and look forward';
};
const getMessageClass = () => {
if (zoneTransition)
return 'safepassage-instruction-zone-transition';
if (feedback && !processingStep)
return 'safepassage-instruction-feedback';
if (processingStep)
return 'safepassage-instruction-processing';
return 'safepassage-instruction-default';
};
// Calculate glow intensity based on quality score and feedback
const getGlowIntensity = () => {
// Base glow on quality score (0-1)
let intensity = qualityScore;
// Boost intensity for positive feedback (both generic and zone-specific)
if (feedback === 'good_position' ||
feedback === 'good_wide_position' ||
feedback === 'good_close_position') {
intensity = Math.max(intensity, 0.9);
}
// Medium intensity for zone positioning messages
if (feedback === 'position_at_comfortable_distance' ||
feedback === 'move_closer_to_camera') {
intensity = Math.max(intensity, 0.6);
}
// Reduce intensity for negative feedback
if (feedback === 'no_face_detected' ||
feedback === 'move_closer' ||
feedback === 'move_further' ||
feedback === 'center_face' ||
feedback === 'improve_lighting') {
intensity = Math.min(intensity, 0.3);
}
// Special handling for zone transitions - pulse effect
if (zoneTransition) {
intensity = Math.max(intensity, 0.7);
}
// Clamp between 0 and 1
return Math.max(0, Math.min(1, intensity));
};
const glowIntensity = getGlowIntensity();
return (_jsxs("div", { className: `safepassage-verification-container ${className}`, style: style, children: [_jsxs("div", { className: "safepassage-video-container", children: [_jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, className: "safepassage-verification-video" }), _jsx("div", { className: "safepassage-video-overlay", children: _jsx("div", { className: "safepassage-oval-frame", style: {
'--glow-intensity': glowIntensity,
'--glow-opacity': glowIntensity > 0.1 ? glowIntensity : 0
} }) })] }), _jsx("div", { className: "safepassage-instructions-area", children: _jsx("div", { className: `safepassage-instruction-text ${getMessageClass()}`, children: getDisplayMessage() }) }), _jsx("canvas", { ref: canvasRef, style: { display: 'none' } }), error && (_jsx("div", { className: "safepassage-error-message", children: error })), _jsx("style", { children: `
.safepassage-verification-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
min-height: 100vh;
margin: 0;
padding: 40px 20px;
text-align: center;
background-color: #2a2a2a;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.safepassage-video-container {
position: relative;
width: 320px;
height: 320px;
margin-bottom: 40px;
}
.safepassage-verification-video {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
background-color: #000;
}
.safepassage-video-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.safepassage-oval-frame {
width: 100%;
height: 100%;
border: 3px solid #ffffff;
border-radius: 50%;
box-shadow:
0 0 20px rgba(255, 255, 255, 0.3),
0 0 calc(30px * var(--glow-intensity, 0)) rgba(34, 197, 94, calc(var(--glow-opacity, 0) * 0.8)),
0 0 calc(50px * var(--glow-intensity, 0)) rgba(34, 197, 94, calc(var(--glow-opacity, 0) * 0.4)),
0 0 calc(80px * var(--glow-intensity, 0)) rgba(34, 197, 94, calc(var(--glow-opacity, 0) * 0.2));
transition: box-shadow 0.3s ease-out;
}
.safepassage-instructions-area {
width: 100%;
max-width: 400px;
}
.safepassage-instruction-text {
font-size: 18px;
font-weight: 500;
color: #ffffff;
text-align: center;
line-height: 1.4;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.safepassage-instruction-zone-transition {
color: #4fc3f7;
font-weight: 600;
animation: safepassage-pulse 0.5s ease-in-out;
}
.safepassage-instruction-feedback {
color: #ffffff;
}
.safepassage-instruction-processing {
color: #81c784;
}
.safepassage-instruction-default {
color: #ffffff;
opacity: 0.9;
}
.safepassage-error-message {
color: #e53e3e;
margin-top: 10px;
}
@keyframes safepassage-pulse {
0% { opacity: 0.7; transform: scale(1); }
50% { opacity: 1; transform: scale(1.02); }
100% { opacity: 0.9; transform: scale(1); }
}
@media (max-width: 480px) {
.safepassage-verification-container {
padding: 20px 15px;
}
.safepassage-video-container {
width: 280px;
height: 280px;
margin-bottom: 30px;
}
.safepassage-instruction-text {
font-size: 16px;
min-height: 45px;
}
}
` })] }));
};
export default SafePassageVerification;