@trustchex/react-native-sdk 1.362.4 → 1.374.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.
Files changed (32) hide show
  1. package/TrustchexSDK.podspec +3 -3
  2. package/android/build.gradle +3 -3
  3. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +71 -17
  4. package/ios/Camera/TrustchexCameraView.swift +166 -119
  5. package/lib/module/Shared/Components/FaceCamera.js +1 -0
  6. package/lib/module/Shared/Components/IdentityDocumentCamera.js +344 -207
  7. package/lib/module/Shared/Components/QrCodeScannerCamera.js +1 -8
  8. package/lib/module/Shared/Libs/mrz.utils.js +202 -9
  9. package/lib/module/Translation/Resources/en.js +0 -4
  10. package/lib/module/Translation/Resources/tr.js +0 -4
  11. package/lib/module/version.js +1 -1
  12. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  13. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  14. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  15. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +1 -0
  16. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
  17. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +8 -0
  18. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  19. package/lib/typescript/src/Translation/Resources/en.d.ts +0 -4
  20. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  21. package/lib/typescript/src/Translation/Resources/tr.d.ts +0 -4
  22. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  23. package/lib/typescript/src/version.d.ts +1 -1
  24. package/package.json +1 -1
  25. package/src/Shared/Components/FaceCamera.tsx +1 -0
  26. package/src/Shared/Components/IdentityDocumentCamera.tsx +443 -265
  27. package/src/Shared/Components/QrCodeScannerCamera.tsx +1 -9
  28. package/src/Shared/Components/TrustchexCamera.tsx +1 -0
  29. package/src/Shared/Libs/mrz.utils.ts +238 -26
  30. package/src/Translation/Resources/en.ts +0 -4
  31. package/src/Translation/Resources/tr.ts +0 -4
  32. package/src/version.ts +1 -1
@@ -13,6 +13,7 @@ import {
13
13
  PermissionsAndroid,
14
14
  Dimensions,
15
15
  ScrollView,
16
+ Animated,
16
17
  type NativeSyntheticEvent,
17
18
  type ViewStyle,
18
19
  } from 'react-native';
@@ -38,6 +39,8 @@ import { useTheme } from '../Contexts/ThemeContext';
38
39
 
39
40
  const { OpenCVModule } = NativeModules;
40
41
 
