@hexar/biometric-identity-sdk-react-native 1.0.34 → 1.0.35

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.
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { ThemeConfig, SupportedLanguage, BiometricError } from '@hexar/biometric-identity-sdk-core';
3
+ export interface ProfilePictureValidationResult {
4
+ isValid: boolean;
5
+ profilePicture: string;
6
+ livenessScore: number;
7
+ faceDetected: boolean;
8
+ warnings: string[];
9
+ }
10
+ export interface ProfilePictureCaptureProps {
11
+ onComplete: (result: ProfilePictureValidationResult) => void;
12
+ onError: (error: BiometricError) => void;
13
+ onCancel?: () => void;
14
+ theme?: ThemeConfig;
15
+ language?: SupportedLanguage;
16
+ }
17
+ export declare const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps>;
18
+ export default ProfilePictureCapture;
19
+ //# sourceMappingURL=ProfilePictureCapture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfilePictureCapture.d.ts","sourceRoot":"","sources":["../../src/components/ProfilePictureCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAS7D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAmC,cAAc,EAAsB,MAAM,oCAAoC,CAAC;AAGzJ,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,CAAC,MAAM,EAAE,8BAA8B,KAAK,IAAI,CAAC;IAC7D,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAmKtE,CAAC;AAuCF,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ProfilePictureCapture = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const VideoRecorder_1 = require("./VideoRecorder");
40
+ const biometric_identity_sdk_core_1 = require("@hexar/biometric-identity-sdk-core");
41
+ const biometric_identity_sdk_core_2 = require("@hexar/biometric-identity-sdk-core");
42
+ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language, }) => {
43
+ const [isValidating, setIsValidating] = (0, react_1.useState)(false);
44
+ const [validationError, setValidationError] = (0, react_1.useState)(null);
45
+ if (language) {
46
+ (0, biometric_identity_sdk_core_1.setLanguage)(language);
47
+ }
48
+ const strings = (0, biometric_identity_sdk_core_1.getStrings)();
49
+ const validateWithBackend = (0, react_1.useCallback)(async (videoResult) => {
50
+ try {
51
+ const sdk = biometric_identity_sdk_core_2.BiometricIdentitySDK.getInstance();
52
+ if (!sdk.isUsingBackend()) {
53
+ throw new Error('Backend not available');
54
+ }
55
+ const sessionId = videoResult.sessionId;
56
+ if (!sessionId) {
57
+ throw new Error('Session ID not available');
58
+ }
59
+ const backendClient = sdk.backendClient;
60
+ const apiEndpoint = backendClient.config.apiEndpoint;
61
+ const apiKey = backendClient.config.apiKey;
62
+ const requestBody = {
63
+ session_id: sessionId,
64
+ video_frames: videoResult.frames,
65
+ video_duration_ms: videoResult.duration,
66
+ challenges_completed: videoResult.challengesCompleted || [],
67
+ };
68
+ biometric_identity_sdk_core_1.logger.info('Validating profile picture with backend', {
69
+ sessionId,
70
+ framesCount: videoResult.frames.length,
71
+ duration: videoResult.duration,
72
+ });
73
+ const response = await fetch(`${apiEndpoint}/profile/validate-face`, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ 'X-API-Key': apiKey,
78
+ },
79
+ body: JSON.stringify(requestBody),
80
+ });
81
+ if (!response.ok) {
82
+ const errorData = await response.json().catch(() => ({}));
83
+ throw new Error(errorData.detail || `Backend validation failed: ${response.status}`);
84
+ }
85
+ const data = await response.json();
86
+ biometric_identity_sdk_core_1.logger.info('Profile picture validation result', {
87
+ isValid: data.is_valid,
88
+ livenessScore: data.liveness_score,
89
+ faceDetected: data.face_detected,
90
+ });
91
+ return {
92
+ isValid: data.is_valid,
93
+ profilePicture: data.profile_picture,
94
+ livenessScore: data.liveness_score,
95
+ faceDetected: data.face_detected,
96
+ warnings: data.warnings || [],
97
+ };
98
+ }
99
+ catch (error) {
100
+ biometric_identity_sdk_core_1.logger.error('Profile picture validation error', error);
101
+ throw error;
102
+ }
103
+ }, []);
104
+ const handleVideoComplete = (0, react_1.useCallback)(async (videoResult) => {
105
+ setIsValidating(true);
106
+ setValidationError(null);
107
+ try {
108
+ const result = await validateWithBackend(videoResult);
109
+ if (!result.isValid) {
110
+ setValidationError('La validación de liveness falló. Por favor, intenta nuevamente.');
111
+ setIsValidating(false);
112
+ return;
113
+ }
114
+ setIsValidating(false);
115
+ onComplete(result);
116
+ }
117
+ catch (error) {
118
+ setIsValidating(false);
119
+ let errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.UNKNOWN_ERROR;
120
+ if (error.message && error.message.toLowerCase().includes('network')) {
121
+ errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.NETWORK_ERROR;
122
+ }
123
+ else if (error.message && error.message.toLowerCase().includes('timeout')) {
124
+ errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.VALIDATION_TIMEOUT;
125
+ }
126
+ else if (error.message && error.message.toLowerCase().includes('liveness')) {
127
+ errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.LIVENESS_CHECK_FAILED;
128
+ }
129
+ const biometricError = {
130
+ name: 'BiometricError',
131
+ message: error.message || 'Error al validar la foto de perfil',
132
+ code: errorCode,
133
+ };
134
+ onError(biometricError);
135
+ }
136
+ }, [validateWithBackend, onComplete, onError]);
137
+ const handleVideoCancel = (0, react_1.useCallback)(() => {
138
+ if (onCancel) {
139
+ onCancel();
140
+ }
141
+ }, [onCancel]);
142
+ if (isValidating) {
143
+ return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
144
+ react_1.default.createElement(react_native_1.View, { style: styles.loadingContainer },
145
+ react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: theme?.primaryColor || '#4f46e5' }),
146
+ react_1.default.createElement(react_native_1.Text, { style: [styles.loadingText, { color: theme?.textColor || '#1e1b4b' }] }, strings.liveness.processing || 'Validando foto de perfil...'),
147
+ react_1.default.createElement(react_native_1.Text, { style: [styles.loadingSubtext, { color: theme?.secondaryTextColor || '#64748b' }] }, "Esto puede tardar unos segundos"))));
148
+ }
149
+ if (validationError) {
150
+ return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
151
+ react_1.default.createElement(react_native_1.View, { style: styles.errorContainer },
152
+ react_1.default.createElement(react_native_1.Text, { style: [styles.errorTitle, { color: theme?.errorColor || '#EF4444' }] }, "Error de validaci\u00F3n"),
153
+ react_1.default.createElement(react_native_1.Text, { style: [styles.errorText, { color: theme?.textColor || '#1e1b4b' }] }, validationError))));
154
+ }
155
+ return (react_1.default.createElement(VideoRecorder_1.VideoRecorder, { theme: theme, language: language, duration: 8000, smartMode: true, onComplete: handleVideoComplete, onCancel: handleVideoCancel }));
156
+ };
157
+ exports.ProfilePictureCapture = ProfilePictureCapture;
158
+ const styles = react_native_1.StyleSheet.create({
159
+ container: {
160
+ flex: 1,
161
+ },
162
+ loadingContainer: {
163
+ flex: 1,
164
+ justifyContent: 'center',
165
+ alignItems: 'center',
166
+ padding: 24,
167
+ },
168
+ loadingText: {
169
+ fontSize: 18,
170
+ fontWeight: '600',
171
+ marginTop: 24,
172
+ },
173
+ loadingSubtext: {
174
+ fontSize: 14,
175
+ marginTop: 8,
176
+ },
177
+ errorContainer: {
178
+ flex: 1,
179
+ justifyContent: 'center',
180
+ alignItems: 'center',
181
+ padding: 24,
182
+ },
183
+ errorTitle: {
184
+ fontSize: 20,
185
+ fontWeight: '600',
186
+ marginBottom: 12,
187
+ },
188
+ errorText: {
189
+ fontSize: 16,
190
+ textAlign: 'center',
191
+ lineHeight: 24,
192
+ },
193
+ });
194
+ exports.default = exports.ProfilePictureCapture;
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@ export { BiometricIdentityFlow } from './components/BiometricIdentityFlow';
6
6
  export { default } from './components/BiometricIdentityFlow';
