@sanctum-key/react-native-sdk 1.0.6 → 1.0.7

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 (206) hide show
  1. package/README.md +3 -3
  2. package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/transformed/classes/classes_dex/classes.dex +0 -0
  3. package/android/build/generated/source/buildConfig/debug/kyc/{transfergratis → SanctumKey}/com/BuildConfig.java +2 -2
  4. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +1 -1
  5. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +1 -1
  6. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  7. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  8. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -2
  9. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/SanctumKey/com/BuildConfig.class +0 -0
  10. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +1 -1
  11. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +1 -1
  12. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  13. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -1
  14. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  15. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  16. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  17. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  18. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  19. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  20. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  21. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  22. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  23. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  24. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  25. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  48. package/android/build/outputs/logs/manifest-merger-debug-report.txt +1 -1
  49. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  50. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$1.class +0 -0
  51. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$2.class +0 -0
  52. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class} +0 -0
  53. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class} +0 -0
  54. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class} +0 -0
  55. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class} +0 -0
  56. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class} +0 -0
  57. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class} +0 -0
  58. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class} +0 -0
  59. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class} +0 -0
  60. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class} +0 -0
  61. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class} +0 -0
  62. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
  63. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class} +0 -0
  64. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class} +0 -0
  65. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule.class → SanctumKey/com/SanctumKeySdkModule.class} +0 -0
  66. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkView.class → SanctumKey/com/SanctumKeySdkView.class} +0 -0
  67. package/android/build.gradle +2 -2
  68. package/android/src/main/AndroidManifest.xml +1 -1
  69. package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkModule.kt +6 -6
  70. package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkView.kt +2 -2
  71. package/build/package.json +9 -7
  72. package/build/src/App.d.ts +2 -2
  73. package/build/src/App.d.ts.map +1 -1
  74. package/build/src/App.js +2 -2
  75. package/build/src/App.js.map +1 -1
  76. package/build/src/{TransfergratisSdk.types.d.ts → SanctumKeySdk.types.d.ts} +3 -3
  77. package/build/src/SanctumKeySdk.types.d.ts.map +1 -0
  78. package/build/src/SanctumKeySdk.types.js +2 -0
  79. package/build/src/SanctumKeySdk.types.js.map +1 -0
  80. package/build/src/{TransfergratisSdkModule.d.ts → SanctumKeySdkModule.d.ts} +4 -4
  81. package/build/src/SanctumKeySdkModule.d.ts.map +1 -0
  82. package/build/src/{TransfergratisSdkModule.js → SanctumKeySdkModule.js} +2 -2
  83. package/build/src/SanctumKeySdkModule.js.map +1 -0
  84. package/build/src/{TransfergratisSdkModule.web.d.ts → SanctumKeySdkModule.web.d.ts} +4 -4
  85. package/build/src/SanctumKeySdkModule.web.d.ts.map +1 -0
  86. package/build/src/{TransfergratisSdkModule.web.js → SanctumKeySdkModule.web.js} +3 -3
  87. package/build/src/SanctumKeySdkModule.web.js.map +1 -0
  88. package/build/src/SanctumKeySdkView.d.ts +4 -0
  89. package/build/src/SanctumKeySdkView.d.ts.map +1 -0
  90. package/build/src/SanctumKeySdkView.js +7 -0
  91. package/build/src/SanctumKeySdkView.js.map +1 -0
  92. package/build/src/SanctumKeySdkView.web.d.ts +4 -0
  93. package/build/src/SanctumKeySdkView.web.d.ts.map +1 -0
  94. package/build/src/{TransfergratisSdkView.web.js → SanctumKeySdkView.web.js} +2 -2
  95. package/build/src/SanctumKeySdkView.web.js.map +1 -0
  96. package/build/src/api/axios.js +2 -2
  97. package/build/src/api/axios.js.map +1 -1
  98. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  99. package/build/src/components/EnhancedCameraView.js +66 -338
  100. package/build/src/components/EnhancedCameraView.js.map +1 -1
  101. package/build/src/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
  102. package/build/src/components/KYCElements/EmailVerificationTemplate.js +93 -15
  103. package/build/src/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
  104. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  105. package/build/src/components/KYCElements/IDCardCapture.js +167 -695
  106. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  107. package/build/src/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -1
  108. package/build/src/components/KYCElements/PhoneVerificationTemplate.js +269 -40
  109. package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
  110. package/build/src/components/KYCElements/SelfieCapture.d.ts +1 -1
  111. package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  112. package/build/src/components/KYCElements/SelfieCapture.js +130 -192
  113. package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
  114. package/build/src/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  115. package/build/src/components/KYCElements/SelfieCaptureTemplate.js +131 -433
  116. package/build/src/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  117. package/build/src/components/NativeCameraView.js +1 -1
  118. package/build/src/components/NativeCameraView.js.map +1 -1
  119. package/build/src/components/OverLay/IdCard.d.ts +3 -2
  120. package/build/src/components/OverLay/IdCard.d.ts.map +1 -1
  121. package/build/src/components/OverLay/IdCard.js +149 -141
  122. package/build/src/components/OverLay/IdCard.js.map +1 -1
  123. package/build/src/components/OverLay/SelfieOverlay.d.ts +2 -1
  124. package/build/src/components/OverLay/SelfieOverlay.d.ts.map +1 -1
  125. package/build/src/components/OverLay/SelfieOverlay.js +37 -95
  126. package/build/src/components/OverLay/SelfieOverlay.js.map +1 -1
  127. package/build/src/components/OverLay/type.d.ts +1 -0
  128. package/build/src/components/OverLay/type.d.ts.map +1 -1
  129. package/build/src/components/OverLay/type.js.map +1 -1
  130. package/build/src/components/Svgs/scanningLine.d.ts +2 -1
  131. package/build/src/components/Svgs/scanningLine.d.ts.map +1 -1
  132. package/build/src/components/Svgs/scanningLine.js +55 -51
  133. package/build/src/components/Svgs/scanningLine.js.map +1 -1
  134. package/build/src/config/KYCConfig.js +1 -1
  135. package/build/src/config/KYCConfig.js.map +1 -1
  136. package/build/src/config/allowedDomains.js +6 -6
  137. package/build/src/config/allowedDomains.js.map +1 -1
  138. package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  139. package/build/src/hooks/useTemplateKYCFlow.js +37 -38
  140. package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
  141. package/build/src/index.d.ts +3 -3
  142. package/build/src/index.d.ts.map +1 -1
  143. package/build/src/index.js +3 -3
  144. package/build/src/index.js.map +1 -1
  145. package/build/src/modules/api/CardAuthentification.d.ts +0 -5
  146. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  147. package/build/src/modules/api/CardAuthentification.js +114 -116
  148. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  149. package/build/src/modules/api/KYCService.d.ts +11 -1
  150. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  151. package/build/src/modules/api/KYCService.js +101 -38
  152. package/build/src/modules/api/KYCService.js.map +1 -1
  153. package/build/src/modules/camera/NativeCameraModule.js +17 -17
  154. package/build/src/modules/camera/NativeCameraModule.js.map +1 -1
  155. package/expo-module.config.json +2 -2
  156. package/ios/TransfergratisSdk.podspec +2 -2
  157. package/ios/TransfergratisSdkModule.swift +12 -12
  158. package/package.json +9 -7
  159. package/src/App.tsx +2 -2
  160. package/src/{TransfergratisSdk.types.ts → SanctumKeySdk.types.ts} +2 -2
  161. package/src/{TransfergratisSdkModule.ts → SanctumKeySdkModule.ts} +3 -3
  162. package/src/{TransfergratisSdkModule.web.ts → SanctumKeySdkModule.web.ts} +3 -3
  163. package/src/SanctumKeySdkView.tsx +11 -0
  164. package/src/{TransfergratisSdkView.web.tsx → SanctumKeySdkView.web.tsx} +2 -2
  165. package/src/api/axios.ts +2 -2
  166. package/src/components/EnhancedCameraView.tsx +81 -400
  167. package/src/components/KYCElements/EmailVerificationTemplate.tsx +115 -26
  168. package/src/components/KYCElements/IDCardCapture.tsx +228 -868
  169. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +328 -60
  170. package/src/components/KYCElements/SelfieCapture.tsx +184 -213
  171. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +330 -662
  172. package/src/components/NativeCameraView.tsx +1 -1
  173. package/src/components/OverLay/IdCard.tsx +218 -217
  174. package/src/components/OverLay/SelfieOverlay.tsx +56 -134
  175. package/src/components/OverLay/type.ts +1 -0
  176. package/src/components/Svgs/scanningLine.tsx +71 -72
  177. package/src/config/KYCConfig.ts +1 -1
  178. package/src/config/allowedDomains.ts +6 -6
  179. package/src/hooks/useTemplateKYCFlow.tsx +45 -39
  180. package/src/i18n/README.md +1 -1
  181. package/src/index.ts +3 -3
  182. package/src/modules/api/CardAuthentification.ts +202 -200
  183. package/src/modules/api/KYCService.ts +168 -53
  184. package/src/modules/camera/NativeCameraModule.ts +17 -17
  185. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$1.class +0 -0
  186. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$2.class +0 -0
  187. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
  188. package/build/src/TransfergratisSdk.types.d.ts.map +0 -1
  189. package/build/src/TransfergratisSdk.types.js +0 -2
  190. package/build/src/TransfergratisSdk.types.js.map +0 -1
  191. package/build/src/TransfergratisSdkModule.d.ts.map +0 -1
  192. package/build/src/TransfergratisSdkModule.js.map +0 -1
  193. package/build/src/TransfergratisSdkModule.web.d.ts.map +0 -1
  194. package/build/src/TransfergratisSdkModule.web.js.map +0 -1
  195. package/build/src/TransfergratisSdkView.d.ts +0 -4
  196. package/build/src/TransfergratisSdkView.d.ts.map +0 -1
  197. package/build/src/TransfergratisSdkView.js +0 -7
  198. package/build/src/TransfergratisSdkView.js.map +0 -1
  199. package/build/src/TransfergratisSdkView.web.d.ts +0 -4
  200. package/build/src/TransfergratisSdkView.web.d.ts.map +0 -1
  201. package/build/src/TransfergratisSdkView.web.js.map +0 -1
  202. package/src/TransfergratisSdkView.tsx +0 -11
  203. /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/results.bin +0 -0
  204. /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/transformed/classes/classes_dex/classes.dex +0 -0
  205. /package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/results.bin +0 -0
  206. /package/android/build/{intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/transfergratis/com/BuildConfig.class → tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/BuildConfig.class.uniqueId0} +0 -0
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
- import { View, Text, StyleSheet, Image, ScrollView, Platform, Modal, TouchableOpacity, ActivityIndicator } from 'react-native';
2
+ import { View, Text, StyleSheet, Image, ScrollView, Platform, ActivityIndicator } from 'react-native';
3
3
  import { showAlert } from '../../utils/platformAlert';
