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

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 +107 -330
  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 +114 -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 +166 -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 +282 -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 +12 -1
  150. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  151. package/build/src/modules/api/KYCService.js +102 -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 +131 -385
  167. package/src/components/KYCElements/EmailVerificationTemplate.tsx +141 -26
  168. package/src/components/KYCElements/IDCardCapture.tsx +228 -868
  169. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +342 -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 +169 -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,9 +1,10 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert, Pressable, Modal, FlatList } from 'react-native';
3
3
  import { TemplateComponent, LocalizedText } from '../../types/KYC.types';
4
4
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
5
5
  import { useI18n } from '../../hooks/useI18n';
6
6
  import { Button } from '../ui/Button';
7
+ import kycService, { errorMessage } from '../../modules/api/KYCService';
7
8
 
8
9
  interface PhoneVerificationTemplateProps {
9
10
  component: TemplateComponent;
@@ -14,6 +15,20 @@ interface PhoneVerificationTemplateProps {
14
15
  }
15
16
 
16
17
  type VerificationStep = 'phone' | 'otp';
18
+ const CODE_LENGTH = 6;
19
+
20
+ // Easily expand this list with any countries your app supports
21
+ const COUNTRY_CODES = [
22
+ { code: '+254', label: '🇰🇪 Kenya (+254)' },
23
+ { code: '+255', label: '🇹🇿 Tanzania (+255)' },
24
+ { code: '+256', label: '🇺🇬 Uganda (+256)' },
25
+ { code: '+250', label: '🇷🇼 Rwanda (+250)' },
26
+ { code: '+234', label: '🇳🇬 Nigeria (+234)' },
27
+ { code: '+27', label: '🇿🇦 South Africa (+27)' },
28
+ { code: '+44', label: '🇬🇧 UK (+44)' },
29
+ { code: '+1', label: '🇺🇸 US/Canada (+1)' },
30
+ { code: '+91', label: '🇮🇳 India (+91)' },
31
+ ];
17
32
 
18
33
  export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps> = ({
19
34
  component,
@@ -21,27 +36,59 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
21
36
  onValueChange,
22
37
  error: propError,
23
38
  }) => {
24
- const { actions, getLocalizedText } = useTemplateKYCFlowContext();
39
+ const { actions, getLocalizedText, state, apiKey } = useTemplateKYCFlowContext();
25
40
  const { t } = useI18n();
26
- // const config = component.config as PhoneVerificationConfig;
41
+
42
+ const auth = apiKey ? { apiKey } : (state.session?.token ? { token: state.session.token } : undefined);
43
+ const sessionId = state.session?.session_id || '';
27
44
 
28
45
  // State
29
46
  const [step, setStep] = useState<VerificationStep>('phone');
47
+ const [countryCode, setCountryCode] = useState('+254');
30
48
  const [phone, setPhone] = useState('');
49
+ const [showCountryPicker, setShowCountryPicker] = useState(false);
50
+
31
51
  const [otp, setOtp] = useState('');
32
52
  const [localError, setLocalError] = useState<string | null>(null);
33
53
  const [isSimulating, setIsSimulating] = useState(false);
54
+
55
+ // 🚨 NEW: Track actual focus state for visual feedback
56
+ const [isInputFocused, setIsInputFocused] = useState(false);
57
+
58
+ const inputRef = useRef<TextInput>(null);
34
59
 
35
60
  const title = getLocalizedText(component.labels as LocalizedText);
36
61
  const instructions = getLocalizedText(component.instructions as LocalizedText);
37
62
 
38
- // Determine button text based on step
39
63
  const verifyButtonText = getLocalizedText((component.ui as any).buttonText) || t('common.verify') || 'Verify';
40
64
  const sendButtonText = t('common.sendCode') || 'Send Verification Code';
41
65
  const buttonText = step === 'phone' ? sendButtonText : verifyButtonText;
42
66
 
43
- const handleSendCode = () => {
44
- if (!phone || phone.length < 5) {
67
+ // --- AUTO SUBMIT LOGIC ---
68
+ useEffect(() => {
69
+ if (otp.length === CODE_LENGTH && step === 'otp' && !isSimulating) {
70
+ handleVerifyCode();
71
+ }
72
+ }, [otp]);
73
+
74
+ // --- AUTO FOCUS LOGIC ---
75
+ useEffect(() => {
76
+ let focusTimer: ReturnType<typeof setTimeout>;
77
+
78
+ if (step === 'otp' && !isSimulating) {
79
+ focusTimer = setTimeout(() => {
80
+ inputRef.current?.focus();
81
+ }, 100);
82
+ }
83
+
84
+ return () => {
85
+ if (focusTimer) clearTimeout(focusTimer);
86
+ };
87
+ }, [step, isSimulating]); // 🚨 Must watch both states
88
+
89
+ const handleSendCode = async () => {
90
+ const trimmedPhone = phone.trim();
91
+ if (!trimmedPhone || trimmedPhone.length < 5) {
45
92
  setLocalError(t('errors.invalidPhone') || 'Please enter a valid phone number');
46
93
  return;
47
94
  }
@@ -49,43 +96,56 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
49
96
  setLocalError(null);
50
97
  setIsSimulating(true);
51
98
 
52
- // Simulate API call to send code
53
- setTimeout(() => {
54
- setIsSimulating(false);
99
+ const fullPhoneNumber = `${countryCode}${trimmedPhone}`;
100
+
101
+ try {
102
+ await kycService.sendWhatsAppVerificationCode(sessionId, fullPhoneNumber, auth);
55
103
  setStep('otp');
56
- }, 1500);
104
+ } catch (err: any) {
105
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send verification code');
106
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
107
+ } finally {
108
+ setIsSimulating(false);
109
+ }
57
110
  };
58
111
 
59
- const handleVerifyCode = () => {
60
- if (!otp || otp.length < 4) {
112
+ const handleVerifyCode = async () => {
113
+ if (!otp || otp.length < CODE_LENGTH) {
61
114
  setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
62
115
  return;
63
116
  }
64
117
 
65
118
  setLocalError(null);
66
119
  setIsSimulating(true);
120
+
121
+ const fullPhoneNumber = `${countryCode}${phone.trim()}`;
67
122
 
68
- // Simulate verification API
69
- setTimeout(() => {
123
+ try {
124
+ await kycService.verifyWhatsAppCode(sessionId, otp.trim(), fullPhoneNumber, auth);
125
+
126
+ const data = { phone: fullPhoneNumber, otp, verified: true };
127
+ onValueChange(data);
128
+ actions.nextComponent(data);
129
+ } catch (err: any) {
130
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.wrongCode') || 'Invalid verification code');
131
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
132
+ setOtp('');
133
+ // Refocus so they can type immediately after an error
134
+ inputRef.current?.focus();
135
+ } finally {
70
136
  setIsSimulating(false);
71
-
72
- // Mock validation logic
73
- if (otp === '123456') {
74
- onValueChange({ phone, otp, verified: true });
75
- actions.nextComponent();
76
- } else {
77
- setLocalError(t('errors.wrongCode') || 'Invalid verification code. Try 123456');
78
- }
79
- }, 1500);
137
+ }
80
138
  };
81
139
 
82
140
  const onChangePhone = (text: string) => {
83
- setPhone(text);
141
+ const cleaned = text.replace(/[^0-9]/g, '');
142
+ setPhone(cleaned);
84
143
  if (localError) setLocalError(null);
85
144
  };
86
145
 
87
146
  const onChangeOtp = (text: string) => {
88
- setOtp(text);
147
+ const cleaned = text.replace(/[^0-9]/g, '');
148
+ setOtp(cleaned);
89
149
  if (localError) setLocalError(null);
90
150
  };
91
151
 
@@ -95,40 +155,91 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
95
155
  setLocalError(null);
96
156
  };
97
157
 
158
+ const renderOtpBoxes = () => {
159
+ const boxes = new Array(CODE_LENGTH).fill(0);
160
+ return (
161
+ <Pressable style={styles.otpBoxesContainer} onPress={() => inputRef.current?.focus()}>
162
+ {boxes.map((_, index) => {
163
+ const digit = otp[index] || '';
164
+ const isFilled = index < otp.length;
165
+
166
+ // 🚨 NEW: Only highlight if the input is ACTUALLY focused.
167
+ // Highlights the next empty box, or the last box if full.
168
+ const isActiveIndex = index === otp.length || (index === CODE_LENGTH - 1 && otp.length === CODE_LENGTH);
169
+ const isCurrent = isInputFocused && isActiveIndex;
170
+
171
+ return (
172
+ <View
173
+ key={index}
174
+ style={[
175
+ styles.otpBox,
176
+ isFilled && styles.otpBoxFilled,
177
+ isCurrent && styles.otpBoxActive // Active overrides filled
178
+ ]}
179
+ >
180
+ <Text style={styles.otpBoxText}>{digit}</Text>
181
+ </View>
182
+ );
183
+ })}
184
+ </Pressable>
185
+ );
186
+ };
187
+
98
188
  return (
99
189
  <View style={styles.container}>
100
190
  <Text style={styles.title}>{title}</Text>
101
191
  <Text style={styles.instructions}>
102
- {step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${phone}`)}
192
+ {step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${countryCode} ${phone}`)}
103
193
  </Text>
104
194
 
105
195
  <View style={styles.contentContainer}>
106
196
  {step === 'phone' ? (
107
197
  <View style={styles.inputContainer}>
108
198
  <Text style={styles.label}>{t('common.phone') || 'Phone Number'}</Text>
109
- <TextInput
110
- style={styles.input}
111
- placeholder="+1234567890"
112
- value={phone}
113
- onChangeText={onChangePhone}
114
- keyboardType="phone-pad"
115
- autoComplete="tel"
116
- editable={!isSimulating}
117
- />
199
+
200
+ <View style={styles.phoneInputRow}>
201
+ <TouchableOpacity
202
+ style={styles.countryPickerBtn}
203
+ onPress={() => setShowCountryPicker(true)}
204
+ disabled={isSimulating}
205
+ >
206
+ <Text style={styles.countryPickerText}>{countryCode}</Text>
207
+ <Text style={styles.dropdownIcon}>▼</Text>
208
+ </TouchableOpacity>
209
+
210
+ <TextInput
211
+ style={styles.phoneInput}
212
+ placeholder="712 345 678"
213
+ value={phone}
214
+ onChangeText={onChangePhone}
215
+ keyboardType="phone-pad"
216
+ autoComplete="tel"
217
+ editable={!isSimulating}
218
+ />
219
+ </View>
118
220
  </View>
119
221
  ) : (
120
222
  <View style={styles.inputContainer}>
121
223
  <Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>
122
- <TextInput
123
- style={styles.input}
124
- placeholder="123456"
125
- value={otp}
126
- onChangeText={onChangeOtp}
127
- keyboardType="number-pad"
128
- maxLength={6}
129
- editable={!isSimulating}
130
- />
131
- <TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink}>
224
+
225
+ <View style={styles.otpWrapper}>
226
+ {renderOtpBoxes()}
227
+ <TextInput
228
+ ref={inputRef}
229
+ style={styles.hiddenInput}
230
+ value={otp}
231
+ onChangeText={onChangeOtp}
232
+ keyboardType="number-pad"
233
+ maxLength={CODE_LENGTH}
234
+ editable={!isSimulating}
235
+ textContentType="oneTimeCode"
236
+ caretHidden={true}
237
+ onFocus={() => setIsInputFocused(true)}
238
+ onBlur={() => setIsInputFocused(false)}
239
+ />
240
+ </View>
241
+
242
+ <TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink} disabled={isSimulating}>
132
243
  <Text style={styles.changeText}>{t('common.back') || 'Change number'}</Text>
133
244
  </TouchableOpacity>
134
245
  </View>
@@ -144,18 +255,31 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
144
255
  style={styles.button}
145
256
  disabled={
146
257
  isSimulating ||
147
- (step === 'phone' ? !phone : !otp)
258
+ (step === 'phone' ? !phone : otp.length < CODE_LENGTH)
148
259
  }
149
260
  />
150
261
 
151
262
  {step === 'otp' && (
152
263
  <TouchableOpacity
153
- onPress={() => {
154
- // Resend logic
155
- Alert.alert(
156
- t('common.codeResent') || 'Code Resent',
157
- t('common.codeResentMessage', { email: phone }) || 'Code resent to ' + phone
158
- );
264
+ onPress={async () => {
265
+ if (isSimulating) return;
266
+ setLocalError(null);
267
+ setIsSimulating(true);
268
+ const fullPhoneNumber = `${countryCode}${phone.trim()}`;
269
+ try {
270
+ await kycService.sendWhatsAppVerificationCode(sessionId, fullPhoneNumber, auth);
271
+ Alert.alert(
272
+ t('common.codeResent') || 'Code Resent',
273
+ t('common.codeResentMessage', { email: fullPhoneNumber }) || 'Code resent to ' + fullPhoneNumber
274
+ );
275
+ // Refocus after resending
276
+ inputRef.current?.focus();
277
+ } catch (err: any) {
278
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');
279
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
280
+ } finally {
281
+ setIsSimulating(false);
282
+ }
159
283
  }}
160
284
  style={styles.resendButton}
161
285
  disabled={isSimulating}
@@ -164,11 +288,51 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
164
288
  </TouchableOpacity>
165
289
  )}
166
290
  </View>
291
+
292
+ {/* COUNTRY PICKER MODAL */}
293
+ <Modal
294
+ visible={showCountryPicker}
295
+ animationType="slide"
296
+ transparent={true}
297
+ onRequestClose={() => setShowCountryPicker(false)}
298
+ >
299
+ <TouchableOpacity
300
+ style={styles.modalOverlay}
301
+ activeOpacity={1}
302
+ onPress={() => setShowCountryPicker(false)}
303
+ >
304
+ <View style={styles.modalContent}>
305
+ <View style={styles.modalHeader}>
306
+ <Text style={styles.modalTitle}>Select Country</Text>
307
+ <TouchableOpacity onPress={() => setShowCountryPicker(false)}>
308
+ <Text style={styles.modalClose}>✕</Text>
309
+ </TouchableOpacity>
310
+ </View>
311
+ <FlatList
312
+ data={COUNTRY_CODES}
313
+ keyExtractor={(item) => item.code}
314
+ renderItem={({ item }) => (
315
+ <TouchableOpacity
316
+ style={styles.countryItem}
317
+ onPress={() => {
318
+ setCountryCode(item.code);
319
+ setShowCountryPicker(false);
320
+ }}
321
+ >
322
+ <Text style={styles.countryItemLabel}>{item.label}</Text>
323
+ {countryCode === item.code && <Text style={styles.checkMark}>✓</Text>}
324
+ </TouchableOpacity>
325
+ )}
326
+ />
327
+ </View>
328
+ </TouchableOpacity>
329
+ </Modal>
167
330
  </View>
168
331
  );
169
332
  };
170
333
 
171
334
  const styles = StyleSheet.create({
335
+ // ... Keeping all previous styles identical for safety ...
172
336
  container: {
173
337
  padding: 24,
174
338
  backgroundColor: 'white',
@@ -195,9 +359,7 @@ const styles = StyleSheet.create({
195
359
  lineHeight: 24,
196
360
  textAlign: 'center',
197
361
  },
198
- contentContainer: {
199
- // width: '100%',
200
- },
362
+ contentContainer: {},
201
363
  inputContainer: {
202
364
  marginBottom: 24,
203
365
  },
@@ -208,7 +370,35 @@ const styles = StyleSheet.create({
208
370
  marginBottom: 8,
209
371
  marginLeft: 4,
210
372
  },
211
- input: {
373
+ phoneInputRow: {
374
+ flexDirection: 'row',
375
+ alignItems: 'center',
376
+ gap: 10,
377
+ },
378
+ countryPickerBtn: {
379
+ flexDirection: 'row',
380
+ alignItems: 'center',
381
+ justifyContent: 'space-between',
382
+ borderWidth: 1,
383
+ borderColor: '#e0e0e0',
384
+ paddingHorizontal: 12,
385
+ paddingVertical: 16,
386
+ borderRadius: 12,
387
+ backgroundColor: '#f8f9fa',
388
+ minWidth: 90,
389
+ },
390
+ countryPickerText: {
391
+ fontSize: 16,
392
+ color: '#333',
393
+ fontWeight: '600',
394
+ },
395
+ dropdownIcon: {
396
+ fontSize: 12,
397
+ color: '#666',
398
+ marginLeft: 8,
399
+ },
400
+ phoneInput: {
401
+ flex: 1,
212
402
  borderWidth: 1,
213
403
  borderColor: '#e0e0e0',
214
404
  padding: 16,
@@ -217,6 +407,98 @@ const styles = StyleSheet.create({
217
407
  backgroundColor: '#f8f9fa',
218
408
  color: '#333',
219
409
  },
410
+ modalOverlay: {
411
+ flex: 1,
412
+ backgroundColor: 'rgba(0,0,0,0.5)',
413
+ justifyContent: 'flex-end',
414
+ },
415
+ modalContent: {
416
+ backgroundColor: 'white',
417
+ borderTopLeftRadius: 24,
418
+ borderTopRightRadius: 24,
419
+ maxHeight: '60%',
420
+ paddingBottom: 40,
421
+ },
422
+ modalHeader: {
423
+ flexDirection: 'row',
424
+ justifyContent: 'space-between',
425
+ alignItems: 'center',
426
+ padding: 20,
427
+ borderBottomWidth: 1,
428
+ borderBottomColor: '#f0f0f0',
429
+ },
430
+ modalTitle: {
431
+ fontSize: 18,
432
+ fontWeight: '700',
433
+ color: '#1a1a1a',
434
+ },
435
+ modalClose: {
436
+ fontSize: 20,
437
+ color: '#666',
438
+ padding: 5,
439
+ },
440
+ countryItem: {
441
+ flexDirection: 'row',
442
+ justifyContent: 'space-between',
443
+ alignItems: 'center',
444
+ paddingVertical: 16,
445
+ paddingHorizontal: 24,
446
+ borderBottomWidth: 1,
447
+ borderBottomColor: '#f8f9fa',
448
+ },
449
+ countryItemLabel: {
450
+ fontSize: 16,
451
+ color: '#333',
452
+ },
453
+ checkMark: {
454
+ color: '#2DBD60',
455
+ fontSize: 18,
456
+ fontWeight: 'bold',
457
+ },
458
+ otpWrapper: {
459
+ position: 'relative',
460
+ width: '100%',
461
+ height: 60,
462
+ },
463
+ otpBoxesContainer: {
464
+ flexDirection: 'row',
465
+ justifyContent: 'space-between',
466
+ alignItems: 'center',
467
+ width: '100%',
468
+ height: '100%',
469
+ },
470
+ otpBox: {
471
+ width: '14%',
472
+ aspectRatio: 1,
473
+ borderWidth: 1,
474
+ borderColor: '#e0e0e0',
475
+ borderRadius: 12,
476
+ backgroundColor: '#f8f9fa',
477
+ justifyContent: 'center',
478
+ alignItems: 'center',
479
+ },
480
+ otpBoxFilled: {
481
+ borderColor: '#9ca3af',
482
+ backgroundColor: '#ffffff',
483
+ },
484
+ otpBoxActive: {
485
+ borderColor: '#2DBD60',
486
+ borderWidth: 2,
487
+ backgroundColor: '#ffffff',
488
+ },
489
+ otpBoxText: {
490
+ fontSize: 24,
491
+ fontWeight: '700',
492
+ color: '#1a1a1a',
493
+ },
494
+ hiddenInput: {
495
+ position: 'absolute',
496
+ top: 0,
497
+ left: 0,
498
+ width: '100%',
499
+ height: '100%',
500
+ opacity: 0,
501
+ },
220
502
  errorText: {
221
503
  color: '#dc2626',
222
504
  marginBottom: 16,
@@ -230,11 +512,11 @@ const styles = StyleSheet.create({
230
512
  button: {
231
513
  height: 50,
232
514
  borderRadius: 12,
233
- width: "100%"
515
+ width: "100%",
234
516
  },
235
517
  changeLink: {
236
518
  alignSelf: 'flex-end',
237
- marginTop: 8,
519
+ marginTop: 12,
238
520
  },
239
521
  changeText: {
240
522
  color: '#2DBD60',
@@ -250,4 +532,4 @@ const styles = StyleSheet.create({
250
532
  fontSize: 14,
251
533
  textDecorationLine: 'underline',
252
534
  },
253
- });
535
+ });