@hexar/biometric-identity-sdk-react-native 1.1.1 → 1.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorScreen.d.ts","sourceRoot":"","sources":["../../src/components/ErrorScreen.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEpG,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAqGlD,CAAC;AA4EF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ErrorScreen.d.ts","sourceRoot":"","sources":["../../src/components/ErrorScreen.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAoB,MAAM,OAAO,CAAC;AAOzC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAA2B,MAAM,oCAAoC,CAAC;AAE7H,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAoGlD,CAAC;AA4EF,eAAe,WAAW,CAAC"}
@@ -2,46 +2,73 @@
2
2
  /**
3
3
  * Error Screen Component
4
4
  */
5
- var __importDefault = (this && this.__importDefault) || function (mod) {
6
- return (mod && mod.__esModule) ? mod : { "default": mod };
7
- };
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
8
38
  Object.defineProperty(exports, "__esModule", { value: true });
9
39
  exports.ErrorScreen = void 0;
10
- const react_1 = __importDefault(require("react"));
40
+ const react_1 = __importStar(require("react"));
11
41
  const react_native_1 = require("react-native");
12
- const ErrorScreen = ({ error, theme, language = 'en', onRetry, onClose, }) => {
42
+ const biometric_identity_sdk_core_1 = require("@hexar/biometric-identity-sdk-core");
43
+ const ErrorScreen = ({ error, theme, language, onRetry, onClose, }) => {
44
+ (0, react_1.useEffect)(() => {
45
+ if (language) {
46
+ (0, biometric_identity_sdk_core_1.setLanguage)(language);
47
+ }
48
+ }, [language]);
49
+ const strings = (0, biometric_identity_sdk_core_1.getStrings)();
13
50
  const getErrorMessage = () => {
14
- const messages = {
15
- CAMERA_PERMISSION_DENIED: {
16
- en: 'Camera permission is required to capture your ID and face video.',
17
- es: 'Se requiere permiso de cámara para capturar tu documento y video facial.',
18
- },
19
- FACE_NOT_DETECTED: {
20
- en: 'No face detected in the document. Please ensure the photo is clear.',
21
- es: 'No se detectó rostro en el documento. Asegúrate de que la foto sea clara.',
22
- },
23
- POOR_IMAGE_QUALITY: {
24
- en: 'Image quality is insufficient. Please avoid glare, blur, and ensure good lighting.',
25
- es: 'La calidad de la imagen es insuficiente. Evita brillos, desenfoque y asegura buena iluminación.',
26
- },
27
- LIVENESS_CHECK_FAILED: {
28
- en: 'Liveness verification failed. Please record a new video following the instructions.',
29
- es: 'La verificación de presencia vital falló. Por favor graba un nuevo video siguiendo las instrucciones.',
30
- },
31
- FACE_MATCH_FAILED: {
32
- en: 'The face in the video does not match the document photo.',
33
- es: 'El rostro en el video no coincide con la foto del documento.',
34
- },
35
- DOCUMENT_TAMPERED: {
36
- en: 'Document appears to be tampered or altered.',
37
- es: 'El documento parece estar adulterado o alterado.',
38
- },
51
+ if (error.message) {
52
+ return error.message;
53
+ }
54
+ const getCameraPermissionMessage = () => {
55
+ const permissionError = strings.errors.cameraPermissionDenied;
56
+ if (typeof permissionError === 'object' && permissionError?.message) {
57
+ return permissionError.message;
58
+ }
59
+ return typeof permissionError === 'string' ? permissionError : 'Camera permission is required.';
39
60
  };
40
- const message = messages[error.code] || {
41
- en: 'An unexpected error occurred. Please try again.',
42
- es: 'Ocurrió un error inesperado. Por favor intenta de nuevo.',
61
+ const errorMessageMap = {
62
+ CAMERA_PERMISSION_DENIED: getCameraPermissionMessage(),
63
+ FACE_NOT_DETECTED: strings.errors.faceNotDetected || 'No face detected.',
64
+ POOR_IMAGE_QUALITY: strings.errors.poorImageQuality || 'Image quality is insufficient.',
65
+ LIVENESS_CHECK_FAILED: strings.errors.livenessCheckFailed || 'Liveness verification failed.',
66
+ FACE_MATCH_FAILED: strings.errors.faceMatchFailed || 'Face match failed.',
67
+ DOCUMENT_TAMPERED: strings.errors.documentTampered || 'Document appears to be tampered.',
68
+ NETWORK_ERROR: strings.errors.networkError || 'Network error.',
69
+ VALIDATION_TIMEOUT: strings.errors.timeout || 'Validation timeout.',
43
70
  };
44
- return language === 'es' ? message.es : message.en;
71
+ return errorMessageMap[error.code] || strings.errors.unknownError || 'An unexpected error occurred.';
45
72
  };
46
73
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
47
74
  react_1.default.createElement(react_native_1.View, { style: styles.content },
@@ -50,10 +77,10 @@ const ErrorScreen = ({ error, theme, language = 'en', onRetry, onClose, }) => {
50
77
  { backgroundColor: theme?.errorColor || '#EF4444' },
51
78
  ] },
52
79
  react_1.default.createElement(react_native_1.Text, { style: styles.icon }, "\u2717")),
53
- react_1.default.createElement(react_native_1.Text, { style: [styles.title, { color: theme?.textColor || '#000000' }] }, language === 'es' ? 'Error de Verificación' : 'Verification Error'),
80
+ react_1.default.createElement(react_native_1.Text, { style: [styles.title, { color: theme?.textColor || '#000000' }] }, strings.result.failure.title || strings.errors.unknownError || 'Verification Error'),
54
81
  react_1.default.createElement(react_native_1.Text, { style: [styles.message, { color: theme?.secondaryTextColor || '#6B7280' }] }, getErrorMessage()),
55
82
  react_1.default.createElement(react_native_1.View, { style: styles.codeContainer },
56
- react_1.default.createElement(react_native_1.Text, { style: styles.codeLabel }, language === 'es' ? 'Código de Error:' : 'Error Code:'),
83
+ react_1.default.createElement(react_native_1.Text, { style: styles.codeLabel }, strings.common.error || 'Error Code:'),
57
84
  react_1.default.createElement(react_native_1.Text, { style: styles.codeText }, error.code)),
58
85
  react_1.default.createElement(react_native_1.View, { style: styles.buttonsContainer },
59
86
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [
@@ -61,9 +88,9 @@ const ErrorScreen = ({ error, theme, language = 'en', onRetry, onClose, }) => {
61
88
  styles.retryButton,
62
89
  { backgroundColor: theme?.primaryColor || '#6366F1' },
63
90
  ], onPress: onRetry },
64
- react_1.default.createElement(react_native_1.Text, { style: styles.buttonText }, language === 'es' ? 'Reintentar' : 'Try Again')),
91
+ react_1.default.createElement(react_native_1.Text, { style: styles.buttonText }, strings.common.retry || 'Try Again')),
65
92
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.button, styles.closeButton], onPress: onClose },
66
- react_1.default.createElement(react_native_1.Text, { style: [styles.buttonText, { color: theme?.textColor || '#000000' }] }, language === 'es' ? 'Cerrar' : 'Close'))))));
93
+ react_1.default.createElement(react_native_1.Text, { style: [styles.buttonText, { color: theme?.textColor || '#000000' }] }, strings.common.close || 'Close'))))));
67
94
  };