4
4
  import { EnhancedCameraView } from '../EnhancedCameraView';
5
5
  import { GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
@@ -11,140 +11,71 @@ import { removeDuplicates } from '../../utils/remove-duplicate';
11
11
  import { backVerification, checkTemplateType, frontVerification } from '../../modules/api/CardAuthentification';
12
12
  import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
13
13
  import pathToBase64 from '../../utils/pathToBase64';
14
- import kycService, { truncateFields } from '../../modules/api/KYCService';
15
- import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance, getObbConfidence, OBB_CONFIDENCE_THRESHOLD } from '../../utils/cropByObb';
16
- import { isMobileWeb } from '../../utils/deviceDetection';
17
- import { logger } from '../../utils/logger';
18
- export const IDCardCapture = ({ component, value = {}, onValueChange, error, language = 'en', }) => {
14
+ import { cropByObb, cropImageWithBBoxWithTolerance, getObbConfidence, OBB_CONFIDENCE_THRESHOLD } from '../../utils/cropByObb';
15
+ export const IDCardCapture = ({ component, value = {}, onValueChange, error, language = 'en' }) => {
19
16
  const { t, locale } = useI18n();
20
17
  const [showCamera, setShowCamera] = useState(false);
21
18
  const [capturedImages, setCapturedImages] = useState(value || {});
22
- // const [cropImageUri, setCropImageUri] = useState<string>('');
23
19
  const [currentSide, setCurrentSide] = useState('front');
24
20
  const [bboxBySide, setBboxBySide] = useState({});
25
21
  const [silentCaptureResult, setSilentCaptureResult] = useState({ success: false, isAnalyzing: false });
26
- const [showQRModal, setShowQRModal] = useState(false);
27
- // Mapping des types de documents backend vers SDK
22
+ const [isProcessingCapture, setIsProcessingCapture] = useState(false);
23
+ const [processingImagePath, setProcessingImagePath] = useState(null);
28
24
  const documentTypeMapping = {
29
- 'nationalId': 'national_id',
30
- 'passport': 'passport',
31
- 'driversLicense': 'drivers_licence',
32
- 'residencePermit': 'permanent_residence',
33
- 'healthInsuranceCard': 'health_insurance_card',
25
+ 'nationalId': 'national_id', 'passport': 'passport', 'driversLicense': 'drivers_licence',
26
+ 'residencePermit': 'permanent_residence', 'healthInsuranceCard': 'health_insurance_card',
34
27
  };
35
- // const [imageNaturalSize, setImageNaturalSize] = useState<{ width: number; height: number } | null>(null);
36
28
  const { actions, state, env } = useTemplateKYCFlowContext();
37
29
  const getLocalizedText = (text) => {
38
- // console.log("text", text, JSON.stringify(component, null, 2));
39
- if (text && typeof text[currentSide] === 'object' && text[currentSide][locale]) {
30
+ if (text && typeof text[currentSide] === 'object' && text[currentSide][locale])
40
31
  return text[currentSide][locale] || '';
41
- }
42
32
  return "";
43
33
  };
44
- // Récupérer les données depuis le composant country_selection
45
34
  const countrySelectionData = useMemo(() => {
46
35
  const countrySelectionComponent = state.template.components.find(c => c.type === 'country_selection');
47
- if (countrySelectionComponent) {
48
- return state.componentData[countrySelectionComponent.id];
49
- }
50
- return null;
36
+ return countrySelectionComponent ? state.componentData[countrySelectionComponent.id] : null;
51
37
  }, [state.template.components, state.componentData]);
52
- // Extraire selectedDocumentType depuis countrySelectionData
53
38
  const selectedDocumentType = useMemo(() => {
54
39
  if (!countrySelectionData?.documentType)
55
40
  return null;
56
41
  const backendDocType = countrySelectionData.documentType;
57
42
  const mappedType = documentTypeMapping[backendDocType] || backendDocType;
58
- const region = countrySelectionData.region || 'root';
59
- return { type: mappedType, region };
43
+ return { type: mappedType, region: countrySelectionData.region || 'root' };
60
44
  }, [countrySelectionData, documentTypeMapping]);
61
- // Récupérer countryData pour compatibilité avec le code existant
62
- const countryData = useMemo(() => {
63
- return countrySelectionData;
64
- }, [countrySelectionData]);
65
- // console.log();
66
- // Synchroniser capturedImages avec value quand les données sont chargées (ex: reprise de session)
45
+ const countryData = useMemo(() => countrySelectionData, [countrySelectionData]);
67
46
  useEffect(() => {
68
47
  if (value && Object.keys(value).length > 0) {
69
- // Vérifier si les données ont changé
70
- const valueChanged = JSON.stringify(value) !== JSON.stringify(capturedImages);
71
- if (valueChanged) {
72
- logger.log("Updating capturedImages from value:", Object.keys(value));
73
- logger.log("Value data sample:", truncateFields(value));
48
+ if (JSON.stringify(value) !== JSON.stringify(capturedImages)) {
74
49
  const updatedImages = value;
75
50
  setCapturedImages(updatedImages);
76
- // Si on a des images chargées, mettre à jour silentCaptureResult pour l'affichage
77
51
  Object.keys(updatedImages).forEach((side) => {
78
52
  const imageData = updatedImages[side];
79
53
  if (imageData?.dir) {
80
54
  setSilentCaptureResult(prev => ({
81
- ...prev,
82
- path: imageData.dir,
83
- success: true,
84
- isAnalyzing: false,
85
- mrz: imageData.mrz || '',
86
- templatePath: imageData.templatePath || '',
55
+ ...prev, path: imageData.dir, success: true, isAnalyzing: false, mrz: imageData.mrz || '', templatePath: imageData.templatePath || '',
87
56
  }));
88
57
  }
89
58
  });
90
59
  }
91
60
  }
92
61
  }, [value]);
93
- useEffect(() => {
94
- logger.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
95
- if (capturedImages[currentSide]?.dir && silentCaptureResult?.bbox) {
96
- cropImageWithBBox(capturedImages[currentSide].dir, silentCaptureResult.bbox)?.then((uri) => {
97
- // setCropImageUri(uri);
98
- });
99
- }
100
- }, [capturedImages[currentSide]?.dir, silentCaptureResult?.bbox]);
101
62
  const cameraConfig = useMemo(() => {
102
63
  const instructions = selectedDocumentType
103
64
  ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
104
65
  : getLocalizedText(component.instructions);
105
66
  return {
106
- aspectRatio: 4 / 3,
107
- quality: 0.7,
108
- flashMode: 'auto',
109
- cameraType: 'back',
110
- autoFocus: 'on',
111
- whiteBalance: 'auto',
112
- allowRetake: true,
113
- maxRetakes: 3,
114
- overlay: {
115
- showGuide: true,
116
- guideText: instructions,
117
- bbox: {
118
- xMin: 20,
119
- yMin: 140,
120
- xMax: 370,
121
- yMax: 340,
122
- borderColor: '#2DBD60',
123
- borderWidth: 3,
124
- cornerRadius: 8
125
- }
126
- }
67
+ cameraType: 'back', flashMode: 'auto',
68
+ overlay: { guideText: instructions, bbox: { xMin: 15, yMin: 20, xMax: 85, yMax: 70, borderColor: '#2DBD60', borderWidth: 3, cornerRadius: 8 } }
127
69
  };
128
70
  }, [selectedDocumentType, locale, component.instructions]);
129
71
  const retakePicture = (sideToRetake) => {
130
- // 1. Turn the camera back on and hide the stepper
72
+ // Completely wipe all processing states to prevent leakage
73
+ setIsProcessingCapture(false);
74
+ setProcessingImagePath(null);
75
+ setSilentCaptureResult({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '', mrz: '', bbox: undefined });
131
76
  setShowCamera(true);
132
77
  actions.showCustomStepper(false);
133
- // 2. WIPE LOCAL STATE: Use functional update to prevent stale closures
134
- setCapturedImages((prev) => {
135
- const newState = { ...prev };
136
- delete newState[sideToRetake];
137
- return newState;
138
- });
139
- setSilentCaptureResult({
140
- path: '',
141
- success: false,
142
- isAnalyzing: false,
143
- error: '',
144
- templatePath: '',
145
- mrz: '',
146
- bbox: undefined,
147
- });
78
+ setCapturedImages((prev) => { const newState = { ...prev }; delete newState[sideToRetake]; return newState; });
148
79
  if (value) {
149
80
  const newValue = { ...value };
150
81
  delete newValue[sideToRetake];
@@ -152,9 +83,8 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
152
83
  }
153
84
  };
154
85
  const getCurrentSideVerification = (currentSide) => {
155
- if (!selectedDocumentType || !countryData?.regionMapping) {
86
+ if (!selectedDocumentType || !countryData?.regionMapping)
156
87
  return { authMethod: [], mrzTypes: [], regionMapping: null, key: 'root' };
157
- }
158
88
  const regionMapping = countryData.regionMapping[selectedDocumentType.type];
159
89
  const authMethod = [];
160
90
  const mrzTypes = [];
@@ -163,25 +93,20 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
163
93
  regionMapping[key].forEach((item) => {
164
94
  if (item[currentSide]) {
165
95
  authMethod.push(item[currentSide]);
166
- if (item?.mrz_type) {
96
+ if (item?.mrz_type)
167
97
  mrzTypes.push(item?.mrz_type);
168
- }
169
98
  }
170
99
  });
171
100
  }
172
- logger.log("regionMapping", JSON.stringify(truncateFields({ regionMapping, selectedDocumentType: selectedDocumentType.region, key }), null, 2));
173
- return { authMethod: removeDuplicates(authMethod), mrzTypes: removeDuplicates(mrzTypes), regionMapping: regionMapping, key: key };
101
+ return { authMethod: removeDuplicates(authMethod), mrzTypes: removeDuplicates(mrzTypes), regionMapping, key };
174
102
  };
175
103
  const getCorrespondingMrzType = (templatePath, mapping, selectedDocumentType = "root") => {
176
104
  if (!mapping || !mapping[selectedDocumentType])
177
105
  return null;
178
- // Extraire le nom du fichier depuis le template_path
179
106
  const fileName = templatePath.split("/").pop()?.replace(".jpg", "").replace(".png", "");
180
107
  if (!fileName)
181
108
  return null;
182
- // Normaliser en .py
183
- const pyName = `${fileName}.py`; // ex: cameroon_id_back_3_1.py
184
- // Chercher dans la liste
109
+ const pyName = `${fileName}.py`;
185
110
  const found = mapping[selectedDocumentType].find((item) => item.py_file === pyName);
186
111
  return found?.mrz_type || null;
187
112
  };
@@ -195,10 +120,52 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
195
120
  const found = mapping[selectedDocumentType].find((item) => item.py_file === pyName);
196
121
  return found?.[side] || null;
197
122
  };
198
- const handleSilentCapture = async (result) => {
199
- if (silentCaptureResult.isAnalyzing) {
123
+ const autoCapture = async (capturePath, verified) => {
124
+ if (isProcessingCapture)
200
125
  return;
126
+ setIsProcessingCapture(true);
127
+ setProcessingImagePath(capturePath);
128
+ try {
129
+ let imagePathForUpload = capturePath;
130
+ if (verified.bbox) {
131
+ try {
132
+ imagePathForUpload = await cropImageWithBBoxWithTolerance(capturePath, verified.bbox, 0.15);
133
+ }
134
+ catch {
135
+ imagePathForUpload = capturePath;
136
+ }
137
+ }
138
+ const base64 = await pathToBase64(imagePathForUpload);
139
+ const newImages = {
140
+ ...capturedImages,
141
+ [currentSide]: { dir: imagePathForUpload, file: base64, mrz: verified.mrz || "", templatePath: verified.templatePath },
142
+ };
143
+ setCapturedImages(newImages);
144
+ if (verified.country && verified.documentType) {
145
+ onValueChange({
146
+ ...newImages,
147
+ country: verified.country,
148
+ documentType: GovernmentDocumentTypeBackend[verified.documentType] || '',
149
+ });
150
+ }
151
+ setTimeout(() => {
152
+ setShowCamera(false);
153
+ actions.showCustomStepper(true);
154
+ setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false }));
155
+ setIsProcessingCapture(false);
156
+ setProcessingImagePath(null);
157
+ }, 600);
201
158
  }
159
+ catch (e) {
160
+ showAlert('Error', e?.message || 'Impossible de capturer la photo');
161
+ setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false }));
162
+ setIsProcessingCapture(false);
163
+ setProcessingImagePath(null);
164
+ }
165
+ };
166
+ const handleSilentCapture = async (result) => {
167
+ if (silentCaptureResult.isAnalyzing || isProcessingCapture)
168
+ return;
202
169
  if (result.success && result.path) {
203
170
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
204
171
  let templatePath = silentCaptureResult.templatePath || '';
@@ -209,336 +176,120 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
209
176
  return;
210
177
  }
211
178
  const regionMappings = getCurrentSideVerification(currentSide);
212
- // logger.log("regionMappings", JSON.stringify(truncateFields({regionMappings, templatePath}), null, 2));
213
- if (templatePath.length === 0) {
214
- try {
215
- logger.log("checkTemplateType - BEFORE call", {
216
- selectedDocumentTypeType: selectedDocumentType?.type,
217
- countrySelectionDataDocumentType: countrySelectionData?.documentType,
218
- docTypeToSend: selectedDocumentType?.type
219
- });
179
+ try {
180
+ if (templatePath.length === 0) {
220
181
  const templateType = await checkTemplateType({ path: result.path || '', docType: selectedDocumentType?.type, docRegion: countryData?.code || "", postfix: currentSide }, env);
221
182
  templateResponse = templateType;
222
- if (templateType.template_path) {
183
+ if (templateType.template_path)
223
184
  templatePath = templateType.template_path;
224
- logger.log("templatePath", templatePath);
225
- setSilentCaptureResult((prev) => ({ ...prev, templatePath: templatePath }));
226
- }
227
185
  if (templateType.card_obb) {
228
186
  const obbConfidence = getObbConfidence(templateType.card_obb);
229
187
  if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
230
- setSilentCaptureResult((prev) => ({
231
- ...prev,
232
- isAnalyzing: false,
233
- success: false,
234
- error: t('kyc.idCardCapture.cardNotFullyInFrame'),
235
- }));
188
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: t('kyc.idCardCapture.cardNotFullyInFrame') }));
236
189
  return;
237
190
  }
238
- let bbox;
239
191
  try {
240
- const crop = await cropByObb(result?.path || '', templateType.card_obb);
241
- bbox = crop.bbox;
242
- templateBbox = bbox;
192
+ const crop = await cropByObb(result.path, templateType.card_obb);
193
+ templateBbox = crop.bbox;
243
194
  }
244
195
  catch { }
245
196
  }
246
197
  }
