UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

224 lines (207 loc) • 6.71 kB
"use strict"; import { useState, useRef, useEffect, useCallback, useMemo } from 'react'; import { useOxy } from '../context/OxyContext'; import { useThemeColors } from '../styles'; import { toast } from '../../lib/sonner'; import StepBasedScreen from '../components/StepBasedScreen'; import SignUpWelcomeStep from './steps/SignUpWelcomeStep'; import SignUpIdentityStep from './steps/SignUpIdentityStep'; import SignUpSecurityStep from './steps/SignUpSecurityStep'; import SignUpSummaryStep from './steps/SignUpSummaryStep'; import { TTLCache, registerCacheForCleanup } from '../../utils/cache'; // Types for better type safety import { jsx as _jsx } from "react/jsx-runtime"; // Constants const USERNAME_MIN_LENGTH = 3; const PASSWORD_MIN_LENGTH = 8; // Email validation regex const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Main component const SignUpScreen = ({ navigate, goBack, onAuthenticated, theme }) => { const { signUp, oxyServices } = useOxy(); const colors = useThemeColors(theme); // Form data state const [username, setUsername] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); // Validation state const [validationState, setValidationState] = useState({ status: 'idle', message: '' }); // Error message state const [errorMessage, setErrorMessage] = useState(''); // Username validation with caching - uses centralized cache const usernameCache = useRef(new TTLCache(5 * 60 * 1000)); // 5 minutes cache // Register cache for cleanup on mount useEffect(() => { registerCacheForCleanup(usernameCache.current); return () => { usernameCache.current.clear(); }; }, []); const validateUsername = useCallback(async usernameToValidate => { if (!usernameToValidate || usernameToValidate.length < USERNAME_MIN_LENGTH) { setValidationState({ status: 'invalid', message: 'Username must be at least 3 characters' }); return false; } // Check cache first const cached = usernameCache.current.get(usernameToValidate); if (cached !== null) { const isValid = cached; setValidationState({ status: isValid ? 'valid' : 'invalid', message: isValid ? '' : 'Username is already taken' }); return isValid; } setValidationState({ status: 'validating', message: '' }); try { const result = await oxyServices.checkUsernameAvailability(usernameToValidate); const isValid = result.available; // Cache the result usernameCache.current.set(usernameToValidate, isValid); setValidationState({ status: isValid ? 'valid' : 'invalid', message: isValid ? '' : result.message || 'Username is already taken' }); return isValid; } catch (error) { console.error('Username validation error:', error); setValidationState({ status: 'invalid', message: 'Unable to validate username. Please try again.' }); return false; } }, [oxyServices]); // Email validation const validateEmail = useCallback(emailToValidate => { return EMAIL_REGEX.test(emailToValidate); }, []); // Password validation const validatePassword = useCallback(passwordToValidate => { return passwordToValidate.length >= PASSWORD_MIN_LENGTH; }, []); // Handle form completion const handleComplete = useCallback(async stepData => { if (!username || !email || !password) { toast.error('Please fill in all required fields'); return; } if (!validateEmail(email)) { toast.error('Please enter a valid email address'); return; } if (!validatePassword(password)) { toast.error('Password must be at least 8 characters long'); return; } if (password !== confirmPassword) { toast.error('Passwords do not match'); return; } try { setIsLoading(true); const user = await signUp(username, email, password); toast.success('Account created successfully! Welcome to Oxy!'); // Navigate to welcome screen or handle authentication navigate('WelcomeNewUser', { newUser: user }); } catch (error) { toast.error(error.message || 'Sign up failed'); } finally { setIsLoading(false); } }, [username, email, password, confirmPassword, validateEmail, validatePassword, signUp, navigate]); // Step configurations const steps = useMemo(() => [{ id: 'welcome', component: SignUpWelcomeStep, canProceed: () => true }, { id: 'identity', component: SignUpIdentityStep, canProceed: () => !!(username.trim() && email.trim() && validateEmail(email) && validationState.status === 'valid'), onEnter: () => { // Auto-validate username when entering this step if (username && validationState.status === 'idle') { validateUsername(username); } } }, { id: 'security', component: SignUpSecurityStep, canProceed: () => !!(password && validatePassword(password) && password === confirmPassword) }, { id: 'summary', component: SignUpSummaryStep, canProceed: () => true }], [username, email, password, confirmPassword, validationState.status, validateEmail, validatePassword, validateUsername]); // Step data for the reusable component const stepData = useMemo(() => [ // Welcome step - no data needed {}, // Identity step { username, email, setUsername, setEmail, validationState, setValidationState, setErrorMessage, validateEmail, validateUsername }, // Security step { password, confirmPassword, setPassword, setConfirmPassword, showPassword, showConfirmPassword, setShowPassword, setShowConfirmPassword, setErrorMessage, validatePassword }, // Summary step { isLoading }], [username, email, password, confirmPassword, showPassword, showConfirmPassword, validationState, errorMessage, validateEmail, validatePassword, isLoading]); return /*#__PURE__*/_jsx(StepBasedScreen, { steps: steps, stepData: stepData, onComplete: handleComplete, navigate: navigate, goBack: goBack, onAuthenticated: onAuthenticated, theme: theme, showProgressIndicator: true, enableAnimations: true, oxyServices: oxyServices }); }; export default SignUpScreen; //# sourceMappingURL=SignUpScreen.js.map