@hexar/biometric-identity-sdk-react-native 1.0.26 → 1.0.28
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.
- package/dist/components/BiometricIdentityFlow.d.ts.map +1 -1
- package/dist/components/BiometricIdentityFlow.js +77 -24
- package/dist/components/ResultScreen.d.ts.map +1 -1
- package/dist/components/ResultScreen.js +54 -25
- package/dist/components/VideoRecorder.d.ts.map +1 -1
- package/dist/components/VideoRecorder.js +30 -12
- package/package.json +2 -2
- package/src/components/BiometricIdentityFlow.tsx +77 -24
- package/src/components/ResultScreen.tsx +27 -23
- package/src/components/VideoRecorder.tsx +30 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BiometricIdentityFlow.d.ts","sourceRoot":"","sources":["../../src/components/BiometricIdentityFlow.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,EAOL,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,cAAc,EAKd,iBAAiB,EAElB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"BiometricIdentityFlow.d.ts","sourceRoot":"","sources":["../../src/components/BiometricIdentityFlow.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,EAOL,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,cAAc,EAKd,iBAAiB,EAElB,MAAM,oCAAoC,CAAC;AA8B5C,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,OAAO,CAAC,EAAE,SAAS,CAAC;KACrB,CAAC;CACH;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAuatE,CAAC;AA8NF,eAAe,qBAAqB,CAAC"}
|
|
@@ -44,18 +44,25 @@ const biometric_identity_sdk_core_1 = require("@hexar/biometric-identity-sdk-cor
|
|
|
44
44
|
const useBiometricSDK_1 = require("../hooks/useBiometricSDK");
|
|
45
45
|
const CameraCapture_1 = require("./CameraCapture");
|
|
46
46
|
const VideoRecorder_1 = require("./VideoRecorder");
|
|
47
|
-
const getInstructionMap = (strings) =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
47
|
+
const getInstructionMap = (strings) => {
|
|
48
|
+
if (!strings || !strings.liveness || !strings.liveness.instructions) {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
|
|
53
|
+
look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
|
|
54
|
+
look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
|
|
55
|
+
look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
|
|
56
|
+
turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
|
|
57
|
+
turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
|
|
58
|
+
turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
|
|
59
|
+
turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
|
|
60
|
+
smile: { text: strings.liveness.instructions.smile || 'Smile' },
|
|
61
|
+
blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
|
|
62
|
+
open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
|
|
63
|
+
stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
|
|
64
|
+
};
|
|
65
|
+
};
|
|
59
66
|
const ValidationProgress_1 = require("./ValidationProgress");
|
|
60
67
|
const ResultScreen_1 = require("./ResultScreen");
|
|
61
68
|
const ErrorScreen_1 = require("./ErrorScreen");
|
|
@@ -77,6 +84,12 @@ const BiometricIdentityFlow = ({ onValidationComplete, onError, theme, language,
|
|
|
77
84
|
(0, react_1.useEffect)(() => {
|
|
78
85
|
if (language) {
|
|
79
86
|
(0, biometric_identity_sdk_core_1.setLanguage)(language);
|
|
87
|
+
const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
88
|
+
biometric_identity_sdk_core_1.logger.info('Language set in BiometricIdentityFlow', {
|
|
89
|
+
language,
|
|
90
|
+
lookLeft: currentStrings.liveness?.instructions?.lookLeft,
|
|
91
|
+
lookRight: currentStrings.liveness?.instructions?.lookRight
|
|
92
|
+
});
|
|
80
93
|
}
|
|
81
94
|
}, [language]);
|
|
82
95
|
const strings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
@@ -112,25 +125,65 @@ const BiometricIdentityFlow = ({ onValidationComplete, onError, theme, language,
|
|
|
112
125
|
}
|
|
113
126
|
catch (error) {
|
|
114
127
|
biometric_identity_sdk_core_1.logger.warn('Failed to fetch challenges, using defaults');
|
|
128
|
+
if (language) {
|
|
129
|
+
(0, biometric_identity_sdk_core_1.setLanguage)(language);
|
|
130
|
+
}
|
|
131
|
+
const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
132
|
+
const instructionMap = getInstructionMap(currentStrings);
|
|
115
133
|
const defaultChallenges = sdk.getDefaultChallenges();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
biometric_identity_sdk_core_1.logger.info('Translating default challenges', {
|
|
135
|
+
language,
|
|
136
|
+
actionCount: defaultChallenges.length,
|
|
137
|
+
instructionMapKeys: Object.keys(instructionMap),
|
|
138
|
+
lookLeftTranslation: instructionMap['look_left']?.text,
|
|
139
|
+
lookRightTranslation: instructionMap['look_right']?.text
|
|
140
|
+
});
|
|
141
|
+
const translatedChallenges = defaultChallenges.map(challenge => {
|
|
142
|
+
const translated = instructionMap[challenge.action];
|
|
143
|
+
const finalInstruction = translated?.text || challenge.instruction;
|
|
144
|
+
biometric_identity_sdk_core_1.logger.info('Translating challenge', {
|
|
145
|
+
action: challenge.action,
|
|
146
|
+
original: challenge.instruction,
|
|
147
|
+
translated: finalInstruction
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
...challenge,
|
|
151
|
+
instruction: finalInstruction,
|
|
152
|
+
icon: translated?.icon
|
|
153
|
+
};
|
|
154
|
+
});
|
|
122
155
|
setCurrentChallenges(translatedChallenges);
|
|
123
156
|
}
|
|
124
157
|
setIsLoadingChallenges(false);
|
|
125
158
|
}
|
|
126
159
|
else if (mode === 'video' && smartLivenessMode) {
|
|
160
|
+
if (language) {
|
|
161
|
+
(0, biometric_identity_sdk_core_1.setLanguage)(language);
|
|
162
|
+
}
|
|
163
|
+
const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
164
|
+
const instructionMap = getInstructionMap(currentStrings);
|
|
127
165
|
const defaultChallenges = sdk.getDefaultChallenges();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
166
|
+
biometric_identity_sdk_core_1.logger.info('Translating default challenges (offline mode)', {
|
|
167
|
+
language,
|
|
168
|
+
actionCount: defaultChallenges.length,
|
|
169
|
+
instructionMapKeys: Object.keys(instructionMap),
|
|
170
|
+
lookLeftTranslation: instructionMap['look_left']?.text,
|
|
171
|
+
lookRightTranslation: instructionMap['look_right']?.text
|
|
172
|
+
});
|
|
173
|
+
const translatedChallenges = defaultChallenges.map(challenge => {
|
|
174
|
+
const translated = instructionMap[challenge.action];
|
|
175
|
+
const finalInstruction = translated?.text || challenge.instruction;
|
|
176
|
+
biometric_identity_sdk_core_1.logger.info('Translating challenge (offline)', {
|
|
177
|
+
action: challenge.action,
|
|
178
|
+
original: challenge.instruction,
|
|
179
|
+
translated: finalInstruction
|
|
180
|
+
});
|
|
181
|
+
return {
|
|
182
|
+
...challenge,
|
|
183
|
+
instruction: finalInstruction,
|
|
184
|
+
icon: translated?.icon
|
|
185
|
+
};
|
|
186
|
+
});
|
|
134
187
|
setCurrentChallenges(translatedChallenges);
|
|
135
188
|
}
|
|
136
189
|
setShowCamera(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResultScreen.d.ts","sourceRoot":"","sources":["../../src/components/ResultScreen.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"ResultScreen.d.ts","sourceRoot":"","sources":["../../src/components/ResultScreen.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAoB,MAAM,OAAO,CAAC;AAQzC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAA2B,MAAM,oCAAoC,CAAC;AAE/H,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqHpD,CAAC;AAmKF,eAAe,YAAY,CAAC"}
|
|
@@ -3,14 +3,51 @@
|
|
|
3
3
|
* Result Screen Component
|
|
4
4
|
* Shows validation result (pass/fail)
|
|
5
5
|
*/
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
9
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
40
|
exports.ResultScreen = void 0;
|
|
11
|
-
const react_1 =
|
|
41
|
+
const react_1 = __importStar(require("react"));
|
|
12
42
|
const react_native_1 = require("react-native");
|
|
43
|
+
const biometric_identity_sdk_core_1 = require("@hexar/biometric-identity-sdk-core");
|
|
13
44
|
const ResultScreen = ({ result, theme, language = 'en', onClose, }) => {
|
|
45
|
+
(0, react_1.useEffect)(() => {
|
|
46
|
+
if (language) {
|
|
47
|
+
(0, biometric_identity_sdk_core_1.setLanguage)(language);
|
|
48
|
+
}
|
|
49
|
+
}, [language]);
|
|
50
|
+
const strings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
14
51
|
const isSuccess = result.isValidFaceMatch && result.isDocumentAuthentic;
|
|
15
52
|
const successColor = theme?.successColor || '#10B981';
|
|
16
53
|
const errorColor = theme?.errorColor || '#EF4444';
|
|
@@ -21,27 +58,19 @@ const ResultScreen = ({ result, theme, language = 'en', onClose, }) => {
|
|
|
21
58
|
{ backgroundColor: isSuccess ? successColor : errorColor },
|
|
22
59
|
] },
|
|
23
60
|
react_1.default.createElement(react_native_1.Text, { style: styles.icon }, isSuccess ? '✓' : '✗')),
|
|
24
|
-
react_1.default.createElement(react_native_1.Text, { style: [styles.title, { color: theme?.textColor || '#000000' }] }, isSuccess
|
|
25
|
-
|
|
26
|
-
: (language === 'es' ? 'Verificación Fallida' : 'Verification Failed')),
|
|
27
|
-
react_1.default.createElement(react_native_1.Text, { style: [styles.subtitle, { color: theme?.secondaryTextColor || '#6B7280' }] }, isSuccess
|
|
28
|
-
? (language === 'es'
|
|
29
|
-
? 'Tu identidad ha sido verificada correctamente'
|
|
30
|
-
: 'Your identity has been verified successfully')
|
|
31
|
-
: (language === 'es'
|
|
32
|
-
? 'No pudimos verificar tu identidad'
|
|
33
|
-
: 'We were unable to verify your identity')),
|
|
61
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.title, { color: theme?.textColor || '#000000' }] }, isSuccess ? strings.result.success.title : strings.result.failure.title),
|
|
62
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.subtitle, { color: theme?.secondaryTextColor || '#6B7280' }] }, isSuccess ? strings.result.success.message : strings.result.failure.message),
|
|
34
63
|
react_1.default.createElement(react_native_1.View, { style: styles.scoresContainer },
|
|
35
|
-
react_1.default.createElement(ScoreCard, { label:
|
|
36
|
-
react_1.default.createElement(ScoreCard, { label:
|
|
64
|
+
react_1.default.createElement(ScoreCard, { label: strings.validation.matchingFaces || 'Face Match', score: result.matchScore, passed: result.isValidFaceMatch, theme: theme, strings: strings }),
|
|
65
|
+
react_1.default.createElement(ScoreCard, { label: strings.liveness.title || 'Liveness', score: result.livenessScore, passed: result.livenessScore >= 80, theme: theme, strings: strings })),
|
|
37
66
|
result.extractedDocumentData && (react_1.default.createElement(react_native_1.View, { style: styles.dataContainer },
|
|
38
|
-
react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.textColor || '#000000' }] },
|
|
39
|
-
react_1.default.createElement(DataRow, { label:
|
|
40
|
-
react_1.default.createElement(DataRow, { label:
|
|
41
|
-
react_1.default.createElement(DataRow, { label:
|
|
42
|
-
react_1.default.createElement(DataRow, { label:
|
|
67
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.textColor || '#000000' }] }, strings.result.success.verificationComplete || 'Extracted Data'),
|
|
68
|
+
react_1.default.createElement(DataRow, { label: "Nombre", value: `${result.extractedDocumentData.firstName} ${result.extractedDocumentData.lastName}`, theme: theme }),
|
|
69
|
+
react_1.default.createElement(DataRow, { label: "Documento", value: result.extractedDocumentData.documentNumber, theme: theme }),
|
|
70
|
+
react_1.default.createElement(DataRow, { label: "Fecha de Nacimiento", value: result.extractedDocumentData.dateOfBirth, theme: theme }),
|
|
71
|
+
react_1.default.createElement(DataRow, { label: "Nacionalidad", value: result.extractedDocumentData.nationality, theme: theme }))),
|
|
43
72
|
result.warnings.length > 0 && (react_1.default.createElement(react_native_1.View, { style: styles.warningsContainer },
|
|
44
|
-
react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.warningColor || '#F59E0B' }] },
|
|
73
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.warningColor || '#F59E0B' }] }, "Advertencias"),
|
|
45
74
|
result.warnings.map((warning, index) => (react_1.default.createElement(react_native_1.Text, { key: index, style: styles.warningText },
|
|
46
75
|
"\u2022 ",
|
|
47
76
|
warning.replace(/_/g, ' '))))))),
|
|
@@ -50,16 +79,16 @@ const ResultScreen = ({ result, theme, language = 'en', onClose, }) => {
|
|
|
50
79
|
styles.button,
|
|
51
80
|
{ backgroundColor: theme?.primaryColor || '#6366F1' },
|
|
52
81
|
], onPress: onClose },
|
|
53
|
-
react_1.default.createElement(react_native_1.Text, { style: styles.buttonText },
|
|
82
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.buttonText }, strings.common.continue)))));
|
|
54
83
|
};
|
|
55
84
|
exports.ResultScreen = ResultScreen;
|
|
56
|
-
const ScoreCard = ({ label, score, passed, theme }) => (react_1.default.createElement(react_native_1.View, { style: scoreStyles.container },
|
|
85
|
+
const ScoreCard = ({ label, score, passed, theme, strings }) => (react_1.default.createElement(react_native_1.View, { style: scoreStyles.container },
|
|
57
86
|
react_1.default.createElement(react_native_1.Text, { style: scoreStyles.label }, label),
|
|
58
87
|
react_1.default.createElement(react_native_1.Text, { style: [
|
|
59
88
|
scoreStyles.score,
|
|
60
89
|
{ color: passed ? (theme?.successColor || '#10B981') : (theme?.errorColor || '#EF4444') },
|
|
61
90
|
] }, Math.round(score)),
|
|
62
|
-
react_1.default.createElement(react_native_1.Text, { style: scoreStyles.status }, passed ? '
|
|
91
|
+
react_1.default.createElement(react_native_1.Text, { style: scoreStyles.status }, passed ? `✓ ${strings.common.done || 'Passed'}` : `✗ ${strings.common.error || 'Failed'}`)));
|
|
63
92
|
const scoreStyles = react_native_1.StyleSheet.create({
|
|
64
93
|
container: {
|
|
65
94
|
flex: 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiDD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAi2BtD,CAAC;AA4OF,eAAe,aAAa,CAAC"}
|
|
@@ -69,18 +69,25 @@ const getDefaultChallenges = (strings) => [
|
|
|
69
69
|
order: 4,
|
|
70
70
|
},
|
|
71
71
|
];
|
|
72
|
-
const getInstructionMap = (strings) =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
72
|
+
const getInstructionMap = (strings) => {
|
|
73
|
+
if (!strings || !strings.liveness || !strings.liveness.instructions) {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
|
|
78
|
+
look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
|
|
79
|
+
look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
|
|
80
|
+
look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
|
|
81
|
+
turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
|
|
82
|
+
turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
|
|
83
|
+
turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
|
|
84
|
+
turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
|
|
85
|
+
smile: { text: strings.liveness.instructions.smile || 'Smile' },
|
|
86
|
+
blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
|
|
87
|
+
open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
|
|
88
|
+
stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
|
|
89
|
+
};
|
|
90
|
+
};
|
|
84
91
|
const VideoRecorder = ({ theme, language, duration, instructions, challenges: propChallenges, sessionId, smartMode = true, onComplete, onCancel, onFetchChallenges, }) => {
|
|
85
92
|
(0, react_1.useEffect)(() => {
|
|
86
93
|
if (language) {
|
|
@@ -145,9 +152,20 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
145
152
|
(0, react_1.useEffect)(() => {
|
|
146
153
|
const initChallenges = async () => {
|
|
147
154
|
try {
|
|
155
|
+
if (language) {
|
|
156
|
+
(0, biometric_identity_sdk_core_1.setLanguage)(language);
|
|
157
|
+
}
|
|
148
158
|
let challengeList;
|
|
149
159
|
const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
150
160
|
const instructionMap = getInstructionMap(currentStrings);
|
|
161
|
+
biometric_identity_sdk_core_1.logger.info('Initializing challenges', {
|
|
162
|
+
language,
|
|
163
|
+
hasStrings: !!currentStrings,
|
|
164
|
+
hasInstructions: !!currentStrings?.liveness?.instructions,
|
|
165
|
+
instructionMapKeys: Object.keys(instructionMap),
|
|
166
|
+
turnLeftTranslation: instructionMap['turn_left']?.text,
|
|
167
|
+
turnRightTranslation: instructionMap['turn_right']?.text
|
|
168
|
+
});
|
|
151
169
|
if (propChallenges && propChallenges.length > 0) {
|
|
152
170
|
challengeList = propChallenges.map(challenge => ({
|
|
153
171
|
...challenge,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hexar/biometric-identity-sdk-react-native",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"description": "React Native wrapper for Biometric Identity SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"clean": "rm -rf dist"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@hexar/biometric-identity-sdk-core": ">=1.0.
|
|
14
|
+
"@hexar/biometric-identity-sdk-core": ">=1.0.18",
|
|
15
15
|
"react": ">=18.0.0",
|
|
16
16
|
"react-native": ">=0.70.0",
|
|
17
17
|
"react-native-permissions": ">=4.0.0",
|
|
@@ -29,18 +29,25 @@ import { useBiometricSDK } from '../hooks/useBiometricSDK';
|
|
|
29
29
|
import { CameraCapture } from './CameraCapture';
|
|
30
30
|
import { VideoRecorder, VideoRecordingResult } from './VideoRecorder';
|
|
31
31
|
|
|
32
|
-
const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
32
|
+
const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> => {
|
|
33
|
+
if (!strings || !strings.liveness || !strings.liveness.instructions) {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
|
|
38
|
+
look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
|
|
39
|
+
look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
|
|
40
|
+
look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
|
|
41
|
+
turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
|
|
42
|
+
turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
|
|
43
|
+
turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
|
|
44
|
+
turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
|
|
45
|
+
smile: { text: strings.liveness.instructions.smile || 'Smile' },
|
|
46
|
+
blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
|
|
47
|
+
open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
|
|
48
|
+
stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
|
|
49
|
+
};
|
|
50
|
+
};
|
|
44
51
|
import { ValidationProgress } from './ValidationProgress';
|
|
45
52
|
import { ResultScreen } from './ResultScreen';
|
|
46
53
|
import { ErrorScreen } from './ErrorScreen';
|
|
@@ -102,6 +109,12 @@ export const BiometricIdentityFlow: React.FC<BiometricIdentityFlowProps> = ({
|
|
|
102
109
|
useEffect(() => {
|
|
103
110
|
if (language) {
|
|
104
111
|
setLanguage(language);
|
|
112
|
+
const currentStrings = getStrings();
|
|
113
|
+
logger.info('Language set in BiometricIdentityFlow', {
|
|
114
|
+
language,
|
|
115
|
+
lookLeft: currentStrings.liveness?.instructions?.lookLeft,
|
|
116
|
+
lookRight: currentStrings.liveness?.instructions?.lookRight
|
|
117
|
+
});
|
|
105
118
|
}
|
|
106
119
|
}, [language]);
|
|
107
120
|
|
|
@@ -142,24 +155,64 @@ export const BiometricIdentityFlow: React.FC<BiometricIdentityFlowProps> = ({
|
|
|
142
155
|
setCurrentChallenges(challenges);
|
|
143
156
|
} catch (error) {
|
|
144
157
|
logger.warn('Failed to fetch challenges, using defaults');
|
|
158
|
+
if (language) {
|
|
159
|
+
setLanguage(language);
|
|
160
|
+
}
|
|
161
|
+
const currentStrings = getStrings();
|
|
162
|
+
const instructionMap = getInstructionMap(currentStrings);
|
|
145
163
|
const defaultChallenges = sdk.getDefaultChallenges();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
164
|
+
logger.info('Translating default challenges', {
|
|
165
|
+
language,
|
|
166
|
+
actionCount: defaultChallenges.length,
|
|
167
|
+
instructionMapKeys: Object.keys(instructionMap),
|
|
168
|
+
lookLeftTranslation: instructionMap['look_left']?.text,
|
|
169
|
+
lookRightTranslation: instructionMap['look_right']?.text
|
|
170
|
+
});
|
|
171
|
+
const translatedChallenges = defaultChallenges.map(challenge => {
|
|
172
|
+
const translated = instructionMap[challenge.action];
|
|
173
|
+
const finalInstruction = translated?.text || challenge.instruction;
|
|
174
|
+
logger.info('Translating challenge', {
|
|
175
|
+
action: challenge.action,
|
|
176
|
+
original: challenge.instruction,
|
|
177
|
+
translated: finalInstruction
|
|
178
|
+
});
|
|
179
|
+
return {
|
|
180
|
+
...challenge,
|
|
181
|
+
instruction: finalInstruction,
|
|
182
|
+
icon: translated?.icon
|
|
183
|
+
};
|
|
184
|
+
});
|
|
152
185
|
setCurrentChallenges(translatedChallenges);
|
|
153
186
|
}
|
|
154
187
|
setIsLoadingChallenges(false);
|
|
155
188
|
} else if (mode === 'video' && smartLivenessMode) {
|
|
189
|
+
if (language) {
|
|
190
|
+
setLanguage(language);
|
|
191
|
+
}
|
|
192
|
+
const currentStrings = getStrings();
|
|
193
|
+
const instructionMap = getInstructionMap(currentStrings);
|
|
156
194
|
const defaultChallenges = sdk.getDefaultChallenges();
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
195
|
+
logger.info('Translating default challenges (offline mode)', {
|
|
196
|
+
language,
|
|
197
|
+
actionCount: defaultChallenges.length,
|
|
198
|
+
instructionMapKeys: Object.keys(instructionMap),
|
|
199
|
+
lookLeftTranslation: instructionMap['look_left']?.text,
|
|
200
|
+
lookRightTranslation: instructionMap['look_right']?.text
|
|
201
|
+
});
|
|
202
|
+
const translatedChallenges = defaultChallenges.map(challenge => {
|
|
203
|
+
const translated = instructionMap[challenge.action];
|
|
204
|
+
const finalInstruction = translated?.text || challenge.instruction;
|
|
205
|
+
logger.info('Translating challenge (offline)', {
|
|
206
|
+
action: challenge.action,
|
|
207
|
+
original: challenge.instruction,
|
|
208
|
+
translated: finalInstruction
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
...challenge,
|
|
212
|
+
instruction: finalInstruction,
|
|
213
|
+
icon: translated?.icon
|
|
214
|
+
};
|
|
215
|
+
});
|
|
163
216
|
setCurrentChallenges(translatedChallenges);
|
|
164
217
|
}
|
|
165
218
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Shows validation result (pass/fail)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React from 'react';
|
|
6
|
+
import React, { useEffect } from 'react';
|
|
7
7
|
import {
|
|
8
8
|
View,
|
|
9
9
|
Text,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
TouchableOpacity,
|
|
12
12
|
ScrollView,
|
|
13
13
|
} from 'react-native';
|
|
14
|
-
import { ValidationResult, ThemeConfig, SupportedLanguage } from '@hexar/biometric-identity-sdk-core';
|
|
14
|
+
import { ValidationResult, ThemeConfig, SupportedLanguage, getStrings, setLanguage } from '@hexar/biometric-identity-sdk-core';
|
|
15
15
|
|
|
16
16
|
export interface ResultScreenProps {
|
|
17
17
|
result: ValidationResult;
|
|
@@ -26,6 +26,13 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
|
|
|
26
26
|
language = 'en',
|
|
27
27
|
onClose,
|
|
28
28
|
}) => {
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (language) {
|
|
31
|
+
setLanguage(language);
|
|
32
|
+
}
|
|
33
|
+
}, [language]);
|
|
34
|
+
|
|
35
|
+
const strings = getStrings();
|
|
29
36
|
const isSuccess = result.isValidFaceMatch && result.isDocumentAuthentic;
|
|
30
37
|
const successColor = theme?.successColor || '#10B981';
|
|
31
38
|
const errorColor = theme?.errorColor || '#EF4444';
|
|
@@ -45,34 +52,28 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
|
|
|
45
52
|
|
|
46
53
|
{/* Result Title */}
|
|
47
54
|
<Text style={[styles.title, { color: theme?.textColor || '#000000' }]}>
|
|
48
|
-
{isSuccess
|
|
49
|
-
? (language === 'es' ? '¡Verificación Exitosa!' : 'Verification Successful!')
|
|
50
|
-
: (language === 'es' ? 'Verificación Fallida' : 'Verification Failed')}
|
|
55
|
+
{isSuccess ? strings.result.success.title : strings.result.failure.title}
|
|
51
56
|
</Text>
|
|
52
57
|
|
|
53
58
|
<Text style={[styles.subtitle, { color: theme?.secondaryTextColor || '#6B7280' }]}>
|
|
54
|
-
{isSuccess
|
|
55
|
-
? (language === 'es'
|
|
56
|
-
? 'Tu identidad ha sido verificada correctamente'
|
|
57
|
-
: 'Your identity has been verified successfully')
|
|
58
|
-
: (language === 'es'
|
|
59
|
-
? 'No pudimos verificar tu identidad'
|
|
60
|
-
: 'We were unable to verify your identity')}
|
|
59
|
+
{isSuccess ? strings.result.success.message : strings.result.failure.message}
|
|
61
60
|
</Text>
|
|
62
61
|
|
|
63
62
|
{/* Scores */}
|
|
64
63
|
<View style={styles.scoresContainer}>
|
|
65
64
|
<ScoreCard
|
|
66
|
-
label={
|
|
65
|
+
label={strings.validation.matchingFaces || 'Face Match'}
|
|
67
66
|
score={result.matchScore}
|
|
68
67
|
passed={result.isValidFaceMatch}
|
|
69
68
|
theme={theme}
|
|
69
|
+
strings={strings}
|
|
70
70
|
/>
|
|
71
71
|
<ScoreCard
|
|
72
|
-
label={
|
|
72
|
+
label={strings.liveness.title || 'Liveness'}
|
|
73
73
|
score={result.livenessScore}
|
|
74
74
|
passed={result.livenessScore >= 80}
|
|
75
75
|
theme={theme}
|
|
76
|
+
strings={strings}
|
|
76
77
|
/>
|
|
77
78
|
</View>
|
|
78
79
|
|
|
@@ -80,25 +81,25 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
|
|
|
80
81
|
{result.extractedDocumentData && (
|
|
81
82
|
<View style={styles.dataContainer}>
|
|
82
83
|
<Text style={[styles.sectionTitle, { color: theme?.textColor || '#000000' }]}>
|
|
83
|
-
{
|
|
84
|
+
{strings.result.success.verificationComplete || 'Extracted Data'}
|
|
84
85
|
</Text>
|
|
85
86
|
<DataRow
|
|
86
|
-
label=
|
|
87
|
+
label="Nombre"
|
|
87
88
|
value={`${result.extractedDocumentData.firstName} ${result.extractedDocumentData.lastName}`}
|
|
88
89
|
theme={theme}
|
|
89
90
|
/>
|
|
90
91
|
<DataRow
|
|
91
|
-
label=
|
|
92
|
+
label="Documento"
|
|
92
93
|
value={result.extractedDocumentData.documentNumber}
|
|
93
94
|
theme={theme}
|
|
94
95
|
/>
|
|
95
96
|
<DataRow
|
|
96
|
-
label=
|
|
97
|
+
label="Fecha de Nacimiento"
|
|
97
98
|
value={result.extractedDocumentData.dateOfBirth}
|
|
98
99
|
theme={theme}
|
|
99
100
|
/>
|
|
100
101
|
<DataRow
|
|
101
|
-
label=
|
|
102
|
+
label="Nacionalidad"
|
|
102
103
|
value={result.extractedDocumentData.nationality}
|
|
103
104
|
theme={theme}
|
|
104
105
|
/>
|
|
@@ -109,7 +110,7 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
|
|
|
109
110
|
{result.warnings.length > 0 && (
|
|
110
111
|
<View style={styles.warningsContainer}>
|
|
111
112
|
<Text style={[styles.sectionTitle, { color: theme?.warningColor || '#F59E0B' }]}>
|
|
112
|
-
|
|
113
|
+
Advertencias
|
|
113
114
|
</Text>
|
|
114
115
|
{result.warnings.map((warning, index) => (
|
|
115
116
|
<Text key={index} style={styles.warningText}>
|
|
@@ -130,7 +131,7 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
|
|
|
130
131
|
onPress={onClose}
|
|
131
132
|
>
|
|
132
133
|
<Text style={styles.buttonText}>
|
|
133
|
-
{
|
|
134
|
+
{strings.common.continue}
|
|
134
135
|
</Text>
|
|
135
136
|
</TouchableOpacity>
|
|
136
137
|
</View>
|
|
@@ -143,9 +144,10 @@ interface ScoreCardProps {
|
|
|
143
144
|
score: number;
|
|
144
145
|
passed: boolean;
|
|
145
146
|
theme?: ThemeConfig;
|
|
147
|
+
strings: any;
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
const ScoreCard: React.FC<ScoreCardProps> = ({ label, score, passed, theme }) => (
|
|
150
|
+
const ScoreCard: React.FC<ScoreCardProps> = ({ label, score, passed, theme, strings }) => (
|
|
149
151
|
<View style={scoreStyles.container}>
|
|
150
152
|
<Text style={scoreStyles.label}>{label}</Text>
|
|
151
153
|
<Text
|
|
@@ -156,7 +158,9 @@ const ScoreCard: React.FC<ScoreCardProps> = ({ label, score, passed, theme }) =>
|
|
|
156
158
|
>
|
|
157
159
|
{Math.round(score)}
|
|
158
160
|
</Text>
|
|
159
|
-
<Text style={scoreStyles.status}>
|
|
161
|
+
<Text style={scoreStyles.status}>
|
|
162
|
+
{passed ? `✓ ${strings.common.done || 'Passed'}` : `✗ ${strings.common.error || 'Failed'}`}
|
|
163
|
+
</Text>
|
|
160
164
|
</View>
|
|
161
165
|
);
|
|
162
166
|
|
|
@@ -83,18 +83,25 @@ const getDefaultChallenges = (strings: any): ChallengeAction[] => [
|
|
|
83
83
|
},
|
|
84
84
|
];
|
|
85
85
|
|
|
86
|
-
const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> =>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
86
|
+
const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> => {
|
|
87
|
+
if (!strings || !strings.liveness || !strings.liveness.instructions) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
|
|
92
|
+
look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
|
|
93
|
+
look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
|
|
94
|
+
look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
|
|
95
|
+
turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
|
|
96
|
+
turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
|
|
97
|
+
turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
|
|
98
|
+
turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
|
|
99
|
+
smile: { text: strings.liveness.instructions.smile || 'Smile' },
|
|
100
|
+
blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
|
|
101
|
+
open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
|
|
102
|
+
stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
|
|
103
|
+
};
|
|
104
|
+
};
|
|
98
105
|
|
|
99
106
|
export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
100
107
|
theme,
|
|
@@ -184,9 +191,20 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
184
191
|
useEffect(() => {
|
|
185
192
|
const initChallenges = async () => {
|
|
186
193
|
try {
|
|
194
|
+
if (language) {
|
|
195
|
+
setLanguage(language);
|
|
196
|
+
}
|
|
187
197
|
let challengeList: ChallengeAction[];
|
|
188
198
|
const currentStrings = getStrings();
|
|
189
199
|
const instructionMap = getInstructionMap(currentStrings);
|
|
200
|
+
logger.info('Initializing challenges', {
|
|
201
|
+
language,
|
|
202
|
+
hasStrings: !!currentStrings,
|
|
203
|
+
hasInstructions: !!currentStrings?.liveness?.instructions,
|
|
204
|
+
instructionMapKeys: Object.keys(instructionMap),
|
|
205
|
+
turnLeftTranslation: instructionMap['turn_left']?.text,
|
|
206
|
+
turnRightTranslation: instructionMap['turn_right']?.text
|
|
207
|
+
});
|
|
190
208
|
|
|
191
209
|
if (propChallenges && propChallenges.length > 0) {
|
|
192
210
|
challengeList = propChallenges.map(challenge => ({
|