@velion-la/onboarding-sdk-react-native 1.0.0-beta.2

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 (557) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +12 -0
  3. package/README.md +803 -0
  4. package/lib/commonjs/camera/AddressSearchBar.js +304 -0
  5. package/lib/commonjs/camera/AddressSearchBar.js.map +1 -0
  6. package/lib/commonjs/camera/DocumentCapture.js +505 -0
  7. package/lib/commonjs/camera/DocumentCapture.js.map +1 -0
  8. package/lib/commonjs/camera/LivenessCamera.js +534 -0
  9. package/lib/commonjs/camera/LivenessCamera.js.map +1 -0
  10. package/lib/commonjs/camera/MapAddressPicker.js +341 -0
  11. package/lib/commonjs/camera/MapAddressPicker.js.map +1 -0
  12. package/lib/commonjs/core/VelionOnboarding.js +815 -0
  13. package/lib/commonjs/core/VelionOnboarding.js.map +1 -0
  14. package/lib/commonjs/core/sessionDeclinedCopy.js +56 -0
  15. package/lib/commonjs/core/sessionDeclinedCopy.js.map +1 -0
  16. package/lib/commonjs/core/sessionStateMapper.js +137 -0
  17. package/lib/commonjs/core/sessionStateMapper.js.map +1 -0
  18. package/lib/commonjs/core/sseReasonMessages.js +450 -0
  19. package/lib/commonjs/core/sseReasonMessages.js.map +1 -0
  20. package/lib/commonjs/data/iso3166Alpha2Codes.json +1 -0
  21. package/lib/commonjs/data/iso3166CountryNames.json +251 -0
  22. package/lib/commonjs/i18n/index.js +30 -0
  23. package/lib/commonjs/i18n/index.js.map +1 -0
  24. package/lib/commonjs/i18n/strings/en.js +261 -0
  25. package/lib/commonjs/i18n/strings/en.js.map +1 -0
  26. package/lib/commonjs/i18n/strings/es.js +266 -0
  27. package/lib/commonjs/i18n/strings/es.js.map +1 -0
  28. package/lib/commonjs/index.js +213 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/package.json +1 -0
  31. package/lib/commonjs/steps/AddressInfoStep.js +650 -0
  32. package/lib/commonjs/steps/AddressInfoStep.js.map +1 -0
  33. package/lib/commonjs/steps/DocumentsOcrStep.js +397 -0
  34. package/lib/commonjs/steps/DocumentsOcrStep.js.map +1 -0
  35. package/lib/commonjs/steps/EmailContactStep.js +145 -0
  36. package/lib/commonjs/steps/EmailContactStep.js.map +1 -0
  37. package/lib/commonjs/steps/EmailOtpStep.js +220 -0
  38. package/lib/commonjs/steps/EmailOtpStep.js.map +1 -0
  39. package/lib/commonjs/steps/FaceMatchStep.js +166 -0
  40. package/lib/commonjs/steps/FaceMatchStep.js.map +1 -0
  41. package/lib/commonjs/steps/FingerprintStep.js +92 -0
  42. package/lib/commonjs/steps/FingerprintStep.js.map +1 -0
  43. package/lib/commonjs/steps/HumanBasicInfoStep.js +243 -0
  44. package/lib/commonjs/steps/HumanBasicInfoStep.js.map +1 -0
  45. package/lib/commonjs/steps/HumanExtendedInfoStep.js +268 -0
  46. package/lib/commonjs/steps/HumanExtendedInfoStep.js.map +1 -0
  47. package/lib/commonjs/steps/LegalBasicInfoStep.js +188 -0
  48. package/lib/commonjs/steps/LegalBasicInfoStep.js.map +1 -0
  49. package/lib/commonjs/steps/LegalExtendedInfoStep.js +166 -0
  50. package/lib/commonjs/steps/LegalExtendedInfoStep.js.map +1 -0
  51. package/lib/commonjs/steps/LivenessMatchStep.js +707 -0
  52. package/lib/commonjs/steps/LivenessMatchStep.js.map +1 -0
  53. package/lib/commonjs/steps/PhoneContactStep.js +146 -0
  54. package/lib/commonjs/steps/PhoneContactStep.js.map +1 -0
  55. package/lib/commonjs/steps/PhoneOtpStep.js +219 -0
  56. package/lib/commonjs/steps/PhoneOtpStep.js.map +1 -0
  57. package/lib/commonjs/steps/SwornDeclarationStep.js +196 -0
  58. package/lib/commonjs/steps/SwornDeclarationStep.js.map +1 -0
  59. package/lib/commonjs/steps/TaxInfoStep.js +244 -0
  60. package/lib/commonjs/steps/TaxInfoStep.js.map +1 -0
  61. package/lib/commonjs/steps/TermsAndConditionsStep.js +238 -0
  62. package/lib/commonjs/steps/TermsAndConditionsStep.js.map +1 -0
  63. package/lib/commonjs/steps/_shared.js +40 -0
  64. package/lib/commonjs/steps/_shared.js.map +1 -0
  65. package/lib/commonjs/steps/types.js +6 -0
  66. package/lib/commonjs/steps/types.js.map +1 -0
  67. package/lib/commonjs/transport/api.js +176 -0
  68. package/lib/commonjs/transport/api.js.map +1 -0
  69. package/lib/commonjs/transport/irisWebSocket.js +121 -0
  70. package/lib/commonjs/transport/irisWebSocket.js.map +1 -0
  71. package/lib/commonjs/transport/secureUrl.js +50 -0
  72. package/lib/commonjs/transport/secureUrl.js.map +1 -0
  73. package/lib/commonjs/transport/sse.js +248 -0
  74. package/lib/commonjs/transport/sse.js.map +1 -0
  75. package/lib/commonjs/types/index.js +391 -0
  76. package/lib/commonjs/types/index.js.map +1 -0
  77. package/lib/commonjs/ui/MapFallbackBoundary.js +42 -0
  78. package/lib/commonjs/ui/MapFallbackBoundary.js.map +1 -0
  79. package/lib/commonjs/ui/StepShell.js +118 -0
  80. package/lib/commonjs/ui/StepShell.js.map +1 -0
  81. package/lib/commonjs/ui/VelionOnboardingScreen.js +217 -0
  82. package/lib/commonjs/ui/VelionOnboardingScreen.js.map +1 -0
  83. package/lib/commonjs/ui/forms/index.js +38 -0
  84. package/lib/commonjs/ui/forms/index.js.map +1 -0
  85. package/lib/commonjs/ui/forms/useInlineValidation.js +88 -0
  86. package/lib/commonjs/ui/forms/useInlineValidation.js.map +1 -0
  87. package/lib/commonjs/ui/forms/validators.js +34 -0
  88. package/lib/commonjs/ui/forms/validators.js.map +1 -0
  89. package/lib/commonjs/ui/icons/HomeHeartIcon.js +38 -0
  90. package/lib/commonjs/ui/icons/HomeHeartIcon.js.map +1 -0
  91. package/lib/commonjs/ui/icons/StepIcon.js +59 -0
  92. package/lib/commonjs/ui/icons/StepIcon.js.map +1 -0
  93. package/lib/commonjs/ui/overlays/AnalyzingOverlay.js +284 -0
  94. package/lib/commonjs/ui/overlays/AnalyzingOverlay.js.map +1 -0
  95. package/lib/commonjs/ui/overlays/LoadingOverlay.js +108 -0
  96. package/lib/commonjs/ui/overlays/LoadingOverlay.js.map +1 -0
  97. package/lib/commonjs/ui/overlays/ProgressBar.js +95 -0
  98. package/lib/commonjs/ui/overlays/ProgressBar.js.map +1 -0
  99. package/lib/commonjs/ui/overlays/RetryDialog.js +125 -0
  100. package/lib/commonjs/ui/overlays/RetryDialog.js.map +1 -0
  101. package/lib/commonjs/ui/overlays/index.js +33 -0
  102. package/lib/commonjs/ui/overlays/index.js.map +1 -0
  103. package/lib/commonjs/ui/primitives/Banner.js +81 -0
  104. package/lib/commonjs/ui/primitives/Banner.js.map +1 -0
  105. package/lib/commonjs/ui/primitives/Button.js +142 -0
  106. package/lib/commonjs/ui/primitives/Button.js.map +1 -0
  107. package/lib/commonjs/ui/primitives/Card.js +50 -0
  108. package/lib/commonjs/ui/primitives/Card.js.map +1 -0
  109. package/lib/commonjs/ui/primitives/Checkbox.js +139 -0
  110. package/lib/commonjs/ui/primitives/Checkbox.js.map +1 -0
  111. package/lib/commonjs/ui/primitives/CountrySelect.js +288 -0
  112. package/lib/commonjs/ui/primitives/CountrySelect.js.map +1 -0
  113. package/lib/commonjs/ui/primitives/DatePicker.js +245 -0
  114. package/lib/commonjs/ui/primitives/DatePicker.js.map +1 -0
  115. package/lib/commonjs/ui/primitives/Input.js +101 -0
  116. package/lib/commonjs/ui/primitives/Input.js.map +1 -0
  117. package/lib/commonjs/ui/primitives/Select.js +209 -0
  118. package/lib/commonjs/ui/primitives/Select.js.map +1 -0
  119. package/lib/commonjs/ui/primitives/Spinner.js +25 -0
  120. package/lib/commonjs/ui/primitives/Spinner.js.map +1 -0
  121. package/lib/commonjs/ui/primitives/index.js +69 -0
  122. package/lib/commonjs/ui/primitives/index.js.map +1 -0
  123. package/lib/commonjs/ui/theme/ThemeProvider.js +74 -0
  124. package/lib/commonjs/ui/theme/ThemeProvider.js.map +1 -0
  125. package/lib/commonjs/ui/theme/index.js +68 -0
  126. package/lib/commonjs/ui/theme/index.js.map +1 -0
  127. package/lib/commonjs/ui/theme/tokens.js +154 -0
  128. package/lib/commonjs/ui/theme/tokens.js.map +1 -0
  129. package/lib/commonjs/utils/EventEmitter.js +144 -0
  130. package/lib/commonjs/utils/EventEmitter.js.map +1 -0
  131. package/lib/commonjs/utils/base64.js +53 -0
  132. package/lib/commonjs/utils/base64.js.map +1 -0
  133. package/lib/commonjs/utils/deviceFingerprint.js +132 -0
  134. package/lib/commonjs/utils/deviceFingerprint.js.map +1 -0
  135. package/lib/commonjs/utils/documentTypeOptions.js +35 -0
  136. package/lib/commonjs/utils/documentTypeOptions.js.map +1 -0
  137. package/lib/commonjs/utils/googleMapsPreflight.js +77 -0
  138. package/lib/commonjs/utils/googleMapsPreflight.js.map +1 -0
  139. package/lib/commonjs/utils/hmac.js +58 -0
  140. package/lib/commonjs/utils/hmac.js.map +1 -0
  141. package/lib/commonjs/utils/iso3166Countries.js +98 -0
  142. package/lib/commonjs/utils/iso3166Countries.js.map +1 -0
  143. package/lib/commonjs/utils/jpegFromUri.js +44 -0
  144. package/lib/commonjs/utils/jpegFromUri.js.map +1 -0
  145. package/lib/commonjs/utils/keepAwake.js +58 -0
  146. package/lib/commonjs/utils/keepAwake.js.map +1 -0
  147. package/lib/commonjs/utils/log.js +42 -0
  148. package/lib/commonjs/utils/log.js.map +1 -0
  149. package/lib/commonjs/utils/osGeocoder.js +70 -0
  150. package/lib/commonjs/utils/osGeocoder.js.map +1 -0
  151. package/lib/commonjs/utils/parseGoogleAddressComponents.js +74 -0
  152. package/lib/commonjs/utils/parseGoogleAddressComponents.js.map +1 -0
  153. package/lib/commonjs/utils/personNameNormalize.js +24 -0
  154. package/lib/commonjs/utils/personNameNormalize.js.map +1 -0
  155. package/lib/commonjs/utils/sdkError.js +34 -0
  156. package/lib/commonjs/utils/sdkError.js.map +1 -0
  157. package/lib/commonjs/utils/sdkVersion.js +13 -0
  158. package/lib/commonjs/utils/sdkVersion.js.map +1 -0
  159. package/lib/commonjs/utils/stepConfig.js +65 -0
  160. package/lib/commonjs/utils/stepConfig.js.map +1 -0
  161. package/lib/commonjs/utils/taxInfoConfig.js +19 -0
  162. package/lib/commonjs/utils/taxInfoConfig.js.map +1 -0
  163. package/lib/module/camera/AddressSearchBar.js +299 -0
  164. package/lib/module/camera/AddressSearchBar.js.map +1 -0
  165. package/lib/module/camera/DocumentCapture.js +500 -0
  166. package/lib/module/camera/DocumentCapture.js.map +1 -0
  167. package/lib/module/camera/LivenessCamera.js +529 -0
  168. package/lib/module/camera/LivenessCamera.js.map +1 -0
  169. package/lib/module/camera/MapAddressPicker.js +337 -0
  170. package/lib/module/camera/MapAddressPicker.js.map +1 -0
  171. package/lib/module/core/VelionOnboarding.js +811 -0
  172. package/lib/module/core/VelionOnboarding.js.map +1 -0
  173. package/lib/module/core/sessionDeclinedCopy.js +52 -0
  174. package/lib/module/core/sessionDeclinedCopy.js.map +1 -0
  175. package/lib/module/core/sessionStateMapper.js +128 -0
  176. package/lib/module/core/sessionStateMapper.js.map +1 -0
  177. package/lib/module/core/sseReasonMessages.js +446 -0
  178. package/lib/module/core/sseReasonMessages.js.map +1 -0
  179. package/lib/module/data/iso3166Alpha2Codes.json +1 -0
  180. package/lib/module/data/iso3166CountryNames.json +251 -0
  181. package/lib/module/i18n/index.js +25 -0
  182. package/lib/module/i18n/index.js.map +1 -0
  183. package/lib/module/i18n/strings/en.js +257 -0
  184. package/lib/module/i18n/strings/en.js.map +1 -0
  185. package/lib/module/i18n/strings/es.js +262 -0
  186. package/lib/module/i18n/strings/es.js.map +1 -0
  187. package/lib/module/index.js +36 -0
  188. package/lib/module/index.js.map +1 -0
  189. package/lib/module/steps/AddressInfoStep.js +646 -0
  190. package/lib/module/steps/AddressInfoStep.js.map +1 -0
  191. package/lib/module/steps/DocumentsOcrStep.js +391 -0
  192. package/lib/module/steps/DocumentsOcrStep.js.map +1 -0
  193. package/lib/module/steps/EmailContactStep.js +140 -0
  194. package/lib/module/steps/EmailContactStep.js.map +1 -0
  195. package/lib/module/steps/EmailOtpStep.js +215 -0
  196. package/lib/module/steps/EmailOtpStep.js.map +1 -0
  197. package/lib/module/steps/FaceMatchStep.js +161 -0
  198. package/lib/module/steps/FaceMatchStep.js.map +1 -0
  199. package/lib/module/steps/FingerprintStep.js +87 -0
  200. package/lib/module/steps/FingerprintStep.js.map +1 -0
  201. package/lib/module/steps/HumanBasicInfoStep.js +238 -0
  202. package/lib/module/steps/HumanBasicInfoStep.js.map +1 -0
  203. package/lib/module/steps/HumanExtendedInfoStep.js +263 -0
  204. package/lib/module/steps/HumanExtendedInfoStep.js.map +1 -0
  205. package/lib/module/steps/LegalBasicInfoStep.js +183 -0
  206. package/lib/module/steps/LegalBasicInfoStep.js.map +1 -0
  207. package/lib/module/steps/LegalExtendedInfoStep.js +161 -0
  208. package/lib/module/steps/LegalExtendedInfoStep.js.map +1 -0
  209. package/lib/module/steps/LivenessMatchStep.js +701 -0
  210. package/lib/module/steps/LivenessMatchStep.js.map +1 -0
  211. package/lib/module/steps/PhoneContactStep.js +141 -0
  212. package/lib/module/steps/PhoneContactStep.js.map +1 -0
  213. package/lib/module/steps/PhoneOtpStep.js +214 -0
  214. package/lib/module/steps/PhoneOtpStep.js.map +1 -0
  215. package/lib/module/steps/SwornDeclarationStep.js +191 -0
  216. package/lib/module/steps/SwornDeclarationStep.js.map +1 -0
  217. package/lib/module/steps/TaxInfoStep.js +239 -0
  218. package/lib/module/steps/TaxInfoStep.js.map +1 -0
  219. package/lib/module/steps/TermsAndConditionsStep.js +233 -0
  220. package/lib/module/steps/TermsAndConditionsStep.js.map +1 -0
  221. package/lib/module/steps/_shared.js +33 -0
  222. package/lib/module/steps/_shared.js.map +1 -0
  223. package/lib/module/steps/types.js +4 -0
  224. package/lib/module/steps/types.js.map +1 -0
  225. package/lib/module/transport/api.js +173 -0
  226. package/lib/module/transport/api.js.map +1 -0
  227. package/lib/module/transport/irisWebSocket.js +115 -0
  228. package/lib/module/transport/irisWebSocket.js.map +1 -0
  229. package/lib/module/transport/secureUrl.js +45 -0
  230. package/lib/module/transport/secureUrl.js.map +1 -0
  231. package/lib/module/transport/sse.js +242 -0
  232. package/lib/module/transport/sse.js.map +1 -0
  233. package/lib/module/types/index.js +457 -0
  234. package/lib/module/types/index.js.map +1 -0
  235. package/lib/module/ui/MapFallbackBoundary.js +37 -0
  236. package/lib/module/ui/MapFallbackBoundary.js.map +1 -0
  237. package/lib/module/ui/StepShell.js +114 -0
  238. package/lib/module/ui/StepShell.js.map +1 -0
  239. package/lib/module/ui/VelionOnboardingScreen.js +212 -0
  240. package/lib/module/ui/VelionOnboardingScreen.js.map +1 -0
  241. package/lib/module/ui/forms/index.js +5 -0
  242. package/lib/module/ui/forms/index.js.map +1 -0
  243. package/lib/module/ui/forms/useInlineValidation.js +84 -0
  244. package/lib/module/ui/forms/useInlineValidation.js.map +1 -0
  245. package/lib/module/ui/forms/validators.js +27 -0
  246. package/lib/module/ui/forms/validators.js.map +1 -0
  247. package/lib/module/ui/icons/HomeHeartIcon.js +33 -0
  248. package/lib/module/ui/icons/HomeHeartIcon.js.map +1 -0
  249. package/lib/module/ui/icons/StepIcon.js +55 -0
  250. package/lib/module/ui/icons/StepIcon.js.map +1 -0
  251. package/lib/module/ui/overlays/AnalyzingOverlay.js +279 -0
  252. package/lib/module/ui/overlays/AnalyzingOverlay.js.map +1 -0
  253. package/lib/module/ui/overlays/LoadingOverlay.js +104 -0
  254. package/lib/module/ui/overlays/LoadingOverlay.js.map +1 -0
  255. package/lib/module/ui/overlays/ProgressBar.js +90 -0
  256. package/lib/module/ui/overlays/ProgressBar.js.map +1 -0
  257. package/lib/module/ui/overlays/RetryDialog.js +121 -0
  258. package/lib/module/ui/overlays/RetryDialog.js.map +1 -0
  259. package/lib/module/ui/overlays/index.js +6 -0
  260. package/lib/module/ui/overlays/index.js.map +1 -0
  261. package/lib/module/ui/primitives/Banner.js +77 -0
  262. package/lib/module/ui/primitives/Banner.js.map +1 -0
  263. package/lib/module/ui/primitives/Button.js +138 -0
  264. package/lib/module/ui/primitives/Button.js.map +1 -0
  265. package/lib/module/ui/primitives/Card.js +46 -0
  266. package/lib/module/ui/primitives/Card.js.map +1 -0
  267. package/lib/module/ui/primitives/Checkbox.js +136 -0
  268. package/lib/module/ui/primitives/Checkbox.js.map +1 -0
  269. package/lib/module/ui/primitives/CountrySelect.js +284 -0
  270. package/lib/module/ui/primitives/CountrySelect.js.map +1 -0
  271. package/lib/module/ui/primitives/DatePicker.js +241 -0
  272. package/lib/module/ui/primitives/DatePicker.js.map +1 -0
  273. package/lib/module/ui/primitives/Input.js +97 -0
  274. package/lib/module/ui/primitives/Input.js.map +1 -0
  275. package/lib/module/ui/primitives/Select.js +205 -0
  276. package/lib/module/ui/primitives/Select.js.map +1 -0
  277. package/lib/module/ui/primitives/Spinner.js +21 -0
  278. package/lib/module/ui/primitives/Spinner.js.map +1 -0
  279. package/lib/module/ui/primitives/index.js +12 -0
  280. package/lib/module/ui/primitives/index.js.map +1 -0
  281. package/lib/module/ui/theme/ThemeProvider.js +67 -0
  282. package/lib/module/ui/theme/ThemeProvider.js.map +1 -0
  283. package/lib/module/ui/theme/index.js +5 -0
  284. package/lib/module/ui/theme/index.js.map +1 -0
  285. package/lib/module/ui/theme/tokens.js +145 -0
  286. package/lib/module/ui/theme/tokens.js.map +1 -0
  287. package/lib/module/utils/EventEmitter.js +139 -0
  288. package/lib/module/utils/EventEmitter.js.map +1 -0
  289. package/lib/module/utils/base64.js +47 -0
  290. package/lib/module/utils/base64.js.map +1 -0
  291. package/lib/module/utils/deviceFingerprint.js +129 -0
  292. package/lib/module/utils/deviceFingerprint.js.map +1 -0
  293. package/lib/module/utils/documentTypeOptions.js +31 -0
  294. package/lib/module/utils/documentTypeOptions.js.map +1 -0
  295. package/lib/module/utils/googleMapsPreflight.js +73 -0
  296. package/lib/module/utils/googleMapsPreflight.js.map +1 -0
  297. package/lib/module/utils/hmac.js +53 -0
  298. package/lib/module/utils/hmac.js.map +1 -0
  299. package/lib/module/utils/iso3166Countries.js +92 -0
  300. package/lib/module/utils/iso3166Countries.js.map +1 -0
  301. package/lib/module/utils/jpegFromUri.js +40 -0
  302. package/lib/module/utils/jpegFromUri.js.map +1 -0
  303. package/lib/module/utils/keepAwake.js +53 -0
  304. package/lib/module/utils/keepAwake.js.map +1 -0
  305. package/lib/module/utils/log.js +37 -0
  306. package/lib/module/utils/log.js.map +1 -0
  307. package/lib/module/utils/osGeocoder.js +65 -0
  308. package/lib/module/utils/osGeocoder.js.map +1 -0
  309. package/lib/module/utils/parseGoogleAddressComponents.js +69 -0
  310. package/lib/module/utils/parseGoogleAddressComponents.js.map +1 -0
  311. package/lib/module/utils/personNameNormalize.js +20 -0
  312. package/lib/module/utils/personNameNormalize.js.map +1 -0
  313. package/lib/module/utils/sdkError.js +29 -0
  314. package/lib/module/utils/sdkError.js.map +1 -0
  315. package/lib/module/utils/sdkVersion.js +9 -0
  316. package/lib/module/utils/sdkVersion.js.map +1 -0
  317. package/lib/module/utils/stepConfig.js +59 -0
  318. package/lib/module/utils/stepConfig.js.map +1 -0
  319. package/lib/module/utils/taxInfoConfig.js +15 -0
  320. package/lib/module/utils/taxInfoConfig.js.map +1 -0
  321. package/lib/typescript/camera/AddressSearchBar.d.ts +37 -0
  322. package/lib/typescript/camera/AddressSearchBar.d.ts.map +1 -0
  323. package/lib/typescript/camera/DocumentCapture.d.ts +34 -0
  324. package/lib/typescript/camera/DocumentCapture.d.ts.map +1 -0
  325. package/lib/typescript/camera/LivenessCamera.d.ts +50 -0
  326. package/lib/typescript/camera/LivenessCamera.d.ts.map +1 -0
  327. package/lib/typescript/camera/MapAddressPicker.d.ts +57 -0
  328. package/lib/typescript/camera/MapAddressPicker.d.ts.map +1 -0
  329. package/lib/typescript/core/VelionOnboarding.d.ts +115 -0
  330. package/lib/typescript/core/VelionOnboarding.d.ts.map +1 -0
  331. package/lib/typescript/core/sessionDeclinedCopy.d.ts +16 -0
  332. package/lib/typescript/core/sessionDeclinedCopy.d.ts.map +1 -0
  333. package/lib/typescript/core/sessionStateMapper.d.ts +33 -0
  334. package/lib/typescript/core/sessionStateMapper.d.ts.map +1 -0
  335. package/lib/typescript/core/sseReasonMessages.d.ts +20 -0
  336. package/lib/typescript/core/sseReasonMessages.d.ts.map +1 -0
  337. package/lib/typescript/i18n/index.d.ts +13 -0
  338. package/lib/typescript/i18n/index.d.ts.map +1 -0
  339. package/lib/typescript/i18n/strings/en.d.ts +3 -0
  340. package/lib/typescript/i18n/strings/en.d.ts.map +1 -0
  341. package/lib/typescript/i18n/strings/es.d.ts +260 -0
  342. package/lib/typescript/i18n/strings/es.d.ts.map +1 -0
  343. package/lib/typescript/index.d.ts +26 -0
  344. package/lib/typescript/index.d.ts.map +1 -0
  345. package/lib/typescript/steps/AddressInfoStep.d.ts +26 -0
  346. package/lib/typescript/steps/AddressInfoStep.d.ts.map +1 -0
  347. package/lib/typescript/steps/DocumentsOcrStep.d.ts +26 -0
  348. package/lib/typescript/steps/DocumentsOcrStep.d.ts.map +1 -0
  349. package/lib/typescript/steps/EmailContactStep.d.ts +15 -0
  350. package/lib/typescript/steps/EmailContactStep.d.ts.map +1 -0
  351. package/lib/typescript/steps/EmailOtpStep.d.ts +21 -0
  352. package/lib/typescript/steps/EmailOtpStep.d.ts.map +1 -0
  353. package/lib/typescript/steps/FaceMatchStep.d.ts +27 -0
  354. package/lib/typescript/steps/FaceMatchStep.d.ts.map +1 -0
  355. package/lib/typescript/steps/FingerprintStep.d.ts +25 -0
  356. package/lib/typescript/steps/FingerprintStep.d.ts.map +1 -0
  357. package/lib/typescript/steps/HumanBasicInfoStep.d.ts +25 -0
  358. package/lib/typescript/steps/HumanBasicInfoStep.d.ts.map +1 -0
  359. package/lib/typescript/steps/HumanExtendedInfoStep.d.ts +23 -0
  360. package/lib/typescript/steps/HumanExtendedInfoStep.d.ts.map +1 -0
  361. package/lib/typescript/steps/LegalBasicInfoStep.d.ts +17 -0
  362. package/lib/typescript/steps/LegalBasicInfoStep.d.ts.map +1 -0
  363. package/lib/typescript/steps/LegalExtendedInfoStep.d.ts +17 -0
  364. package/lib/typescript/steps/LegalExtendedInfoStep.d.ts.map +1 -0
  365. package/lib/typescript/steps/LivenessMatchStep.d.ts +26 -0
  366. package/lib/typescript/steps/LivenessMatchStep.d.ts.map +1 -0
  367. package/lib/typescript/steps/PhoneContactStep.d.ts +15 -0
  368. package/lib/typescript/steps/PhoneContactStep.d.ts.map +1 -0
  369. package/lib/typescript/steps/PhoneOtpStep.d.ts +19 -0
  370. package/lib/typescript/steps/PhoneOtpStep.d.ts.map +1 -0
  371. package/lib/typescript/steps/SwornDeclarationStep.d.ts +20 -0
  372. package/lib/typescript/steps/SwornDeclarationStep.d.ts.map +1 -0
  373. package/lib/typescript/steps/TaxInfoStep.d.ts +19 -0
  374. package/lib/typescript/steps/TaxInfoStep.d.ts.map +1 -0
  375. package/lib/typescript/steps/TermsAndConditionsStep.d.ts +21 -0
  376. package/lib/typescript/steps/TermsAndConditionsStep.d.ts.map +1 -0
  377. package/lib/typescript/steps/_shared.d.ts +11 -0
  378. package/lib/typescript/steps/_shared.d.ts.map +1 -0
  379. package/lib/typescript/steps/types.d.ts +68 -0
  380. package/lib/typescript/steps/types.d.ts.map +1 -0
  381. package/lib/typescript/transport/api.d.ts +35 -0
  382. package/lib/typescript/transport/api.d.ts.map +1 -0
  383. package/lib/typescript/transport/irisWebSocket.d.ts +72 -0
  384. package/lib/typescript/transport/irisWebSocket.d.ts.map +1 -0
  385. package/lib/typescript/transport/secureUrl.d.ts +14 -0
  386. package/lib/typescript/transport/secureUrl.d.ts.map +1 -0
  387. package/lib/typescript/transport/sse.d.ts +54 -0
  388. package/lib/typescript/transport/sse.d.ts.map +1 -0
  389. package/lib/typescript/types/index.d.ts +1010 -0
  390. package/lib/typescript/types/index.d.ts.map +1 -0
  391. package/lib/typescript/ui/MapFallbackBoundary.d.ts +28 -0
  392. package/lib/typescript/ui/MapFallbackBoundary.d.ts.map +1 -0
  393. package/lib/typescript/ui/StepShell.d.ts +19 -0
  394. package/lib/typescript/ui/StepShell.d.ts.map +1 -0
  395. package/lib/typescript/ui/VelionOnboardingScreen.d.ts +19 -0
  396. package/lib/typescript/ui/VelionOnboardingScreen.d.ts.map +1 -0
  397. package/lib/typescript/ui/forms/index.d.ts +3 -0
  398. package/lib/typescript/ui/forms/index.d.ts.map +1 -0
  399. package/lib/typescript/ui/forms/useInlineValidation.d.ts +30 -0
  400. package/lib/typescript/ui/forms/useInlineValidation.d.ts.map +1 -0
  401. package/lib/typescript/ui/forms/validators.d.ts +11 -0
  402. package/lib/typescript/ui/forms/validators.d.ts.map +1 -0
  403. package/lib/typescript/ui/icons/HomeHeartIcon.d.ts +14 -0
  404. package/lib/typescript/ui/icons/HomeHeartIcon.d.ts.map +1 -0
  405. package/lib/typescript/ui/icons/StepIcon.d.ts +26 -0
  406. package/lib/typescript/ui/icons/StepIcon.d.ts.map +1 -0
  407. package/lib/typescript/ui/overlays/AnalyzingOverlay.d.ts +24 -0
  408. package/lib/typescript/ui/overlays/AnalyzingOverlay.d.ts.map +1 -0
  409. package/lib/typescript/ui/overlays/LoadingOverlay.d.ts +19 -0
  410. package/lib/typescript/ui/overlays/LoadingOverlay.d.ts.map +1 -0
  411. package/lib/typescript/ui/overlays/ProgressBar.d.ts +26 -0
  412. package/lib/typescript/ui/overlays/ProgressBar.d.ts.map +1 -0
  413. package/lib/typescript/ui/overlays/RetryDialog.d.ts +17 -0
  414. package/lib/typescript/ui/overlays/RetryDialog.d.ts.map +1 -0
  415. package/lib/typescript/ui/overlays/index.d.ts +4 -0
  416. package/lib/typescript/ui/overlays/index.d.ts.map +1 -0
  417. package/lib/typescript/ui/primitives/Banner.d.ts +11 -0
  418. package/lib/typescript/ui/primitives/Banner.d.ts.map +1 -0
  419. package/lib/typescript/ui/primitives/Button.d.ts +27 -0
  420. package/lib/typescript/ui/primitives/Button.d.ts.map +1 -0
  421. package/lib/typescript/ui/primitives/Card.d.ts +10 -0
  422. package/lib/typescript/ui/primitives/Card.d.ts.map +1 -0
  423. package/lib/typescript/ui/primitives/Checkbox.d.ts +25 -0
  424. package/lib/typescript/ui/primitives/Checkbox.d.ts.map +1 -0
  425. package/lib/typescript/ui/primitives/CountrySelect.d.ts +31 -0
  426. package/lib/typescript/ui/primitives/CountrySelect.d.ts.map +1 -0
  427. package/lib/typescript/ui/primitives/DatePicker.d.ts +42 -0
  428. package/lib/typescript/ui/primitives/DatePicker.d.ts.map +1 -0
  429. package/lib/typescript/ui/primitives/Input.d.ts +17 -0
  430. package/lib/typescript/ui/primitives/Input.d.ts.map +1 -0
  431. package/lib/typescript/ui/primitives/Select.d.ts +26 -0
  432. package/lib/typescript/ui/primitives/Select.d.ts.map +1 -0
  433. package/lib/typescript/ui/primitives/Spinner.d.ts +10 -0
  434. package/lib/typescript/ui/primitives/Spinner.d.ts.map +1 -0
  435. package/lib/typescript/ui/primitives/index.d.ts +10 -0
  436. package/lib/typescript/ui/primitives/index.d.ts.map +1 -0
  437. package/lib/typescript/ui/theme/ThemeProvider.d.ts +23 -0
  438. package/lib/typescript/ui/theme/ThemeProvider.d.ts.map +1 -0
  439. package/lib/typescript/ui/theme/index.d.ts +3 -0
  440. package/lib/typescript/ui/theme/index.d.ts.map +1 -0
  441. package/lib/typescript/ui/theme/tokens.d.ts +42 -0
  442. package/lib/typescript/ui/theme/tokens.d.ts.map +1 -0
  443. package/lib/typescript/utils/EventEmitter.d.ts +98 -0
  444. package/lib/typescript/utils/EventEmitter.d.ts.map +1 -0
  445. package/lib/typescript/utils/base64.d.ts +19 -0
  446. package/lib/typescript/utils/base64.d.ts.map +1 -0
  447. package/lib/typescript/utils/deviceFingerprint.d.ts +42 -0
  448. package/lib/typescript/utils/deviceFingerprint.d.ts.map +1 -0
  449. package/lib/typescript/utils/documentTypeOptions.d.ts +13 -0
  450. package/lib/typescript/utils/documentTypeOptions.d.ts.map +1 -0
  451. package/lib/typescript/utils/googleMapsPreflight.d.ts +44 -0
  452. package/lib/typescript/utils/googleMapsPreflight.d.ts.map +1 -0
  453. package/lib/typescript/utils/hmac.d.ts +39 -0
  454. package/lib/typescript/utils/hmac.d.ts.map +1 -0
  455. package/lib/typescript/utils/iso3166Countries.d.ts +27 -0
  456. package/lib/typescript/utils/iso3166Countries.d.ts.map +1 -0
  457. package/lib/typescript/utils/jpegFromUri.d.ts +29 -0
  458. package/lib/typescript/utils/jpegFromUri.d.ts.map +1 -0
  459. package/lib/typescript/utils/keepAwake.d.ts +16 -0
  460. package/lib/typescript/utils/keepAwake.d.ts.map +1 -0
  461. package/lib/typescript/utils/log.d.ts +18 -0
  462. package/lib/typescript/utils/log.d.ts.map +1 -0
  463. package/lib/typescript/utils/osGeocoder.d.ts +38 -0
  464. package/lib/typescript/utils/osGeocoder.d.ts.map +1 -0
  465. package/lib/typescript/utils/parseGoogleAddressComponents.d.ts +43 -0
  466. package/lib/typescript/utils/parseGoogleAddressComponents.d.ts.map +1 -0
  467. package/lib/typescript/utils/personNameNormalize.d.ts +10 -0
  468. package/lib/typescript/utils/personNameNormalize.d.ts.map +1 -0
  469. package/lib/typescript/utils/sdkError.d.ts +14 -0
  470. package/lib/typescript/utils/sdkError.d.ts.map +1 -0
  471. package/lib/typescript/utils/sdkVersion.d.ts +7 -0
  472. package/lib/typescript/utils/sdkVersion.d.ts.map +1 -0
  473. package/lib/typescript/utils/stepConfig.d.ts +8 -0
  474. package/lib/typescript/utils/stepConfig.d.ts.map +1 -0
  475. package/lib/typescript/utils/taxInfoConfig.d.ts +7 -0
  476. package/lib/typescript/utils/taxInfoConfig.d.ts.map +1 -0
  477. package/package.json +99 -0
  478. package/src/camera/AddressSearchBar.tsx +363 -0
  479. package/src/camera/DocumentCapture.tsx +505 -0
  480. package/src/camera/LivenessCamera.tsx +602 -0
  481. package/src/camera/MapAddressPicker.tsx +397 -0
  482. package/src/core/VelionOnboarding.ts +974 -0
  483. package/src/core/sessionDeclinedCopy.ts +76 -0
  484. package/src/core/sessionStateMapper.ts +167 -0
  485. package/src/core/sseReasonMessages.ts +531 -0
  486. package/src/data/iso3166Alpha2Codes.json +1 -0
  487. package/src/data/iso3166CountryNames.json +251 -0
  488. package/src/i18n/index.ts +26 -0
  489. package/src/i18n/strings/en.ts +289 -0
  490. package/src/i18n/strings/es.ts +291 -0
  491. package/src/index.ts +137 -0
  492. package/src/steps/AddressInfoStep.tsx +726 -0
  493. package/src/steps/DocumentsOcrStep.tsx +472 -0
  494. package/src/steps/EmailContactStep.tsx +159 -0
  495. package/src/steps/EmailOtpStep.tsx +250 -0
  496. package/src/steps/FaceMatchStep.tsx +177 -0
  497. package/src/steps/FingerprintStep.tsx +99 -0
  498. package/src/steps/HumanBasicInfoStep.tsx +274 -0
  499. package/src/steps/HumanExtendedInfoStep.tsx +353 -0
  500. package/src/steps/LegalBasicInfoStep.tsx +205 -0
  501. package/src/steps/LegalExtendedInfoStep.tsx +171 -0
  502. package/src/steps/LivenessMatchStep.tsx +846 -0
  503. package/src/steps/PhoneContactStep.tsx +160 -0
  504. package/src/steps/PhoneOtpStep.tsx +249 -0
  505. package/src/steps/SwornDeclarationStep.tsx +220 -0
  506. package/src/steps/TaxInfoStep.tsx +277 -0
  507. package/src/steps/TermsAndConditionsStep.tsx +261 -0
  508. package/src/steps/_shared.ts +44 -0
  509. package/src/steps/types.ts +72 -0
  510. package/src/transport/api.ts +257 -0
  511. package/src/transport/irisWebSocket.ts +186 -0
  512. package/src/transport/secureUrl.ts +47 -0
  513. package/src/transport/sse.ts +310 -0
  514. package/src/types/index.ts +1198 -0
  515. package/src/ui/MapFallbackBoundary.tsx +46 -0
  516. package/src/ui/StepShell.tsx +137 -0
  517. package/src/ui/VelionOnboardingScreen.tsx +223 -0
  518. package/src/ui/forms/index.ts +12 -0
  519. package/src/ui/forms/useInlineValidation.ts +115 -0
  520. package/src/ui/forms/validators.ts +29 -0
  521. package/src/ui/icons/HomeHeartIcon.tsx +34 -0
  522. package/src/ui/icons/StepIcon.tsx +77 -0
  523. package/src/ui/overlays/AnalyzingOverlay.tsx +323 -0
  524. package/src/ui/overlays/LoadingOverlay.tsx +115 -0
  525. package/src/ui/overlays/ProgressBar.tsx +99 -0
  526. package/src/ui/overlays/RetryDialog.tsx +128 -0
  527. package/src/ui/overlays/index.ts +3 -0
  528. package/src/ui/primitives/Banner.tsx +81 -0
  529. package/src/ui/primitives/Button.tsx +164 -0
  530. package/src/ui/primitives/Card.tsx +55 -0
  531. package/src/ui/primitives/Checkbox.tsx +153 -0
  532. package/src/ui/primitives/CountrySelect.tsx +356 -0
  533. package/src/ui/primitives/DatePicker.tsx +266 -0
  534. package/src/ui/primitives/Input.tsx +110 -0
  535. package/src/ui/primitives/Select.tsx +259 -0
  536. package/src/ui/primitives/Spinner.tsx +17 -0
  537. package/src/ui/primitives/index.ts +9 -0
  538. package/src/ui/theme/ThemeProvider.tsx +102 -0
  539. package/src/ui/theme/index.ts +17 -0
  540. package/src/ui/theme/tokens.ts +159 -0
  541. package/src/utils/EventEmitter.ts +155 -0
  542. package/src/utils/base64.ts +44 -0
  543. package/src/utils/deviceFingerprint.ts +184 -0
  544. package/src/utils/documentTypeOptions.ts +31 -0
  545. package/src/utils/googleMapsPreflight.ts +72 -0
  546. package/src/utils/hmac.ts +61 -0
  547. package/src/utils/iso3166Countries.ts +111 -0
  548. package/src/utils/jpegFromUri.ts +42 -0
  549. package/src/utils/keepAwake.ts +62 -0
  550. package/src/utils/log.ts +37 -0
  551. package/src/utils/osGeocoder.ts +75 -0
  552. package/src/utils/parseGoogleAddressComponents.ts +123 -0
  553. package/src/utils/personNameNormalize.ts +17 -0
  554. package/src/utils/sdkError.ts +40 -0
  555. package/src/utils/sdkVersion.ts +6 -0
  556. package/src/utils/stepConfig.ts +75 -0
  557. package/src/utils/taxInfoConfig.ts +14 -0