7
7
  export { CameraCapture } from './components/CameraCapture';
8
8
  export { VideoRecorder } from './components/VideoRecorder';
9
+ export { ProfilePictureCapture } from './components/ProfilePictureCapture';
10
+ export type { ProfilePictureValidationResult } from './components/ProfilePictureCapture';
9
11
  export { ValidationProgress } from './components/ValidationProgress';
10
12
  export { ResultScreen } from './components/ResultScreen';
11
13
  export { ErrorScreen } from './components/ErrorScreen';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,cAAc,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,YAAY,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,cAAc,oCAAoC,CAAC"}
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  return (mod && mod.__esModule) ? mod : { "default": mod };
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.useBiometricSDK = exports.InstructionsScreen = exports.ErrorScreen = exports.ResultScreen = exports.ValidationProgress = exports.VideoRecorder = exports.CameraCapture = exports.default = exports.BiometricIdentityFlow = void 0;
24
+ exports.useBiometricSDK = exports.InstructionsScreen = exports.ErrorScreen = exports.ResultScreen = exports.ValidationProgress = exports.ProfilePictureCapture = exports.VideoRecorder = exports.CameraCapture = exports.default = exports.BiometricIdentityFlow = void 0;
25
25
  // Main component
26
26
  var BiometricIdentityFlow_1 = require("./components/BiometricIdentityFlow");
