@hexar/biometric-identity-sdk-react-native 1.8.0 → 1.10.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.
@@ -1 +1 @@
1
- {"version":3,"file":"ProfilePictureCapture.d.ts","sourceRoot":"","sources":["../../src/components/ProfilePictureCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAWxE,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,CA8VtE,CAAC;AAsEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"ProfilePictureCapture.d.ts","sourceRoot":"","sources":["../../src/components/ProfilePictureCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAYxE,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,CAyYtE,CAAC;AA4EF,eAAe,qBAAqB,CAAC"}
@@ -46,6 +46,7 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
46
46
  const [isValidating, setIsValidating] = (0, react_1.useState)(false);
47
47
  const [currentChallenges, setCurrentChallenges] = (0, react_1.useState)([]);
48
48
  const [isLoadingChallenges, setIsLoadingChallenges] = (0, react_1.useState)(false);
49
+ const [loadingTimedOut, setLoadingTimedOut] = (0, react_1.useState)(false);
49
50
  const animatedProgress = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
50
51
  const [displayProgress, setDisplayProgress] = (0, react_1.useState)(0);
51
52
  const animationRef = (0, react_1.useRef)(null);
@@ -58,6 +59,19 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
58
59
  (0, biometric_identity_sdk_core_1.setLanguage)(language);
59
60
  }
60
61
  }, [language]);