@@ -0,0 +1,846 @@
1
+ /**
2
+ * LIVENESS_MATCH — Iris-driven active liveness over WebSocket.
3
+ *
4
+ * Flow:
5
+ * 1. POST /liveness-init → { liveness_session_id, websocket_url, expires_at,
6
+ * challenges, connection_token, frame_integrity_key_base64 }.
7
+ * 2. Optionally import the HMAC key for frame integrity.
8
+ * 3. Open vision-camera (front), connect WS with ?token & ?lang.
9
+ * 4. SESSION_READY → start frame capture loop (interval-driven), watchdog 25s.
10
+ * 5. Process CHALLENGE / CHALLENGE_PROGRESS / CHALLENGE_COMPLETE / WARNING /
11
+ * ERROR / SESSION_COMPLETE.
12
+ * 6. Terminal WARNING (SPOOF_RISK_HIGH, …) → in-flow retry dialog (re-init
13
+ * + fresh WS) without bailing the whole flow.
14
+ * 7. SESSION_COMPLETE → POST /liveness-verify → resolve the step.
15
+ */
16
+
17
+ import { useCallback, useEffect, useRef, useState } from 'react';
18
+ import { ScrollView, StyleSheet, Text, View } from 'react-native';
19
+ import * as ImageManipulator from 'expo-image-manipulator';
20
+ import {
21
+ SdkErrorCode,
22
+ type LivenessSessionResponse,
23
+ type SdkError,
24
+ } from '../types';
25
+ import { Banner } from '../ui/primitives/Banner';
26
+ import { Button } from '../ui/primitives/Button';
27
+ import { AnalyzingOverlay } from '../ui/overlays/AnalyzingOverlay';
28
+ import { RetryDialog } from '../ui/overlays/RetryDialog';
29
+ import { StepShell } from '../ui/StepShell';
30
+ import { StepIcon } from '../ui/icons/StepIcon';
31
+ import { mixHex, useTheme } from '../ui/theme';
32
+ import { getStrings } from '../i18n';
33
+ import { log } from '../utils/log';
34
+ import { readJpegFromUri } from '../utils/jpegFromUri';
35
+ import {
36
+ buildSignedFrameEnvelope,
37
+ importHmacKey,
38
+ type HmacKey,
39
+ } from '../utils/hmac';
40
+ import {
41
+ TERMINAL_WARNING_CODES,
42
+ openIrisWebSocket,
43
+ type IrisChallenge,
44
+ type IrisConnection,
45
+ type IrisServerMessage,
46
+ } from '../transport/irisWebSocket';
47
+ import {
48
+ LivenessCamera,
49
+ type LivenessCameraHandle,
50
+ type LivenessOvalCue,
51
+ } from '../camera/LivenessCamera';
52
+ import type { ApiCaller } from '../transport/api';
53
+ import type { StepDeps, StepModule } from './types';
54
+
55
+ // Capture loop defaults tuned for the Iris orchestrator's frame cadence.
56
+ const FRAME_MS_DEFAULT = 350;
57
+ const FRAME_MS_TURN = 220;
58
+ const PROGRESS_WATCHDOG_MS = 25_000;
59
+ const FRAME_MAX_LONG_EDGE = 720;
60
+ const FRAME_QUALITY = 0.55;
61
+
62
+ export class LivenessMatchStep implements StepModule {
63
+ private resolveStep!: () => void;
64
+ private rejectStep!: (err: SdkError) => void;
65
+ private destroyed = false;
66
+
67
+ constructor(private readonly deps: StepDeps) {}
68
+
69
+ run(): Promise<void> {
70
+ return new Promise((resolve, reject) => {
71
+ this.resolveStep = resolve;
72
+ this.rejectStep = reject;
73
+ this.deps.ui.setNode(
74
+ <LivenessMatchView
75
+ api={this.deps.api}
76
+ sessionId={this.deps.sessionId}
77
+ locale={this.deps.locale}
78
+ calibrationLabel={this.deps.livenessCalibrationLabel}
79
+ showIntro
80
+ showBackButton
81
+ onComplete={() => {
82
+ if (this.destroyed) return;
83
+ this.deps.ui.setNode(null);
84
+ this.resolveStep();
85
+ }}
86
+ onCancel={(err) => {
87
+ if (this.destroyed) return;
88
+ this.deps.ui.setNode(null);
89
+ this.rejectStep(err);
90
+ }}
91
+ />,
92
+ );
93
+ });
94
+ }
95
+
96
+ destroy(): void {
97
+ this.destroyed = true;
98
+ this.deps.ui.setNode(null);
99
+ }
100
+ }
101
+
102
+ interface ViewProps {
103
+ api: ApiCaller;
104
+ sessionId: string;
105
+ locale: string;
106
+ /** Optional Iris calibration label forwarded to /liveness-init body. */
107
+ calibrationLabel?: string | undefined;
108
+ showIntro: boolean;
109
+ showBackButton: boolean;
110
+ onComplete: () => void;
111
+ onCancel: (err: SdkError) => void;
112
+ }
113
+
114
+ type Phase = 'intro' | 'capturing' | 'finalizing';
115
+
116
+ function LivenessMatchView({
117
+ api,
118
+ sessionId,
119
+ locale,
120
+ calibrationLabel,
121
+ showIntro,
122
+ showBackButton,
123
+ onComplete,
124
+ onCancel,
125
+ }: ViewProps) {
126
+ const t = getStrings(locale);
127
+ const theme = useTheme();
128
+ const cameraRef = useRef<LivenessCameraHandle>(null);
129
+
130
+ const [phase, setPhase] = useState<Phase>(showIntro ? 'intro' : 'capturing');
131
+ const [statusText, setStatusText] = useState<string | null>(null);
132
+ const [challengeText, setChallengeText] = useState<string | null>(null);
133
+ const [ovalCue, setOvalCue] = useState<LivenessOvalCue>('NEUTRAL');
134
+ const [toast, setToast] = useState<
135
+ | {
136
+ kind: 'success' | 'warning';
137
+ message: string;
138
+ }
139
+ | null
140
+ >(null);
141
+ const [progress, setProgress] = useState<number | null>(null);
142
+ const [retry, setRetry] = useState<{ message: string; code: string } | null>(null);
143
+ const [bannerError, setBannerError] = useState<string | null>(null);
144
+
145
+ // Long-lived state that the capture loop / WS handlers need without
146
+ // triggering re-renders: kept in refs.
147
+ const wsRef = useRef<IrisConnection | null>(null);
148
+ const hmacRef = useRef<HmacKey | null>(null);
149
+ const livenessSessionIdRef = useRef<string | null>(null);
150
+ const currentChallengeRef = useRef<IrisChallenge | null>(null);
151
+ // Tracks the position of the active challenge inside the session sequence
152
+ // so we can recognise the LAST challenge completing and switch to the
153
+ // "Analyzing" overlay immediately, instead of letting the user stare at
154
+ // the camera with the last instruction frozen on screen for the seconds
155
+ // it takes the worker to drain its queue and the orchestrator to emit
156
+ // SESSION_COMPLETE.
157
+ const lastChallengeMetaRef = useRef<{ index: number; total: number } | null>(
158
+ null,
159
+ );
160
+ const frameMsRef = useRef<number>(FRAME_MS_DEFAULT);
161
+ const captureTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
162
+ const inFlightRef = useRef(false);
163
+ const framesSentRef = useRef(0);
164
+ const watchdogRef = useRef<ReturnType<typeof setTimeout> | null>(null);
165
+ const finalizingRef = useRef(false);
166
+ const toastTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
167
+ // Tracks the toast currently on screen so we can extend its visibility when
168
+ // the same message arrives again (instead of dismount/remount, which made
169
+ // warnings flash in <2s and the user couldn't read them).
170
+ const activeToastRef = useRef<{ kind: 'success' | 'warning'; message: string } | null>(null);
171
+
172
+ // Each toast stays on screen at least this long. The orchestrator can spam
173
+ // WARNINGs (POSITIONING_REQUIRED, SPOOF_RISK_HIGH, …) several times per
174
+ // second; without a floor the user literally cannot read them.
175
+ const TOAST_MIN_DURATION_MS = 3200;
176
+
177
+ const showToast = useCallback(
178
+ (kind: 'success' | 'warning', message: string) => {
179
+ const active = activeToastRef.current;
180
+ if (active && active.kind === kind && active.message === message) {
181
+ // Identical toast already visible — just refresh the timer so it
182
+ // stays up while warnings keep arriving. Do NOT touch `setToast`:
183
+ // a new object would re-trigger the entry animation and flicker.
184
+ if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
185
+ toastTimerRef.current = setTimeout(() => {
186
+ setToast(null);
187
+ activeToastRef.current = null;
188
+ }, TOAST_MIN_DURATION_MS);
189
+ return;
190
+ }
191
+ // Different content: replace and guarantee the new toast its own
192
+ // minimum window.
193
+ activeToastRef.current = { kind, message };
194
+ setToast({ kind, message });
195
+ if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
196
+ toastTimerRef.current = setTimeout(() => {
197
+ setToast(null);
198
+ activeToastRef.current = null;
199
+ }, TOAST_MIN_DURATION_MS);
200
+ },
201
+ [],
202
+ );
203
+
204
+ const stopWatchdog = useCallback(() => {
205
+ if (watchdogRef.current) {
206
+ clearTimeout(watchdogRef.current);
207
+ watchdogRef.current = null;
208
+ }
209
+ }, []);
210
+
211
+ const stopCapture = useCallback(() => {
212
+ if (captureTimerRef.current) {
213
+ clearInterval(captureTimerRef.current);
214
+ captureTimerRef.current = null;
215
+ }
216
+ inFlightRef.current = false;
217
+ }, []);
218
+
219
+ const failWith = useCallback(
220
+ (err: SdkError) => {
221
+ stopWatchdog();
222
+ stopCapture();
223
+ try {
224
+ wsRef.current?.close(1000, 'fail');
225
+ } catch {
226
+ /* ignore */
227
+ }
228
+ wsRef.current = null;
229
+ onCancel(err);
230
+ },
231
+ [onCancel, stopCapture, stopWatchdog],
232
+ );
233
+
234
+ const captureAndSend = useCallback(async () => {
235
+ if (inFlightRef.current) return;
236
+ const ws = wsRef.current;
237
+ if (!ws || !ws.isOpen()) return;
238
+ inFlightRef.current = true;
239
+ try {
240
+ const photo = await cameraRef.current?.takeFrame();
241
+ if (!photo || !ws.isOpen()) return;
242
+ const compressed = await ImageManipulator.manipulateAsync(
243
+ photo.path.startsWith('file://') ? photo.path : `file://${photo.path}`,
244
+ [{ resize: { width: FRAME_MAX_LONG_EDGE } }],
245
+ { compress: FRAME_QUALITY, format: ImageManipulator.SaveFormat.JPEG },
246
+ );
247
+ if (!ws.isOpen()) return;
248
+ const jpeg = await readJpegFromUri(compressed.uri);
249
+ const key = hmacRef.current;
250
+ if (key) {
251
+ const envelope = buildSignedFrameEnvelope({
252
+ key,
253
+ sessionId: livenessSessionIdRef.current ?? sessionId,
254
+ frameId: framesSentRef.current,
255
+ jpegBytes: jpeg.bytes,
256
+ jpegBase64: jpeg.base64,
257
+ });
258
+ ws.sendJson(envelope.json);
259
+ } else {
260
+ // Vision-camera + RN's WebSocket need an ArrayBuffer for binary frames.
261
+ // `Uint8Array.buffer` is a `SharedArrayBuffer` on some Hermes versions
262
+ // — slice into a fresh ArrayBuffer to be safe.
263
+ const ab = jpeg.bytes.buffer.slice(
264
+ jpeg.bytes.byteOffset,
265
+ jpeg.bytes.byteOffset + jpeg.bytes.byteLength,
266
+ ) as ArrayBuffer;
267
+ ws.sendBinary(ab);
268
+ }
269
+ framesSentRef.current += 1;
270
+ } catch (err) {
271
+ log.warn('[Liveness] frame send failed', err);
272
+ } finally {
273
+ inFlightRef.current = false;
274
+ }
275
+ }, [sessionId]);
276
+
277
+ const startCaptureLoop = useCallback(
278
+ (ms: number) => {
279
+ stopCapture();
280
+ frameMsRef.current = ms;
281
+ captureTimerRef.current = setInterval(() => {
282
+ void captureAndSend();
283
+ }, ms);
284
+ },
285
+ [captureAndSend, stopCapture],
286
+ );
287
+
288
+ const adjustFps = useCallback(
289
+ (ms: number) => {
290
+ if (frameMsRef.current === ms) return;
291
+ if (captureTimerRef.current) {
292
+ startCaptureLoop(ms);
293
+ } else {
294
+ frameMsRef.current = ms;
295
+ }
296
+ },
297
+ [startCaptureLoop],
298
+ );
299
+
300
+ const challengeLabel = useCallback(
301
+ (id: IrisChallenge | null): string => {
302
+ if (!id) return '';
303
+ return t.liveness.challenge[id] ?? '';
304
+ },
305
+ [t.liveness.challenge],
306
+ );
307
+
308
+ const presentTerminalRetry = useCallback(
309
+ (code: string, message?: string) => {
310
+ stopCapture();
311
+ stopWatchdog();
312
+ try {
313
+ wsRef.current?.close(4001, 'terminal_warning_retry_pending');
314
+ } catch {
315
+ /* ignore */
316
+ }
317
+ wsRef.current = null;
318
+ setRetry({ code, message: message?.trim() || t.liveness.terminalRetryFallback });
319
+ },
320
+ [stopCapture, stopWatchdog, t.liveness.terminalRetryFallback],
321
+ );
322
+
323
+ const resetWatchdog = useCallback(() => {
324
+ stopWatchdog();
325
+ watchdogRef.current = setTimeout(() => {
326
+ log.warn('[Liveness] progress watchdog fired');
327
+ // The watchdog used to be a hard fail — onCancel → step rejects →
328
+ // orchestrator surfaces the error and exits the whole liveness step.
329
+ // That left the user with no in-flow recovery: they had to restart
330
+ // the entire onboarding. The Iris-side "no progress in N seconds" is
331
+ // usually a recoverable user-side issue (lighting, framing, not
332
+ // looking at the camera), so we treat it the same way as a terminal
333
+ // WARNING: tear down the WS, show the retry dialog, and let the user
334
+ // restart the verification without leaving the step.
335
+ presentTerminalRetry('NO_PROGRESS', t.liveness.noProgressMessage);
336
+ }, PROGRESS_WATCHDOG_MS);
337
+ }, [presentTerminalRetry, stopWatchdog, t.liveness.noProgressMessage]);
338
+
339
+ const handleWsMessage = useCallback(
340
+ (msg: IrisServerMessage) => {
341
+ switch (msg.type) {
342
+ case 'SESSION_READY': {
343
+ setStatusText(null);
344
+ // The ~1-2 s gap between SESSION_READY and the first CHALLENGE is
345
+ // where users decide their posture for the whole session. We use
346
+ // it for two pieces of guidance, layered:
347
+ // - A short "Heads up — starting now" toast above the oval so
348
+ // they're not caught looking away when (e.g.) the BLINK window
349
+ // opens.
350
+ // - A persistent `challengeText` ("Center your face in the oval")
351
+ // anchored above the oval so they keep adjusting until the
352
+ // first real challenge arrives and overwrites it. Without
353
+ // this, every Android calibration session started with the
354
+ // face at ~25 % of the frame width and SMILE / MOVE_FARTHER
355
+ // failed before the user understood what was being asked.
356
+ setChallengeText(t.liveness.centerFace);
357
+ showToast('success', t.liveness.getReady);
358
+ startCaptureLoop(FRAME_MS_DEFAULT);
359
+ resetWatchdog();
360
+ return;
361
+ }
362
+ case 'CHALLENGE': {
363
+ const ch = (msg.challenge ?? null) as IrisChallenge | null;
364
+ currentChallengeRef.current = ch;
365
+ // Stash the sequence position so CHALLENGE_COMPLETE can tell
366
+ // whether this was the LAST challenge and switch to the analyzing
367
+ // overlay immediately instead of waiting for SESSION_COMPLETE.
368
+ if (
369
+ typeof msg.sequenceIndex === 'number' &&
370
+ typeof msg.totalChallenges === 'number'
371
+ ) {
372
+ lastChallengeMetaRef.current = {
373
+ index: msg.sequenceIndex,
374
+ total: msg.totalChallenges,
375
+ };
376
+ }
377
+ adjustFps(
378
+ ch === 'TURN_LEFT' || ch === 'TURN_RIGHT'
379
+ ? FRAME_MS_TURN
380
+ : FRAME_MS_DEFAULT,
381
+ );
382
+ setChallengeText(challengeLabel(ch));
383
+ // Map distance challenges to an oval-size cue so the viewfinder
384
+ // shape reinforces the instruction (grows for closer, returns to
385
+ // neutral for farther). All other challenges keep the neutral
386
+ // oval — the dim mask shouldn't move under a turn / smile / blink.
387
+ setOvalCue(
388
+ ch === 'MOVE_CLOSER'
389
+ ? 'MOVE_CLOSER'
390
+ : ch === 'MOVE_FARTHER'
391
+ ? 'MOVE_FARTHER'
392
+ : 'NEUTRAL',
393
+ );
394
+ setProgress(0);
395
+ resetWatchdog();
396
+ return;
397
+ }
398
+ case 'CHALLENGE_PROGRESS': {
399
+ if (typeof msg.confidence === 'number') {
400
+ setProgress(msg.confidence);
401
+ }
402
+ resetWatchdog();
403
+ return;
404
+ }
405
+ case 'CHALLENGE_COMPLETE': {
406
+ setProgress(1);
407
+ currentChallengeRef.current = null;
408
+ adjustFps(FRAME_MS_DEFAULT);
409
+ // Reset the oval back to neutral after each challenge so the
410
+ // next CHALLENGE (or finalize) doesn't inherit a "zoomed" oval.
411
+ setOvalCue('NEUTRAL');
412
+ // Last challenge of the sequence completed → switch to the
413
+ // "Analyzing your video" overlay right away. SESSION_COMPLETE
414
+ // can take several seconds to land while the worker drains
415
+ // its queue; without this the user would stare at the camera
416
+ // and the stale instruction the whole time.
417
+ const meta = lastChallengeMetaRef.current;
418
+ if (meta && meta.index >= meta.total - 1) {
419
+ stopCapture();
420
+ setPhase('finalizing');
421
+ }
422
+ resetWatchdog();
423
+ return;
424
+ }
425
+ case 'WARNING': {
426
+ if (msg.code && TERMINAL_WARNING_CODES.has(msg.code)) {
427
+ presentTerminalRetry(msg.code, msg.message);
428
+ return;
429
+ }
430
+ const isPositioningReady = msg.code === 'POSITIONING_READY';
431
+ // Per-code SDK-side fallback used when the orchestrator forwarded a
432
+ // code but no localised text (e.g. older orchestrator versions, or
433
+ // the rare WS message that drops the body). The orchestrator's own
434
+ // text is preferred — it always wins when present — so the user sees
435
+ // the server's exact wording in the session's locale.
436
+ const FALLBACK_BY_CODE: Record<string, string> = {
437
+ TOO_FAR: t.liveness.warningTooFar,
438
+ TOO_CLOSE: t.liveness.warningTooClose,
439
+ MOVE_CLOSER_HINT: t.liveness.warningMoveCloserHint,
440
+ MOVE_FARTHER_HINT: t.liveness.warningMoveFartherHint,
441
+ };
442
+ // Timeout fallback is dynamic: the orchestrator now sends a
443
+ // challenge-specific message ("No pudimos reconocer el giro hacia
444
+ // la izquierda"), but if it ever drops it the SDK rebuilds the
445
+ // same text from the locally tracked active challenge.
446
+ let timeoutFallback: string | undefined;
447
+ if (msg.code === 'CHALLENGE_TIMEOUT') {
448
+ const ch = currentChallengeRef.current;
449
+ switch (ch) {
450
+ case 'TURN_LEFT': timeoutFallback = t.liveness.timeoutTurnLeft; break;
451
+ case 'TURN_RIGHT': timeoutFallback = t.liveness.timeoutTurnRight; break;
452
+ case 'BLINK': timeoutFallback = t.liveness.timeoutBlink; break;
453
+ case 'SMILE': timeoutFallback = t.liveness.timeoutSmile; break;
454
+ case 'MOVE_CLOSER': timeoutFallback = t.liveness.timeoutMoveCloser; break;
455
+ case 'MOVE_FARTHER': timeoutFallback = t.liveness.timeoutMoveFarther; break;
456
+ default: timeoutFallback = t.liveness.timeoutGeneric;
457
+ }
458
+ }
459
+ const codedFallback =
460
+ timeoutFallback ??
461
+ (msg.code ? FALLBACK_BY_CODE[msg.code] : undefined) ??
462
+ t.liveness.warningGeneric;
463
+ // Prefer the server-localised message — it's situationally
464
+ // accurate ("El fondo tiene mucho movimiento o reflejos. Pasá a
465
+ // un fondo neutro y plano"). Drop to the SDK fallback only when
466
+ // the orchestrator did not provide one.
467
+ const baseText = msg.message ?? codedFallback;
468
+ const text = isPositioningReady
469
+ ? t.liveness.positioningReady
470
+ : typeof msg.consecutiveMisses === 'number' &&
471
+ msg.consecutiveMisses >= 3
472
+ ? t.liveness.warningConsecutiveMisses
473
+ .replace('{n}', String(msg.consecutiveMisses))
474
+ .concat(' — ', baseText)
475
+ : baseText;
476
+ showToast(isPositioningReady ? 'success' : 'warning', text);
477
+ resetWatchdog();
478
+ return;
479
+ }
480
+ case 'ERROR': {
481
+ failWith({
482
+ code: SdkErrorCode.LIVENESS_FAILED,
483
+ message:
484
+ msg.message ?? msg.code ?? 'Iris reported an error during liveness.',
485
+ });
486
+ return;
487
+ }
488
+ case 'SESSION_COMPLETE': {
489
+ stopWatchdog();
490
+ if (finalizingRef.current) return;
491
+ finalizingRef.current = true;
492
+ stopCapture();
493
+ try {
494
+ wsRef.current?.close(1000, 'session_complete');
495
+ } catch {
496
+ /* ignore */
497
+ }
498
+ wsRef.current = null;
499
+ setPhase('finalizing');
500
+ (async () => {
501
+ try {
502
+ await api.post(`/sessions/${sessionId}/liveness-verify`, {});
503
+ onComplete();
504
+ } catch (err) {
505
+ const message =
506
+ err && typeof err === 'object' && 'message' in err
507
+ ? String((err as { message?: unknown }).message)
508
+ : 'Failed to notify the backend that liveness completed.';
509
+ failWith({
510
+ code: SdkErrorCode.LIVENESS_FAILED,
511
+ message,
512
+ cause: err,
513
+ });
514
+ }
515
+ })();
516
+ return;
517
+ }
518
+ default:
519
+ return;
520
+ }
521
+ },
522
+ [
523
+ adjustFps,
524
+ api,
525
+ challengeLabel,
526
+ failWith,
527
+ onComplete,
528
+ presentTerminalRetry,
529
+ resetWatchdog,
530
+ sessionId,
531
+ showToast,
532
+ startCaptureLoop,
533
+ stopCapture,
534
+ stopWatchdog,
535
+ t.liveness.getReady,
536
+ t.liveness.positioningReady,
537
+ t.liveness.warningConsecutiveMisses,
538
+ t.liveness.warningGeneric,
539
+ ],
540
+ );
541
+
542
+ const startVerification = useCallback(async () => {
543
+ setBannerError(null);
544
+ setRetry(null);
545
+ setStatusText(t.liveness.connecting);
546
+ finalizingRef.current = false;
547
+ framesSentRef.current = 0;
548
+ currentChallengeRef.current = null;
549
+ lastChallengeMetaRef.current = null;
550
+ frameMsRef.current = FRAME_MS_DEFAULT;
551
+ hmacRef.current = null;
552
+
553
+ let init: LivenessSessionResponse;
554
+ try {
555
+ // `calibration_label` is the wire name expected by the backend
556
+ // (snake_case mirroring iris-test's `/sessions` POST). Only sent when
557
+ // the host explicitly enables calibration via VelionConfig.
558
+ const body: Record<string, unknown> = {};
559
+ if (calibrationLabel && calibrationLabel.length > 0) {
560
+ body.calibration_label = calibrationLabel;
561
+ }
562
+ init = await api.post<LivenessSessionResponse>(
563
+ `/sessions/${sessionId}/liveness-init`,
564
+ body,
565
+ );
566
+ } catch (err) {
567
+ failWith({
568
+ code: SdkErrorCode.LIVENESS_INIT_FAILED,
569
+ message:
570
+ (err && typeof err === 'object' && 'message' in err
571
+ ? String((err as { message?: unknown }).message)
572
+ : '') || 'Failed to create the Iris liveness session.',
573
+ cause: err,
574
+ });
575
+ return;
576
+ }
577
+ if (!init.liveness_session_id || !init.websocket_url) {
578
+ failWith({
579
+ code: SdkErrorCode.LIVENESS_INIT_FAILED,
580
+ message: 'liveness-init response missing required fields.',
581
+ });
582
+ return;
583
+ }
584
+ livenessSessionIdRef.current = init.liveness_session_id;
585
+ if (init.frame_integrity_key_base64) {
586
+ try {
587
+ hmacRef.current = importHmacKey(init.frame_integrity_key_base64);
588
+ } catch (err) {
589
+ log.warn('[Liveness] failed to import HMAC key', err);
590
+ hmacRef.current = null;
591
+ }
592
+ }
593
+
594
+ setStatusText(t.liveness.preparingCamera);
595
+ let connection: IrisConnection;
596
+ try {
597
+ connection = openIrisWebSocket({
598
+ websocketUrl: init.websocket_url,
599
+ connectionToken: init.connection_token,
600
+ locale,
601
+ onOpen: () => setStatusText(t.liveness.connecting),
602
+ onMessage: handleWsMessage,
603
+ onClose: (code, reason) => {
604
+ if (finalizingRef.current) return;
605
+ // Code 4001 is the close we send ourselves from
606
+ // `presentTerminalRetry` to tear down the WS before showing the
607
+ // retry dialog. Checking the React state `retry` here is unsafe
608
+ // — this handler is registered via closure on a stale render,
609
+ // so `retry` reads `null` even when we just called `setRetry`.
610
+ // The close code is reliable: we always pair 4001 with the
611
+ // retry-dialog mount.
612
+ if (code === 4001) return;
613
+ stopCapture();
614
+ stopWatchdog();
615
+ if (code === 1000) return;
616
+ failWith({
617
+ code: SdkErrorCode.LIVENESS_FAILED,
618
+ message: `Iris WebSocket closed before completion (code=${code}${
619
+ reason ? `, reason=${reason}` : ''
620
+ }).`,
621
+ });
622
+ },
623
+ });
624
+ } catch (err) {
625
+ failWith({
626
+ code: SdkErrorCode.LIVENESS_FAILED,
627
+ message:
628
+ err instanceof Error
629
+ ? err.message
630
+ : 'Failed to open the Iris WebSocket.',
631
+ cause: err,
632
+ });
633
+ return;
634
+ }
635
+ wsRef.current = connection;
636
+ }, [
637
+ api,
638
+ calibrationLabel,
639
+ failWith,
640
+ handleWsMessage,
641
+ locale,
642
+ retry,
643
+ sessionId,
644
+ stopCapture,
645
+ stopWatchdog,
646
+ t.liveness.connecting,
647
+ t.liveness.preparingCamera,
648
+ ]);
649
+
650
+ // Re-run after a terminal retry — same cleanup as start, but reuses the
651
+ // mounted camera (no DOM teardown).
652
+ const restartVerification = useCallback(async () => {
653
+ setRetry(null);
654
+ setProgress(null);
655
+ setChallengeText(null);
656
+ setOvalCue('NEUTRAL');
657
+ await startVerification();
658
+ }, [startVerification]);
659
+
660
+ // Effect: when the user enters the capture phase (intro skipped or
661
+ // confirmed), run the start sequence.
662
+ useEffect(() => {
663
+ if (phase !== 'capturing') return;
664
+ void startVerification();
665
+ return () => {
666
+ stopCapture();
667
+ stopWatchdog();
668
+ if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
669
+ try {
670
+ wsRef.current?.close(1000, 'unmount');
671
+ } catch {
672
+ /* ignore */
673
+ }
674
+ wsRef.current = null;
675
+ };
676
+ // We intentionally only re-run when phase flips into capturing — the
677
+ // first start; the retry path is handled by `restartVerification`.
678
+ // eslint-disable-next-line react-hooks/exhaustive-deps
679
+ }, [phase]);
680
+
681
+ if (retry) {
682
+ return (
683
+ <RetryDialog
684
+ title={t.liveness.terminalRetryTitle}
685
+ message={retry.message}
686
+ retryLabel={t.liveness.terminalRetryButton}
687
+ cancelLabel={t.liveness.terminalCancelButton}
688
+ onRetry={() => {
689
+ void restartVerification();
690
+ }}
691
+ onCancel={() => {
692
+ setRetry(null);
693
+ onCancel({
694
+ code: SdkErrorCode.LIVENESS_FAILED,
695
+ message: retry.message,
696
+ });
697
+ }}
698
+ />
699
+ );
700
+ }
701
+
702
+ if (phase === 'intro') {
703
+ return (
704
+ <View style={{ flex: 1 }}>
705
+ <StepShell
706
+ title={t.liveness.introTitle}
707
+ subtitle={t.liveness.introSubtitle}
708
+ iconNode={<StepIcon name="face" />}
709
+ >
710
+ <ScrollView contentContainerStyle={styles.tipsList}>
711
+ {t.liveness.introTips.map((tip, idx) => (
712
+ <View
713
+ key={idx}
714
+ style={[
715
+ styles.tipRow,
716
+ {
717
+ borderColor: theme.borderColor,
718
+ backgroundColor: mixHex(
719
+ theme.primaryColor,
720
+ theme.backgroundColor,
721
+ 6,
722
+ ),
723
+ borderRadius: theme.borderRadiusPx,
724
+ },
725
+ ]}
726
+ >
727
+ <View
728
+ style={[
729
+ styles.tipIcon,
730
+ {
731
+ backgroundColor: mixHex(
732
+ theme.primaryColor,
733
+ theme.backgroundColor,
734
+ 18,
735
+ ),
736
+ },
737
+ ]}
738
+ >
739
+ <Text
740
+ style={[styles.tipIconText, { color: theme.primaryColor }]}
741
+ >
742
+ {idx + 1}
743
+ </Text>
744
+ </View>
745
+ <Text style={[styles.tipText, { color: theme.textColor }]}>
746
+ {tip}
747
+ </Text>
748
+ </View>
749
+ ))}
750
+ </ScrollView>
751
+ {bannerError ? <Banner message={bannerError} /> : null}
752
+ <View style={styles.introActions}>
753
+ {showBackButton ? (
754
+ <Button
755
+ variant="ghost"
756
+ label={t.liveness.introBack}
757
+ onPress={() =>
758
+ onCancel({
759
+ code: SdkErrorCode.LIVENESS_FAILED,
760
+ message: 'User exited the liveness intro.',
761
+ })
762
+ }
763
+ fullWidth={false}
764
+ style={styles.introBtn}
765
+ />
766
+ ) : null}
767
+ <Button
768
+ label={t.liveness.introStart}
769
+ onPress={() => setPhase('capturing')}
770
+ fullWidth={!showBackButton}
771
+ style={showBackButton ? styles.introBtn : undefined}
772
+ />
773
+ </View>
774
+ </StepShell>
775
+ </View>
776
+ );
777
+ }
778
+
779
+ if (phase === 'finalizing') {
780
+ return (
781
+ <AnalyzingOverlay
782
+ headline={t.liveness.analyzingHeadline}
783
+ caption={t.liveness.analyzingCaption}
784
+ />
785
+ );
786
+ }
787
+
788
+ return (
789
+ <View style={{ flex: 1 }}>
790
+ <LivenessCamera
791
+ ref={cameraRef}
792
+ statusText={statusText}
793
+ challengeText={challengeText}
794
+ ovalCue={ovalCue}
795
+ toast={toast}
796
+ progress={progress}
797
+ permissionDeniedLabel={t.liveness.cameraPermissionDenied}
798
+ retryLabel={t.liveness.cameraTryAgain}
799
+ cancelLabel={t.common.cancel}
800
+ // Intentionally no onCancel during capture. The capture screen is
801
+ // meant to be fullscreen and uninterruptible — if the user wants
802
+ // out, the 25 s progress watchdog catches stalled sessions and
803
+ // routes them through the RetryDialog (with its own Cancel option).
804
+ // The intro screen keeps the explicit "Atrás" button.
805
+ />
806
+ </View>
807
+ );
808
+ }
809
+
810
+ const styles = StyleSheet.create({
811
+ tipsList: {
812
+ paddingVertical: 4,
813
+ gap: 10,
814
+ },
815
+ tipRow: {
816
+ flexDirection: 'row',
817
+ alignItems: 'center',
818
+ gap: 12,
819
+ padding: 12,
820
+ borderWidth: 1,
821
+ },
822
+ tipIcon: {
823
+ width: 32,
824
+ height: 32,
825
+ borderRadius: 16,
826
+ alignItems: 'center',
827
+ justifyContent: 'center',
828
+ },
829
+ tipIconText: {
830
+ fontSize: 14,
831
+ fontWeight: '700',
832
+ },
833
+ tipText: {
834
+ flex: 1,
835
+ fontSize: 14,
836
+ lineHeight: 20,
837
+ },
838
+ introActions: {
839
+ flexDirection: 'row',
840
+ gap: 10,
841
+ marginTop: 6,
842
+ },
843
+ introBtn: {
844
+ flex: 1,
845
+ },
846
+ });