Files
pwap/mobile/src/screens/LockScreen.tsx
T
pezkuwichain 8d30519efc Fix all shadow deprecation warnings across entire mobile app
- Replaced shadowColor/shadowOffset/shadowOpacity/shadowRadius with boxShadow
- Fixed 28 files (21 screens + 7 components)
- Preserved elevation for Android compatibility
- All React Native Web deprecation warnings resolved

Files fixed:
- All screen components
- All reusable components
- Navigation components
- Modal components
2026-01-14 15:05:10 +03:00

309 lines
7.6 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
Pressable,
Alert,
} from 'react-native';
import { useBiometricAuth } from '../contexts/BiometricAuthContext';
import { AppColors, KurdistanColors } from '../theme/colors';
import { Button, Input } from '../components';
/**
* Lock Screen
* Shown when app is locked - requires biometric or PIN
*
* PRIVACY: All authentication happens locally
*/
export default function LockScreen() {
const {
isBiometricSupported,
isBiometricEnrolled,
isBiometricEnabled,
biometricType,
authenticate,
verifyPinCode,
} = useBiometricAuth();
const [showPinInput, setShowPinInput] = useState(false);
const [pin, setPin] = useState('');
const [verifying, setVerifying] = useState(false);
const handleBiometricAuth = React.useCallback(async () => {
const success = await authenticate();
if (!success) {
// Biometric failed, show PIN option
setShowPinInput(true);
}
}, [authenticate]);
useEffect(() => {
// Auto-trigger biometric on mount if enabled
if (isBiometricEnabled && isBiometricSupported && isBiometricEnrolled) {
handleBiometricAuth();
}
}, [isBiometricEnabled, isBiometricSupported, isBiometricEnrolled, handleBiometricAuth]);
const handlePinSubmit = async () => {
if (!pin || pin.length < 4) {
Alert.alert('Error', 'Please enter your PIN');
return;
}
try {
setVerifying(true);
const success = await verifyPinCode(pin);
if (!success) {
Alert.alert('Error', 'Incorrect PIN. Please try again.');
setPin('');
}
} catch {
Alert.alert('Error', 'Failed to verify PIN');
} finally {
setVerifying(false);
}
};
const getBiometricIcon = () => {
switch (biometricType) {
case 'facial': return '😊';
case 'fingerprint': return '👆';
case 'iris': return '👁️';
default: return '🔒';
}
};
const getBiometricLabel = () => {
switch (biometricType) {
case 'facial': return 'Face ID';
case 'fingerprint': return 'Fingerprint';
case 'iris': return 'Iris';
default: return 'Biometric';
}
};
return (
<View style={styles.container}>
{/* Logo */}
<View style={styles.logoContainer}>
<Text style={styles.logo}>🌟</Text>
<Text style={styles.appName}>PezkuwiChain</Text>
<Text style={styles.subtitle}>Digital Kurdistan</Text>
</View>
{/* Lock Icon */}
<View style={styles.lockIcon}>
<Text style={styles.lockEmoji}>🔒</Text>
</View>
{/* Title */}
<Text style={styles.title}>App Locked</Text>
<Text style={styles.description}>
Authenticate to unlock and access your wallet
</Text>
{/* Biometric or PIN */}
<View style={styles.authContainer}>
{!showPinInput ? (
// Biometric Button
isBiometricEnabled && isBiometricSupported && isBiometricEnrolled ? (
<View style={styles.biometricContainer}>
<Pressable
onPress={handleBiometricAuth}
style={styles.biometricButton}
>
<Text style={styles.biometricIcon}>{getBiometricIcon()}</Text>
</Pressable>
<Text style={styles.biometricLabel}>
Tap to use {getBiometricLabel()}
</Text>
<Pressable
onPress={() => setShowPinInput(true)}
style={styles.usePinButton}
>
<Text style={styles.usePinText}>Use PIN instead</Text>
</Pressable>
</View>
) : (
// No biometric, show PIN immediately
<View style={styles.noBiometricContainer}>
<Text style={styles.noBiometricText}>
Biometric authentication not available
</Text>
<Button
title="Enter PIN"
onPress={() => setShowPinInput(true)}
variant="primary"
fullWidth
/>
</View>
)
) : (
// PIN Input
<View style={styles.pinContainer}>
<Input
label="Enter PIN"
value={pin}
onChangeText={setPin}
keyboardType="numeric"
maxLength={6}
secureTextEntry
placeholder="Enter your PIN"
autoFocus
/>
<Button
title="Unlock"
onPress={handlePinSubmit}
loading={verifying}
disabled={verifying || pin.length < 4}
variant="primary"
fullWidth
/>
{isBiometricEnabled && (
<Pressable
onPress={() => {
setShowPinInput(false);
setPin('');
}}
style={styles.backButton}
>
<Text style={styles.backText}>
Use {getBiometricLabel()} instead
</Text>
</Pressable>
)}
</View>
)}
</View>
{/* Privacy Notice */}
<View style={styles.privacyNotice}>
<Text style={styles.privacyText}>
🔐 Authentication happens on your device only
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: AppColors.background,
paddingHorizontal: 24,
justifyContent: 'center',
alignItems: 'center',
},
logoContainer: {
alignItems: 'center',
marginBottom: 40,
},
logo: {
fontSize: 64,
marginBottom: 8,
},
appName: {
fontSize: 28,
fontWeight: '700',
color: KurdistanColors.kesk,
marginBottom: 4,
},
subtitle: {
fontSize: 16,
color: AppColors.textSecondary,
},
lockIcon: {
width: 100,
height: 100,
borderRadius: 50,
backgroundColor: AppColors.surface,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)',
elevation: 8,
},
lockEmoji: {
fontSize: 48,
},
title: {
fontSize: 24,
fontWeight: '700',
color: AppColors.text,
marginBottom: 8,
},
description: {
fontSize: 16,
color: AppColors.textSecondary,
textAlign: 'center',
marginBottom: 40,
},
authContainer: {
width: '100%',
maxWidth: 360,
},
biometricContainer: {
alignItems: 'center',
},
biometricButton: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: KurdistanColors.kesk,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 16,
boxShadow: '0px 4px 12px rgba(0, 128, 0, 0.3)',
elevation: 8,
},
biometricIcon: {
fontSize: 40,
},
biometricLabel: {
fontSize: 16,
color: AppColors.text,
marginBottom: 24,
},
usePinButton: {
paddingVertical: 12,
},
usePinText: {
fontSize: 14,
color: KurdistanColors.kesk,
fontWeight: '600',
},
noBiometricContainer: {
alignItems: 'center',
},
noBiometricText: {
fontSize: 14,
color: AppColors.textSecondary,
marginBottom: 16,
textAlign: 'center',
},
pinContainer: {
gap: 16,
},
backButton: {
paddingVertical: 12,
alignItems: 'center',
},
backText: {
fontSize: 14,
color: KurdistanColors.kesk,
fontWeight: '600',
},
privacyNotice: {
position: 'absolute',
bottom: 40,
paddingHorizontal: 24,
},
privacyText: {
fontSize: 12,
color: AppColors.textSecondary,
textAlign: 'center',
},
});