@hexar/biometric-identity-sdk-react-native 1.1.16 → 1.1.18
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/ProfilePictureCapture.d.ts.map +1 -1
- package/dist/components/ProfilePictureCapture.js +25 -2
- package/dist/components/VideoRecorder.d.ts.map +1 -1
- package/dist/components/VideoRecorder.js +50 -3
- package/package.json +1 -1
- package/src/components/ProfilePictureCapture.tsx +32 -2
- package/src/components/VideoRecorder.tsx +50 -3
|
@@ -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,
|
|
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,CAiPtE,CAAC;AAuBF,eAAe,qBAAqB,CAAC"}
|
|
@@ -58,16 +58,25 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
|
|
|
58
58
|
setIsLoadingChallenges(true);
|
|
59
59
|
try {
|
|
60
60
|
const challenges = await fetchChallenges('active');
|
|
61
|
+
biometric_identity_sdk_core_1.logger.info('ProfilePictureCapture: Challenges loaded', {
|
|
62
|
+
challengeCount: challenges.length,
|
|
63
|
+
challenges: challenges.map(c => c.action)
|
|
64
|
+
});
|
|
61
65
|
setCurrentChallenges(challenges);
|
|
62
66
|
}
|
|
63
67
|
catch (error) {
|
|
64
|
-
biometric_identity_sdk_core_1.logger.warn('Failed to fetch challenges, VideoRecorder will use defaults');
|
|
68
|
+
biometric_identity_sdk_core_1.logger.warn('Failed to fetch challenges, VideoRecorder will use defaults', error);
|
|
65
69
|
setCurrentChallenges([]);
|
|
66
70
|
}
|
|
67
71
|
setIsLoadingChallenges(false);
|
|
68
72
|
};
|
|
69
73
|
loadChallenges();
|
|
70
74
|
}
|
|
75
|
+
else if (isInitialized) {
|
|
76
|
+
// If not using backend, still initialize with empty array (VideoRecorder will use defaults)
|
|
77
|
+
setCurrentChallenges([]);
|
|
78
|
+
setIsLoadingChallenges(false);
|
|
79
|
+
}
|
|
71
80
|
}, [isInitialized, isUsingBackend, fetchChallenges]);
|
|
72
81
|
const strings = (0, biometric_identity_sdk_core_1.getStrings)();
|
|
73
82
|
const validateWithBackend = (0, react_1.useCallback)(async (videoResult) => {
|
|
@@ -142,6 +151,13 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
|
|
|
142
151
|
}
|
|
143
152
|
}, [isInitialized, isUsingBackend, sdk]);
|
|
144
153
|
const handleVideoComplete = (0, react_1.useCallback)(async (videoResult) => {
|
|
154
|
+
biometric_identity_sdk_core_1.logger.info('ProfilePictureCapture: Video recording completed', {
|
|
155
|
+
framesCount: videoResult.frames?.length || 0,
|
|
156
|
+
duration: videoResult.duration,
|
|
157
|
+
challengesCompleted: videoResult.challengesCompleted || [],
|
|
158
|
+
challengesCompletedCount: videoResult.challengesCompleted?.length || 0,
|
|
159
|
+
expectedChallengesCount: currentChallenges.length,
|
|
160
|
+
});
|
|
145
161
|
setIsValidating(true);
|
|
146
162
|
try {
|
|
147
163
|
const result = await validateWithBackend(videoResult);
|
|
@@ -185,7 +201,7 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
|
|
|
185
201
|
};
|
|
186
202
|
onError(biometricError);
|
|
187
203
|
}
|
|
188
|
-
}, [validateWithBackend, onComplete, onError, strings, language]);
|
|
204
|
+
}, [validateWithBackend, onComplete, onError, strings, language, currentChallenges]);
|
|
189
205
|
const handleVideoCancel = (0, react_1.useCallback)(() => {
|
|
190
206
|
if (onCancel) {
|
|
191
207
|
onCancel();
|
|
@@ -198,6 +214,13 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
|
|
|
198
214
|
react_1.default.createElement(react_native_1.Text, { style: [styles.loadingText, { color: theme?.textColor || '#1e1b4b' }] }, strings.liveness.processing || strings.validation.checkingLiveness || 'Processing...'),
|
|
199
215
|
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'))));
|
|
200
216
|
}
|
|
217
|
+
// Wait for initialization and challenge loading before showing VideoRecorder
|
|
218
|
+
if (!isInitialized || (isUsingBackend && isLoadingChallenges)) {
|
|
219
|
+
return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
|
|
220
|
+
react_1.default.createElement(react_native_1.View, { style: styles.loadingContainer },
|
|
221
|
+
react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: theme?.primaryColor || '#4f46e5' }),
|
|
222
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.loadingText, { color: theme?.textColor || '#1e1b4b' }] }, strings.liveness.preparing || 'Preparing...'))));
|
|
223
|
+
}
|
|
201
224
|
return (react_1.default.createElement(VideoRecorder_1.VideoRecorder, { theme: theme, language: language, smartMode: true, challenges: currentChallenges, sessionId: sdk.getSessionId() || undefined, onComplete: handleVideoComplete, onCancel: handleVideoCancel, onFetchChallenges: handleFetchChallenges }));
|
|
202
225
|
};
|
|
203
226
|
exports.ProfilePictureCapture = ProfilePictureCapture;
|
|
@@ -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,CAg5BtD,CAAC;AA4OF,eAAe,aAAa,CAAC"}
|
|
@@ -200,6 +200,13 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
200
200
|
},
|
|
201
201
|
];
|
|
202
202
|
}
|
|
203
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: Challenges initialized', {
|
|
204
|
+
challengeCount: challengeList.length,
|
|
205
|
+
challengeActions: challengeList.map(c => c.action),
|
|
206
|
+
source: propChallenges && propChallenges.length > 0 ? 'propChallenges' :
|
|
207
|
+
onFetchChallenges ? 'onFetchChallenges' :
|
|
208
|
+
instructions && instructions.length > 0 ? 'instructions' : 'default'
|
|
209
|
+
});
|
|
203
210
|
setChallenges(challengeList);
|
|
204
211
|
setPhase('countdown');
|
|
205
212
|
}
|
|
@@ -494,6 +501,12 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
494
501
|
}
|
|
495
502
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, minDurationMs, resetAndRetry, strings]);
|
|
496
503
|
const runChallenge = (0, react_1.useCallback)((index) => {
|
|
504
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: runChallenge called', {
|
|
505
|
+
index,
|
|
506
|
+
challengesLength: challenges.length,
|
|
507
|
+
challengeActions: challenges.map(c => c.action),
|
|
508
|
+
completedChallengesCount: completedChallenges.length
|
|
509
|
+
});
|
|
497
510
|
if (index >= challenges.length) {
|
|
498
511
|
setOverallProgress(100);
|
|
499
512
|
if (recordingTimeoutRef.current) {
|
|
@@ -556,7 +569,16 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
556
569
|
}, 100);
|
|
557
570
|
setTimeout(() => {
|
|
558
571
|
clearInterval(progressInterval);
|
|
559
|
-
setCompletedChallenges(prev =>
|
|
572
|
+
setCompletedChallenges(prev => {
|
|
573
|
+
const updated = [...prev, challenge.action];
|
|
574
|
+
biometric_identity_sdk_core_1.logger.info('VideoRecorder: Challenge completed', {
|
|
575
|
+
action: challenge.action,
|
|
576
|
+
index,
|
|
577
|
+
completedCount: updated.length,
|
|
578
|
+
completedActions: updated
|
|
579
|
+
});
|
|
580
|
+
return updated;
|
|
581
|
+
});
|
|
560
582
|
react_native_1.Animated.timing(fadeAnim, {
|
|
561
583
|
toValue: 0,
|
|
562
584
|
duration: 200,
|
|
@@ -593,7 +615,32 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
593
615
|
else {
|
|
594
616
|
startFrameCapture();
|
|
595
617
|
}
|
|
596
|
-
|
|
618
|
+
/**
|
|
619
|
+
* If challenges are not available, set default challenges and run challenge
|
|
620
|
+
* This should not happen if ProfilePictureCapture properly waits for challenges
|
|
621
|
+
* But as a fallback, set defaults and retry after state update
|
|
622
|
+
* Schedule runChallenge to execute after state update completes
|
|
623
|
+
* This ensures the updated challenges are available when runChallenge executes
|
|
624
|
+
* Check again after state update - runChallenge callback should have latest challenges
|
|
625
|
+
*/
|
|
626
|
+
if (challenges.length === 0) {
|
|
627
|
+
biometric_identity_sdk_core_1.logger.warn('VideoRecorder: No challenges available when starting recording - challenges should have been loaded by initChallenges');
|
|
628
|
+
const defaultChallenges = smartMode ? getDefaultChallenges((0, biometric_identity_sdk_core_1.getStrings)()) : [
|
|
629
|
+
{
|
|
630
|
+
action: 'stay_still',
|
|
631
|
+
instruction: (0, biometric_identity_sdk_core_1.getStrings)().liveness.instructions.stayStill || 'Look at the camera and stay still',
|
|
632
|
+
duration_ms: duration || 5000,
|
|
633
|
+
order: 1,
|
|
634
|
+
},
|
|
635
|
+
];
|
|
636
|
+
setChallenges(defaultChallenges);
|
|
637
|
+
Promise.resolve().then(() => {
|
|
638
|
+
runChallenge(0);
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
runChallenge(0);
|
|
643
|
+
}
|
|
597
644
|
const maxDuration = Math.max(totalDuration, minDurationMs + 2000);
|
|
598
645
|
recordingTimeoutRef.current = setTimeout(() => {
|
|
599
646
|
if (isRecordingRef.current) {
|
|
@@ -608,7 +655,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
|
|
|
608
655
|
recordingTimeoutRef.current = null;
|
|
609
656
|
}
|
|
610
657
|
};
|
|
611
|
-
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture]);
|
|
658
|
+
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture, challenges, smartMode, duration]);
|
|
612
659
|
const currentChallenge = challenges[currentChallengeIndex];
|
|
613
660
|
const getDirectionIndicator = () => {
|
|
614
661
|
if (!currentChallenge)
|
package/package.json
CHANGED
|
@@ -61,14 +61,22 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
|
|
|
61
61
|
setIsLoadingChallenges(true);
|
|
62
62
|
try {
|
|
63
63
|
const challenges = await fetchChallenges('active');
|
|
64
|
+
logger.info('ProfilePictureCapture: Challenges loaded', {
|
|
65
|
+
challengeCount: challenges.length,
|
|
66
|
+
challenges: challenges.map(c => c.action)
|
|
67
|
+
});
|
|
64
68
|
setCurrentChallenges(challenges);
|
|
65
69
|
} catch (error) {
|
|
66
|
-
logger.warn('Failed to fetch challenges, VideoRecorder will use defaults');
|
|
70
|
+
logger.warn('Failed to fetch challenges, VideoRecorder will use defaults', error);
|
|
67
71
|
setCurrentChallenges([]);
|
|
68
72
|
}
|
|
69
73
|
setIsLoadingChallenges(false);
|
|
70
74
|
};
|
|
71
75
|
loadChallenges();
|
|
76
|
+
} else if (isInitialized) {
|
|
77
|
+
// If not using backend, still initialize with empty array (VideoRecorder will use defaults)
|
|
78
|
+
setCurrentChallenges([]);
|
|
79
|
+
setIsLoadingChallenges(false);
|
|
72
80
|
}
|
|
73
81
|
}, [isInitialized, isUsingBackend, fetchChallenges]);
|
|
74
82
|
|
|
@@ -154,6 +162,14 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
|
|
|
154
162
|
}, [isInitialized, isUsingBackend, sdk]);
|
|
155
163
|
|
|
156
164
|
const handleVideoComplete = useCallback(async (videoResult: VideoRecordingResult) => {
|
|
165
|
+
logger.info('ProfilePictureCapture: Video recording completed', {
|
|
166
|
+
framesCount: videoResult.frames?.length || 0,
|
|
167
|
+
duration: videoResult.duration,
|
|
168
|
+
challengesCompleted: videoResult.challengesCompleted || [],
|
|
169
|
+
challengesCompletedCount: videoResult.challengesCompleted?.length || 0,
|
|
170
|
+
expectedChallengesCount: currentChallenges.length,
|
|
171
|
+
});
|
|
172
|
+
|
|
157
173
|
setIsValidating(true);
|
|
158
174
|
|
|
159
175
|
try {
|
|
@@ -201,7 +217,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
|
|
|
201
217
|
} as BiometricError;
|
|
202
218
|
onError(biometricError);
|
|
203
219
|
}
|
|
204
|
-
}, [validateWithBackend, onComplete, onError, strings, language]);
|
|
220
|
+
}, [validateWithBackend, onComplete, onError, strings, language, currentChallenges]);
|
|
205
221
|
|
|
206
222
|
const handleVideoCancel = useCallback(() => {
|
|
207
223
|
if (onCancel) {
|
|
@@ -225,6 +241,20 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
|
|
|
225
241
|
);
|
|
226
242
|
}
|
|
227
243
|
|
|
244
|
+
// Wait for initialization and challenge loading before showing VideoRecorder
|
|
245
|
+
if (!isInitialized || (isUsingBackend && isLoadingChallenges)) {
|
|
246
|
+
return (
|
|
247
|
+
<SafeAreaView style={[styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }]}>
|
|
248
|
+
<View style={styles.loadingContainer}>
|
|
249
|
+
<ActivityIndicator size="large" color={theme?.primaryColor || '#4f46e5'} />
|
|
250
|
+
<Text style={[styles.loadingText, { color: theme?.textColor || '#1e1b4b' }]}>
|
|
251
|
+
{strings.liveness.preparing || 'Preparing...'}
|
|
252
|
+
</Text>
|
|
253
|
+
</View>
|
|
254
|
+
</SafeAreaView>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
228
258
|
return (
|
|
229
259
|
<VideoRecorder
|
|
230
260
|
theme={theme}
|
|
@@ -238,6 +238,13 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
238
238
|
];
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
logger.info('VideoRecorder: Challenges initialized', {
|
|
242
|
+
challengeCount: challengeList.length,
|
|
243
|
+
challengeActions: challengeList.map(c => c.action),
|
|
244
|
+
source: propChallenges && propChallenges.length > 0 ? 'propChallenges' :
|
|
245
|
+
onFetchChallenges ? 'onFetchChallenges' :
|
|
246
|
+
instructions && instructions.length > 0 ? 'instructions' : 'default'
|
|
247
|
+
});
|
|
241
248
|
setChallenges(challengeList);
|
|
242
249
|
setPhase('countdown');
|
|
243
250
|
} catch (error) {
|
|
@@ -574,6 +581,13 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
574
581
|
}, [frames, completedChallenges, challenges, sessionId, onComplete, minDurationMs, resetAndRetry, strings]);
|
|
575
582
|
|
|
576
583
|
const runChallenge = useCallback((index: number) => {
|
|
584
|
+
logger.info('VideoRecorder: runChallenge called', {
|
|
585
|
+
index,
|
|
586
|
+
challengesLength: challenges.length,
|
|
587
|
+
challengeActions: challenges.map(c => c.action),
|
|
588
|
+
completedChallengesCount: completedChallenges.length
|
|
589
|
+
});
|
|
590
|
+
|
|
577
591
|
if (index >= challenges.length) {
|
|
578
592
|
setOverallProgress(100);
|
|
579
593
|
|
|
@@ -647,7 +661,16 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
647
661
|
setTimeout(() => {
|
|
648
662
|
clearInterval(progressInterval);
|
|
649
663
|
|
|
650
|
-
setCompletedChallenges(prev =>
|
|
664
|
+
setCompletedChallenges(prev => {
|
|
665
|
+
const updated = [...prev, challenge.action];
|
|
666
|
+
logger.info('VideoRecorder: Challenge completed', {
|
|
667
|
+
action: challenge.action,
|
|
668
|
+
index,
|
|
669
|
+
completedCount: updated.length,
|
|
670
|
+
completedActions: updated
|
|
671
|
+
});
|
|
672
|
+
return updated;
|
|
673
|
+
});
|
|
651
674
|
|
|
652
675
|
Animated.timing(fadeAnim, {
|
|
653
676
|
toValue: 0,
|
|
@@ -687,7 +710,31 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
687
710
|
startFrameCapture();
|
|
688
711
|
}
|
|
689
712
|
|
|
690
|
-
|
|
713
|
+
/**
|
|
714
|
+
* If challenges are not available, set default challenges and run challenge
|
|
715
|
+
* This should not happen if ProfilePictureCapture properly waits for challenges
|
|
716
|
+
* But as a fallback, set defaults and retry after state update
|
|
717
|
+
* Schedule runChallenge to execute after state update completes
|
|
718
|
+
* This ensures the updated challenges are available when runChallenge executes
|
|
719
|
+
* Check again after state update - runChallenge callback should have latest challenges
|
|
720
|
+
*/
|
|
721
|
+
if (challenges.length === 0) {
|
|
722
|
+
logger.warn('VideoRecorder: No challenges available when starting recording - challenges should have been loaded by initChallenges');
|
|
723
|
+
const defaultChallenges = smartMode ? getDefaultChallenges(getStrings()) : [
|
|
724
|
+
{
|
|
725
|
+
action: 'stay_still',
|
|
726
|
+
instruction: getStrings().liveness.instructions.stayStill || 'Look at the camera and stay still',
|
|
727
|
+
duration_ms: duration || 5000,
|
|
728
|
+
order: 1,
|
|
729
|
+
},
|
|
730
|
+
];
|
|
731
|
+
setChallenges(defaultChallenges);
|
|
732
|
+
Promise.resolve().then(() => {
|
|
733
|
+
runChallenge(0);
|
|
734
|
+
});
|
|
735
|
+
} else {
|
|
736
|
+
runChallenge(0);
|
|
737
|
+
}
|
|
691
738
|
|
|
692
739
|
const maxDuration = Math.max(totalDuration, minDurationMs + 2000);
|
|
693
740
|
recordingTimeoutRef.current = setTimeout(() => {
|
|
@@ -704,7 +751,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
|
|
|
704
751
|
recordingTimeoutRef.current = null;
|
|
705
752
|
}
|
|
706
753
|
};
|
|
707
|
-
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture]);
|
|
754
|
+
}, [device, totalDuration, minDurationMs, handleVideoComplete, handleRecordingError, runChallenge, stopRecording, startFrameCapture, challenges, smartMode, duration]);
|
|
708
755
|
|
|
709
756
|
const currentChallenge = challenges[currentChallengeIndex];
|
|
710
757
|
|