27
27
  Object.defineProperty(exports, "BiometricIdentityFlow", { enumerable: true, get: function () { return BiometricIdentityFlow_1.BiometricIdentityFlow; } });
@@ -32,6 +32,8 @@ var CameraCapture_1 = require("./components/CameraCapture");
32
32
  Object.defineProperty(exports, "CameraCapture", { enumerable: true, get: function () { return CameraCapture_1.CameraCapture; } });
33
33
  var VideoRecorder_1 = require("./components/VideoRecorder");
34
34
  Object.defineProperty(exports, "VideoRecorder", { enumerable: true, get: function () { return VideoRecorder_1.VideoRecorder; } });
35
+ var ProfilePictureCapture_1 = require("./components/ProfilePictureCapture");
36
+ Object.defineProperty(exports, "ProfilePictureCapture", { enumerable: true, get: function () { return ProfilePictureCapture_1.ProfilePictureCapture; } });
35
37
  var ValidationProgress_1 = require("./components/ValidationProgress");
36
38
  Object.defineProperty(exports, "ValidationProgress", { enumerable: true, get: function () { return ValidationProgress_1.ValidationProgress; } });
37
39
  var ResultScreen_1 = require("./components/ResultScreen");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "React Native wrapper for Biometric Identity SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,231 @@