68
95
  exports.ErrorScreen = ErrorScreen;
69
96
  const styles = react_native_1.StyleSheet.create({
@@ -1 +1 @@
1
- {"version":3,"file":"ProfilePictureCapture.d.ts","sourceRoot":"","sources":["../../src/components/ProfilePictureCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AASxE,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,CA4KtE,CAAC;AAuCF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"ProfilePictureCapture.d.ts","sourceRoot":"","sources":["../../src/components/ProfilePictureCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AASxE,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,CAwKtE,CAAC;AAuCF,eAAe,qBAAqB,CAAC"}
@@ -38,8 +38,9 @@ const react_1 = __importStar(require("react"));
38
38
  const react_native_1 = require("react-native");
39
39
  const VideoRecorder_1 = require("./VideoRecorder");
40
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");
41
+ const useBiometricSDK_1 = require("../hooks/useBiometricSDK");
42
42
  const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language, }) => {
43
+ const { sdk, isInitialized, isUsingBackend, } = (0, useBiometricSDK_1.useBiometricSDK)();
43
44
  const [isValidating, setIsValidating] = (0, react_1.useState)(false);
44
45
  const [validationError, setValidationError] = (0, react_1.useState)(null);
45
46
  (0, react_1.useEffect)(() => {
@@ -50,8 +51,11 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
50
51
  const strings = (0, biometric_identity_sdk_core_1.getStrings)();
51
52
  const validateWithBackend = (0, react_1.useCallback)(async (videoResult) => {
52
53
  try {
53
- const sdk = biometric_identity_sdk_core_2.BiometricIdentitySDK.getInstance();
54
- if (!sdk.isUsingBackend()) {
54
+ if (!isInitialized) {
55
+ biometric_identity_sdk_core_1.logger.error('SDK not initialized');
56
+ throw new Error('NETWORK_ERROR');
57
+ }
58
+ if (!isUsingBackend) {
55
59
  biometric_identity_sdk_core_1.logger.error('Backend not available - SDK not configured with backend');
56
60
  throw new Error('NETWORK_ERROR');
57
61
  }
@@ -64,6 +68,7 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
64
68
  sessionId,
65
69
  framesCount: videoResult.frames.length,
66
70
  duration: videoResult.duration,
71
+ isUsingBackend,
67
72
  });
68
73
  const result = await sdk.validateProfilePicture({
69
74
  sessionId,
@@ -88,14 +93,14 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
88
93
  biometric_identity_sdk_core_1.logger.error('Profile picture validation error', error);
89
94
  throw error;
90
95
  }
91
- }, []);
96
+ }, [isInitialized, isUsingBackend, sdk]);
92
97
  const handleVideoComplete = (0, react_1.useCallback)(async (videoResult) => {
93
98
  setIsValidating(true);
94
99
  setValidationError(null);
95
100
  try {
96
101
  const result = await validateWithBackend(videoResult);
97
102
  if (!result.isValid) {
98
- setValidationError('La validación de liveness falló. Por favor, intenta nuevamente.');
103
+ setValidationError(strings.errors.livenessCheckFailed || 'Liveness check failed');
99
104
  setIsValidating(false);
100
105
  return;
101
106
  }
@@ -104,46 +109,32 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
104
109
  }
105
110
  catch (error) {
106
111
  setIsValidating(false);
112
+ if (error && typeof error === 'object' && 'code' in error) {
113
+ onError(error);
114
+ return;
115
+ }
107
116
  let errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.UNKNOWN_ERROR;
108
- const getErrorMessage = (code) => {
109
- const messages = {
110
- NETWORK_ERROR: {
111
- en: 'Connection error. Please check your internet connection.',
112
- es: 'Error de conexión. Verifica tu conexión a internet.',
113
- },
114
- VALIDATION_TIMEOUT: {
115
- en: 'Validation took too long. Please try again.',
116
- es: 'La validación tardó demasiado. Intenta nuevamente.',
117
- },
118
- LIVENESS_CHECK_FAILED: {
119
- en: 'Liveness verification failed. Please try again.',
120
- es: 'La verificación de presencia vital falló. Por favor intenta de nuevo.',
121
- },
122
- UNKNOWN_ERROR: {
123
- en: 'An unexpected error occurred. Please try again.',
124
- es: 'Ocurrió un error inesperado. Por favor intenta de nuevo.',
125
- },
126
- };
127
- const message = messages[code] || messages.UNKNOWN_ERROR;
128
- return language === 'es' || language === 'es-AR' ? message.es : message.en;
129
- };
130
- if (error.message === 'NETWORK_ERROR' || (error.message && error.message.toLowerCase().includes('network'))) {
117
+ let errorMessage = strings.errors.unknownError || 'An unexpected error occurred.';
118
+ if (error?.message === 'NETWORK_ERROR' || (error?.message && error.message.toLowerCase().includes('network'))) {
131
119
  errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.NETWORK_ERROR;
120
+ errorMessage = strings.errors.networkError || 'Network error. Please check your connection.';
132
121
  }
133
- else if (error.message && error.message.toLowerCase().includes('timeout')) {
122
+ else if (error?.message && error.message.toLowerCase().includes('timeout')) {
134
123
  errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.VALIDATION_TIMEOUT;
124
+ errorMessage = strings.errors.timeout || 'Timeout. Please try again.';
135
125
  }
136
- else if (error.message && error.message.toLowerCase().includes('liveness')) {
126
+ else if (error?.message && error.message.toLowerCase().includes('liveness')) {
137
127
  errorCode = biometric_identity_sdk_core_1.BiometricErrorCode.LIVENESS_CHECK_FAILED;
128
+ errorMessage = strings.errors.livenessCheckFailed || 'Liveness check failed. Please try again.';
138
129
  }
139
130
  const biometricError = {
140
131
  name: 'BiometricError',
141
- message: getErrorMessage(errorCode),
132
+ message: errorMessage,
142
133
  code: errorCode,
143
134
  };
144
135
  onError(biometricError);
145
136
  }
146
- }, [validateWithBackend, onComplete, onError]);
137
+ }, [validateWithBackend, onComplete, onError, strings, language]);
147
138
  const handleVideoCancel = (0, react_1.useCallback)(() => {
148
139
  if (onCancel) {
149
140
  onCancel();
@@ -153,13 +144,13 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
153
144
  return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
154
145
  react_1.default.createElement(react_native_1.View, { style: styles.loadingContainer },
155
146
  react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: theme?.primaryColor || '#4f46e5' }),
156
- react_1.default.createElement(react_native_1.Text, { style: [styles.loadingText, { color: theme?.textColor || '#1e1b4b' }] }, strings.liveness.processing || 'Validando foto de perfil...'),
157
- react_1.default.createElement(react_native_1.Text, { style: [styles.loadingSubtext, { color: theme?.secondaryTextColor || '#64748b' }] }, "Esto puede tardar unos segundos"))));
147
+ react_1.default.createElement(react_native_1.Text, { style: [styles.loadingText, { color: theme?.textColor || '#1e1b4b' }] }, strings.liveness.processing || strings.validation.checkingLiveness || 'Processing...'),
148
+ react_1.default.createElement(react_native_1.Text, { style: [styles.loadingSubtext, { color: theme?.secondaryTextColor || '#64748b' }] }, strings.validation.almostDone || strings.common.loading || 'This may take a few seconds'))));
158
149
  }