247
- catch (e) {
248
- logger.log("error checking template type", truncateFields(e));
249
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du template' }));
250
- return; // Return early if checkTemplateType fails
251
- }
252
- }
253
- logger.log("templatePath before verification", templatePath, "currentSide", currentSide);
254
- if (currentSide === 'front') {
255
- logger.log("frontVerification - BEFORE call", {
256
- selectedDocumentTypeType: selectedDocumentType?.type,
257
- countrySelectionDataDocumentType: countrySelectionData?.documentType,
258
- docTypeToSend: selectedDocumentType?.type
259
- });
260
- logger.log("Calling frontVerification", { templatePath, selectedDocumentType: selectedDocumentType?.type, regionMappings });
261
- console.log("About to call frontVerification", typeof frontVerification);
262
- try {
198
+ let verificationRes;
199
+ if (currentSide === 'front') {
263
200
  const matchedAuthMethod = getCorrespondingAuthMethod(templatePath, regionMappings.regionMapping, regionMappings.key || '', 'front');
264
- const resolvedMrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '');
265
- const mrzType = resolvedMrzType || '';
266
- const authMethod = matchedAuthMethod
267
- ? regionMappings.authMethod.filter((method) => method === matchedAuthMethod)
268
- : regionMappings.authMethod.filter((method) => method !== 'MRZ' && method !== '2D_barcode');
269
- console.log("mrzType calculated", mrzType);
270
- const verificationParams = {
271
- path: result.path,
272
- regionMapping: {
273
- authMethod,
274
- mrzTypes: regionMappings.mrzTypes,
275
- },
276
- selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType?.type] || '',
277
- code: countryData?.code || '',
278
- currentSide: currentSide,
279
- templatePath: templatePath,
280
- mrzType: mrzType,
281
- };
282
- console.log("frontVerification params", verificationParams);
283
- console.log("About to call frontVerification function");
284
- const promise = frontVerification(verificationParams, env);
285
- console.log("frontVerification promise created", promise);
286
- promise.then((res) => {
287
- logger.log("front verification result", truncateFields(res));
288
- const bbox = res?.bbox || templateBbox;
289
- setSilentCaptureResult((prev) => ({
290
- ...prev,
291
- path: result.path,
292
- templatePath: templatePath,
293
- bbox: bbox,
294
- success: true,
295
- mrz: res?.mrz ? JSON.stringify(res.mrz) : "",
296
- isAnalyzing: false,
297
- country: countryData?.code,
298
- documentType: selectedDocumentType.type,
299
- }));
300
- // Stocker le bbox pour ce côté
301
- if (bbox && typeof bbox === 'object') {
302
- setBboxBySide((prev) => ({ ...prev, [currentSide]: bbox }));
303
- }
304
- }).catch((e) => {
305
- }).catch((e) => {
306
- console.log("error front verification", e);
307
- logger.log("error front verification", truncateFields(e));
308
- const isCardNotFullyInFrame = e?.message === 'CARD_NOT_FULLY_IN_FRAME' ||
309
- e?.message?.includes('entirement') ||
310
- e?.message?.includes('fully in frame');
311
- const errorMessage = isCardNotFullyInFrame
312
- ? t('kyc.idCardCapture.cardNotFullyInFrame')
313
- : (e?.message || 'Erreur de détection du MRZ');
314
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
315
- });
316
- }
317
- catch (error) {
318
- console.log("Error setting up frontVerification call", error);
319
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: error?.message || 'Erreur lors de la configuration de la vérification' }));
320
- }
321
- }
322
- else {
323
- const backRegionMappings = getCurrentSideVerification(currentSide);
324
- logger.log("Calling backVerification", { templatePath, selectedDocumentType: selectedDocumentType.type, backRegionMappings });
325
- const matchedBackAuthMethod = getCorrespondingAuthMethod(templatePath, backRegionMappings.regionMapping, backRegionMappings.key || '', 'back');
326
- const backMrzType = getCorrespondingMrzType(templatePath, backRegionMappings.regionMapping, backRegionMappings.key || '') || '';
327
- backVerification({
328
- path: result.path,
329
- regionMapping: {
330
- authMethod: matchedBackAuthMethod
331
- ? backRegionMappings.authMethod.filter((method) => method === matchedBackAuthMethod)
332
- : backRegionMappings.authMethod.filter((method) => method !== 'MRZ' && method !== '2D_barcode'),
333
- mrzTypes: backRegionMappings.mrzTypes,
334
- },
335
- selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type] || '',
336
- code: countryData?.code || '',
337
- currentSide: currentSide,
338
- templatePath: templatePath,
339
- mrzType: backMrzType,
340
- templateResponse,
341
- }, env).then((res) => {
342
- logger.log("back verification result", truncateFields(res));
343
- const bbox = res?.bbox || templateBbox;
344
- setSilentCaptureResult((prev) => ({
345
- ...prev,
346
- path: result.path,
347
- bbox: bbox,
348
- success: true,
349
- mrz: res?.mrz ? JSON.stringify(res.mrz) : "",
350
- isAnalyzing: false,
351
- country: countryData?.code,
352
- documentType: selectedDocumentType.type,
353
- }));
354
- // Stocker le bbox pour ce côté
355
- if (bbox && typeof bbox === 'object') {
356
- setBboxBySide((prev) => ({ ...prev, [currentSide]: bbox }));
357
- }
358
- }).catch((e) => {
359
- logger.log("error back verification", truncateFields(e));
360
- const isCardNotFullyInFrame = e?.message === 'CARD_NOT_FULLY_IN_FRAME' ||
361
- e?.message?.includes('entirement') ||
362
- e?.message?.includes('fully in frame');
363
- const errorMessage = isCardNotFullyInFrame
364
- ? t('kyc.idCardCapture.cardNotFullyInFrame')
365
- : (e?.message || 'Erreur de détection du MRZ');
366
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
367
- });
368
- }
369
- }
370
- };
371
- const handleCapture = async (result) => {
372
- if (silentCaptureResult.path) {
373
- let imagePathForUpload = silentCaptureResult.path;
374
- if (silentCaptureResult.bbox) {
375
- try {
376
- imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
201
+ const mrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '';
202
+ verificationRes = await frontVerification({ path: result.path, regionMapping: { authMethod: matchedAuthMethod ? [matchedAuthMethod] : regionMappings.authMethod, mrzTypes: regionMappings.mrzTypes }, selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType?.type] || '', code: countryData?.code || '', currentSide, templatePath, mrzType }, env);
377
203
  }
378
- catch (error) {
379
- imagePathForUpload = silentCaptureResult.path;
204
+ else {
205
+ const matchedBackAuthMethod = getCorrespondingAuthMethod(templatePath, regionMappings.regionMapping, regionMappings.key || '', 'back');
206
+ const backMrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '';
207
+ verificationRes = await backVerification({ path: result.path, regionMapping: { authMethod: matchedBackAuthMethod ? [matchedBackAuthMethod] : regionMappings.authMethod, mrzTypes: regionMappings.mrzTypes }, selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type] || '', code: countryData?.code || '', currentSide, templatePath, mrzType: backMrzType, templateResponse }, env);
380
208
  }
209
+ const bbox = verificationRes?.bbox || templateBbox;
210
+ const mrz = verificationRes?.mrz ? JSON.stringify(verificationRes.mrz) : "";
211
+ const verifiedResult = { path: result.path, templatePath, bbox, success: true, mrz, isAnalyzing: false, country: countryData?.code, documentType: selectedDocumentType.type };
212
+ setSilentCaptureResult(verifiedResult);
213
+ if (bbox)
214
+ setBboxBySide(prev => ({ ...prev, [currentSide]: bbox }));
215
+ await autoCapture(result.path, verifiedResult);
381
216
  }
382
- const base64 = await pathToBase64(imagePathForUpload);
383
- // FIX: Use 'imagePathForUpload' (the cropped image) for the local display directory!
384
- const newImages = {
385
- ...capturedImages,
386
- [currentSide]: {
387
- dir: imagePathForUpload, // <--- CHANGED THIS LINE
388
- file: base64,
389
- mrz: silentCaptureResult.mrz || "",
390
- templatePath: silentCaptureResult.templatePath
391
- }
392
- };
393
- setCapturedImages(newImages);
394
- if (silentCaptureResult.country && silentCaptureResult.documentType) {
395
- onValueChange({
396
- ...newImages,
397
- country: silentCaptureResult.country,
398
- documentType: GovernmentDocumentTypeBackend[silentCaptureResult.documentType] || '',
399
- });
217
+ catch (error) {
218
+ const isCardNotFullyInFrame = error?.message === 'CARD_NOT_FULLY_IN_FRAME' || error?.message?.includes('entirement');
219
+ const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (error?.message || 'Erreur de détection');
220
+ setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: errorMessage }));
400
221
  }
