@hexar/biometric-identity-sdk-react-native 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +68 -0
  2. package/dist/components/BiometricIdentityFlow.d.ts +17 -0
  3. package/dist/components/BiometricIdentityFlow.d.ts.map +1 -0
  4. package/dist/components/BiometricIdentityFlow.js +366 -0
  5. package/dist/components/CameraCapture.d.ts +15 -0
  6. package/dist/components/CameraCapture.d.ts.map +1 -0
  7. package/dist/components/CameraCapture.js +238 -0
  8. package/dist/components/ErrorScreen.d.ts +15 -0
  9. package/dist/components/ErrorScreen.d.ts.map +1 -0
  10. package/dist/components/ErrorScreen.js +142 -0
  11. package/dist/components/InstructionsScreen.d.ts +14 -0
  12. package/dist/components/InstructionsScreen.d.ts.map +1 -0
  13. package/dist/components/InstructionsScreen.js +181 -0
  14. package/dist/components/ResultScreen.d.ts +15 -0
  15. package/dist/components/ResultScreen.d.ts.map +1 -0
  16. package/dist/components/ResultScreen.js +182 -0
  17. package/dist/components/ValidationProgress.d.ts +14 -0
  18. package/dist/components/ValidationProgress.d.ts.map +1 -0
  19. package/dist/components/ValidationProgress.js +143 -0
  20. package/dist/components/VideoRecorder.d.ts +43 -0
  21. package/dist/components/VideoRecorder.d.ts.map +1 -0
  22. package/dist/components/VideoRecorder.js +631 -0
  23. package/dist/hooks/useBiometricSDK.d.ts +25 -0
  24. package/dist/hooks/useBiometricSDK.d.ts.map +1 -0
  25. package/dist/hooks/useBiometricSDK.js +173 -0
  26. package/dist/index.d.ts +15 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +47 -0
  29. package/package.json +27 -0
  30. package/src/components/BiometricIdentityFlow.tsx +557 -0
  31. package/src/components/CameraCapture.tsx +262 -0
  32. package/src/components/ErrorScreen.tsx +201 -0
  33. package/src/components/InstructionsScreen.tsx +269 -0
  34. package/src/components/ResultScreen.tsx +301 -0
  35. package/src/components/ValidationProgress.tsx +223 -0
  36. package/src/components/VideoRecorder.tsx +794 -0
  37. package/src/hooks/useBiometricSDK.ts +230 -0
  38. package/src/index.ts +24 -0
  39. package/tsconfig.json +20 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Camera Capture Component for React Native
