@hexar/biometric-identity-sdk-react-native 1.1.26 → 1.2.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.
|
@@ -263,7 +263,7 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
|
|
|
263
263
|
react_1.default.createElement(react_native_1.View, { style: styles.progressContainer },
|
|
264
264
|
react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: theme?.primaryColor || '#4f46e5' }),
|
|
265
265
|
react_1.default.createElement(react_native_1.Text, { style: [styles.progressTitle, { color: theme?.textColor || '#1e1b4b' }] }, strings.validation.title || strings.liveness.processing || strings.validation.checkingLiveness || 'Validating Profile Picture'),
|
|
266
|
-
react_1.default.createElement(react_native_1.Text, { style: [styles.progressStatusText, { color: theme?.secondaryTextColor || '#64748b' }] }, strings.validation.
|
|
266
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.progressStatusText, { color: theme?.secondaryTextColor || '#64748b' }] }, strings.validation.validatingFace || strings.liveness.processing || 'Validando rostro...'),
|
|
267
267
|
react_1.default.createElement(react_native_1.View, { style: styles.progressBarContainer },
|
|
268
268
|
react_1.default.createElement(react_native_1.View, { style: styles.progressBar },
|
|
269
269
|
react_1.default.createElement(react_native_1.Animated.View, { style: [
|
|
@@ -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;AAiDD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
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,CAm6BtD,CAAC;AA6OF,eAAe,aAAa,CAAC"}
|
|
@@ -349,16 +349,16 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
349
349
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, resetAndRetry, handleRecordingError, strings, minDurationMs, phase]);
|
|
350
350
|
const startFrameCapture = (0, react_1.useCallback)(() => {
|
|
351
351
|
if (cameraRef.current && device) {
|
|
352
|
-
biometric_identity_sdk_core_1.logger.info('Starting frame capture');
|
|
352
|
+
biometric_identity_sdk_core_1.logger.info('Starting serial frame capture');
|
|
353
353
|
framesRef.current = [];
|
|
354
354
|
let consecutiveErrors = 0;
|
|
355
|
-
const maxConsecutiveErrors =
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
355
|
+
const maxConsecutiveErrors = 10;
|
|
356
|
+
const MAX_FRAMES = 80;
|
|
357
|
+
// Serial capture: each frame is captured only after the previous one
|
|
358
|
+
// finishes, preventing overlapping takePhoto() calls that cause the
|
|
359
|
+
// camera to throw "busy" errors and kill the capture loop.
|
|
360
|
+
const captureNextFrame = async () => {
|
|
361
|
+
if (!isRecordingRef.current || framesRef.current.length >= MAX_FRAMES) {
|
|
362
362
|
return;
|
|
363
363
|
}
|
|
364
364
|
try {
|
|
@@ -395,7 +395,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
395
395
|
}
|
|
396
396
|
if (base64Data) {
|
|
397
397
|
setFrames(prev => {
|
|
398
|
-
const newFrames = prev.length <
|
|
398
|
+
const newFrames = prev.length < MAX_FRAMES ? [...prev, base64Data] : prev;
|
|
399
399
|
framesRef.current = newFrames;
|
|
400
400
|
if (newFrames.length % 10 === 0) {
|
|
401
401
|
biometric_identity_sdk_core_1.logger.info('Captured frames:', newFrames.length);
|
|
@@ -410,14 +410,19 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
410
410
|
}
|
|
411
411
|
catch (error) {
|
|
412
412
|
consecutiveErrors++;
|
|
413
|
+
biometric_identity_sdk_core_1.logger.warn('Frame capture error', consecutiveErrors, '/', maxConsecutiveErrors, error?.message);
|
|
413
414
|
if (consecutiveErrors >= maxConsecutiveErrors) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
frameCaptureInterval.current = null;
|
|
417
|
-
}
|
|
415
|
+
biometric_identity_sdk_core_1.logger.error('Too many consecutive capture errors, stopping');
|
|
416
|
+
return;
|
|
418
417
|
}
|
|
419
418
|
}
|
|
420
|
-
|
|
419
|
+
// Schedule next capture only after this one completes (serial chain).
|
|
420
|
+
// Small delay lets the camera hardware reset between captures.
|
|
421
|
+
if (isRecordingRef.current && framesRef.current.length < MAX_FRAMES) {
|
|
422
|
+
frameCaptureInterval.current = setTimeout(captureNextFrame, 150);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
captureNextFrame();
|
|
421
426
|
}
|
|
422
427
|
else {
|
|
423
428
|
biometric_identity_sdk_core_1.logger.warn('Cannot start frame capture: camera or device not available');
|
|
@@ -429,7 +434,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
429
434
|
recordingTimeoutRef.current = null;
|
|
430
435
|
}
|
|
431
436
|
if (frameCaptureInterval.current) {
|
|
432
|
-
|
|
437
|
+
clearTimeout(frameCaptureInterval.current);
|
|
433
438
|
frameCaptureInterval.current = null;
|
|
434
439
|
}
|
|
435
440
|
if (!isRecordingRef.current) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hexar/biometric-identity-sdk-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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.1.
|
|
14
|
+
"@hexar/biometric-identity-sdk-core": ">=1.1.7",
|
|
15
15
|
"react": ">=18.0.0",
|
|
16
16
|
"react-native": ">=0.70.0",
|
|
17
17
|
"react-native-permissions": ">=4.0.0",
|
|
@@ -294,7 +294,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
|
|
|
294
294
|
</Text>
|
|
295
295
|
|
|
296
296
|
<Text style={[styles.progressStatusText, { color: theme?.secondaryTextColor || '#64748b' }]}>
|
|
297
|
-
{strings.validation.
|
|
297
|
+
{(strings.validation as any).validatingFace || strings.liveness.processing || 'Validando rostro...'}
|
|
298
298
|
</Text>
|
|
299
299
|
|
|
300
300
|
{/* Progress Bar */}
|
|
@@ -417,29 +417,29 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
417
417
|
|
|
418
418
|
const startFrameCapture = useCallback(() => {
|
|
419
419
|
if (cameraRef.current && device) {
|
|
420
|
-
logger.info('Starting frame capture');
|
|
420
|
+
logger.info('Starting serial frame capture');
|
|
421
421
|
framesRef.current = [];
|
|
422
422
|
let consecutiveErrors = 0;
|
|
423
|
-
const maxConsecutiveErrors =
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
423
|
+
const maxConsecutiveErrors = 10;
|
|
424
|
+
const MAX_FRAMES = 80;
|
|
425
|
+
|
|
426
|
+
// Serial capture: each frame is captured only after the previous one
|
|
427
|
+
// finishes, preventing overlapping takePhoto() calls that cause the
|
|
428
|
+
// camera to throw "busy" errors and kill the capture loop.
|
|
429
|
+
const captureNextFrame = async () => {
|
|
430
|
+
if (!isRecordingRef.current || framesRef.current.length >= MAX_FRAMES) {
|
|
431
431
|
return;
|
|
432
432
|
}
|
|
433
|
-
|
|
433
|
+
|
|
434
434
|
try {
|
|
435
435
|
const photo = await cameraRef.current?.takePhoto({
|
|
436
436
|
flash: 'off',
|
|
437
437
|
});
|
|
438
|
-
|
|
438
|
+
|
|
439
439
|
if (photo) {
|
|
440
440
|
consecutiveErrors = 0;
|
|
441
441
|
let base64Data: string | null = null;
|
|
442
|
-
|
|
442
|
+
|
|
443
443
|
try {
|
|
444
444
|
const RNFS = require('react-native-fs');
|
|
445
445
|
base64Data = await RNFS.readFile(photo.path, 'base64');
|
|
@@ -463,10 +463,10 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
463
463
|
base64Data = null;
|
|
464
464
|
}
|
|
465
465
|
}
|
|
466
|
-
|
|
466
|
+
|
|
467
467
|
if (base64Data) {
|
|
468
468
|
setFrames(prev => {
|
|
469
|
-
const newFrames = prev.length <
|
|
469
|
+
const newFrames = prev.length < MAX_FRAMES ? [...prev, base64Data!] : prev;
|
|
470
470
|
framesRef.current = newFrames;
|
|
471
471
|
if (newFrames.length % 10 === 0) {
|
|
472
472
|
logger.info('Captured frames:', newFrames.length);
|
|
@@ -479,14 +479,21 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
479
479
|
}
|
|
480
480
|
} catch (error: any) {
|
|
481
481
|
consecutiveErrors++;
|
|
482
|
+
logger.warn('Frame capture error', consecutiveErrors, '/', maxConsecutiveErrors, error?.message);
|
|
482
483
|
if (consecutiveErrors >= maxConsecutiveErrors) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
frameCaptureInterval.current = null;
|
|
486
|
-
}
|
|
484
|
+
logger.error('Too many consecutive capture errors, stopping');
|
|
485
|
+
return;
|
|
487
486
|
}
|
|
488
487
|
}
|
|
489
|
-
|
|
488
|
+
|
|
489
|
+
// Schedule next capture only after this one completes (serial chain).
|
|
490
|
+
// Small delay lets the camera hardware reset between captures.
|
|
491
|
+
if (isRecordingRef.current && framesRef.current.length < MAX_FRAMES) {
|
|
492
|
+
frameCaptureInterval.current = setTimeout(captureNextFrame, 150);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
captureNextFrame();
|
|
490
497
|
} else {
|
|
491
498
|
logger.warn('Cannot start frame capture: camera or device not available');
|
|
492
499
|
}
|
|
@@ -499,7 +506,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
499
506
|
}
|
|
500
507
|
|
|
501
508
|
if (frameCaptureInterval.current) {
|
|
502
|
-
|
|
509
|
+
clearTimeout(frameCaptureInterval.current);
|
|
503
510
|
frameCaptureInterval.current = null;
|
|
504
511
|
}
|
|
505
512
|
|