401
- setShowCamera(false);
402
- actions.showCustomStepper(true);
403
- }
404
- else {
405
- showAlert('Erreur', result.error || 'Impossible de prendre la photo');
406
222
  }
407
223
  };
408
224
  const handleError = (event) => {
409
225
  showAlert('Erreur', event.message);
410
226
  setShowCamera(false);
227
+ setIsProcessingCapture(false);
411
228
  };
412
- useEffect(() => {
413
- actions.showCustomStepper(!showCamera);
414
- }, [showCamera]);
415
- // Cross-device polling logic
416
- useEffect(() => {
417
- if (!showQRModal || !state.session.session_id)
418
- return;
419
- const pollInterval = setInterval(async () => {
420
- try {
421
- const result = await kycService.getVerificationResult(state.session.session_id);
422
- const sessionData = result[state.session.session_id]?.data;
423
- if (sessionData) {
424
- // Check if verification is completed or if we have ID card data
425
- // Since the requirement is "verification restarts", we might look for overall completion
426
- // or we could check if user_data is populated.
427
- // For now, let's assume if status is not PENDING/INITIALIZED it might be done.
428
- // Or simplier: if the mobile flow completes, the session status updates.
429
- logger.log('Polling result:', truncateFields(sessionData));
430
- if (sessionData.verification_status === 'completed' || sessionData.verification_status === 'approved' || sessionData.verification_status === 'review') {
431
- clearInterval(pollInterval);
432
- setShowQRModal(false);
433
- actions.submitVerification(); // Or handleComplete
434
- }
435
- }
436
- }
437
- catch (error) {
438
- console.error('Polling error:', error);
439
- }
440
- }, 5000);
441
- return () => clearInterval(pollInterval);
442
- }, [showQRModal, state.session.session_id, actions]);
443
- const getQrCodeUrl = () => {
444
- // Only available on web platform
445
- if (Platform.OS !== 'web')
446
- return '';
447
- if (typeof window === 'undefined' || !window.location || !window.location.href)
448
- return '';
449
- try {
450
- const currentUrl = new URL(window.location.href);
451
- if (!currentUrl.searchParams.has('kyc_id') && state.session.session_id) {
452
- currentUrl.searchParams.set('kyc_id', state.session.session_id);
453
- }
454
- currentUrl.searchParams.set('component_index', String(state.currentComponentIndex));
455
- if (countrySelectionData?.code) {
456
- currentUrl.searchParams.set('country', countrySelectionData.code);
457
- if (countrySelectionData.documentType)
458
- currentUrl.searchParams.set('document_type', countrySelectionData.documentType);
459
- if (countrySelectionData.region)
460
- currentUrl.searchParams.set('region', countrySelectionData.region);
461
- }
462
- return `https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${encodeURIComponent(currentUrl.toString())}`;
463
- }
464
- catch (error) {
465
- console.warn('Error generating QR code URL:', error);
466
- return '';
467
- }
468
- };
469
- // En reprise sur un autre appareil: afficher un chargement tant que les données de session ne sont pas restaurées
470
- const isResumingSession = Boolean(state.session.session_id && state.currentComponentIndex > 0);
471
- const sessionDataRestored = state.session.sessionDataRestored !== false;
472
- if (isResumingSession && !sessionDataRestored && (!countrySelectionData || !selectedDocumentType)) {
473
- return (<View style={styles.root}>
474
- <View style={[styles.container, { justifyContent: 'center', alignItems: 'center' }]}>
475
- <ActivityIndicator size="large" color="#2DBD60"/>
476
- <Text style={[styles.description, { marginTop: 16 }]}>
477
- {state.currentLanguage === 'en' ? 'Loading your session...' : 'Chargement de votre session...'}
478
- </Text>
479
- </View>
480
- </View>);
481
- }
482
- // Vérifier si les données sont disponibles, sinon afficher un message d'erreur
229
+ useEffect(() => { actions.showCustomStepper(!showCamera); }, [showCamera]);
483
230
  if (!countrySelectionData || !selectedDocumentType) {
484
231
  return (<View style={styles.root}>
485
232
  <View style={styles.container}>
486
233
  <Text style={styles.title}>{getLocalizedText(component.labels)}</Text>
487
234
  <Text style={styles.description}>
488
- {state.currentLanguage === "en"
489
- ? "Please complete the country and document selection first."
490
- : "Veuillez d'abord compléter la sélection du pays et du document."}
235
+ {state.currentLanguage === "en" ? "Please complete the country and document selection first." : "Veuillez d'abord compléter la sélection du pays et du document."}
491
236
  </Text>
492
- {error && (<Text style={styles.errorText}>{error}</Text>)}
493
237
  </View>
494
238
  </View>);
495
239
  }
