// app/lock.tsx — Full-screen PIN lock screen import React, { useEffect, useState, useCallback } from 'react'; import { View, Text, StyleSheet, Alert, TouchableOpacity, SafeAreaView, } from 'react-native'; import { router } from 'expo-router'; import * as LocalAuthentication from 'expo-local-authentication'; import { Shield } from 'lucide-react-native'; import PinPad from '@/components/ui/PinPad'; import { verifyPin, disablePin } from '@/lib/pinService'; import { getDatabase } from '@/lib/database'; import { useUserStore } from '@/store/useUserStore'; export default function LockScreen() { const [pin, setPin] = useState(''); const [error, setError] = useState(''); const [biometricAvailable, setBiometricAvailable] = useState(false); const clearUser = useUserStore((s) => s.clearUser); useEffect(() => { checkBiometric(); }, []); const checkBiometric = async () => { try { const hardware = await LocalAuthentication.hasHardwareAsync(); const enrolled = await LocalAuthentication.isEnrolledAsync(); setBiometricAvailable(hardware && enrolled); } catch { setBiometricAvailable(false); } }; const handleBiometric = useCallback(async () => { try { const result = await LocalAuthentication.authenticateAsync({ promptMessage: 'Unlock TerritoryLog', fallbackLabel: 'Use PIN', cancelLabel: 'Cancel', }); if (result.success) { router.replace('/(tabs)'); } else { setError('Biometric authentication failed. Use your PIN.'); } } catch { setError('Biometric not available.'); } }, []); // Auto-trigger biometric on mount if available useEffect(() => { if (biometricAvailable) { handleBiometric(); } }, [biometricAvailable, handleBiometric]); const handlePinChange = async (newPin: string) => { setPin(newPin); setError(''); if (newPin.length === 6) { const ok = await verifyPin(newPin); if (ok) { router.replace('/(tabs)'); } else { setError('Incorrect PIN. Try again.'); setTimeout(() => setPin(''), 600); } } }; const handleForgotPin = () => { Alert.alert( 'Forgot PIN?', 'Resetting your PIN will clear ALL app data. This cannot be undone.', [ { text: 'Cancel', style: 'cancel' }, { text: 'Clear & Reset', style: 'destructive', onPress: async () => { try { const db = await getDatabase(); await db.execAsync(` DELETE FROM contacts; DELETE FROM visits; DELETE FROM territories; DELETE FROM users; DELETE FROM sync_log; DELETE FROM topics; `); await disablePin(); clearUser(); router.replace('/onboarding'); } catch (e) { Alert.alert('Error', 'Could not clear data. Please try again.'); } }, }, ] ); }; return ( {/* Logo */} TerritoryLog Enter your PIN to continue {/* Error */} {error ? {error} : null} {/* PIN Pad */} {/* Biometric button */} {biometricAvailable && ( Use Biometrics )} {/* Forgot PIN */} Forgot PIN? (Clear App Data) ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#111827', }, content: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 24, gap: 32, }, logoArea: { alignItems: 'center', gap: 12, }, iconCircle: { width: 80, height: 80, borderRadius: 40, backgroundColor: 'rgba(26,107,114,0.15)', justifyContent: 'center', alignItems: 'center', borderWidth: 2, borderColor: 'rgba(26,107,114,0.4)', }, appName: { fontSize: 28, fontWeight: '700', color: '#F9FAFB', letterSpacing: 0.5, }, subtitle: { fontSize: 15, color: '#9CA3AF', }, error: { color: '#F87171', fontSize: 14, textAlign: 'center', }, biometricBtn: { paddingVertical: 12, paddingHorizontal: 28, borderRadius: 24, borderWidth: 1, borderColor: '#1A6B72', }, biometricText: { color: '#1A6B72', fontSize: 15, fontWeight: '600', }, forgotBtn: { marginTop: 8, }, forgotText: { color: '#6B7280', fontSize: 13, textDecorationLine: 'underline', }, });