useAltcha.ts
1 import { useState, useEffect, useCallback } from 'react'; 2 import { altchaService, AltchaVerification } from '../services/altcha/AltchaService'; 3 4 export interface UseAltchaOptions { 5 autoVerify?: boolean; 6 maxnumber?: number; 7 onVerified?: (verification: AltchaVerification) => void; 8 onError?: (error: Error) => void; 9 } 10 11 export interface UseAltchaReturn { 12 isVerified: boolean; 13 isLoading: boolean; 14 stage: 'idle' | 'generating' | 'solving' | 'verifying' | 'error'; 15 progress: number; 16 hashRate: number; 17 error: Error | null; 18 verification: AltchaVerification | null; 19 verify: () => Promise<void>; 20 reset: () => void; 21 getRemainingTime: () => number; 22 } 23 24 /** 25 * Hook for managing ALTCHA verification state 26 */ 27 export function useAltcha(options: UseAltchaOptions = {}): UseAltchaReturn { 28 const { 29 autoVerify = false, 30 maxnumber = 100000, 31 onVerified, 32 onError, 33 } = options; 34 35 const [isVerified, setIsVerified] = useState(false); 36 const [isLoading, setIsLoading] = useState(false); 37 const [stage, setStage] = useState<UseAltchaReturn['stage']>('idle'); 38 const [progress, setProgress] = useState(0); 39 const [hashRate, setHashRate] = useState(0); 40 const [error, setError] = useState<Error | null>(null); 41 const [verification, setVerification] = useState<AltchaVerification | null>(null); 42 43 // Check initial verification status 44 useEffect(() => { 45 const checkVerification = () => { 46 const verified = altchaService.isVerified(); 47 setIsVerified(verified); 48 49 if (verified) { 50 // Get stored verification details 51 const stored = sessionStorage.getItem('altcha_verification'); 52 if (stored) { 53 try { 54 setVerification(JSON.parse(stored)); 55 } catch (e) { 56 console.error('Failed to parse stored verification:', e); 57 } 58 } 59 } 60 }; 61 62 checkVerification(); 63 64 // Check periodically for expiration 65 const interval = setInterval(checkVerification, 30000); 66 return () => clearInterval(interval); 67 }, []); 68 69 // Verify function 70 const verify = useCallback(async () => { 71 if (isLoading || isVerified) return; 72 73 setIsLoading(true); 74 setError(null); 75 setProgress(0); 76 setHashRate(0); 77 setStage('idle'); 78 79 try { 80 const result = await altchaService.completeAltchaFlow({ 81 maxnumber, 82 onProgress: (stage, progress, hashRate) => { 83 setStage(stage); 84 if (progress !== undefined) setProgress(progress); 85 if (hashRate !== undefined) setHashRate(hashRate); 86 }, 87 }); 88 89 setVerification(result); 90 setIsVerified(true); 91 setStage('idle'); 92 onVerified?.(result); 93 } catch (err) { 94 const error = err instanceof Error ? err : new Error('Verification failed'); 95 setError(error); 96 setStage('error'); 97 onError?.(error); 98 } finally { 99 setIsLoading(false); 100 } 101 }, [isLoading, isVerified, maxnumber, onVerified, onError]); 102 103 // Reset function 104 const reset = useCallback(() => { 105 altchaService.clearVerification(); 106 setIsVerified(false); 107 setVerification(null); 108 setError(null); 109 setProgress(0); 110 setHashRate(0); 111 setStage('idle'); 112 }, []); 113 114 // Get remaining time 115 const getRemainingTime = useCallback(() => { 116 return altchaService.getRemainingTime(); 117 }, []); 118 119 // Auto-verify if requested 120 useEffect(() => { 121 if (autoVerify && !isVerified && !isLoading && stage === 'idle') { 122 verify(); 123 } 124 }, [autoVerify, isVerified, isLoading, stage, verify]); 125 126 return { 127 isVerified, 128 isLoading, 129 stage, 130 progress, 131 hashRate, 132 error, 133 verification, 134 verify, 135 reset, 136 getRemainingTime, 137 }; 138 } 139 140 /** 141 * Hook to require ALTCHA verification for a component 142 */ 143 export function useRequireAltcha(options: UseAltchaOptions = {}) { 144 const altcha = useAltcha({ ...options, autoVerify: true }); 145 146 // Throw in render if not verified (for use with error boundaries) 147 if (!altcha.isVerified && !altcha.isLoading && altcha.stage !== 'idle') { 148 throw new Error('ALTCHA verification required'); 149 } 150 151 return altcha; 152 } 153 154 /** 155 * Hook to get ALTCHA verification status without triggering verification 156 */ 157 export function useAltchaStatus() { 158 const [isVerified, setIsVerified] = useState(altchaService.isVerified()); 159 const [remainingTime, setRemainingTime] = useState(altchaService.getRemainingTime()); 160 161 useEffect(() => { 162 const checkStatus = () => { 163 setIsVerified(altchaService.isVerified()); 164 setRemainingTime(altchaService.getRemainingTime()); 165 }; 166 167 // Check immediately 168 checkStatus(); 169 170 // Check every second for countdown 171 const interval = setInterval(checkStatus, 1000); 172 return () => clearInterval(interval); 173 }, []); 174 175 return { 176 isVerified, 177 remainingTime, 178 remainingMinutes: Math.floor(remainingTime / 60000), 179 remainingSeconds: Math.floor((remainingTime % 60000) / 1000), 180 }; 181 }