240
+ // --- CAMERA RENDER ---
496
241
  if (showCamera) {
242
+ const isBusy = isProcessingCapture;
497
243
  return (<View style={styles.cameraContainer}>
498
- <EnhancedCameraView showCamera={true} cameraType={cameraConfig.cameraType} style={styles.camera} onCapture={handleCapture} onError={handleError} onClose={() => setShowCamera(false)} quality="high" showCaptureButton={true} showSwitchCamera={true} onSilentCapture={handleSilentCapture} silentCaptureResult={silentCaptureResult} captureStabilizationDelayMs={3000} enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'} overlayComponent={<IdCardOverlay xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax} instructions={cameraConfig.overlay.guideText} cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0} isSuccess={silentCaptureResult.success} language={state.currentLanguage} stepperProps={{
244
+ <EnhancedCameraView key={currentSide} // 🚨 BUG FIX: Forces the camera instance to completely reset when switching sides
245
+ showCamera={true} isProcessing={isBusy} cameraType={cameraConfig.cameraType} style={styles.camera} onError={handleError} onSilentCapture={handleSilentCapture} silentCaptureResult={silentCaptureResult} overlayComponent={<>
246
+ {!isBusy && silentCaptureResult.isAnalyzing && (<View style={styles.topAnalyzingPillContainer}>
247
+ <View style={styles.topAnalyzingPill}>
248
+ <ActivityIndicator size="small" color="white"/>
249
+ <Text style={styles.analyzingPillText}>
250
+ {state.currentLanguage === 'en' ? 'Scanning...' : 'Analyse...'}
251
+ </Text>
252
+ </View>
253
+ </View>)}
254
+
255
+ {isBusy && (<View style={StyleSheet.absoluteFillObject}>
256
+ {processingImagePath && (<Image source={{ uri: processingImagePath.startsWith('file://') ? processingImagePath : `file://${processingImagePath}` }} style={StyleSheet.absoluteFillObject} resizeMode="cover"/>)}
257
+ <View style={styles.processingOverlay}>
258
+ <ActivityIndicator size="large" color="#2DBD60"/>
259
+ <Text style={styles.processingText}>
260
+ {state.currentLanguage === 'en' ? 'Perfect!\nProcessing Document...' : 'Parfait!\nTraitement du document...'}
261
+ </Text>
262
+ </View>
263
+ </View>)}
264
+
265
+ <IdCardOverlay xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax} instructions={cameraConfig.overlay.guideText} cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0} isSuccess={silentCaptureResult.success} language={state.currentLanguage} stepperProps={{
499
266
  back: () => {
500
267
  if (currentSide === 'back') {
501
268
  setCurrentSide('front');
502
269
  setShowCamera(false);
503
- // Si une image front existe, on la restaure pour l'afficher
270
+ // 🚨 Clean up any residual state when going backwards
271
+ setIsProcessingCapture(false);
272
+ setProcessingImagePath(null);
504
273
  if (capturedImages['front']?.dir) {
505
- // Restaurer l'état de capture du front pour afficher l'image
506
274
  const frontImage = capturedImages['front'];
507
- const frontBbox = bboxBySide['front'];
508
- setSilentCaptureResult((prev) => ({
509
- path: frontImage.dir,
510
- success: true,
511
- isAnalyzing: false,
512
- error: '',
513
- mrz: frontImage.mrz || '',
514
- templatePath: frontImage.templatePath || '',
515
- country: silentCaptureResult.country,
516
- documentType: silentCaptureResult.documentType,
517
- bbox: frontBbox // Restaurer le bbox pour recalculer le cropImageUri
518
- }));
275
+ setSilentCaptureResult((prev) => ({ ...prev, path: frontImage.dir, success: true, isAnalyzing: false, error: '', mrz: frontImage.mrz || '', templatePath: frontImage.templatePath || '', bbox: bboxBySide['front'] }));
519
276
  }
520
277
  else {
521
- // Pas d'image front, on réinitialise
522
- setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
278
+ setSilentCaptureResult((prev) => ({ ...prev, path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
523
279
  }
524
280
  }
525
281
  else {
526
- // Retour au composant précédent (country_selection)
527
282
  actions.previousComponent();
528
283
  }
529
284
  },
530
285
  selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
531
- step: state.currentComponentIndex + 1,
532
- totalSteps: state.template.components.length,
533
- side: currentSide,
534
- }}/>}/>
286
+ step: state.currentComponentIndex + 1, totalSteps: state.template.components.length, side: currentSide,
287
+ }}/>
288
+ </>}/>
535
289
 