1
+ import React, { useState, useCallback, useRef } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ ActivityIndicator,
7
+ SafeAreaView,
8
+ } from 'react-native';
9
+ import { VideoRecorder, VideoRecordingResult } from './VideoRecorder';
10
+ import { ThemeConfig, SupportedLanguage, getStrings, setLanguage, logger, BiometricError, BiometricErrorCode } from '@hexar/biometric-identity-sdk-core';
11
+ import { BiometricIdentitySDK } from '@hexar/biometric-identity-sdk-core';
12
+
13
+ export interface ProfilePictureValidationResult {
14
+ isValid: boolean;
15
+ profilePicture: string;
16
+ livenessScore: number;
17
+ faceDetected: boolean;
18
+ warnings: string[];
19
+ }
20
+
21
+ export interface ProfilePictureCaptureProps {
22
+ onComplete: (result: ProfilePictureValidationResult) => void;
23
+ onError: (error: BiometricError) => void;
24
+ onCancel?: () => void;
25
+ theme?: ThemeConfig;
26
+ language?: SupportedLanguage;
27
+ }
28
+
29
+ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
30
+ onComplete,
31
+ onError,
32
+ onCancel,
33
+ theme,
34
+ language,
35
+ }) => {
36
+ const [isValidating, setIsValidating] = useState(false);
37
+ const [validationError, setValidationError] = useState<string | null>(null);
38
+
39
+ if (language) {
40
+ setLanguage(language);
41
+ }
42
+ const strings = getStrings();
43
+
44
+ const validateWithBackend = useCallback(async (videoResult: VideoRecordingResult): Promise<ProfilePictureValidationResult> => {
45
+ try {
46
+ const sdk = BiometricIdentitySDK.getInstance();
47
+
48
+ if (!sdk.isUsingBackend()) {
49
+ throw new Error('Backend not available');
50
+ }
51
+
52
+ const sessionId = videoResult.sessionId;
53
+ if (!sessionId) {
54
+ throw new Error('Session ID not available');
55
+ }
56
+
57
+ const backendClient = (sdk as any).backendClient;
58
+ const apiEndpoint = backendClient.config.apiEndpoint;
59
+ const apiKey = backendClient.config.apiKey;
60
+
61
+ const requestBody = {
62
+ session_id: sessionId,
63
+ video_frames: videoResult.frames,
64
+ video_duration_ms: videoResult.duration,
65
+ challenges_completed: videoResult.challengesCompleted || [],
66
+ };
67
+
68
+ logger.info('Validating profile picture with backend', {
69
+ sessionId,
70
+ framesCount: videoResult.frames.length,
71
+ duration: videoResult.duration,
72
+ });
73
+
74
+ const response = await fetch(`${apiEndpoint}/profile/validate-face`, {
75
+ method: 'POST',
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ 'X-API-Key': apiKey,
79
+ },
80
+ body: JSON.stringify(requestBody),
81
+ });
82
+
83
+ if (!response.ok) {
84
+ const errorData = await response.json().catch(() => ({}));
85
+ throw new Error(errorData.detail || `Backend validation failed: ${response.status}`);
86
+ }
87
+
88
+ const data = await response.json();
89
+
90
+ logger.info('Profile picture validation result', {
91
+ isValid: data.is_valid,
92
+ livenessScore: data.liveness_score,
93
+ faceDetected: data.face_detected,
94
+ });
95
+
96
+ return {
97
+ isValid: data.is_valid,
98
+ profilePicture: data.profile_picture,
99
+ livenessScore: data.liveness_score,
100
+ faceDetected: data.face_detected,
101
+ warnings: data.warnings || [],
102
+ };
103
+ } catch (error: any) {
104
+ logger.error('Profile picture validation error', error);
105
+ throw error;
106
+ }
107
+ }, []);
108
+
109
+ const handleVideoComplete = useCallback(async (videoResult: VideoRecordingResult) => {
110
+ setIsValidating(true);
111
+ setValidationError(null);
112
+
113
+ try {
114
+ const result = await validateWithBackend(videoResult);
115
+
116
+ if (!result.isValid) {
117
+ setValidationError('La validación de liveness falló. Por favor, intenta nuevamente.');
118
+ setIsValidating(false);
119
+ return;
120
+ }
121
+
122
+ setIsValidating(false);
123
+ onComplete(result);
124
+ } catch (error: any) {
125
+ setIsValidating(false);
126
+ let errorCode = BiometricErrorCode.UNKNOWN_ERROR;
127
+
128
+ if (error.message && error.message.toLowerCase().includes('network')) {
129
+ errorCode = BiometricErrorCode.NETWORK_ERROR;
130
+ } else if (error.message && error.message.toLowerCase().includes('timeout')) {
131
+ errorCode = BiometricErrorCode.VALIDATION_TIMEOUT;
132
+ } else if (error.message && error.message.toLowerCase().includes('liveness')) {
133
+ errorCode = BiometricErrorCode.LIVENESS_CHECK_FAILED;
134
+ }
135
+
136
+ const biometricError: BiometricError = {
137
+ name: 'BiometricError',
138
+ message: error.message || 'Error al validar la foto de perfil',
139
+ code: errorCode,
140
+ } as BiometricError;
141
+ onError(biometricError);
142
+ }
143
+ }, [validateWithBackend, onComplete, onError]);
144
+
145
+ const handleVideoCancel = useCallback(() => {
146
+ if (onCancel) {
147
+ onCancel();
148
+ }
149
+ }, [onCancel]);
150
+
151
+ if (isValidating) {
152
+ return (
153
+ <SafeAreaView style={[styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }]}>
154
+ <View style={styles.loadingContainer}>
155
+ <ActivityIndicator size="large" color={theme?.primaryColor || '#4f46e5'} />
156
+ <Text style={[styles.loadingText, { color: theme?.textColor || '#1e1b4b' }]}>
157
+ {strings.liveness.processing || 'Validando foto de perfil...'}
158
+ </Text>
159
+ <Text style={[styles.loadingSubtext, { color: theme?.secondaryTextColor || '#64748b' }]}>
160
+ Esto puede tardar unos segundos
161
+ </Text>
162
+ </View>
163
+ </SafeAreaView>
164
+ );
165
+ }
166
+
167
+ if (validationError) {
168
+ return (
169
+ <SafeAreaView style={[styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }]}>
170
+ <View style={styles.errorContainer}>
171
+ <Text style={[styles.errorTitle, { color: theme?.errorColor || '#EF4444' }]}>
172
+ Error de validación
173
+ </Text>
174
+ <Text style={[styles.errorText, { color: theme?.textColor || '#1e1b4b' }]}>
175
+ {validationError}
176
+ </Text>
177
+ </View>
178
+ </SafeAreaView>
179
+ );
180
+ }
181
+
182
+ return (
183
+ <VideoRecorder
184
+ theme={theme}
185
+ language={language}
186
+ duration={8000}
187
+ smartMode={true}
188
+ onComplete={handleVideoComplete}
189
+ onCancel={handleVideoCancel}
190
+ />
191
+ );
192
+ };
193
+
194
+ const styles = StyleSheet.create({
195
+ container: {
196
+ flex: 1,
197
+ },
198
+ loadingContainer: {
199
+ flex: 1,
200
+ justifyContent: 'center',
201
+ alignItems: 'center',
202
+ padding: 24,
203
+ },
204
+ loadingText: {
205
+ fontSize: 18,
206
+ fontWeight: '600',
207
+ marginTop: 24,
208
+ },
209
+ loadingSubtext: {
210
+ fontSize: 14,
211
+ marginTop: 8,
212
+ },
213
+ errorContainer: {
214
+ flex: 1,
215
+ justifyContent: 'center',
216
+ alignItems: 'center',
217
+ padding: 24,
218
+ },
219
+ errorTitle: {
220
+ fontSize: 20,
221
+ fontWeight: '600',
222
+ marginBottom: 12,
223
+ },
224
+ errorText: {
225
+ fontSize: 16,
226
+ textAlign: 'center',
227
+ lineHeight: 24,
228
+ },
229
+ });
230
+
231
+ export default ProfilePictureCapture;
package/src/index.ts CHANGED
@@ -10,6 +10,8 @@ export { default } from './components/BiometricIdentityFlow';
10
10
  // Individual components (for custom implementations)
11
11
  export { CameraCapture } from './components/CameraCapture';
12
12
  export { VideoRecorder } from './components/VideoRecorder';
13
+ export { ProfilePictureCapture } from './components/ProfilePictureCapture';
14
+ export type { ProfilePictureValidationResult } from './components/ProfilePictureCapture';
13
15
  export { ValidationProgress } from './components/ValidationProgress';
14
16
  export { ResultScreen } from './components/ResultScreen';
15
17
  export { ErrorScreen } from './components/ErrorScreen';