import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, SafeAreaView, StatusBar, Animated, Platform, Alert, } from 'react-native'; import { LinearGradient } from 'expo-linear-gradient'; import { KurdistanColors } from '../theme/colors'; import AsyncStorage from '@react-native-async-storage/async-storage'; import * as LocalAuthentication from 'expo-local-authentication'; const HUMAN_VERIFIED_KEY = '@pezkuwi_human_verified'; interface VerifyHumanScreenProps { onVerified: () => void; } const VerifyHumanScreen: React.FC = ({ onVerified }) => { const [isChecked, setIsChecked] = useState(false); const [isBiometricSupported, setIsBiometricSupported] = useState(false); const [scaleValue] = useState(new Animated.Value(1)); // Check biometric support on mount useEffect(() => { checkBiometricSupport(); }, []); const checkBiometricSupport = async () => { try { const compatible = await LocalAuthentication.hasHardwareAsync(); const enrolled = await LocalAuthentication.isEnrolledAsync(); setIsBiometricSupported(compatible && enrolled); } catch (error) { console.warn('Biometric check failed:', error); } }; const handleBiometricAuth = async () => { try { const result = await LocalAuthentication.authenticateAsync({ promptMessage: 'Verify you are human', fallbackLabel: 'Use Passcode', cancelLabel: 'Cancel', disableDeviceFallback: false, }); if (result.success) { completeVerification(); } else { // Only show alert if it wasn't a user cancellation if (result.error !== 'user_cancel') { Alert.alert('Verification Failed', 'Could not verify identity. Please try again.'); } } } catch (error) { console.error('Biometric auth error:', error); Alert.alert('Error', 'Biometric authentication unavailable'); } }; const handleManualVerify = () => { if (!isChecked) return; completeVerification(); }; const completeVerification = async () => { // Save verification status try { await AsyncStorage.setItem(HUMAN_VERIFIED_KEY, 'true'); } catch (_error) { if (__DEV__) console.error('Failed to save verification:', _error); } // Animate and navigate Animated.sequence([ Animated.timing(scaleValue, { toValue: 1.1, duration: 100, useNativeDriver: true, }), Animated.timing(scaleValue, { toValue: 1, duration: 100, useNativeDriver: true, }), ]).start(() => { setTimeout(() => onVerified(), 200); }); }; const toggleCheck = () => { setIsChecked(!isChecked); }; return ( {/* Security Icon */} πŸ›‘οΈ {/* Title */} Identity Verification Please confirm your identity to continue {/* Biometric Button (Primary) */} {isBiometricSupported ? ( πŸ‘† Verify with Biometrics ) : ( /* Fallback to Manual Checkbox */ {isChecked && βœ“} I'm not a robot )} {/* Info Text */} {isBiometricSupported ? "Secure hardware-backed verification" : "This helps protect the Pezkuwi network from automated attacks" } {/* Continue Button (Only for manual fallback) */} {!isBiometricSupported && ( Continue )} {/* Footer */} Secure & Private ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: KurdistanColors.kesk, }, gradient: { flex: 1, }, content: { flex: 1, padding: 20, justifyContent: 'center', alignItems: 'center', }, iconContainer: { width: 100, height: 100, borderRadius: 50, backgroundColor: KurdistanColors.spi, justifyContent: 'center', alignItems: 'center', marginBottom: 30, boxShadow: '0 4px 8px rgba(0, 0, 0, 0.3)', elevation: 8, }, iconText: { fontSize: 50, }, title: { fontSize: 28, fontWeight: 'bold', color: KurdistanColors.spi, textAlign: 'center', marginBottom: 10, }, subtitle: { fontSize: 16, color: KurdistanColors.spi, textAlign: 'center', opacity: 0.9, marginBottom: 40, }, biometricButton: { flexDirection: 'row', alignItems: 'center', backgroundColor: KurdistanColors.spi, borderRadius: 16, paddingVertical: 20, paddingHorizontal: 32, marginBottom: 20, boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)', elevation: 8, }, biometricIcon: { fontSize: 24, marginRight: 12, }, biometricText: { fontSize: 18, fontWeight: 'bold', color: KurdistanColors.kesk, }, verificationBox: { flexDirection: 'row', alignItems: 'center', backgroundColor: KurdistanColors.spi, borderRadius: 12, padding: 20, marginBottom: 20, boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)', elevation: 4, width: '100%', maxWidth: 400, }, checkbox: { width: 32, height: 32, borderRadius: 6, borderWidth: 2, borderColor: KurdistanColors.kesk, marginRight: 15, justifyContent: 'center', alignItems: 'center', }, checkboxChecked: { backgroundColor: KurdistanColors.kesk, borderColor: KurdistanColors.kesk, }, checkmark: { fontSize: 20, color: KurdistanColors.spi, fontWeight: 'bold', }, verificationText: { fontSize: 18, color: KurdistanColors.reş, fontWeight: '500', }, infoText: { fontSize: 13, color: KurdistanColors.spi, textAlign: 'center', opacity: 0.8, marginBottom: 40, paddingHorizontal: 20, }, continueButton: { backgroundColor: KurdistanColors.spi, borderRadius: 12, padding: 16, alignItems: 'center', width: '100%', maxWidth: 400, boxShadow: '0 4px 6px rgba(0, 0, 0, 0.3)', elevation: 6, }, continueButtonDisabled: { backgroundColor: 'rgba(255, 255, 255, 0.3)', boxShadow: 'none', elevation: 0, }, continueButtonText: { fontSize: 18, fontWeight: 'bold', color: KurdistanColors.kesk, }, continueButtonTextDisabled: { color: 'rgba(0, 0, 0, 0.3)', }, footer: { position: 'absolute', bottom: 30, alignItems: 'center', }, footerText: { fontSize: 14, color: KurdistanColors.spi, opacity: 0.8, }, }); export default VerifyHumanScreen; // Export helper to check verification status export const checkHumanVerification = async (): Promise => { try { const verified = await AsyncStorage.getItem(HUMAN_VERIFIED_KEY); return verified === 'true'; } catch (_error) { if (__DEV__) console.error('Failed to check verification:', _error); return false; } };