536
- {silentCaptureResult.error ? (<View style={styles.floatingErrorBanner}>
537
- <Text style={styles.floatingErrorText}>
538
- {silentCaptureResult.error}
539
- </Text>
290
+ {silentCaptureResult.error && !isBusy ? (<View style={styles.floatingErrorBanner}>
291
+ <Text style={styles.floatingErrorText}>{silentCaptureResult.error}</Text>
540
292
  </View>) : null}
541
-
542
293
  </View>);
543
294
  }
544
295
  return (<View style={styles.root}>
@@ -551,349 +302,70 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
551
302
  <Text style={{ fontSize: 14, color: '#666', textAlign: 'center', marginBottom: 16, lineHeight: 22 }}>
552
303
  {getLocalizedText(component.instructions)}
553
304
  </Text>
554
-
555
305
  <View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
556
- {silentCaptureResult?.error === 'TOO_FAR_AWAY' && (<View style={{
557
- backgroundColor: '#FF9500',
558
- padding: 12,
559
- borderRadius: 8,
560
- width: '100%',
561
- shadowColor: '#000',
562
- shadowOffset: { width: 0, height: 2 },
563
- shadowOpacity: 0.2,
564
- shadowRadius: 4,
565
- elevation: 4
566
- }}>
567
- <Text style={{ color: 'white', fontWeight: 'bold', textAlign: 'center', fontSize: 16 }}>
568
- {state.currentLanguage === "en"
569
- ? "Move the document closer to the camera."
570
- : "Veuillez rapprocher le document de la caméra."}
571
- </Text>
572
- </View>)}
306
+
307
+ {silentCaptureResult?.error === 'TOO_FAR_AWAY' && (<View style={styles.warningBanner}>
308
+ <Text style={styles.warningText}>
309
+ {state.currentLanguage === "en" ? "Move the document closer to the camera and place it on a flat surface." : "Veuillez rapprocher le document de la caméra et le poser à plat."}
310
+ </Text>
311
+ </View>)}
573
312
 
574
- <View style={{
575
- width: '100%',
576
- height: 200,
577
- borderRadius: 12,
578
- padding: 1,
579
- overflow: 'hidden',
580
- shadowColor: '#000',
581
- shadowOffset: { width: 0, height: 4 },
582
- shadowOpacity: 0.18,
583
- shadowRadius: 8,
584
- elevation: 8,
585
- backgroundColor: '#000',
586
- }}>
587
- {capturedImages[currentSide]?.dir ? (<Image source={{ uri: capturedImages[currentSide].dir }} style={{
588
- width: '100%',
589
- height: '100%',
590
- borderRadius: 12,
591
- resizeMode: 'contain',
592
- }}/>) : silentCaptureResult.path ? (<Image source={{ uri: silentCaptureResult.path }} style={{
593
- width: '100%',
594
- height: '100%',
595
- borderRadius: 12,
596
- resizeMode: 'contain',
597
- }}/>) : null}
313
+ <View style={styles.imagePreviewWrapper}>
314
+ {capturedImages[currentSide]?.dir ? (<Image source={{ uri: capturedImages[currentSide].dir }} style={styles.previewImage}/>) : silentCaptureResult.path ? (<Image source={{ uri: silentCaptureResult.path }} style={styles.previewImage}/>) : null}
598
315
  </View>
599
- {/* Capture button si aucune image n'a été capturée */}
600
- {!capturedImages[currentSide]?.dir && (<Button title={state.currentLanguage === "en" ? "Take Photo" : "Prendre une photo"} onPress={() => {
601
- setShowCamera(true);
602
- actions.showCustomStepper(false);
603
- }} variant="primary" size="large" fullWidth/>)}
604
- {/* retake button si une image a été capturée */}
316
+
317
+ {!capturedImages[currentSide]?.dir && (<Button title={state.currentLanguage === "en" ? "Start Scanning" : "Commencer la numérisation"} onPress={() => { setShowCamera(true); actions.showCustomStepper(false); }} variant="primary" size="large" fullWidth/>)}
318
+
605
319
  {capturedImages[currentSide]?.dir && (<>
606
- <Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => {
607
- retakePicture(currentSide);
608
- }} variant="outline" size="medium" fullWidth/>
320
+ <Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => retakePicture(currentSide)} variant="outline" size="medium" fullWidth/>
609
321
  <Button title={t('common.next')} onPress={() => {
610
322
  if (!selectedDocumentType) {
611
323
  showAlert('Error', 'Document type not selected');
612
324
  return;
613
325
  }
326
+ // 🚨 BUG FIX: Clear all state before moving forward
614
327
  if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
615
328
  actions.nextComponent();
616
- return;
617
329
  }
618
330
  else {
619
331
  setShowCamera(true);
620
332
  setCurrentSide('back');
621
- setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
622
- // setCropImageUri('');
333
+ setSilentCaptureResult({ success: false, isAnalyzing: false, path: '', error: '' });
334
+ setIsProcessingCapture(false);
335
+ setProcessingImagePath(null);
623
336
  }
624
337
  }} variant="primary" size="large" fullWidth/>