62
+ // 15-second timeout on initial loading (challenge fetch / SDK init)
63
+ (0, react_1.useEffect)(() => {
64
+ const isLoading = !isInitialized || (isUsingBackend && isLoadingChallenges);
65
+ if (isLoading) {
66
+ const timer = setTimeout(() => {
67
+ setLoadingTimedOut(true);
68
+ }, 15000);
69
+ return () => clearTimeout(timer);
70
+ }
71
+ else {
72
+ setLoadingTimedOut(false);
73
+ }
74
+ }, [isInitialized, isUsingBackend, isLoadingChallenges]);
61
75
  (0, react_1.useEffect)(() => {
62
76
  if (isInitialized && isUsingBackend) {
63
77
  const loadChallenges = async () => {
@@ -151,10 +165,10 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
151
165
  hasStartedAnimation.current = true;
152
166
  animatedProgress.setValue(0);
153
167
  setDisplayProgress(0);
154
- // Start animation to 90% over 60 seconds
168
+ // Start animation to 90% over 25 seconds
155
169
  animationRef.current = react_native_1.Animated.timing(animatedProgress, {
156
170
  toValue: 90,
157
- duration: 60000, // 60 seconds
171
+ duration: 25000, // 25 seconds
158
172
  useNativeDriver: false,
159
173
  });
160
174
  animationRef.current.start();
@@ -284,6 +298,7 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
284
298
  react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: theme?.primaryColor || '#4f46e5' }),
285
299
  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'),
286
300
  react_1.default.createElement(react_native_1.Text, { style: [styles.progressStatusText, { color: theme?.secondaryTextColor || '#64748b' }] }, strings.validation.validatingFace || strings.liveness.processing || 'Validando rostro...'),
301
+ react_1.default.createElement(react_native_1.Text, { style: [styles.progressDoNotLockText, { color: theme?.secondaryTextColor || '#64748b' }] }, strings.validation.doNotLockScreen || 'No bloquees la pantalla'),
287
302
  react_1.default.createElement(react_native_1.View, { style: styles.progressBarContainer },
288
303
  react_1.default.createElement(react_native_1.View, { style: styles.progressBar },
289
304
  react_1.default.createElement(react_native_1.Animated.View, { style: [
@@ -300,6 +315,15 @@ const ProfilePictureCapture = ({ onComplete, onError, onCancel, theme, language,
300
315
  }
301
316
  // Wait for initialization and challenge loading before showing guide or VideoRecorder
302
317
  if (!isInitialized || (isUsingBackend && isLoadingChallenges)) {
318
+ if (loadingTimedOut) {
319
+ return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
320
+ react_1.default.createElement(react_native_1.View, { style: styles.loadingContainer },
321
+ react_1.default.createElement(react_native_1.Text, { style: { fontSize: 48, marginBottom: 16 } }, "\u23F3"),
322
+ react_1.default.createElement(react_native_1.Text, { style: [styles.loadingText, { color: theme?.textColor || '#1e1b4b', fontSize: 20, fontWeight: 'bold' }] }, strings.errors.systemBusy),
323
+ react_1.default.createElement(react_native_1.Text, { style: { fontSize: 15, color: theme?.secondaryTextColor || '#64748b', textAlign: 'center', marginTop: 12, paddingHorizontal: 24, lineHeight: 22 } }, strings.errors.systemBusyMessage),
324
+ onCancel && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: { backgroundColor: theme?.primaryColor || '#4f46e5', paddingVertical: 14, paddingHorizontal: 32, borderRadius: 12, marginTop: 32 }, onPress: onCancel },
325
+ react_1.default.createElement(react_native_1.Text, { style: { color: '#FFFFFF', fontSize: 16, fontWeight: '600' } }, strings.common?.back || 'Go back'))))));
326
+ }
303
327
  return (react_1.default.createElement(react_native_1.SafeAreaView, { style: [styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }] },
304
328
  react_1.default.createElement(react_native_1.View, { style: styles.loadingContainer },
305
329
  react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: theme?.primaryColor || '#4f46e5' }),
@@ -348,6 +372,12 @@ const styles = react_native_1.StyleSheet.create({
348
372
  },
349
373
  progressStatusText: {
350
374
  fontSize: 16,
375
+ marginBottom: 8,
376
+ textAlign: 'center',
377
+ },
378
+ progressDoNotLockText: {
379
+ fontSize: 14,
380
+ fontWeight: '600',
351
381
  marginBottom: 32,
352
382
  textAlign: 'center',
353
383
  },
@@ -55,10 +55,10 @@ const ValidationProgress = ({ progress, theme, language = 'en', }) => {
55
55
  // Start animation from 0 to 90% over 60 seconds (1 minute) - only once
56
56
  if (!hasStartedAnimation.current) {
57
57
  hasStartedAnimation.current = true;
58
- // Start animation to 90% over 70 seconds, regardless of backend progress
58
+ // Start animation to 90% over 35 seconds, regardless of backend progress
59
59
  animationRef.current = react_native_1.Animated.timing(animatedProgress, {
60
60
  toValue: 90,
61
- duration: 70000, // 70 seconds
61
+ duration: 35000, // 35 seconds
62
62
  useNativeDriver: false,
63
63
  });
64
64
  animationRef.current.start();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.8.0",
3
+ "version": "1.10.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.4.0",
14
+ "@hexar/biometric-identity-sdk-core": ">=1.7.0",
15
15
  "react": ">=18.0.0",
16
16
  "react-native": ">=0.70.0",
17
17
  "react-native-permissions": ">=4.0.0",
@@ -6,6 +6,7 @@ import {
6
6
  ActivityIndicator,
7
7
  SafeAreaView,
8
8
  Animated,
9
+ TouchableOpacity,
9
10
  } from 'react-native';
10
11
  import { VideoRecorder, VideoRecordingResult } from './VideoRecorder';
11
12
  import { FacePositioningGuide } from './FacePositioningGuide';
@@ -47,6 +48,7 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
47
48
  const [isValidating, setIsValidating] = useState(false);
48
49
  const [currentChallenges, setCurrentChallenges] = useState<any[]>([]);
49
50
  const [isLoadingChallenges, setIsLoadingChallenges] = useState(false);
51
+ const [loadingTimedOut, setLoadingTimedOut] = useState(false);
50
52
  const animatedProgress = useRef(new Animated.Value(0)).current;
51
53
  const [displayProgress, setDisplayProgress] = useState(0);
52
54
  const animationRef = useRef<Animated.CompositeAnimation | null>(null);
@@ -62,6 +64,19 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
62
64
  }
63
65
  }, [language]);
64
66
 
67
+ // 15-second timeout on initial loading (challenge fetch / SDK init)
68
+ useEffect(() => {
69
+ const isLoading = !isInitialized || (isUsingBackend && isLoadingChallenges);
70
+ if (isLoading) {
71
+ const timer = setTimeout(() => {
72
+ setLoadingTimedOut(true);
73
+ }, 15000);
74
+ return () => clearTimeout(timer);
75
+ } else {
76
+ setLoadingTimedOut(false);
77
+ }
78
+ }, [isInitialized, isUsingBackend, isLoadingChallenges]);
79
+
65
80
  useEffect(() => {
66
81
  if (isInitialized && isUsingBackend) {
67
82
  const loadChallenges = async () => {
@@ -163,10 +178,10 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
163
178
  animatedProgress.setValue(0);
164
179
  setDisplayProgress(0);
165
180
 
166
- // Start animation to 90% over 60 seconds
181
+ // Start animation to 90% over 25 seconds
167
182
  animationRef.current = Animated.timing(animatedProgress, {
168
183
  toValue: 90,
169
- duration: 60000, // 60 seconds
184
+ duration: 25000, // 25 seconds
170
185
  useNativeDriver: false,
171
186
  });
172
187
 
@@ -315,6 +330,10 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
315
330
  {(strings.validation as any).validatingFace || strings.liveness.processing || 'Validando rostro...'}
316
331
  </Text>
317
332
 
333
+ <Text style={[styles.progressDoNotLockText, { color: theme?.secondaryTextColor || '#64748b' }]}>
334
+ {strings.validation.doNotLockScreen || 'No bloquees la pantalla'}
335
+ </Text>
336
+
318
337
  {/* Progress Bar */}
319
338
  <View style={styles.progressBarContainer}>
320
339
  <View style={styles.progressBar}>
@@ -343,6 +362,31 @@ export const ProfilePictureCapture: React.FC<ProfilePictureCaptureProps> = ({
343
362
 
344
363
  // Wait for initialization and challenge loading before showing guide or VideoRecorder
345
364
  if (!isInitialized || (isUsingBackend && isLoadingChallenges)) {
365
+ if (loadingTimedOut) {
366
+ return (
367
+ <SafeAreaView style={[styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }]}>
368
+ <View style={styles.loadingContainer}>
369
+ <Text style={{ fontSize: 48, marginBottom: 16 }}>⏳</Text>
370
+ <Text style={[styles.loadingText, { color: theme?.textColor || '#1e1b4b', fontSize: 20, fontWeight: 'bold' }]}>
371
+ {strings.errors.systemBusy}
372
+ </Text>
373
+ <Text style={{ fontSize: 15, color: theme?.secondaryTextColor || '#64748b', textAlign: 'center', marginTop: 12, paddingHorizontal: 24, lineHeight: 22 }}>
374
+ {strings.errors.systemBusyMessage}
375
+ </Text>
376
+ {onCancel && (
377
+ <TouchableOpacity
378
+ style={{ backgroundColor: theme?.primaryColor || '#4f46e5', paddingVertical: 14, paddingHorizontal: 32, borderRadius: 12, marginTop: 32 }}
379
+ onPress={onCancel}
380
+ >
381
+ <Text style={{ color: '#FFFFFF', fontSize: 16, fontWeight: '600' }}>
382
+ {strings.common?.back || 'Go back'}
383
+ </Text>
384
+ </TouchableOpacity>
385
+ )}
386
+ </View>
387
+ </SafeAreaView>
388
+ );
389
+ }
346
390
  return (
347
391
  <SafeAreaView style={[styles.container, { backgroundColor: theme?.backgroundColor || '#FFFFFF' }]}>
348
392
  <View style={styles.loadingContainer}>
@@ -417,6 +461,12 @@ const styles = StyleSheet.create({
417
461
  },
418
462
  progressStatusText: {
419
463
  fontSize: 16,
464
+ marginBottom: 8,
465
+ textAlign: 'center',
466
+ },
467
+ progressDoNotLockText: {
468
+ fontSize: 14,
469
+ fontWeight: '600',
420
470
  marginBottom: 32,
421
471
  textAlign: 'center',
422
472
  },
@@ -40,10 +40,10 @@ export const ValidationProgress: React.FC<ValidationProgressProps> = ({
40
40
  if (!hasStartedAnimation.current) {
41
41
  hasStartedAnimation.current = true;
42
42
 
43
- // Start animation to 90% over 70 seconds, regardless of backend progress
43
+ // Start animation to 90% over 35 seconds, regardless of backend progress
44
44
  animationRef.current = Animated.timing(animatedProgress, {
45
45
  toValue: 90,
46
- duration: 70000, // 70 seconds
46
+ duration: 35000, // 35 seconds
47
47
  useNativeDriver: false,
48
48
  });
49
49