42
+ const AnimatedText = Animated.createAnimatedComponent(TextView);
43
+
41
44
  export type DocumentScannedData = {
42
45
  documentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
43
46
  image: string;
@@ -125,7 +128,6 @@ interface Barcode {
125
128
  const HOLOGRAM_IMAGE_COUNT = 12;
126
129
  const HOLOGRAM_DETECTION_THRESHOLD = 1000; // Lowered for better sensitivity while still filtering noise
127
130
  const HOLOGRAM_DETECTION_RETRY_COUNT = 3; // More attempts but faster each (~1s per attempt)
128
- const SECOND_FACE_DETECTION_RETRY_COUNT = 10;
129
131
  const MIN_BRIGHTNESS_THRESHOLD = 45;
130
132
  const MAX_CONSECUTIVE_QUALITY_FAILURES = 30;
131
133
 
@@ -206,6 +208,9 @@ const IdentityDocumentCamera = ({
206
208
  // Barcode caching - persist detected barcode across frames for reliability
207
209
  const cachedBarcode = useRef<Barcode | null>(null);
208
210
 
211
+ // Error message flash animation
212
+ const errorFlashAnim = useRef(new Animated.Value(1)).current;
213
+
209
214
  // Test mode tracking
210
215
  const [testModeData, setTestModeData] = useState<{
211
216
  mrzText: string;
@@ -451,25 +456,16 @@ const IdentityDocumentCamera = ({
451
456
  // Generate message - match UI display logic exactly for consistency
452
457
  let message = '';
453
458
 
454
- if (status === 'SCANNED') {
455
- message =
456
- completedStep === 'SCAN_ID_FRONT_OR_PASSPORT'
457
- ? detectedDocumentType === 'PASSPORT'
458
- ? t('identityDocumentCamera.passportScanned')
459
- : t('identityDocumentCamera.frontSideScanned')
460
- : completedStep === 'SCAN_ID_BACK'
461
- ? t('identityDocumentCamera.backSideScanned')
462
- : completedStep === 'SCAN_HOLOGRAM'
463
- ? t('identityDocumentCamera.hologramVerified')
464
- : t('identityDocumentCamera.scanCompleted');
459
+ if (nextStep === 'COMPLETED') {
460
+ message = t('identityDocumentCamera.scanCompleted');
465
461
  } else if (status === 'INCORRECT') {
466
462
  message =
467
463
  nextStep === 'SCAN_ID_FRONT_OR_PASSPORT'
468
- ? t('identityDocumentCamera.wrongSideFront')
464
+ ? t('identityDocumentCamera.alignIDFront')
469
465
  : nextStep === 'SCAN_ID_BACK'
470
- ? t('identityDocumentCamera.wrongSideBack')
466
+ ? t('identityDocumentCamera.alignIDBack')
471
467
  : nextStep === 'SCAN_HOLOGRAM'
472
- ? t('identityDocumentCamera.wrongSideFront')
468
+ ? t('identityDocumentCamera.alignIDFront')
473
469
  : t('identityDocumentCamera.alignPhotoSide');
474
470
  } else if (isBrightnessLow) {
475
471
  message = t('identityDocumentCamera.lowBrightness');
@@ -567,6 +563,27 @@ const IdentityDocumentCamera = ({
567
563
  }
568
564
  }, [nextStep]);
569
565
 
566
+ // Error flash animation - flash red text when wrong side detected
567
+ useEffect(() => {
568
+ if (status === 'INCORRECT') {
569
+ errorFlashAnim.setValue(1);
570
+ Animated.loop(
571
+ Animated.sequence([
572
+ Animated.timing(errorFlashAnim, {
573
+ toValue: 0.3,
574
+ duration: 300,
575
+ useNativeDriver: false,
576
+ }),
577
+ Animated.timing(errorFlashAnim, {
578
+ toValue: 1,
579
+ duration: 300,
580
+ useNativeDriver: false,
581
+ }),
582
+ ])
583
+ ).start();
584
+ }
585
+ }, [status, errorFlashAnim]);
586
+
570
587
  // Native OpenCV: detect hologram from sequence of face images
571
588
  const detectHologramNative = useCallback(
572
589
  async (images: string[]): Promise<[string, string] | []> => {
@@ -1918,10 +1935,11 @@ const IdentityDocumentCamera = ({
1918
1935
  nextStep,
1919
1936
  frameDimensions,
1920
1937
  currentHologramImage,
1938
+ detectedDocumentType,
1921
1939
  currentFaceImage,
1940
+ testMode,
1922
1941
  hasRequiredMRZFields,
1923
1942
  areMRZFieldsEqual,
1924
- detectedDocumentType,
1925
1943
  onlyMRZScan,
1926
1944
  isTorchOn,
1927
1945
  setIsTorchOn,
@@ -1931,7 +1949,6 @@ const IdentityDocumentCamera = ({
1931
1949
  logMRZValidationFailure,
1932
1950
  currentSecondaryFaceImage,
1933
1951
  detectHologramNative,
1934
- mrzUtils,
1935
1952
  ]
1936
1953
  );
1937
1954
 
@@ -3188,14 +3205,17 @@ const IdentityDocumentCamera = ({
3188
3205
  </TextView>
3189
3206
  )}
3190
3207
 
3191
- <TextView
3208
+ <AnimatedText
3192
3209
  style={[
3193
3210
  styles.topZoneText,
3194
3211
  // Priority order for coloring (later styles override earlier ones)
3195
3212
  // 1. Success (green) - scan completed
3196
- status === 'SCANNED' && styles.topZoneTextSuccess,
3197
- // 2. Error (red) - wrong side
3213
+ nextStep === 'COMPLETED' && styles.topZoneTextSuccess,
3214
+ // 2. Error (red) - wrong side - with flash opacity
3198
3215
  status === 'INCORRECT' && styles.topZoneTextError,
3216
+ status === 'INCORRECT' && {
3217
+ opacity: errorFlashAnim,
3218
+ },
3199
3219
  // 3. Warning (yellow) - quality issues
3200
3220
  (isBrightnessLow || isFrameBlurry) && styles.topZoneTextWarning,
3201
3221
  // 4. Scanning (green) - all elements detected AND inside scan area
@@ -3208,23 +3228,15 @@ const IdentityDocumentCamera = ({
3208
3228
  // 5. Default (white) - aligning (not all detected OR elements outside scan area)
3209
3229
  ]}
3210
3230
  >
3211
- {status === 'SCANNED'
3212
- ? completedStep === 'SCAN_ID_FRONT_OR_PASSPORT'
3213
- ? detectedDocumentType === 'PASSPORT'
3214
- ? t('identityDocumentCamera.passportScanned')
3215
- : t('identityDocumentCamera.frontSideScanned')
3216
- : completedStep === 'SCAN_ID_BACK'
3217
- ? t('identityDocumentCamera.backSideScanned')
3218
- : completedStep === 'SCAN_HOLOGRAM'
3219
- ? t('identityDocumentCamera.hologramVerified')
3220
- : t('identityDocumentCamera.scanCompleted')
3231
+ {nextStep === 'COMPLETED'
3232
+ ? t('identityDocumentCamera.scanCompleted')
3221
3233
  : status === 'INCORRECT'
3222
3234
  ? nextStep === 'SCAN_ID_FRONT_OR_PASSPORT'
3223
- ? t('identityDocumentCamera.wrongSideFront')
3235
+ ? t('identityDocumentCamera.alignIDFront')
3224
3236
  : nextStep === 'SCAN_ID_BACK'
3225
- ? t('identityDocumentCamera.wrongSideBack')
3237
+ ? t('identityDocumentCamera.alignIDBack')
3226
3238
  : nextStep === 'SCAN_HOLOGRAM'
3227
- ? t('identityDocumentCamera.wrongSideFront') // Hologram is on front, show same message as front scan
3239
+ ? t('identityDocumentCamera.alignIDFront')
3228
3240
  : t('identityDocumentCamera.alignPhotoSide')
3229
3241
  : isBrightnessLow
3230
3242
  ? t('identityDocumentCamera.lowBrightness')
@@ -3274,200 +3286,17 @@ const IdentityDocumentCamera = ({
3274
3286
  : nextStep === 'COMPLETED'
3275
3287
  ? t('identityDocumentCamera.scanCompleted')
3276
3288
  : ''}
3277
- </TextView>
3289
+ </AnimatedText>
3278
3290
  </View>
3279
3291
  <View style={styles.leftZone} />
3280
3292
  <View style={styles.rightZone} />
3281
- <View style={styles.bottomZone}>
3282
- <View style={styles.debugImagesRow}>
3283
- {isDebugEnabled() && (
3284
- <View style={styles.imageContainer}>
3285
- {currentFaceImage ? (
3286
- <Image
3287
- source={{
3288
- uri: `data:image/jpeg;base64,${currentFaceImage}`,
3289
- }}
3290
- style={styles.faceImage}
3291
- />
3292
- ) : (
3293
- <View
3294
- style={[
3295
- styles.faceImage,
3296
- { backgroundColor: '#333', justifyContent: 'center' },
3297
- ]}
3298
- >
3299
- <TextView
3300
- style={{
3301
- color: '#666',
3302
- fontSize: 10,
3303
- textAlign: 'center',
3304
- }}
3305
- >
3306
- Waiting...
3307
- </TextView>
3308
- </View>
3309
- )}
3310
- <TextView
3311
- style={[
3312
- styles.imageContainerText,
3313
- currentFaceImage && { color: '#4CAF50' },
3314
- ]}
3315
- >
3316
- {`${currentFaceImage ? '✓ ' : ''}Face`}
3317
- </TextView>
3318
- </View>
3319
- )}
3320
- {isDebugEnabled() && (
3321
- <View style={styles.imageContainer}>
3322
- {currentSecondaryFaceImage ? (
3323
- <Image
3324
- source={{
3325
- uri: `data:image/jpeg;base64,${currentSecondaryFaceImage}`,
3326
- }}
3327
- style={styles.faceImage}
3328
- />
3329
- ) : (
3330
- <View
3331
- style={[
3332
- styles.faceImage,
3333
- { backgroundColor: '#333', justifyContent: 'center' },
3334
- ]}
3335
- >
3336
- <TextView
3337
- style={{
3338
- color: '#666',
3339
- fontSize: 10,
3340
- textAlign: 'center',
3341
- }}
3342
- >
3343
- Waiting...
3344
- </TextView>
3345
- </View>
3346
- )}
3347
- <TextView
3348
- style={[
3349
- styles.imageContainerText,
3350
- currentSecondaryFaceImage && { color: '#4CAF50' },
3351
- ]}
3352
- >
3353
- {`${currentSecondaryFaceImage ? '✓ ' : ''}2nd Face`}
3354
- </TextView>
3355
- </View>
3356
- )}
3357
- {isDebugEnabled() && (
3358
- <View style={styles.imageContainer}>
3359
- {currentHologramImage ? (
3360
- <Image
3361
- source={{
3362
- uri: `data:image/jpeg;base64,${currentHologramImage}`,
3363
- }}
3364
- style={styles.faceImage}
3365
- />
3366
- ) : latestHologramFaceImage && hologramImageCount > 0 ? (
3367
- <View style={{ position: 'relative' }}>
3368
- <Image
3369
- source={{
3370
- uri: `data:image/jpeg;base64,${latestHologramFaceImage}`,
3371
- }}
3372
- style={[styles.faceImage, { opacity: 0.7 }]}
3373
- />
3374
- <View
3375
- style={{
3376
- position: 'absolute',
3377
- bottom: 0,
3378
- left: 0,
3379
- right: 0,
3380
- backgroundColor: 'rgba(0,0,0,0.7)',
3381
- padding: 2,
3382
- }}
3383
- >
3384
- <TextView
3385
- style={{
3386
- color: '#FFA500',
3387
- fontSize: 8,
3388
- textAlign: 'center',
3389
- fontWeight: 'bold',
3390
- }}
3391
- >
3392
- {hologramImageCount}/{HOLOGRAM_IMAGE_COUNT}
3393
- </TextView>
3394
- </View>
3395
- </View>
3396
- ) : (
3397
- <View
3398
- style={[
3399
- styles.faceImage,
3400
- { backgroundColor: '#333', justifyContent: 'center' },
3401
- ]}
3402
- >
3403
- <TextView
3404
- style={{
3405
- color: '#666',
3406
- fontSize: 10,
3407
- textAlign: 'center',
3408
- }}
3409
- >
3410
- Waiting...
3411
- </TextView>
3412
- </View>
3413
- )}
3414
- <TextView
3415
- style={[
3416
- styles.imageContainerText,
3417
- currentHologramImage && { color: '#4CAF50' },
3418
- latestHologramFaceImage &&
3419
- !currentHologramImage && { color: '#FFA500' },
3420
- ]}
3421
- >
3422
- {`${currentHologramImage ? '✓ ' : latestHologramFaceImage ? '⏳ ' : ''}Hologram`}
3423
- </TextView>
3424
- </View>
3425
- )}
3426
- {isDebugEnabled() && (
3427
- <View style={styles.imageContainer}>
3428
- {_currentHologramMaskImage ? (
3429
- <Image
3430
- source={{
3431
- uri: `data:image/jpeg;base64,${_currentHologramMaskImage}`,
3432
- }}
3433
- style={styles.faceImage}
3434
- />
3435
- ) : (
3436
- <View
3437
- style={[
3438
- styles.faceImage,
3439
- { backgroundColor: '#333', justifyContent: 'center' },
3440
- ]}
3441
- >
3442
- <TextView
3443
- style={{
3444
- color: '#666',
3445
- fontSize: 10,
3446
- textAlign: 'center',
3447
- }}
3448
- >
3449
- Waiting...
3450
- </TextView>
3451
- </View>
3452
- )}
3453
- <TextView
3454
- style={[
3455
- styles.imageContainerText,
3456
- _currentHologramMaskImage && { color: '#4CAF50' },
3457
- ]}
3458
- >
3459
- {`${_currentHologramMaskImage ? '✓ ' : ''}Mask`}
3460
- </TextView>
3461
- </View>
3462
- )}
3463
- </View>
3464
- </View>
3293
+ <View style={styles.bottomZone} />
3465
3294
  <View
3466
3295
  style={[
3467
3296
  styles.scanArea,
3468
3297
  {
3469
3298
  borderColor:
3470
- status === 'SCANNED' || nextStep === 'COMPLETED'
3299
+ nextStep === 'COMPLETED'
3471
3300
  ? '#4CAF50'
3472
3301
  : status === 'INCORRECT'
3473
3302
  ? '#f44336'
@@ -3480,7 +3309,7 @@ const IdentityDocumentCamera = ({
3480
3309
  },
3481
3310
  ]}
3482
3311
  >
3483
- {nextStep === 'COMPLETED' || status === 'SCANNED' ? (
3312
+ {nextStep === 'COMPLETED' ? (
3484
3313
  <LottieView
3485
3314
  source={require('../../Shared/Animations/success.json')}
3486
3315
  style={styles.animation}
@@ -3501,13 +3330,6 @@ const IdentityDocumentCamera = ({
3501
3330
  loop={true}
3502
3331
  autoPlay
3503
3332
  />
3504
- ) : status === 'SCANNING' ? (
3505
- <LottieView
3506
- source={require('../../Shared/Animations/scanning.json')}
3507
- style={styles.animation}
3508
- loop={true}
3509
- autoPlay
3510
- />
3511
3333
  ) : null}
3512
3334
  </View>
3513
3335
  {isDebugEnabled() && (
@@ -3523,59 +3345,415 @@ const IdentityDocumentCamera = ({
3523
3345
  >
3524
3346
  <View
3525
3347
  style={{
3526
- marginTop: 10,
3527
- backgroundColor: 'rgba(0, 0, 0, 0.85)',
3528
- padding: 10,
3529
- borderRadius: 8,
3348
+ marginTop: 8,
3349
+ marginHorizontal: 8,
3350
+ backgroundColor: 'rgba(0, 0, 0, 0.9)',
3351
+ padding: 8,
3352
+ borderRadius: 6,
3530
3353
  borderWidth: 1,
3531
- borderColor: '#FF6B6B',
3532
- minWidth: 200,
3354
+ borderColor: 'rgba(255, 82, 82, 0.5)',
3355
+ minWidth: 220,
3356
+ maxWidth: '92%',
3533
3357
  }}
3534
3358
  >
3535
- <TextView
3359
+ {/* Status & State Row */}
3360
+ <View
3536
3361
  style={{
3537
- color: '#FF6B6B',
3538
- fontSize: 11,
3539
- fontWeight: 'bold',
3540
- marginBottom: 6,
3541
- textAlign: 'center',
3362
+ flexDirection: 'row',
3363
+ justifyContent: 'space-between',
3364
+ marginBottom: 4,
3542
3365
  }}
3543
3366
  >
3544
- DEBUG MODE
3545
- </TextView>
3546
- <TextView
3547
- style={{ color: '#88D8B0', fontSize: 9, marginBottom: 2 }}
3548
- >
3549
- {`Step: ${nextStep}`}
3550
- </TextView>
3551
- <TextView
3552
- style={{ color: '#88D8B0', fontSize: 9, marginBottom: 2 }}
3367
+ <TextView
3368
+ style={{
3369
+ color: '#88D8B0',
3370
+ fontSize: 10,
3371
+ fontWeight: 'bold',
3372
+ }}
3373
+ >
3374
+ {`Status: ${status}`}
3375
+ </TextView>
3376
+ <TextView
3377
+ style={{
3378
+ color: '#FFEB3B',
3379
+ fontSize: 10,
3380
+ fontWeight: 'bold',
3381
+ }}
3382
+ >
3383
+ {`Step: ${nextStep}`}
3384
+ </TextView>
3385
+ </View>
3386
+
3387
+ {/* Quality Info - Badges */}
3388
+ <View
3389
+ style={{
3390
+ flexDirection: 'row',
3391
+ justifyContent: 'space-between',
3392
+ marginBottom: 4,
3393
+ gap: 3,
3394
+ }}
3553
3395
  >
3554
- {`Status: ${status}`}
3555
- </TextView>
3396
+ <View
3397
+ style={{
3398
+ backgroundColor: isBrightnessLow ? '#f44336' : '#4CAF50',
3399
+ paddingHorizontal: 6,
3400
+ paddingVertical: 1,
3401
+ borderRadius: 3,
3402
+ }}
3403
+ >
3404
+ <TextView
3405
+ style={{
3406
+ color: '#FFFFFF',
3407
+ fontSize: 8,
3408
+ fontWeight: 'bold',
3409
+ }}
3410
+ >
3411
+ {isBrightnessLow ? 'Low Light' : 'Good Light'}
3412
+ </TextView>
3413
+ </View>
3414
+ <View
3415
+ style={{
3416
+ backgroundColor: isFrameBlurry ? '#f44336' : '#4CAF50',
3417
+ paddingHorizontal: 6,
3418
+ paddingVertical: 1,
3419
+ borderRadius: 3,
3420
+ }}
3421
+ >
3422
+ <TextView
3423
+ style={{
3424
+ color: '#FFFFFF',
3425
+ fontSize: 8,
3426
+ fontWeight: 'bold',
3427
+ }}
3428
+ >
3429
+ {isFrameBlurry ? 'Blurry' : 'Sharp'}
3430
+ </TextView>
3431
+ </View>
3432
+ </View>
3433
+
3434
+ {/* Elements Status */}
3556
3435
  <TextView
3557
- style={{ color: '#88D8B0', fontSize: 9, marginBottom: 2 }}
3436
+ style={{
3437
+ color:
3438
+ elementsOutsideScanArea.length === 0
3439
+ ? '#4CAF50'
3440
+ : '#FFA500',
3441
+ fontSize: 8,
3442
+ marginBottom: 3,
3443
+ }}
3558
3444
  >
3559
- {`Doc Type: ${detectedDocumentType}`}
3445
+ {elementsOutsideScanArea.length === 0
3446
+ ? 'All elements in frame'
3447
+ : `Outside: ${elementsOutsideScanArea.join(', ')}`}
3560
3448
  </TextView>
3449
+
3450
+ {/* Detection Info */}
3561
3451
  <TextView
3562
- style={{ color: '#88D8B0', fontSize: 9, marginBottom: 2 }}
3452
+ style={{
3453
+ color: 'rgba(255, 255, 255, 0.7)',
3454
+ fontSize: 8,
3455
+ marginBottom: 3,
3456
+ }}
3563
3457
  >
3564
- {`Brightness: ${isBrightnessLow ? '⚠️ LOW' : '✓'}`}
3458
+ {`Face: ${documentPlaneBounds ? '✓' : '✗'}, MRZ: ${mrzBounds ? '✓' : '✗'}, Sig: ${signatureBounds ? '✓' : '✗'}`}
3565
3459
  </TextView>
3566
- <TextView
3567
- style={{ color: '#88D8B0', fontSize: 9, marginBottom: 2 }}
3460
+
3461
+ {/* Hologram Count */}
3462
+ {nextStep === 'SCAN_HOLOGRAM' && hologramImageCount > 0 && (
3463
+ <TextView
3464
+ style={{
3465
+ color: '#9C27B0',
3466
+ fontSize: 8,
3467
+ fontWeight: 'bold',
3468
+ marginBottom: 3,
3469
+ }}
3470
+ >
3471
+ {`Hologram: ${hologramImageCount}/${HOLOGRAM_IMAGE_COUNT}`}
3472
+ </TextView>
3473
+ )}
3474
+
3475
+ {/* Additional Info Row */}
3476
+ <View
3477
+ style={{
3478
+ flexDirection: 'row',
3479
+ gap: 8,
3480
+ paddingTop: 3,
3481
+ borderTopWidth: 1,
3482
+ borderTopColor: 'rgba(255, 255, 255, 0.2)',
3483
+ marginBottom: 4,
3484
+ }}
3568
3485
  >
3569
- {`Blur: ${isFrameBlurry ? '⚠️' : '✓'}`}
3570
- </TextView>
3571
- <TextView
3572
- style={{ color: '#88D8B0', fontSize: 9, marginBottom: 2 }}
3486
+ <TextView
3487
+ style={{
3488
+ color: 'rgba(255, 255, 255, 0.7)',
3489
+ fontSize: 8,
3490
+ marginTop: 3,
3491
+ }}
3492
+ >
3493
+ {`Doc: ${detectedDocumentType}`}
3494
+ </TextView>
3495
+ <TextView
3496
+ style={{
3497
+ color: 'rgba(255, 255, 255, 0.7)',
3498
+ fontSize: 8,
3499
+ marginTop: 3,
3500
+ }}
3501
+ >
3502
+ {`Flash: ${isTorchOn ? '🔦' : '○'}`}
3503
+ </TextView>
3504
+ </View>
3505
+
3506
+ {/* Debug Images Grid - Compact */}
3507
+ <View
3508
+ style={{
3509
+ flexDirection: 'row',
3510
+ flexWrap: 'wrap',
3511
+ gap: 4,
3512
+ justifyContent: 'center',
3513
+ }}
3573
3514
  >
3574
- {`Flash: ${isTorchOn ? '🔦' : '○'}`}
3575
- </TextView>
3576
- <TextView style={{ color: '#88D8B0', fontSize: 9 }}>
3577
- {`Face Detection: ${faceDetectionEnabled ? '✓' : '✗'}`}
3578
- </TextView>
3515
+ {/* Face Image */}
3516
+ <View style={{ alignItems: 'center' }}>
3517
+ {currentFaceImage ? (
3518
+ <Image
3519
+ source={{
3520
+ uri: `data:image/jpeg;base64,${currentFaceImage}`,
3521
+ }}
3522
+ style={{
3523
+ width: 40,
3524
+ height: 48,
3525
+ borderRadius: 2,
3526
+ borderWidth: 1,
3527
+ borderColor: '#4CAF50',
3528
+ }}
3529
+ />
3530
+ ) : (
3531
+ <View
3532
+ style={{
3533
+ width: 40,
3534
+ height: 48,
3535
+ borderRadius: 2,
3536
+ borderWidth: 1,
3537
+ borderColor: '#666',
3538
+ backgroundColor: '#222',
3539
+ justifyContent: 'center',
3540
+ alignItems: 'center',
3541
+ }}
3542
+ >
3543
+ <TextView
3544
+ style={{
3545
+ color: '#666',
3546
+ fontSize: 7,
3547
+ }}
3548
+ >
3549
+
3550
+ </TextView>
3551
+ </View>
3552
+ )}
3553
+ <TextView
3554
+ style={{
3555
+ color: currentFaceImage ? '#4CAF50' : '#999',
3556
+ fontSize: 7,
3557
+ marginTop: 1,
3558
+ fontWeight: 'bold',
3559
+ }}
3560
+ >
3561
+ {`${currentFaceImage ? '✓' : '○'} Face`}
3562
+ </TextView>
3563
+ </View>
3564
+
3565
+ {/* Secondary Face Image */}
3566
+ <View style={{ alignItems: 'center' }}>
3567
+ {currentSecondaryFaceImage ? (
3568
+ <Image
3569
+ source={{
3570
+ uri: `data:image/jpeg;base64,${currentSecondaryFaceImage}`,
3571
+ }}
3572
+ style={{
3573
+ width: 40,
3574
+ height: 48,
3575
+ borderRadius: 2,
3576
+ borderWidth: 1,
3577
+ borderColor: '#2196F3',
3578
+ }}
3579
+ />
3580
+ ) : (
3581
+ <View
3582
+ style={{
3583
+ width: 40,
3584
+ height: 48,
3585
+ borderRadius: 2,
3586
+ borderWidth: 1,
3587
+ borderColor: '#666',
3588
+ backgroundColor: '#222',
3589
+ justifyContent: 'center',
3590
+ alignItems: 'center',
3591
+ }}
3592
+ >
3593
+ <TextView
3594
+ style={{
3595
+ color: '#666',
3596
+ fontSize: 7,
3597
+ }}
3598
+ >
3599
+
3600
+ </TextView>
3601
+ </View>
3602
+ )}
3603
+ <TextView
3604
+ style={{
3605
+ color: currentSecondaryFaceImage ? '#2196F3' : '#999',
3606
+ fontSize: 7,
3607
+ marginTop: 1,
3608
+ fontWeight: 'bold',
3609
+ }}
3610
+ >
3611
+ {`${currentSecondaryFaceImage ? '✓' : '○'} 2nd`}
3612
+ </TextView>
3613
+ </View>
3614
+
3615
+ {/* Hologram Image */}
3616
+ <View style={{ alignItems: 'center' }}>
3617
+ {currentHologramImage ? (
3618
+ <Image
3619
+ source={{
3620
+ uri: `data:image/jpeg;base64,${currentHologramImage}`,
3621
+ }}
3622
+ style={{
3623
+ width: 40,
3624
+ height: 48,
3625
+ borderRadius: 2,
3626
+ borderWidth: 1,
3627
+ borderColor: '#9C27B0',
3628
+ }}
3629
+ />
3630
+ ) : latestHologramFaceImage && hologramImageCount > 0 ? (
3631
+ <View style={{ position: 'relative' }}>
3632
+ <Image
3633
+ source={{
3634
+ uri: `data:image/jpeg;base64,${latestHologramFaceImage}`,
3635
+ }}
3636
+ style={{
3637
+ width: 40,
3638
+ height: 48,
3639
+ borderRadius: 2,
3640
+ borderWidth: 1,
3641
+ borderColor: '#FFA500',
3642
+ opacity: 0.7,
3643
+ }}
3644
+ />
3645
+ <View
3646
+ style={{
3647
+ position: 'absolute',
3648
+ bottom: 1,
3649
+ left: 1,
3650
+ right: 1,
3651
+ backgroundColor: 'rgba(0,0,0,0.8)',
3652
+ paddingVertical: 0,
3653
+ borderRadius: 1,
3654
+ }}
3655
+ >
3656
+ <TextView
3657
+ style={{
3658
+ color: '#FFA500',
3659
+ fontSize: 6,
3660
+ textAlign: 'center',
3661
+ fontWeight: 'bold',
3662
+ }}
3663
+ >
3664
+ {hologramImageCount}/{HOLOGRAM_IMAGE_COUNT}
3665
+ </TextView>
3666
+ </View>
3667
+ </View>
3668
+ ) : (
3669
+ <View
3670
+ style={{
3671
+ width: 40,
3672
+ height: 48,
3673
+ borderRadius: 2,
3674
+ borderWidth: 1,
3675
+ borderColor: '#666',
3676
+ backgroundColor: '#222',
3677
+ justifyContent: 'center',
3678
+ alignItems: 'center',
3679
+ }}
3680
+ >
3681
+ <TextView
3682
+ style={{
3683
+ color: '#666',
3684
+ fontSize: 7,
3685
+ }}
3686
+ >
3687
+
3688
+ </TextView>
3689
+ </View>
3690
+ )}
3691
+ <TextView
3692
+ style={{
3693
+ color: currentHologramImage
3694
+ ? '#9C27B0'
3695
+ : latestHologramFaceImage
3696
+ ? '#FFA500'
3697
+ : '#999',
3698
+ fontSize: 7,
3699
+ marginTop: 1,
3700
+ fontWeight: 'bold',
3701
+ }}
3702
+ >
3703
+ {`${currentHologramImage ? '✓' : latestHologramFaceImage ? '⏳' : '○'} Holo`}
3704
+ </TextView>
3705
+ </View>
3706
+
3707
+ {/* Hologram Mask Image */}
3708
+ <View style={{ alignItems: 'center' }}>
3709
+ {_currentHologramMaskImage ? (
3710
+ <Image
3711
+ source={{
3712
+ uri: `data:image/jpeg;base64,${_currentHologramMaskImage}`,
3713
+ }}
3714
+ style={{
3715
+ width: 40,
3716
+ height: 48,
3717
+ borderRadius: 2,
3718
+ borderWidth: 1,
3719
+ borderColor: '#FF6B6B',
3720
+ }}
3721
+ />
3722
+ ) : (
3723
+ <View
3724
+ style={{
3725
+ width: 40,
3726
+ height: 48,
3727
+ borderRadius: 2,
3728
+ borderWidth: 1,
3729
+ borderColor: '#666',
3730
+ backgroundColor: '#222',
3731
+ justifyContent: 'center',
3732
+ alignItems: 'center',
3733
+ }}
3734
+ >
3735
+ <TextView
3736
+ style={{
3737
+ color: '#666',
3738
+ fontSize: 7,
3739
+ }}
3740
+ >
3741
+
3742
+ </TextView>
3743
+ </View>
3744
+ )}
3745
+ <TextView
3746
+ style={{
3747
+ color: _currentHologramMaskImage ? '#FF6B6B' : '#999',
3748
+ fontSize: 7,
3749
+ marginTop: 1,
3750
+ fontWeight: 'bold',
3751
+ }}
3752
+ >
3753
+ {`${_currentHologramMaskImage ? '✓' : '○'} Mask`}
3754
+ </TextView>
3755
+ </View>
3756
+ </View>
3579
3757
  </View>
3580
3758
  </SafeAreaView>
3581
3759
  )}