625
338
  </>)}
626
339
  </View>
627
340
  </View>
628
341
  </ScrollView>
629
-
630
-
631
-
632
- {error && (<Text style={styles.errorText}>{error}</Text>)}
633
-
634
- {/* Cross-Device / Continue on Phone Button (Web Only) */}
635
- {Platform.OS === 'web' && !isMobileWeb() && !capturedImages[currentSide]?.dir && (<Button title={t('kyc.idCardCapture.continueOnPhone')} onPress={() => { setShowQRModal(true); }}/>)}
636
-
637
- {/* QR Code Modal - Web Only */}
638
- {Platform.OS === 'web' && (<Modal visible={showQRModal} transparent={true} animationType="fade" onRequestClose={() => setShowQRModal(false)}>
639
- <View style={styles.modalOverlay}>
640
- <View style={styles.modalContent}>
641
- <Text style={styles.modalTitle}>
642
- {t('kyc.idCardCapture.continueOnMobile')}
643
- </Text>
644
- <Text style={styles.modalDescription}>
645
- {t('kyc.idCardCapture.scanQrCode')}
646
- </Text>
647
-
648
- {showQRModal && getQrCodeUrl() ? (<Image source={{ uri: getQrCodeUrl() }} style={styles.qrCodeImage}/>) : null}
649
-
650
- <TouchableOpacity style={styles.closeButton} onPress={() => setShowQRModal(false)}>
651
- <Text style={styles.closeButtonText}>
652
- {t('common.close')}
653
- </Text>
654
- </TouchableOpacity>
655
- </View>
656
- </View>
657
- </Modal>)}
658
-
659
342
  </View>
660
343
  </View>);
661
344
  };
