@hexar/biometric-identity-sdk-react-native 1.1.17 → 1.1.19
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":"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,CAw6BtD,CAAC;AA4OF,eAAe,aAAa,CAAC"}
|
|
@@ -102,6 +102,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
102
102
|
const [challengeProgress, setChallengeProgress] = (0, react_1.useState)(0);
|
|
103
103
|
const [overallProgress, setOverallProgress] = (0, react_1.useState)(0);
|
|
104
104
|
const [completedChallenges, setCompletedChallenges] = (0, react_1.useState)([]);
|
|
105
|
+
const completedChallengesRef = (0, react_1.useRef)([]);
|
|
105
106
|
const [frames, setFrames] = (0, react_1.useState)([]);
|
|
106
107
|
const framesRef = (0, react_1.useRef)([]);
|
|
107
108
|
const [hasPermission, setHasPermission] = (0, react_1.useState)(false);
|
|
@@ -200,6 +201,13 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
200
201
|
},
|
|
201
202
|
];
|
|
202
203
|
}
|
|
204
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: Challenges initialized', {
|
|
205
|
+
challengeCount: challengeList.length,
|
|
206
|
+
challengeActions: challengeList.map(c => c.action),
|
|
207
|
+
source: propChallenges && propChallenges.length > 0 ? 'propChallenges' :
|
|
208
|
+
onFetchChallenges ? 'onFetchChallenges' :
|
|
209
|
+
instructions && instructions.length > 0 ? 'instructions' : 'default'
|
|
210
|
+
});
|
|
203
211
|
setChallenges(challengeList);
|
|
204
212
|
setPhase('countdown');
|
|
205
213
|
}
|
|
@@ -273,6 +281,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
273
281
|
setFrames([]);
|
|
274
282
|
framesRef.current = [];
|
|
275
283
|
setCompletedChallenges([]);
|
|
284
|
+
completedChallengesRef.current = [];
|
|
276
285
|
setCurrentChallengeIndex(0);
|
|
277
286
|
setChallengeProgress(0);
|
|
278
287
|
setOverallProgress(0);
|
|
@@ -320,15 +329,22 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
320
329
|
return;
|
|
321
330
|
}
|
|
322
331
|
biometric_identity_sdk_core_1.logger.info('Using frames for completion:', finalFrames.length, 'frames');
|
|
332
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
333
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: handleVideoComplete - completed challenges', {
|
|
334
|
+
refCount: completedChallengesRef.current.length,
|
|
335
|
+
stateCount: completedChallenges.length,
|
|
336
|
+
usingRef: completedChallengesRef.current.length > 0,
|
|
337
|
+
challengesCompleted: currentCompletedChallenges
|
|
338
|
+
});
|
|
323
339
|
const result = {
|
|
324
340
|
frames: finalFrames,
|
|
325
341
|
duration: actualDuration,
|
|
326
|
-
instructionsFollowed:
|
|
342
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
327
343
|
qualityScore: finalFrames.length > 0 ? Math.min(100, (finalFrames.length / 30) * 100) : 85,
|
|
328
|
-
challengesCompleted:
|
|
344
|
+
challengesCompleted: currentCompletedChallenges,
|
|
329
345
|
sessionId,
|
|
330
346
|
};
|
|
331
|
-
biometric_identity_sdk_core_1.logger.info('Video recording completed:', result.frames.length, 'frames,', (actualDuration / 1000).toFixed(1) + 's');
|
|
347
|
+
biometric_identity_sdk_core_1.logger.info('Video recording completed:', result.frames.length, 'frames,', (actualDuration / 1000).toFixed(1) + 's', 'challenges:', currentCompletedChallenges.length);
|
|
332
348
|
isRecordingRef.current = false;
|
|
333
349
|
onComplete(result);
|
|
334
350
|
}
|
|
@@ -442,14 +458,15 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
442
458
|
biometric_identity_sdk_core_1.logger.error('Error stopping video recording:', error);
|
|
443
459
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
444
460
|
const currentFrames = framesRef.current.length > 0 ? framesRef.current : frames;
|
|
461
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
445
462
|
if (actualDuration >= minDurationMs && currentFrames.length > 0) {
|
|
446
463
|
biometric_identity_sdk_core_1.logger.info('Stopping recording with frames:', currentFrames.length);
|
|
447
464
|
const result = {
|
|
448
465
|
frames: currentFrames,
|
|
449
466
|
duration: actualDuration,
|
|
450
|
-
instructionsFollowed:
|
|
467
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
451
468
|
qualityScore: Math.min(100, (currentFrames.length / 30) * 100),
|
|
452
|
-
challengesCompleted:
|
|
469
|
+
challengesCompleted: currentCompletedChallenges,
|
|
453
470
|
sessionId,
|
|
454
471
|
};
|
|
455
472
|
onComplete(result);
|
|
@@ -459,9 +476,9 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
459
476
|
const result = {
|
|
460
477
|
frames: currentFrames.length > 0 ? currentFrames : [],
|
|
461
478
|
duration: actualDuration,
|
|
462
|
-
instructionsFollowed:
|
|
479
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
463
480
|
qualityScore: currentFrames.length > 0 ? Math.min(100, (currentFrames.length / 30) * 100) : 0,
|
|
464
|
-
challengesCompleted:
|
|
481
|
+
challengesCompleted: currentCompletedChallenges,
|
|
465
482
|
sessionId,
|
|
466
483
|
};
|
|
467
484
|
onComplete(result);
|
|
@@ -479,13 +496,20 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
479
496
|
}
|
|
480
497
|
setPhase('processing');
|
|
481
498
|
setOverallProgress(100);
|
|
499
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
500
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: stopRecording - completed challenges', {
|
|
501
|
+
refCount: completedChallengesRef.current.length,
|
|
502
|
+
stateCount: completedChallenges.length,
|
|
503
|
+
usingRef: completedChallengesRef.current.length > 0,
|
|
504
|
+
challengesCompleted: currentCompletedChallenges
|
|
505
|
+
});
|
|
482
506
|
biometric_identity_sdk_core_1.logger.info('Completing recording with frames:', currentFrames.length);
|
|
483
507
|
const result = {
|
|
484
508
|
frames: currentFrames.length > 0 ? currentFrames : [],
|
|
485
509
|
duration: actualDuration,
|
|
486
|
-
instructionsFollowed:
|
|
510
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
487
511
|
qualityScore: currentFrames.length > 0 ? Math.min(100, (currentFrames.length / 30) * 100) : 0,
|
|
488
|
-
challengesCompleted:
|
|
512
|
+
challengesCompleted: currentCompletedChallenges,
|
|
489
513
|
sessionId,
|
|
490
514
|
};
|
|
491
515
|
setTimeout(() => {
|
|
@@ -494,6 +518,13 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
494
518
|
}
|
|
495
519
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, minDurationMs, resetAndRetry, strings]);
|
|
496
520
|
const runChallenge = (0, react_1.useCallback)((index) => {
|
|
521
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
522
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: runChallenge called', {
|
|
523
|
+
index,
|
|
524
|
+
challengesLength: challenges.length,
|
|
525
|
+
challengeActions: challenges.map(c => c.action),
|
|
526
|
+
completedChallengesCount: currentCompletedChallenges.length
|
|
527
|
+
});
|
|
497
528
|
if (index >= challenges.length) {
|
|
498
529
|
setOverallProgress(100);
|
|
499
530
|
if (recordingTimeoutRef.current) {
|
|
@@ -515,13 +546,14 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
515
546
|
}
|
|
516
547
|
else {
|
|
517
548
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
549
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
518
550
|
if (actualDuration >= minDurationMs && frames.length > 0) {
|
|
519
551
|
const result = {
|
|
520
552
|
frames,
|
|
521
553
|
duration: actualDuration,
|
|
522
|
-
instructionsFollowed:
|
|
554
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
523
555
|
qualityScore: Math.min(100, (frames.length / 30) * 100),
|
|
524
|
-
challengesCompleted:
|
|
556
|
+
challengesCompleted: currentCompletedChallenges,
|
|
525
557
|
sessionId,
|
|
526
558
|
};
|
|
527
559
|
onComplete(result);
|
|
@@ -556,7 +588,17 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
556
588
|
}, 100);
|
|
557
589
|
setTimeout(() => {
|
|
558
590
|
clearInterval(progressInterval);
|
|
559
|
-
setCompletedChallenges(prev =>
|
|
591
|
+
setCompletedChallenges(prev => {
|
|
592
|
+
const updated = [...prev, challenge.action];
|
|
593
|
+
completedChallengesRef.current = updated; // Keep ref in sync
|
|
594
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: Challenge completed', {
|
|
595
|
+
action: challenge.action,
|
|
596
|
+
index,
|
|
597
|
+
completedCount: updated.length,
|
|
598
|
+
completedActions: updated
|
|
599
|
+
});
|
|
600
|
+
return updated;
|
|
601
|
+
});
|
|
560
602
|
react_native_1.Animated.timing(fadeAnim, {
|
|
561
603
|
toValue: 0,
|
|
562
604
|
duration: 200,
|
|
@@ -571,6 +613,9 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
571
613
|
setPhase('recording');
|
|
572
614
|
recordingStartTime.current = Date.now();
|
|
573
615
|
isRecordingRef.current = true;
|
|
616
|
+
// Reset completed challenges ref when starting new recording
|
|
617
|
+
completedChallengesRef.current = [];
|
|
618
|
+
setCompletedChallenges([]);
|
|
574
619
|
if (cameraRef.current && device) {
|
|
575
620
|
try {
|
|
576
621
|
videoRecordingRef.current = await cameraRef.current.startRecording({
|
|
@@ -593,7 +638,32 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
593
638
|
else {
|
|
594
639
|
startFrameCapture();
|
|
595
640
|
}
|
|
596
|
-
|
|
641
|
+
/**
|
|
642
|
+
* If challenges are not available, set default challenges and run challenge
|
|
643
|
+
* This should not happen if ProfilePictureCapture properly waits for challenges
|
|
644
|
+
* But as a fallback, set defaults and retry after state update
|
|
645
|
+
* Schedule runChallenge to execute after state update completes
|
|
646
|
+
* This ensures the updated challenges are available when runChallenge executes
|
|
647
|
+
* Check again after state update - runChallenge callback should have latest challenges
|
|
648
|
+
*/
|
|
649
|
+
if (challenges.length === 0) {
|
|
650
|
+
biometric_identity_sdk_core_1.logger.warn('VideoRecorder: No challenges available when starting recording - challenges should have been loaded by initChallenges');
|
|
651
|
+
const defaultChallenges = smartMode ? getDefaultChallenges((0, biometric_identity_sdk_core_1.getStrings)()) : [
|
|
652
|
+
{
|
|
653
|
+
action: 'stay_still',
|
|
654
|
+
instruction: (0, biometric_identity_sdk_core_1.getStrings)().liveness.instructions.stayStill || 'Look at the camera and stay still',
|
|
655
|
+
duration_ms: duration || 5000,
|
|
656
|
+
order: 1,
|
|
657
|
+
},
|
|
658
|
+
];
|
|
659
|
+
setChallenges(defaultChallenges);
|
|
660
|
+
Promise.resolve().then(() => {
|
|
661
|
+
runChallenge(0);
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
runChallenge(0);
|
|
666
|
+
}
|
|
597
667
|
const maxDuration = Math.max(totalDuration, minDurationMs + 2000);
|
|
598
668
|
recordingTimeoutRef.current = setTimeout(() => {
|
|
599
669
|
if (isRecordingRef.current) {
|
|
@@ -608,7 +678,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
608
678
|
recordingTimeoutRef.current = null;
|
|
609
679
|
}
|
|
610
680
|
};
|
|
611
|
-
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture]);
|
|
681
|
+
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture, challenges, smartMode, duration]);
|
|
612
682
|
const currentChallenge = challenges[currentChallengeIndex];
|
|
613
683
|
const getDirectionIndicator = () => {
|
|
614
684
|
if (!currentChallenge)
|
package/package.json
CHANGED
|
@@ -129,6 +129,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
129
129
|
const [challengeProgress, setChallengeProgress] = useState(0);
|
|
130
130
|
const [overallProgress, setOverallProgress] = useState(0);
|
|
131
131
|
const [completedChallenges, setCompletedChallenges] = useState<string[]>([]);
|
|
132
|
+
const completedChallengesRef = useRef<string[]>([]);
|
|
132
133
|
const [frames, setFrames] = useState<string[]>([]);
|
|
133
134
|
const framesRef = useRef<string[]>([]);
|
|
134
135
|
const [hasPermission, setHasPermission] = useState(false);
|
|
@@ -238,6 +239,13 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
238
239
|
];
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
logger.info('VideoRecorder: Challenges initialized', {
|
|
243
|
+
challengeCount: challengeList.length,
|
|
244
|
+
challengeActions: challengeList.map(c => c.action),
|
|
245
|
+
source: propChallenges && propChallenges.length > 0 ? 'propChallenges' :
|
|
246
|
+
onFetchChallenges ? 'onFetchChallenges' :
|
|
247
|
+
instructions && instructions.length > 0 ? 'instructions' : 'default'
|
|
248
|
+
});
|
|
241
249
|
setChallenges(challengeList);
|
|
242
250
|
setPhase('countdown');
|
|
243
251
|
} catch (error) {
|
|
@@ -320,6 +328,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
320
328
|
setFrames([]);
|
|
321
329
|
framesRef.current = [];
|
|
322
330
|
setCompletedChallenges([]);
|
|
331
|
+
completedChallengesRef.current = [];
|
|
323
332
|
setCurrentChallengeIndex(0);
|
|
324
333
|
setChallengeProgress(0);
|
|
325
334
|
setOverallProgress(0);
|
|
@@ -385,16 +394,24 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
385
394
|
|
|
386
395
|
logger.info('Using frames for completion:', finalFrames.length, 'frames');
|
|
387
396
|
|
|
397
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
398
|
+
logger.info('VideoRecorder: handleVideoComplete - completed challenges', {
|
|
399
|
+
refCount: completedChallengesRef.current.length,
|
|
400
|
+
stateCount: completedChallenges.length,
|
|
401
|
+
usingRef: completedChallengesRef.current.length > 0,
|
|
402
|
+
challengesCompleted: currentCompletedChallenges
|
|
403
|
+
});
|
|
404
|
+
|
|
388
405
|
const result: VideoRecordingResult = {
|
|
389
406
|
frames: finalFrames,
|
|
390
407
|
duration: actualDuration,
|
|
391
|
-
instructionsFollowed:
|
|
408
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
392
409
|
qualityScore: finalFrames.length > 0 ? Math.min(100, (finalFrames.length / 30) * 100) : 85,
|
|
393
|
-
challengesCompleted:
|
|
410
|
+
challengesCompleted: currentCompletedChallenges,
|
|
394
411
|
sessionId,
|
|
395
412
|
};
|
|
396
413
|
|
|
397
|
-
logger.info('Video recording completed:', result.frames.length, 'frames,', (actualDuration / 1000).toFixed(1) + 's');
|
|
414
|
+
logger.info('Video recording completed:', result.frames.length, 'frames,', (actualDuration / 1000).toFixed(1) + 's', 'challenges:', currentCompletedChallenges.length);
|
|
398
415
|
|
|
399
416
|
isRecordingRef.current = false;
|
|
400
417
|
onComplete(result);
|
|
@@ -515,14 +532,15 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
515
532
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
516
533
|
const currentFrames = framesRef.current.length > 0 ? framesRef.current : frames;
|
|
517
534
|
|
|
535
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
518
536
|
if (actualDuration >= minDurationMs && currentFrames.length > 0) {
|
|
519
537
|
logger.info('Stopping recording with frames:', currentFrames.length);
|
|
520
538
|
const result: VideoRecordingResult = {
|
|
521
539
|
frames: currentFrames,
|
|
522
540
|
duration: actualDuration,
|
|
523
|
-
instructionsFollowed:
|
|
541
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
524
542
|
qualityScore: Math.min(100, (currentFrames.length / 30) * 100),
|
|
525
|
-
challengesCompleted:
|
|
543
|
+
challengesCompleted: currentCompletedChallenges,
|
|
526
544
|
sessionId,
|
|
527
545
|
};
|
|
528
546
|
onComplete(result);
|
|
@@ -531,9 +549,9 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
531
549
|
const result: VideoRecordingResult = {
|
|
532
550
|
frames: currentFrames.length > 0 ? currentFrames : [],
|
|
533
551
|
duration: actualDuration,
|
|
534
|
-
instructionsFollowed:
|
|
552
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
535
553
|
qualityScore: currentFrames.length > 0 ? Math.min(100, (currentFrames.length / 30) * 100) : 0,
|
|
536
|
-
challengesCompleted:
|
|
554
|
+
challengesCompleted: currentCompletedChallenges,
|
|
537
555
|
sessionId,
|
|
538
556
|
};
|
|
539
557
|
onComplete(result);
|
|
@@ -557,13 +575,20 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
557
575
|
setPhase('processing');
|
|
558
576
|
setOverallProgress(100);
|
|
559
577
|
|
|
578
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
579
|
+
logger.info('VideoRecorder: stopRecording - completed challenges', {
|
|
580
|
+
refCount: completedChallengesRef.current.length,
|
|
581
|
+
stateCount: completedChallenges.length,
|
|
582
|
+
usingRef: completedChallengesRef.current.length > 0,
|
|
583
|
+
challengesCompleted: currentCompletedChallenges
|
|
584
|
+
});
|
|
560
585
|
logger.info('Completing recording with frames:', currentFrames.length);
|
|
561
586
|
const result: VideoRecordingResult = {
|
|
562
587
|
frames: currentFrames.length > 0 ? currentFrames : [],
|
|
563
588
|
duration: actualDuration,
|
|
564
|
-
instructionsFollowed:
|
|
589
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
565
590
|
qualityScore: currentFrames.length > 0 ? Math.min(100, (currentFrames.length / 30) * 100) : 0,
|
|
566
|
-
challengesCompleted:
|
|
591
|
+
challengesCompleted: currentCompletedChallenges,
|
|
567
592
|
sessionId,
|
|
568
593
|
};
|
|
569
594
|
|
|
@@ -574,6 +599,14 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
574
599
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, minDurationMs, resetAndRetry, strings]);
|
|
575
600
|
|
|
576
601
|
const runChallenge = useCallback((index: number) => {
|
|
602
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
603
|
+
logger.info('VideoRecorder: runChallenge called', {
|
|
604
|
+
index,
|
|
605
|
+
challengesLength: challenges.length,
|
|
606
|
+
challengeActions: challenges.map(c => c.action),
|
|
607
|
+
completedChallengesCount: currentCompletedChallenges.length
|
|
608
|
+
});
|
|
609
|
+
|
|
577
610
|
if (index >= challenges.length) {
|
|
578
611
|
setOverallProgress(100);
|
|
579
612
|
|
|
@@ -598,13 +631,14 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
598
631
|
stopRecording();
|
|
599
632
|
} else {
|
|
600
633
|
const actualDuration = Date.now() - recordingStartTime.current;
|
|
634
|
+
const currentCompletedChallenges = completedChallengesRef.current.length > 0 ? completedChallengesRef.current : completedChallenges;
|
|
601
635
|
if (actualDuration >= minDurationMs && frames.length > 0) {
|
|
602
636
|
const result: VideoRecordingResult = {
|
|
603
637
|
frames,
|
|
604
638
|
duration: actualDuration,
|
|
605
|
-
instructionsFollowed:
|
|
639
|
+
instructionsFollowed: currentCompletedChallenges.length === challenges.length,
|
|
606
640
|
qualityScore: Math.min(100, (frames.length / 30) * 100),
|
|
607
|
-
challengesCompleted:
|
|
641
|
+
challengesCompleted: currentCompletedChallenges,
|
|
608
642
|
sessionId,
|
|
609
643
|
};
|
|
610
644
|
onComplete(result);
|
|
@@ -647,7 +681,17 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
647
681
|
setTimeout(() => {
|
|
648
682
|
clearInterval(progressInterval);
|
|
649
683
|
|
|
650
|
-
setCompletedChallenges(prev =>
|
|
684
|
+
setCompletedChallenges(prev => {
|
|
685
|
+
const updated = [...prev, challenge.action];
|
|
686
|
+
completedChallengesRef.current = updated; // Keep ref in sync
|
|
687
|
+
logger.info('VideoRecorder: Challenge completed', {
|
|
688
|
+
action: challenge.action,
|
|
689
|
+
index,
|
|
690
|
+
completedCount: updated.length,
|
|
691
|
+
completedActions: updated
|
|
692
|
+
});
|
|
693
|
+
return updated;
|
|
694
|
+
});
|
|
651
695
|
|
|
652
696
|
Animated.timing(fadeAnim, {
|
|
653
697
|
toValue: 0,
|
|
@@ -664,6 +708,9 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
664
708
|
setPhase('recording');
|
|
665
709
|
recordingStartTime.current = Date.now();
|
|
666
710
|
isRecordingRef.current = true;
|
|
711
|
+
// Reset completed challenges ref when starting new recording
|
|
712
|
+
completedChallengesRef.current = [];
|
|
713
|
+
setCompletedChallenges([]);
|
|
667
714
|
|
|
668
715
|
if (cameraRef.current && device) {
|
|
669
716
|
try {
|
|
@@ -687,7 +734,31 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
687
734
|
startFrameCapture();
|
|
688
735
|
}
|
|
689
736
|
|
|
690
|
-
|
|
737
|
+
/**
|
|
738
|
+
* If challenges are not available, set default challenges and run challenge
|
|
739
|
+
* This should not happen if ProfilePictureCapture properly waits for challenges
|
|
740
|
+
* But as a fallback, set defaults and retry after state update
|
|
741
|
+
* Schedule runChallenge to execute after state update completes
|
|
742
|
+
* This ensures the updated challenges are available when runChallenge executes
|
|
743
|
+
* Check again after state update - runChallenge callback should have latest challenges
|
|
744
|
+
*/
|
|
745
|
+
if (challenges.length === 0) {
|
|
746
|
+
logger.warn('VideoRecorder: No challenges available when starting recording - challenges should have been loaded by initChallenges');
|
|
747
|
+
const defaultChallenges = smartMode ? getDefaultChallenges(getStrings()) : [
|
|
748
|
+
{
|
|
749
|
+
action: 'stay_still',
|
|
750
|
+
instruction: getStrings().liveness.instructions.stayStill || 'Look at the camera and stay still',
|
|
751
|
+
duration_ms: duration || 5000,
|
|
752
|
+
order: 1,
|
|
753
|
+
},
|
|
754
|
+
];
|
|
755
|
+
setChallenges(defaultChallenges);
|
|
756
|
+
Promise.resolve().then(() => {
|
|
757
|
+
runChallenge(0);
|
|
758
|
+
});
|
|
759
|
+
} else {
|
|
760
|
+
runChallenge(0);
|
|
761
|
+
}
|
|
691
762
|
|
|
692
763
|
const maxDuration = Math.max(totalDuration, minDurationMs + 2000);
|
|
693
764
|
recordingTimeoutRef.current = setTimeout(() => {
|
|
@@ -704,7 +775,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
704
775
|
recordingTimeoutRef.current = null;
|
|
705
776
|
}
|
|
706
777
|
};
|
|
707
|
-
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture]);
|
|
778
|
+
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture, challenges, smartMode, duration]);
|
|
708
779
|
|
|
709
780
|
const currentChallenge = challenges[currentChallengeIndex];
|
|
710
781
|
|