3
+ * Handles ID document photo capture
4
+ */
5
+
6
+ import React, { useState, useRef } from 'react';
7
+ import {
8
+ View,
9
+ Text,
10
+ StyleSheet,
11
+ TouchableOpacity,
12
+ Dimensions,
13
+ Platform,
14
+ } from 'react-native';
15
+ import { ThemeConfig } from '@hexar/biometric-identity-sdk-core';
16
+
17
+ const { width, height } = Dimensions.get('window');
18
+
19
+ export interface CameraCaptureProps {
20
+ mode: 'front' | 'back';
21
+ theme?: ThemeConfig;
22
+ onCapture: (imageData: string) => void;
23
+ onCancel: () => void;
24
+ }
25
+
26
+ export const CameraCapture: React.FC<CameraCaptureProps> = ({
27
+ mode,
28
+ theme,
29
+ onCapture,
30
+ onCancel,
31
+ }) => {
32
+ const [isCapturing, setIsCapturing] = useState(false);
33
+ const cameraRef = useRef<any>(null);
34
+
35
+ const handleCapture = async () => {
36
+ if (isCapturing) return;
37
+
38
+ try {
39
+ setIsCapturing(true);
40
+
41
+ // In real implementation, this would use react-native-vision-camera
42
+ // For now, simulate capture
43
+ await new Promise(resolve => setTimeout(resolve, 500));
44
+
45
+ // Mock captured image data
46
+ const mockImageData = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...';
47
+
48
+ onCapture(mockImageData);
49
+ } catch (error) {
50
+ console.error('Capture error:', error);
51
+ setIsCapturing(false);
52
+ }
53
+ };
54
+
55
+ const instructions = mode === 'front'
56
+ ? 'Position the front of your ID within the frame'
57
+ : 'Position the back of your ID within the frame';
58
+
59
+ return (
60
+ <View style={styles.container}>
61
+ {/* Camera View - In real implementation, use react-native-vision-camera */}
62
+ <View style={styles.cameraContainer}>
63
+ <View style={styles.mockCamera}>
64
+ <Text style={styles.mockCameraText}>Camera View</Text>
65
+ <Text style={styles.mockCameraSubtext}>
66
+ (In production, this uses react-native-vision-camera)
67
+ </Text>
68
+ </View>
69
+
70
+ {/* Document Frame Overlay */}
71
+ <View style={styles.overlay}>
72
+ <View style={styles.frameContainer}>
73
+ <View style={styles.frame}>
74
+ <View style={[styles.corner, styles.cornerTopLeft]} />
75
+ <View style={[styles.corner, styles.cornerTopRight]} />
76
+ <View style={[styles.corner, styles.cornerBottomLeft]} />
77
+ <View style={[styles.corner, styles.cornerBottomRight]} />
78
+ </View>
79
+ </View>
80
+ </View>
81
+ </View>
82
+
83
+ {/* Instructions */}
84
+ <View style={styles.instructionsContainer}>
85
+ <Text style={styles.instructionsText}>{instructions}</Text>
86
+ <Text style={styles.tipsText}>
87
+ • Ensure good lighting{'\n'}
88
+ • Avoid glare and shadows{'\n'}
89
+ • Keep document flat and complete
90
+ </Text>
91
+ </View>
92
+
93
+ {/* Controls */}
94
+ <View style={styles.controls}>
95
+ <TouchableOpacity
96
+ style={[styles.button, styles.cancelButton]}
97
+ onPress={onCancel}
98
+ >
99
+ <Text style={styles.buttonText}>Cancel</Text>
100
+ </TouchableOpacity>
101
+
102
+ <TouchableOpacity
103
+ style={[
104
+ styles.button,
105
+ styles.captureButton,
106
+ { backgroundColor: theme?.primaryColor || '#6366F1' },
107
+ isCapturing && styles.captureButtonDisabled,
108
+ ]}
109
+ onPress={handleCapture}
110
+ disabled={isCapturing}
111
+ >
112
+ <View style={styles.captureButtonInner} />
113
+ </TouchableOpacity>
114
+
115
+ <View style={styles.buttonPlaceholder} />
116
+ </View>
117
+ </View>
118
+ );
119
+ };
120
+
121
+ const styles = StyleSheet.create({
122
+ container: {
123
+ flex: 1,
124
+ backgroundColor: '#000000',
125
+ },
126
+ cameraContainer: {
127
+ flex: 1,
128
+ position: 'relative',
129
+ },
130
+ mockCamera: {
131
+ flex: 1,
132
+ backgroundColor: '#1F2937',
133
+ justifyContent: 'center',
134
+ alignItems: 'center',
135
+ },
136
+ mockCameraText: {
137
+ color: '#FFFFFF',
138
+ fontSize: 20,
139
+ fontWeight: 'bold',
140
+ marginBottom: 8,
141
+ },
142
+ mockCameraSubtext: {
143
+ color: '#9CA3AF',
144
+ fontSize: 12,
145
+ textAlign: 'center',
146
+ paddingHorizontal: 32,
147
+ },
148
+ overlay: {
149
+ ...StyleSheet.absoluteFillObject,
150
+ justifyContent: 'center',
151
+ alignItems: 'center',
152
+ },
153
+ frameContainer: {
154
+ width: width * 0.85,
155
+ height: width * 0.85 * 0.63, // ID card aspect ratio
156
+ justifyContent: 'center',
157
+ alignItems: 'center',
158
+ },
159
+ frame: {
160
+ width: '100%',
161
+ height: '100%',
162
+ borderWidth: 2,
163
+ borderColor: '#FFFFFF',
164
+ borderRadius: 8,
165
+ borderStyle: 'dashed',
166
+ },
167
+ corner: {
168
+ position: 'absolute',
169
+ width: 24,
170
+ height: 24,
171
+ borderColor: '#FFFFFF',
172
+ },
173
+ cornerTopLeft: {
174
+ top: -2,
175
+ left: -2,
176
+ borderTopWidth: 4,
177
+ borderLeftWidth: 4,
178
+ borderTopLeftRadius: 8,
179
+ },
180
+ cornerTopRight: {
181
+ top: -2,
182
+ right: -2,
183
+ borderTopWidth: 4,
184
+ borderRightWidth: 4,
185
+ borderTopRightRadius: 8,
186
+ },
187
+ cornerBottomLeft: {
188
+ bottom: -2,
189
+ left: -2,
190
+ borderBottomWidth: 4,
191
+ borderLeftWidth: 4,
192
+ borderBottomLeftRadius: 8,
193
+ },
194
+ cornerBottomRight: {
195
+ bottom: -2,
196
+ right: -2,
197
+ borderBottomWidth: 4,
198
+ borderRightWidth: 4,
199
+ borderBottomRightRadius: 8,
200
+ },
201
+ instructionsContainer: {
202
+ paddingVertical: 24,
203
+ paddingHorizontal: 32,
204
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
205
+ },
206
+ instructionsText: {
207
+ color: '#FFFFFF',
208
+ fontSize: 16,
209
+ fontWeight: '600',
210
+ textAlign: 'center',
211
+ marginBottom: 12,
212
+ },
213
+ tipsText: {
214
+ color: '#D1D5DB',
215
+ fontSize: 14,
216
+ lineHeight: 20,
217
+ },
218
+ controls: {
219
+ flexDirection: 'row',
220
+ justifyContent: 'space-between',
221
+ alignItems: 'center',
222
+ paddingVertical: 32,
223
+ paddingHorizontal: 32,
224
+ backgroundColor: 'rgba(0, 0, 0, 0.9)',
225
+ },
226
+ button: {
227
+ paddingVertical: 12,
228
+ paddingHorizontal: 24,
229
+ borderRadius: 8,
230
+ },
231
+ cancelButton: {
232
+ backgroundColor: '#374151',
233
+ },
234
+ buttonText: {
235
+ color: '#FFFFFF',
236
+ fontSize: 16,
237
+ fontWeight: '600',
238
+ },
239
+ captureButton: {
240
+ width: 72,
241
+ height: 72,
242
+ borderRadius: 36,
243
+ justifyContent: 'center',
244
+ alignItems: 'center',
245
+ },
246
+ captureButtonInner: {
247
+ width: 60,
248
+ height: 60,
249
+ borderRadius: 30,
250
+ backgroundColor: '#FFFFFF',
251
+ },
252
+ captureButtonDisabled: {
253
+ opacity: 0.5,
254
+ },
255
+ buttonPlaceholder: {
256
+ width: 80,
257
+ },
258
+ });
259
+
260
+ export default CameraCapture;
261
+
262
+
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Error Screen Component
3
+ */
4
+
5
+ import React from 'react';
6
+ import {
7
+ View,
8
+ Text,
9
+ StyleSheet,
10
+ TouchableOpacity,
11
+ } from 'react-native';
12
+ import { BiometricError, ThemeConfig, SupportedLanguage } from '@hexar/biometric-identity-sdk-core';
13
+
14
+ export interface ErrorScreenProps {
15
+ error: BiometricError;
16
+ theme?: ThemeConfig;
17
+ language?: SupportedLanguage;
18
+ onRetry: () => void;
19
+ onClose: () => void;
20
+ }
21
+
22
+ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
23
+ error,
24
+ theme,
25
+ language = 'en',
26
+ onRetry,
27
+ onClose,
28
+ }) => {
29
+ const getErrorMessage = () => {
30
+ const messages: Record<string, { en: string; es: string }> = {
31
+ CAMERA_PERMISSION_DENIED: {
32
+ en: 'Camera permission is required to capture your ID and face video.',
33
+ es: 'Se requiere permiso de cámara para capturar tu documento y video facial.',
34
+ },
35
+ FACE_NOT_DETECTED: {
36
+ en: 'No face detected in the document. Please ensure the photo is clear.',
37
+ es: 'No se detectó rostro en el documento. Asegúrate de que la foto sea clara.',
38
+ },
39
+ POOR_IMAGE_QUALITY: {
40
+ en: 'Image quality is insufficient. Please avoid glare, blur, and ensure good lighting.',
41
+ es: 'La calidad de la imagen es insuficiente. Evita brillos, desenfoque y asegura buena iluminación.',
42
+ },
43
+ LIVENESS_CHECK_FAILED: {
44
+ en: 'Liveness verification failed. Please record a new video following the instructions.',
45
+ es: 'La verificación de presencia vital falló. Por favor graba un nuevo video siguiendo las instrucciones.',
46
+ },
47
+ FACE_MATCH_FAILED: {
48
+ en: 'The face in the video does not match the document photo.',
49
+ es: 'El rostro en el video no coincide con la foto del documento.',
50
+ },
51
+ DOCUMENT_TAMPERED: {
52
+ en: 'Document appears to be tampered or altered.',
53
+ es: 'El documento parece estar adulterado o alterado.',
54
+ },
55
+ };
56
+
57
+ const message = messages[error.code] || {
58
+ en: 'An unexpected error occurred. Please try again.',
59
+ es: 'Ocurrió un error inesperado. Por favor intenta de nuevo.',
60
+ };
61
+
62
+ return language === 'es' ? message.es : message.en;
63
+ };
64
+
65
+ return (
66
+ <View style={styles.container}>
67
+ <View style={styles.content}>
68
+ {/* Error Icon */}
69
+ <View
70
+ style={[
71
+ styles.iconContainer,
72
+ { backgroundColor: theme?.errorColor || '#EF4444' },
73
+ ]}
74
+ >
75
+ <Text style={styles.icon}>✗</Text>
76
+ </View>
77
+
78
+ {/* Error Title */}
79
+ <Text style={[styles.title, { color: theme?.textColor || '#000000' }]}>
80
+ {language === 'es' ? 'Error de Verificación' : 'Verification Error'}
81
+ </Text>
82
+
83
+ {/* Error Message */}
84
+ <Text style={[styles.message, { color: theme?.secondaryTextColor || '#6B7280' }]}>
85
+ {getErrorMessage()}
86
+ </Text>
87
+
88
+ {/* Error Code */}
89
+ <View style={styles.codeContainer}>
90
+ <Text style={styles.codeLabel}>
91
+ {language === 'es' ? 'Código de Error:' : 'Error Code:'}
92
+ </Text>
93
+ <Text style={styles.codeText}>{error.code}</Text>
94
+ </View>
95
+
96
+ {/* Action Buttons */}
97
+ <View style={styles.buttonsContainer}>
98
+ <TouchableOpacity
99
+ style={[
100
+ styles.button,
101
+ styles.retryButton,
102
+ { backgroundColor: theme?.primaryColor || '#6366F1' },
103
+ ]}
104
+ onPress={onRetry}
105
+ >
106
+ <Text style={styles.buttonText}>
107
+ {language === 'es' ? 'Reintentar' : 'Try Again'}
108
+ </Text>
109
+ </TouchableOpacity>
110
+
111
+ <TouchableOpacity
112
+ style={[styles.button, styles.closeButton]}
113
+ onPress={onClose}
114
+ >
115
+ <Text style={[styles.buttonText, { color: theme?.textColor || '#000000' }]}>
116
+ {language === 'es' ? 'Cerrar' : 'Close'}
117
+ </Text>
118
+ </TouchableOpacity>
119
+ </View>
120
+ </View>
121
+ </View>
122
+ );
123
+ };
124
+
125
+ const styles = StyleSheet.create({
126
+ container: {
127
+ flex: 1,
128
+ backgroundColor: '#FFFFFF',
129
+ justifyContent: 'center',
130
+ alignItems: 'center',
131
+ },
132
+ content: {
133
+ width: '85%',
134
+ alignItems: 'center',
135
+ },
136
+ iconContainer: {
137
+ width: 80,
138
+ height: 80,
139
+ borderRadius: 40,
140
+ justifyContent: 'center',
141
+ alignItems: 'center',
142
+ marginBottom: 24,
143
+ },
144
+ icon: {
145
+ fontSize: 40,
146
+ color: '#FFFFFF',
147
+ fontWeight: 'bold',
148
+ },
149
+ title: {
150
+ fontSize: 24,
151
+ fontWeight: 'bold',
152
+ marginBottom: 16,
153
+ textAlign: 'center',
154
+ },
155
+ message: {
156
+ fontSize: 16,
157
+ textAlign: 'center',
158
+ lineHeight: 24,
159
+ marginBottom: 24,
160
+ },
161
+ codeContainer: {
162
+ backgroundColor: '#F3F4F6',
163
+ paddingVertical: 12,
164
+ paddingHorizontal: 16,
165
+ borderRadius: 8,
166
+ marginBottom: 32,
167
+ },
168
+ codeLabel: {
169
+ fontSize: 12,
170
+ color: '#6B7280',
171
+ marginBottom: 4,
172
+ },
173
+ codeText: {
174
+ fontSize: 14,
175
+ fontWeight: '600',
176
+ color: '#111827',
177
+ },
178
+ buttonsContainer: {
179
+ width: '100%',
180
+ },
181
+ button: {
182
+ paddingVertical: 16,
183
+ paddingHorizontal: 32,
184
+ borderRadius: 8,
185
+ alignItems: 'center',
186
+ marginVertical: 8,
187
+ },
188
+ retryButton: {},
189
+ closeButton: {
190
+ backgroundColor: '#F3F4F6',
191
+ },
192
+ buttonText: {
193
+ fontSize: 16,
194
+ fontWeight: '600',
195
+ color: '#FFFFFF',
196
+ },
197
+ });
198
+
199
+ export default ErrorScreen;
200
+
201
+