662
345
  const styles = StyleSheet.create({
663
- root: {
664
- flex: 1,
665
- maxWidth: 760,
666
- width: '100%',
667
- },
668
- container: {
669
- backgroundColor: 'white',
670
- margin: 10,
671
- borderRadius: 10,
672
- paddingVertical: 16,
673
- paddingHorizontal: 16,
674
- // shadow
675
- shadowColor: '#000',
676
- shadowOffset: { width: 0, height: 2 },
677
- shadowOpacity: 0.35,
678
- shadowRadius: 4.84,
679
- elevation: 10,
680
- // padding: 16,
681
- },
682
- cameraContainer: {
683
- flex: 1,
684
- width: '100%',
685
- height: '100%',
686
- },
687
- previewContainer: {
688
- width: '95%',
689
- backgroundColor: 'white',
690
- margin: 10,
691
- borderRadius: 10,
692
- paddingVertical: 16,
693
- paddingHorizontal: 16,
694
- },
695
- previewItemContainer: {
696
- // marginBottom: 24,
697
- },
698
- title: {
699
- fontSize: 24,
700
- fontWeight: 'bold',
701
- color: '#333',
702
- marginBottom: 8,
703
- textAlign: 'center',
704
- },
705
- subtitle: {
706
- fontSize: 18,
707
- color: '#666',
708
- textAlign: 'center',
709
- marginBottom: 16,
710
- },
711
- sideInfo: {
712
- fontSize: 14,
713
- color: '#888',
714
- textAlign: 'center',
715
- marginBottom: 16,
716
- fontStyle: 'italic',
717
- },
718
- description: {
719
- fontSize: 16,
720
- color: '#666',
721
- textAlign: 'center',
722
- marginBottom: 24,
723
- lineHeight: 22,
724
- },
725
- sidesContainer: {
726
- flex: 1,
727
- },
728
- sideContainer: {
729
- marginBottom: 24,
730
- },
731
- sideTitle: {
732
- fontSize: 25,
733
- fontWeight: 'bold',
734
- color: '#000',
735
- marginBottom: 12,
736
- textAlign: 'center',
737
- },
738
- captureButton: {
739
- borderWidth: 2,
740
- borderColor: '#2DBD60',
741
- borderStyle: 'dashed',
742
- borderRadius: 12,
743
- padding: 32,
744
- alignItems: 'center',
745
- backgroundColor: '#f0f9f0',
746
- },
747
- captureIcon: {
748
- fontSize: 48,
749
- marginBottom: 16,
750
- },
751
- captureText: {
752
- fontSize: 16,
753
- color: '#2DBD60',
754
- fontWeight: '600',
755
- },
756
- capturedImageContainer: {
757
- position: 'relative',
758
- },
759
- capturedImage: {
760
- width: '100%',
761
- height: 200,
762
- borderRadius: 12,
763
- resizeMode: 'contain',
764
- backgroundColor: '#f0f0f0',
765
- },
766
- retakeButton: {
767
- position: 'absolute',
768
- top: 8,
769
- right: 8,
770
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
771
- paddingHorizontal: 12,
772
- paddingVertical: 6,
773
- borderRadius: 6,
774
- },
775
- retakeButtonText: {
776
- color: 'white',
777
- fontSize: 12,
778
- fontWeight: '600',
779
- },
346
+ root: { flex: 1, maxWidth: 760, width: '100%' },
347
+ container: { backgroundColor: 'white', margin: 10, borderRadius: 10, paddingVertical: 16, paddingHorizontal: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.35, shadowRadius: 4.84, elevation: 10 },
348
+ cameraContainer: { flex: 1, width: '100%', height: '100%' },
349
+ previewContainer: { width: '95%', backgroundColor: 'white', margin: 10, borderRadius: 10, paddingVertical: 16, paddingHorizontal: 16 },
350
+ previewItemContainer: {},
351
+ title: { fontSize: 24, fontWeight: 'bold', color: '#333', marginBottom: 8, textAlign: 'center' },
352
+ description: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 24, lineHeight: 22 },
353
+ sideContainer: { marginBottom: 24 },
354
+ sideTitle: { fontSize: 25, fontWeight: 'bold', color: '#000', marginBottom: 12, textAlign: 'center' },
355
+ imagePreviewWrapper: { width: '100%', height: 200, borderRadius: 12, padding: 1, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.18, shadowRadius: 8, elevation: 8, backgroundColor: '#000' },
356
+ previewImage: { width: '100%', height: '100%', borderRadius: 12, resizeMode: 'contain' },
357
+ floatingErrorBanner: { position: 'absolute', top: 60, left: '10%', right: '10%', backgroundColor: 'rgba(220, 38, 38, 0.95)', paddingVertical: 12, paddingHorizontal: 16, borderRadius: 8, alignItems: 'center', justifyContent: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 5, elevation: 8, zIndex: 100 },
358
+ floatingErrorText: { color: 'white', fontSize: 14, fontWeight: '700', textAlign: 'center' },
359
+ processingOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.6)', justifyContent: 'center', alignItems: 'center', zIndex: 9999 },
360
+ processingText: { color: '#FFF', fontSize: 18, fontWeight: 'bold', marginTop: 16, textAlign: 'center' },
361
+ warningBanner: { backgroundColor: '#FF9500', padding: 12, borderRadius: 8, width: '100%', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, elevation: 4 },
362
+ warningText: { color: 'white', fontWeight: 'bold', textAlign: 'center', fontSize: 16 },
363
+ errorText: { color: '#dc2626', fontSize: 14, marginTop: 8, textAlign: 'center' },
364
+ topAnalyzingPillContainer: { position: 'absolute', top: Platform.OS === 'android' ? 60 : 50, left: 0, right: 0, alignItems: 'center', zIndex: 100 },
365
+ topAnalyzingPill: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.6)', paddingVertical: 8, paddingHorizontal: 16, borderRadius: 20, gap: 8 },
366
+ analyzingPillText: { color: 'white', fontSize: 14, fontWeight: 'bold' },
780
367
  camera: {
781
368
  flex: 1,
782
369
  },
783
- completionContainer: {
784
- marginTop: 24,
785
- alignItems: 'center',
786
- },
787
- completionText: {
788
- fontSize: 16,
789
- color: '#2DBD60',
790
- fontWeight: '600',
791
- marginBottom: 16,
792
- },
793
- primaryButton: {
794
- paddingHorizontal: 32,
795
- paddingVertical: 16,
796
- borderRadius: 8,
797
- // minWidth: 120,
798
- alignItems: 'center',
799
- backgroundColor: '#2DBD60',
800
- },
801
- primaryButtonText: {
802
- color: 'white',
803
- fontWeight: '600',
804
- fontSize: 16,
805
- },
806
- errorText: {
807
- color: '#dc2626',
808
- fontSize: 14,
809
- marginTop: 8,
810
- textAlign: 'center',
811
- },
812
- crossDeviceButton: {
813
- marginTop: 16,
814
- padding: 12,
815
- alignItems: 'center',
816
- borderWidth: 1,
817
- borderColor: '#2DBD60',
818
- borderRadius: 8,
819
- backgroundColor: '#f0f9f0',
820
- },
821
- crossDeviceText: {
822
- color: '#2DBD60',
823
- fontSize: 16,
824
- fontWeight: '600',
825
- },
826
- modalOverlay: {
827
- flex: 1,
828
- backgroundColor: 'rgba(0,0,0,0.5)',
829
- justifyContent: 'center',
830
- alignItems: 'center',
831
- },
832
- modalContent: {
833
- backgroundColor: 'white',
834
- borderRadius: 16,
835
- padding: 24,
836
- alignItems: 'center',
837
- width: '90%',
838
- maxWidth: 340,
839
- shadowColor: '#000',
840
- shadowOffset: { width: 0, height: 2 },
841
- shadowOpacity: 0.25,
842
- shadowRadius: 4,
843
- elevation: 5,
844
- },
845
- modalTitle: {
846
- fontSize: 20,
847
- fontWeight: 'bold',
848
- marginBottom: 12,
849
- color: '#333',
850
- },
851
- modalDescription: {
852
- fontSize: 16,
853
- color: '#666',
854
- textAlign: 'center',
855
- marginBottom: 20,
856
- lineHeight: 22,
857
- },
858
- qrCodeImage: {
859
- width: 200,
860
- height: 200,
861
- marginBottom: 20,
862
- },
863
- closeButton: {
864
- paddingVertical: 10,
865
- paddingHorizontal: 20,
866
- backgroundColor: '#f5f5f5',
867
- borderRadius: 8,
868
- },
869
- closeButtonText: {
870
- fontSize: 16,
871
- color: '#666',
872
- fontWeight: '600',
873
- },
874
- floatingErrorBanner: {
875
- position: 'absolute',
876
- top: 60, // Pushes it down slightly from the very top of the screen
877
- left: '10%',
878
- right: '10%',
879
- backgroundColor: 'rgba(220, 38, 38, 0.95)', // Bright red
880
- paddingVertical: 12,
881
- paddingHorizontal: 16,
882
- borderRadius: 8,
883
- alignItems: 'center',
884
- justifyContent: 'center',
885
- shadowColor: '#000',
886
- shadowOffset: { width: 0, height: 4 },
887
- shadowOpacity: 0.3,
888
- shadowRadius: 5,
889
- elevation: 8,
890
- zIndex: 100,
891
- },
892
- floatingErrorText: {
893
- color: 'white',
894
- fontSize: 14,
895
- fontWeight: '700',
896
- textAlign: 'center',
897
- },
898
370
  });
899
371
  //# sourceMappingURL=IDCardCapture.js.map