159
150
  if (validationError) {
160
151
  return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
161
152
  react_1.default.createElement(react_native_1.View, { style: styles.errorContainer },
162
- 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.errorTitle, { color: theme?.errorColor || '#EF4444' }] }, strings.errors.unknownError || strings.validation.title || 'Error'),
163
154
  react_1.default.createElement(react_native_1.Text, { style: [styles.errorText, { color: theme?.textColor || '#1e1b4b' }] }, validationError))));
164
155
  }
165
156
  return (react_1.default.createElement(VideoRecorder_1.VideoRecorder, { theme: theme, language: language, duration: 8000, smartMode: true, onComplete: handleVideoComplete, onCancel: handleVideoCancel }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "React Native wrapper for Biometric Identity SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,14 +2,14 @@
2
2
  * Error Screen Component
3
3
  */
4
4
 
5
- import React from 'react';
5
+ import React, { useEffect } from 'react';
6
6
  import {
7
7
  View,
8
8
  Text,
9
9
  StyleSheet,
10
10
  TouchableOpacity,
11
11
  } from 'react-native';
12
- import { BiometricError, ThemeConfig, SupportedLanguage } from '@hexar/biometric-identity-sdk-core';
12
+ import { BiometricError, ThemeConfig, SupportedLanguage, getStrings, setLanguage } from '@hexar/biometric-identity-sdk-core';
13
13
 
14
14
  export interface ErrorScreenProps {
15
15
  error: BiometricError;
@@ -22,44 +22,43 @@ export interface ErrorScreenProps {
22
22
  export const ErrorScreen: React.FC<ErrorScreenProps> = ({
23
23
  error,
24
24
  theme,
25
- language = 'en',
25
+ language,
26
26
  onRetry,
27
27
  onClose,
28
28
  }) => {
29
+ useEffect(() => {
30
+ if (language) {
31
+ setLanguage(language);
32
+ }
33
+ }, [language]);
34
+
35
+ const strings = getStrings();
36
+
29
37
  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
- },
38
+ if (error.message) {
39
+ return error.message;
40
+ }
41
+
42
+ const getCameraPermissionMessage = () => {
43
+ const permissionError = strings.errors.cameraPermissionDenied;
44
+ if (typeof permissionError === 'object' && permissionError?.message) {
45
+ return permissionError.message;
46
+ }
47
+ return typeof permissionError === 'string' ? permissionError : 'Camera permission is required.';
55
48
  };
56
49
 
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.',
50
+ const errorMessageMap: Record<string, string> = {
51
+ CAMERA_PERMISSION_DENIED: getCameraPermissionMessage(),
52
+ FACE_NOT_DETECTED: strings.errors.faceNotDetected || 'No face detected.',
53
+ POOR_IMAGE_QUALITY: strings.errors.poorImageQuality || 'Image quality is insufficient.',
54
+ LIVENESS_CHECK_FAILED: strings.errors.livenessCheckFailed || 'Liveness verification failed.',
55
+ FACE_MATCH_FAILED: strings.errors.faceMatchFailed || 'Face match failed.',
56
+ DOCUMENT_TAMPERED: strings.errors.documentTampered || 'Document appears to be tampered.',
57
+ NETWORK_ERROR: strings.errors.networkError || 'Network error.',
58
+ VALIDATION_TIMEOUT: strings.errors.timeout || 'Validation timeout.',
60
59
  };
61
60
 
62
- return language === 'es' ? message.es : message.en;
61
+ return errorMessageMap[error.code] || strings.errors.unknownError || 'An unexpected error occurred.';
63
62
  };
64
63
 
65
64
  return (
@@ -77,7 +76,7 @@ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
77
76
 
78
77
  {/* Error Title */}
79
78
  <Text style={[styles.title, { color: theme?.textColor || '#000000' }]}>
80
- {language === 'es' ? 'Error de Verificación' : 'Verification Error'}
79
+ {strings.result.failure.title || strings.errors.unknownError || 'Verification Error'}
81
80
  </Text>
82
81
 
83
82
  {/* Error Message */}
@@ -88,7 +87,7 @@ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
88
87
  {/* Error Code */}
89
88
  <View style={styles.codeContainer}>
90
89
  <Text style={styles.codeLabel}>
91
- {language === 'es' ? 'Código de Error:' : 'Error Code:'}
90
+ {strings.common.error || 'Error Code:'}
92
91
  </Text>
93
92
  <Text style={styles.codeText}>{error.code}</Text>
94
93
  </View>
@@ -104,7 +103,7 @@ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
104
103
  onPress={onRetry}
105
104
  >
106
105
  <Text style={styles.buttonText}>
107
- {language === 'es' ? 'Reintentar' : 'Try Again'}
106
+ {strings.common.retry || 'Try Again'}
108
107
  </Text>
109
108
  </TouchableOpacity>
110
109
 
@@ -113,7 +112,7 @@ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
113
112
  onPress={onClose}
114
113
  >
115
114
  <Text style={[styles.buttonText, { color: theme?.textColor || '#000000' }]}>
116
- {language === 'es' ? 'Cerrar' : 'Close'}
115
+ {strings.common.close || 'Close'}
117
116
  </Text>
118
117
  </TouchableOpacity>
119
118
  </View>
@@ -8,7 +8,7 @@ import {
8
8
  } from 'react-native';
9
9
  import { VideoRecorder, VideoRecordingResult } from './VideoRecorder';
10
10
  import { ThemeConfig, SupportedLanguage, getStrings, setLanguage, logger, BiometricError, BiometricErrorCode } from '@hexar/biometric-identity-sdk-core';
11
- import { BiometricIdentitySDK } from '@hexar/biometric-identity-sdk-core';
11
+ import { useBiometricSDK } from '../hooks/useBiometricSDK';
12
12
 
13
13
  export interface ProfilePictureValidationResult {
14
14
  isValid: boolean;
@@ -33,6 +33,12 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
33
33
  theme,
34
34
  language,
35
35
  }) => {
36
+ const {
37
+ sdk,
38
+ isInitialized,
39
+ isUsingBackend,
40
+ } = useBiometricSDK();
41
+
36
42
  const [isValidating, setIsValidating] = useState(false);
37
43
  const [validationError, setValidationError] = useState<string | null>(null);
38
44
 
@@ -46,9 +52,12 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
46
52
 
47
53
  const validateWithBackend = useCallback(async (videoResult: VideoRecordingResult): Promise<ProfilePictureValidationResult> => {
48
54
  try {
49
- const sdk = BiometricIdentitySDK.getInstance();
50
-
51
- if (!sdk.isUsingBackend()) {
55
+ if (!isInitialized) {
56
+ logger.error('SDK not initialized');
57
+ throw new Error('NETWORK_ERROR');
58
+ }
59
+
60
+ if (!isUsingBackend) {
52
61
  logger.error('Backend not available - SDK not configured with backend');
53
62
  throw new Error('NETWORK_ERROR');
54
63
  }
@@ -63,6 +72,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
63
72
  sessionId,
64
73
  framesCount: videoResult.frames.length,
65
74
  duration: videoResult.duration,
75
+ isUsingBackend,
66
76
  });
67
77
 
68
78
  const result = await (sdk as any).validateProfilePicture({
@@ -89,7 +99,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
89
99
  logger.error('Profile picture validation error', error);
90
100
  throw error;
91
101
  }
92
- }, []);
102
+ }, [isInitialized, isUsingBackend, sdk]);
93
103
 
94
104
  const handleVideoComplete = useCallback(async (videoResult: VideoRecordingResult) => {
95
105
  setIsValidating(true);
@@ -99,7 +109,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
99
109
  const result = await validateWithBackend(videoResult);
100
110
 
101
111
  if (!result.isValid) {
102
- setValidationError('La validación de liveness falló. Por favor, intenta nuevamente.');
112
+ setValidationError(strings.errors.livenessCheckFailed || 'Liveness check failed');
103
113
  setIsValidating(false);
104
114
  return;
105
115
  }
@@ -108,48 +118,34 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
108
118
  onComplete(result);
109
119
  } catch (error: any) {
110
120
  setIsValidating(false);
111
- let errorCode = BiometricErrorCode.UNKNOWN_ERROR;
112
121
 
113
- const getErrorMessage = (code: BiometricErrorCode): string => {
114
- const messages: Record<string, { en: string; es: string }> = {
115
- NETWORK_ERROR: {
116
- en: 'Connection error. Please check your internet connection.',
117
- es: 'Error de conexión. Verifica tu conexión a internet.',
118
- },
119
- VALIDATION_TIMEOUT: {
120
- en: 'Validation took too long. Please try again.',
121
- es: 'La validación tardó demasiado. Intenta nuevamente.',
122
- },
123
- LIVENESS_CHECK_FAILED: {
124
- en: 'Liveness verification failed. Please try again.',
125
- es: 'La verificación de presencia vital falló. Por favor intenta de nuevo.',
126
- },
127
- UNKNOWN_ERROR: {
128
- en: 'An unexpected error occurred. Please try again.',
129
- es: 'Ocurrió un error inesperado. Por favor intenta de nuevo.',
130
- },
131
- };
132
-
133
- const message = messages[code] || messages.UNKNOWN_ERROR;
134
- return language === 'es' || language === 'es-AR' ? message.es : message.en;
135
- };
136
-
137
- if (error.message === 'NETWORK_ERROR' || (error.message && error.message.toLowerCase().includes('network'))) {
122
+ if (error && typeof error === 'object' && 'code' in error) {
123
+ onError(error as BiometricError);
124
+ return;
125
+ }
126
+
127
+ let errorCode = BiometricErrorCode.UNKNOWN_ERROR;
128
+ let errorMessage = strings.errors.unknownError || 'An unexpected error occurred.';
129
+
130
+ if (error?.message === 'NETWORK_ERROR' || (error?.message && error.message.toLowerCase().includes('network'))) {
138
131
  errorCode = BiometricErrorCode.NETWORK_ERROR;
139
- } else if (error.message && error.message.toLowerCase().includes('timeout')) {
132
+ errorMessage = strings.errors.networkError || 'Network error. Please check your connection.';
133
+ } else if (error?.message && error.message.toLowerCase().includes('timeout')) {
140
134
  errorCode = BiometricErrorCode.VALIDATION_TIMEOUT;
141
- } else if (error.message && error.message.toLowerCase().includes('liveness')) {
135
+ errorMessage = strings.errors.timeout || 'Timeout. Please try again.';
136
+ } else if (error?.message && error.message.toLowerCase().includes('liveness')) {
142
137
  errorCode = BiometricErrorCode.LIVENESS_CHECK_FAILED;
138
+ errorMessage = strings.errors.livenessCheckFailed || 'Liveness check failed. Please try again.';
143
139
  }
144
140
 
145
141
  const biometricError: BiometricError = {
146
142
  name: 'BiometricError',
147
- message: getErrorMessage(errorCode),
143
+ message: errorMessage,
148
144
  code: errorCode,
149
145
  } as BiometricError;
150
146
  onError(biometricError);
151
147
  }
152
- }, [validateWithBackend, onComplete, onError]);
148
+ }, [validateWithBackend, onComplete, onError, strings, language]);
153
149
 
154
150
  const handleVideoCancel = useCallback(() => {
155
151
  if (onCancel) {
@@ -163,10 +159,10 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
163
159
  <View style={styles.loadingContainer}>
164
160
  <ActivityIndicator size="large" color={theme?.primaryColor || '#4f46e5'} />
165
161
  <Text style={[styles.loadingText, { color: theme?.textColor || '#1e1b4b' }]}>
166
- {strings.liveness.processing || 'Validando foto de perfil...'}
162
+ {strings.liveness.processing || strings.validation.checkingLiveness || 'Processing...'}
167
163
  </Text>
168
164
  <Text style={[styles.loadingSubtext, { color: theme?.secondaryTextColor || '#64748b' }]}>
169
- Esto puede tardar unos segundos
165
+ {strings.validation.almostDone || strings.common.loading || 'This may take a few seconds'}
170
166
  </Text>
171
167
  </View>
172
168
  </SafeAreaView>
@@ -178,7 +174,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
178
174
  <SafeAreaView style={[styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }]}>
179
175
  <View style={styles.errorContainer}>
180
176
  <Text style={[styles.errorTitle, { color: theme?.errorColor || '#EF4444' }]}>
181
- Error de validación
177
+ {strings.errors.unknownError || strings.validation.title || 'Error'}
182
178
  </Text>
183
179
  <Text style={[styles.errorText, { color: theme?.textColor || '#1e1b4b' }]}>
184
180
  {validationError}