@onairos/react-native 3.1.18 → 3.2.1

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 (734) hide show
  1. package/README.md +374 -403
  2. package/lib/commonjs/api/index.js +1 -75
  3. package/lib/commonjs/api/index.js.map +1 -1
  4. package/lib/commonjs/assets/images/email.png +0 -0
  5. package/lib/commonjs/assets/images/linkedin.png +0 -0
  6. package/lib/commonjs/assets/images/reddit.png +0 -0
  7. package/lib/commonjs/assets/images/youtube.png +0 -0
  8. package/lib/commonjs/components/DataRequestModal.js.map +1 -1
  9. package/lib/commonjs/components/DataRequestScreen.js.map +1 -1
  10. package/lib/commonjs/components/EmailVerificationModal.js.map +1 -1
  11. package/lib/commonjs/components/Onairos.js +3 -1
  12. package/lib/commonjs/components/Onairos.js.map +1 -1
  13. package/lib/commonjs/components/OnairosButton.js +190 -171
  14. package/lib/commonjs/components/OnairosButton.js.map +1 -1
  15. package/lib/commonjs/components/Overlay.js +5 -5
  16. package/lib/commonjs/components/Overlay.js.map +1 -1
  17. package/lib/commonjs/components/PinInput.js +120 -282
  18. package/lib/commonjs/components/PinInput.js.map +1 -1
  19. package/lib/commonjs/components/PlatformList.js.map +1 -1
  20. package/lib/commonjs/components/TrainingModal.js +563 -808
  21. package/lib/commonjs/components/TrainingModal.js.map +1 -1
  22. package/lib/commonjs/components/UniversalOnboarding.js +1296 -2302
  23. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  24. package/lib/commonjs/components/UniversalOnboarding.tsx.new +455 -0
  25. package/lib/commonjs/components/onboarding/OAuthWebView.js +743 -134
  26. package/lib/commonjs/components/onboarding/OAuthWebView.js.map +1 -1
  27. package/lib/commonjs/components/onboarding/OnboardingHeader.js.map +1 -1
  28. package/lib/commonjs/components/onboarding/PinInput.js.map +1 -1
  29. package/lib/commonjs/components/onboarding/PlatformConnector.js.map +1 -1
  30. package/lib/commonjs/components/screens/ConnectorScreen.js.map +1 -1
  31. package/lib/commonjs/components/screens/LoadingScreen.js.map +1 -1
  32. package/lib/commonjs/components/screens/PinCreationScreen.js.map +1 -1
  33. package/lib/commonjs/constants/index.js.map +1 -1
  34. package/lib/commonjs/hooks/useConnections.js +125 -120
  35. package/lib/commonjs/hooks/useConnections.js.map +1 -1
  36. package/lib/commonjs/hooks/useCredentials.js.map +1 -1
  37. package/lib/commonjs/index.js +27 -157
  38. package/lib/commonjs/index.js.map +1 -1
  39. package/lib/commonjs/services/SDK_API_KEY_VALIDATION.md +428 -0
  40. package/lib/commonjs/services/apiKeyService.js +106 -105
  41. package/lib/commonjs/services/apiKeyService.js.map +1 -1
  42. package/lib/commonjs/services/oauthService.js.map +1 -1
  43. package/lib/commonjs/services/platformAuthService.js +722 -956
  44. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  45. package/lib/commonjs/types/ambient.d.js.map +1 -1
  46. package/lib/commonjs/types/declarations.d.js.map +1 -1
  47. package/lib/commonjs/types/index.d.js.map +1 -1
  48. package/lib/commonjs/types/index.js.map +1 -1
  49. package/lib/commonjs/types/node-fix.d.js.map +1 -1
  50. package/lib/commonjs/types/node-override.d.js.map +1 -1
  51. package/lib/commonjs/types/opacity.d.js.map +1 -1
  52. package/lib/commonjs/types/types.d.js.map +1 -1
  53. package/lib/commonjs/types.js.map +1 -1
  54. package/lib/commonjs/utils/Portal.js +4 -4
  55. package/lib/commonjs/utils/Portal.js.map +1 -1
  56. package/lib/commonjs/utils/api.js +24 -24
  57. package/lib/commonjs/utils/api.js.map +1 -1
  58. package/lib/commonjs/utils/auth.js +18 -18
  59. package/lib/commonjs/utils/auth.js.map +1 -1
  60. package/lib/commonjs/utils/crypto.js +13 -13
  61. package/lib/commonjs/utils/crypto.js.map +1 -1
  62. package/lib/commonjs/utils/debugHelper.js.map +1 -1
  63. package/lib/commonjs/utils/encryption.js +12 -12
  64. package/lib/commonjs/utils/encryption.js.map +1 -1
  65. package/lib/commonjs/utils/onairosApi.js.map +1 -1
  66. package/lib/commonjs/utils/programmaticFlow.js +16 -16
  67. package/lib/commonjs/utils/programmaticFlow.js.map +1 -1
  68. package/lib/commonjs/utils/retryHelper.js +27 -27
  69. package/lib/commonjs/utils/retryHelper.js.map +1 -1
  70. package/lib/commonjs/utils/secureStorage.js.map +1 -1
  71. package/lib/module/api/index.js +0 -72
  72. package/lib/module/api/index.js.map +1 -1
  73. package/lib/module/assets/images/email.png +0 -0
  74. package/lib/module/assets/images/linkedin.png +0 -0
  75. package/lib/module/assets/images/reddit.png +0 -0
  76. package/lib/module/assets/images/youtube.png +0 -0
  77. package/lib/module/components/DataRequestModal.js.map +1 -1
  78. package/lib/module/components/DataRequestScreen.js.map +1 -1
  79. package/lib/module/components/EmailVerificationModal.js.map +1 -1
  80. package/lib/module/components/Onairos.js +3 -1
  81. package/lib/module/components/Onairos.js.map +1 -1
  82. package/lib/module/components/OnairosButton.js +192 -172
  83. package/lib/module/components/OnairosButton.js.map +1 -1
  84. package/lib/module/components/Overlay.js +5 -5
  85. package/lib/module/components/Overlay.js.map +1 -1
  86. package/lib/module/components/PinInput.js +120 -283
  87. package/lib/module/components/PinInput.js.map +1 -1
  88. package/lib/module/components/PlatformList.js.map +1 -1
  89. package/lib/module/components/TrainingModal.js +565 -809
  90. package/lib/module/components/TrainingModal.js.map +1 -1
  91. package/lib/module/components/UniversalOnboarding.js +1297 -2305
  92. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  93. package/lib/module/components/UniversalOnboarding.tsx.new +455 -0
  94. package/lib/module/components/onboarding/OAuthWebView.js +744 -136
  95. package/lib/module/components/onboarding/OAuthWebView.js.map +1 -1
  96. package/lib/module/components/onboarding/OnboardingHeader.js.map +1 -1
  97. package/lib/module/components/onboarding/PinInput.js.map +1 -1
  98. package/lib/module/components/onboarding/PlatformConnector.js.map +1 -1
  99. package/lib/module/components/screens/ConnectorScreen.js.map +1 -1
  100. package/lib/module/components/screens/LoadingScreen.js.map +1 -1
  101. package/lib/module/components/screens/PinCreationScreen.js.map +1 -1
  102. package/lib/module/constants/index.js.map +1 -1
  103. package/lib/module/hooks/useConnections.js +125 -119
  104. package/lib/module/hooks/useConnections.js.map +1 -1
  105. package/lib/module/hooks/useCredentials.js.map +1 -1
  106. package/lib/module/index.js +17 -55
  107. package/lib/module/index.js.map +1 -1
  108. package/lib/module/services/SDK_API_KEY_VALIDATION.md +428 -0
  109. package/lib/module/services/apiKeyService.js +106 -105
  110. package/lib/module/services/apiKeyService.js.map +1 -1
  111. package/lib/module/services/oauthService.js.map +1 -1
  112. package/lib/module/services/platformAuthService.js +704 -943
  113. package/lib/module/services/platformAuthService.js.map +1 -1
  114. package/lib/module/types/ambient.d.js.map +1 -1
  115. package/lib/module/types/declarations.d.js.map +1 -1
  116. package/lib/module/types/index.d.js.map +1 -1
  117. package/lib/module/types/index.js.map +1 -1
  118. package/lib/module/types/node-fix.d.js.map +1 -1
  119. package/lib/module/types/node-override.d.js.map +1 -1
  120. package/lib/module/types/opacity.d.js.map +1 -1
  121. package/lib/module/types/types.d.js.map +1 -1
  122. package/lib/module/types.js.map +1 -1
  123. package/lib/module/utils/Portal.js +4 -4
  124. package/lib/module/utils/Portal.js.map +1 -1
  125. package/lib/module/utils/api.js +24 -24
  126. package/lib/module/utils/api.js.map +1 -1
  127. package/lib/module/utils/auth.js +18 -18
  128. package/lib/module/utils/auth.js.map +1 -1
  129. package/lib/module/utils/crypto.js +13 -13
  130. package/lib/module/utils/crypto.js.map +1 -1
  131. package/lib/module/utils/debugHelper.js.map +1 -1
  132. package/lib/module/utils/encryption.js +12 -12
  133. package/lib/module/utils/encryption.js.map +1 -1
  134. package/lib/module/utils/onairosApi.js.map +1 -1
  135. package/lib/module/utils/programmaticFlow.js +16 -16
  136. package/lib/module/utils/programmaticFlow.js.map +1 -1
  137. package/lib/module/utils/retryHelper.js +27 -27
  138. package/lib/module/utils/retryHelper.js.map +1 -1
  139. package/lib/module/utils/secureStorage.js.map +1 -1
  140. package/lib/typescript/api/index.d.ts +7 -15
  141. package/lib/typescript/api/index.d.ts.map +1 -1
  142. package/lib/typescript/components/DataRequestModal.d.ts +10 -10
  143. package/lib/typescript/components/DataRequestScreen.d.ts +10 -10
  144. package/lib/typescript/components/EmailVerificationModal.d.ts +10 -10
  145. package/lib/typescript/components/Onairos.d.ts +3 -3
  146. package/lib/typescript/components/Onairos.d.ts.map +1 -1
  147. package/lib/typescript/components/OnairosButton.d.ts +11 -36
  148. package/lib/typescript/components/OnairosButton.d.ts.map +1 -1
  149. package/lib/typescript/components/Overlay.d.ts +3 -3
  150. package/lib/typescript/components/PinInput.d.ts +3 -13
  151. package/lib/typescript/components/PinInput.d.ts.map +1 -1
  152. package/lib/typescript/components/PlatformList.d.ts +3 -3
  153. package/lib/typescript/components/TrainingModal.d.ts +3 -14
  154. package/lib/typescript/components/TrainingModal.d.ts.map +1 -1
  155. package/lib/typescript/components/UniversalOnboarding.d.ts +3 -16
  156. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  157. package/lib/typescript/components/onboarding/OAuthWebView.d.ts +9 -9
  158. package/lib/typescript/components/onboarding/OAuthWebView.d.ts.map +1 -1
  159. package/lib/typescript/components/onboarding/OnboardingHeader.d.ts +10 -10
  160. package/lib/typescript/components/onboarding/PinInput.d.ts +3 -3
  161. package/lib/typescript/components/onboarding/PlatformConnector.d.ts +12 -12
  162. package/lib/typescript/components/screens/ConnectorScreen.d.ts +8 -8
  163. package/lib/typescript/components/screens/LoadingScreen.d.ts +8 -8
  164. package/lib/typescript/components/screens/PinCreationScreen.d.ts +9 -9
  165. package/lib/typescript/constants/index.d.ts +52 -52
  166. package/lib/typescript/hooks/useConnections.d.ts +8 -13
  167. package/lib/typescript/hooks/useConnections.d.ts.map +1 -1
  168. package/lib/typescript/hooks/useCredentials.d.ts +8 -8
  169. package/lib/typescript/index.d.ts +17 -38
  170. package/lib/typescript/index.d.ts.map +1 -1
  171. package/lib/typescript/services/apiKeyService.d.ts +131 -131
  172. package/lib/typescript/services/apiKeyService.d.ts.map +1 -1
  173. package/lib/typescript/services/oauthService.d.ts +49 -49
  174. package/lib/typescript/services/platformAuthService.d.ts +143 -68
  175. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  176. package/lib/typescript/types/index.d.ts +228 -264
  177. package/lib/typescript/types/index.d.ts.map +1 -1
  178. package/lib/typescript/types.d.ts +268 -266
  179. package/lib/typescript/types.d.ts.map +1 -1
  180. package/lib/typescript/utils/Portal.d.ts +13 -13
  181. package/lib/typescript/utils/api.d.ts +5 -5
  182. package/lib/typescript/utils/auth.d.ts +5 -5
  183. package/lib/typescript/utils/crypto.d.ts +3 -3
  184. package/lib/typescript/utils/debugHelper.d.ts +28 -28
  185. package/lib/typescript/utils/encryption.d.ts +18 -18
  186. package/lib/typescript/utils/onairosApi.d.ts +86 -86
  187. package/lib/typescript/utils/programmaticFlow.d.ts +22 -22
  188. package/lib/typescript/utils/retryHelper.d.ts +68 -68
  189. package/lib/typescript/utils/secureStorage.d.ts +93 -93
  190. package/package.json +149 -145
  191. package/src/api/index.ts +110 -151
  192. package/src/assets/images/email.png +0 -0
  193. package/src/assets/images/linkedin.png +0 -0
  194. package/src/assets/images/reddit.png +0 -0
  195. package/src/assets/images/youtube.png +0 -0
  196. package/src/components/DataRequestScreen.tsx +355 -355
  197. package/src/components/EmailVerificationModal.tsx +363 -363
  198. package/src/components/Onairos.tsx +424 -422
  199. package/src/components/OnairosButton.tsx +359 -339
  200. package/src/components/Overlay.tsx +506 -506
  201. package/src/components/PinInput.tsx +343 -555
  202. package/src/components/PlatformList.tsx +144 -144
  203. package/src/components/TrainingModal.tsx +737 -1047
  204. package/src/components/UniversalOnboarding.tsx +1839 -2888
  205. package/src/components/UniversalOnboarding.tsx.new +455 -0
  206. package/src/components/onboarding/OAuthWebView.tsx +838 -232
  207. package/src/hooks/useConnections.ts +163 -161
  208. package/src/hooks/useCredentials.ts +174 -174
  209. package/src/index.js +14 -14
  210. package/src/index.ts +49 -99
  211. package/src/services/SDK_API_KEY_VALIDATION.md +428 -0
  212. package/src/services/apiKeyService.ts +921 -919
  213. package/src/services/platformAuthService.ts +1113 -1346
  214. package/src/types/ambient.d.ts +28 -28
  215. package/src/types/index.ts +241 -265
  216. package/src/types/node-fix.d.ts +18 -18
  217. package/src/types/node-override.d.ts +23 -23
  218. package/src/types/opacity.d.ts +15 -15
  219. package/src/types/types.d.ts +17 -17
  220. package/src/types.ts +6 -4
  221. package/src/utils/Portal.tsx +82 -82
  222. package/src/utils/api.js +111 -111
  223. package/src/utils/auth.js +103 -103
  224. package/src/utils/crypto.js +59 -59
  225. package/src/utils/encryption.ts +68 -68
  226. package/src/utils/programmaticFlow.ts +112 -112
  227. package/src/utils/retryHelper.ts +274 -274
  228. package/types/index.d.ts +213 -213
  229. package/types/node-env.d.ts +14 -14
  230. package/lib/commonjs/assets/fonts/EBGaramond-VariableFont_wght.ttf +0 -0
  231. package/lib/commonjs/assets/fonts/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  232. package/lib/commonjs/assets/icons/Facebookicon.png +0 -0
  233. package/lib/commonjs/assets/icons/Gmail.png +0 -0
  234. package/lib/commonjs/assets/icons/Linkedinicon.png +0 -0
  235. package/lib/commonjs/assets/icons/Redditicon.png +0 -0
  236. package/lib/commonjs/assets/icons/YouTubeicon2.png +0 -0
  237. package/lib/commonjs/assets/icons/YouTubeicon3.png +0 -0
  238. package/lib/commonjs/assets/icons/farcaster.png +0 -0
  239. package/lib/commonjs/assets/icons/instagram.png +0 -0
  240. package/lib/commonjs/assets/icons/pinterest.png +0 -0
  241. package/lib/commonjs/assets/icons/swerv_logo.png +0 -0
  242. package/lib/commonjs/assets/icons/twitter.jpg +0 -0
  243. package/lib/commonjs/assets/images/Checkbox.svg +0 -3
  244. package/lib/commonjs/assets/images/EnochE.svg +0 -19
  245. package/lib/commonjs/assets/images/Enochicon1.png +0 -0
  246. package/lib/commonjs/assets/images/Face_ID_logo.png +0 -0
  247. package/lib/commonjs/assets/images/Facebookicon.png +0 -0
  248. package/lib/commonjs/assets/images/Gmail.png +0 -0
  249. package/lib/commonjs/assets/images/Googlelogo.png +0 -0
  250. package/lib/commonjs/assets/images/Linkedinicon.png +0 -0
  251. package/lib/commonjs/assets/images/Onairoslogo.png +0 -0
  252. package/lib/commonjs/assets/images/Personalityprofile.svg +0 -3
  253. package/lib/commonjs/assets/images/Personalitytraits.svg +0 -3
  254. package/lib/commonjs/assets/images/Redditicon.png +0 -0
  255. package/lib/commonjs/assets/images/Userpreferences.svg +0 -3
  256. package/lib/commonjs/assets/images/YouTubeicon3.png +0 -0
  257. package/lib/commonjs/assets/images/arrow.svg +0 -20
  258. package/lib/commonjs/assets/images/basicproficon.svg +0 -43
  259. package/lib/commonjs/assets/images/basicprofile.svg +0 -3
  260. package/lib/commonjs/assets/images/checkmark.svg +0 -4
  261. package/lib/commonjs/assets/images/contentanalysis.svg +0 -3
  262. package/lib/commonjs/assets/images/contenticon.svg +0 -23
  263. package/lib/commonjs/assets/images/persona1.png +0 -0
  264. package/lib/commonjs/assets/images/persona2.png +0 -0
  265. package/lib/commonjs/assets/images/persona3.png +0 -0
  266. package/lib/commonjs/assets/images/persona4.png +0 -0
  267. package/lib/commonjs/assets/images/persona5.png +0 -0
  268. package/lib/commonjs/assets/images/personalityicon.svg +0 -18
  269. package/lib/commonjs/assets/images/x-close.svg +0 -3
  270. package/lib/commonjs/components/BodyText.js +0 -27
  271. package/lib/commonjs/components/BodyText.js.map +0 -1
  272. package/lib/commonjs/components/BrandMark.js +0 -44
  273. package/lib/commonjs/components/BrandMark.js.map +0 -1
  274. package/lib/commonjs/components/CodeInput.js +0 -30
  275. package/lib/commonjs/components/CodeInput.js.map +0 -1
  276. package/lib/commonjs/components/EmailInput.js +0 -30
  277. package/lib/commonjs/components/EmailInput.js.map +0 -1
  278. package/lib/commonjs/components/ExistingUserDataConfirmation.js +0 -474
  279. package/lib/commonjs/components/ExistingUserDataConfirmation.js.map +0 -1
  280. package/lib/commonjs/components/GoogleButton.js +0 -55
  281. package/lib/commonjs/components/GoogleButton.js.map +0 -1
  282. package/lib/commonjs/components/HeadingGroup.js +0 -43
  283. package/lib/commonjs/components/HeadingGroup.js.map +0 -1
  284. package/lib/commonjs/components/ModalHeader.js +0 -99
  285. package/lib/commonjs/components/ModalHeader.js.map +0 -1
  286. package/lib/commonjs/components/ModalSheet.js +0 -47
  287. package/lib/commonjs/components/ModalSheet.js.map +0 -1
  288. package/lib/commonjs/components/OnairosSignInButton.js +0 -129
  289. package/lib/commonjs/components/OnairosSignInButton.js.map +0 -1
  290. package/lib/commonjs/components/PersonaImage.js +0 -60
  291. package/lib/commonjs/components/PersonaImage.js.map +0 -1
  292. package/lib/commonjs/components/PersonaLoadingScreen.js +0 -156
  293. package/lib/commonjs/components/PersonaLoadingScreen.js.map +0 -1
  294. package/lib/commonjs/components/PersonalizationConsentScreen.js +0 -316
  295. package/lib/commonjs/components/PersonalizationConsentScreen.js.map +0 -1
  296. package/lib/commonjs/components/PinCreationScreen.js +0 -393
  297. package/lib/commonjs/components/PinCreationScreen.js.map +0 -1
  298. package/lib/commonjs/components/PlatformConnectorsStep.js +0 -828
  299. package/lib/commonjs/components/PlatformConnectorsStep.js.map +0 -1
  300. package/lib/commonjs/components/PlatformToggle.js +0 -180
  301. package/lib/commonjs/components/PlatformToggle.js.map +0 -1
  302. package/lib/commonjs/components/PrimaryButton.js +0 -180
  303. package/lib/commonjs/components/PrimaryButton.js.map +0 -1
  304. package/lib/commonjs/components/SignInMatchAnimation.js +0 -197
  305. package/lib/commonjs/components/SignInMatchAnimation.js.map +0 -1
  306. package/lib/commonjs/components/SignInStep.js +0 -179
  307. package/lib/commonjs/components/SignInStep.js.map +0 -1
  308. package/lib/commonjs/components/VerificationStep.js +0 -154
  309. package/lib/commonjs/components/VerificationStep.js.map +0 -1
  310. package/lib/commonjs/components/WelcomeScreen.js +0 -401
  311. package/lib/commonjs/components/WelcomeScreen.js.map +0 -1
  312. package/lib/commonjs/components/icons/Basicproficon.js +0 -37
  313. package/lib/commonjs/components/icons/Basicproficon.js.map +0 -1
  314. package/lib/commonjs/components/icons/Basicprofile.js +0 -21
  315. package/lib/commonjs/components/icons/Basicprofile.js.map +0 -1
  316. package/lib/commonjs/components/icons/Checkbox.js +0 -21
  317. package/lib/commonjs/components/icons/Checkbox.js.map +0 -1
  318. package/lib/commonjs/components/icons/Checkmark.js +0 -27
  319. package/lib/commonjs/components/icons/Checkmark.js.map +0 -1
  320. package/lib/commonjs/components/icons/Contentanalysis.js +0 -21
  321. package/lib/commonjs/components/icons/Contentanalysis.js.map +0 -1
  322. package/lib/commonjs/components/icons/Contenticon.js +0 -39
  323. package/lib/commonjs/components/icons/Contenticon.js.map +0 -1
  324. package/lib/commonjs/components/icons/EnochE.js +0 -41
  325. package/lib/commonjs/components/icons/EnochE.js.map +0 -1
  326. package/lib/commonjs/components/icons/Personalityicon.js +0 -30
  327. package/lib/commonjs/components/icons/Personalityicon.js.map +0 -1
  328. package/lib/commonjs/components/icons/Personalityprofile.js +0 -21
  329. package/lib/commonjs/components/icons/Personalityprofile.js.map +0 -1
  330. package/lib/commonjs/components/icons/Personalitytraits.js +0 -21
  331. package/lib/commonjs/components/icons/Personalitytraits.js.map +0 -1
  332. package/lib/commonjs/components/icons/Userpreferences.js +0 -21
  333. package/lib/commonjs/components/icons/Userpreferences.js.map +0 -1
  334. package/lib/commonjs/components/icons/index.js +0 -84
  335. package/lib/commonjs/components/icons/index.js.map +0 -1
  336. package/lib/commonjs/config/api.js +0 -34
  337. package/lib/commonjs/config/api.js.map +0 -1
  338. package/lib/commonjs/context/AuthContext.js +0 -345
  339. package/lib/commonjs/context/AuthContext.js.map +0 -1
  340. package/lib/commonjs/hooks/useConnectedAccounts.js +0 -111
  341. package/lib/commonjs/hooks/useConnectedAccounts.js.map +0 -1
  342. package/lib/commonjs/hooks/useUserConnections.js +0 -148
  343. package/lib/commonjs/hooks/useUserConnections.js.map +0 -1
  344. package/lib/commonjs/services/apiClient.js +0 -302
  345. package/lib/commonjs/services/apiClient.js.map +0 -1
  346. package/lib/commonjs/services/authService.js +0 -935
  347. package/lib/commonjs/services/authService.js.map +0 -1
  348. package/lib/commonjs/services/biometricPinService.js +0 -184
  349. package/lib/commonjs/services/biometricPinService.js.map +0 -1
  350. package/lib/commonjs/services/connectedAccountsService.js +0 -268
  351. package/lib/commonjs/services/connectedAccountsService.js.map +0 -1
  352. package/lib/commonjs/services/googleAuthService.js +0 -268
  353. package/lib/commonjs/services/googleAuthService.js.map +0 -1
  354. package/lib/commonjs/services/imageCompressionService.js +0 -260
  355. package/lib/commonjs/services/imageCompressionService.js.map +0 -1
  356. package/lib/commonjs/services/jwtStorageService.js +0 -256
  357. package/lib/commonjs/services/jwtStorageService.js.map +0 -1
  358. package/lib/commonjs/services/mobileTrainingService.js +0 -185
  359. package/lib/commonjs/services/mobileTrainingService.js.map +0 -1
  360. package/lib/commonjs/services/pinEncryptionService.js +0 -84
  361. package/lib/commonjs/services/pinEncryptionService.js.map +0 -1
  362. package/lib/commonjs/services/pinStorageUtils.js +0 -105
  363. package/lib/commonjs/services/pinStorageUtils.js.map +0 -1
  364. package/lib/commonjs/services/storageService.js +0 -404
  365. package/lib/commonjs/services/storageService.js.map +0 -1
  366. package/lib/commonjs/services/trainingApiHelpers.js +0 -73
  367. package/lib/commonjs/services/trainingApiHelpers.js.map +0 -1
  368. package/lib/commonjs/services/userConnectionsService.js +0 -486
  369. package/lib/commonjs/services/userConnectionsService.js.map +0 -1
  370. package/lib/commonjs/services/youtubeMigrationService.js +0 -415
  371. package/lib/commonjs/services/youtubeMigrationService.js.map +0 -1
  372. package/lib/commonjs/theme/index.js +0 -249
  373. package/lib/commonjs/theme/index.js.map +0 -1
  374. package/lib/commonjs/utils/eventUtils.js +0 -288
  375. package/lib/commonjs/utils/eventUtils.js.map +0 -1
  376. package/lib/commonjs/utils/haptics.js +0 -66
  377. package/lib/commonjs/utils/haptics.js.map +0 -1
  378. package/lib/commonjs/utils/imagePreloader.js +0 -6
  379. package/lib/commonjs/utils/imagePreloader.js.map +0 -1
  380. package/lib/module/assets/fonts/EBGaramond-VariableFont_wght.ttf +0 -0
  381. package/lib/module/assets/fonts/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  382. package/lib/module/assets/icons/Facebookicon.png +0 -0
  383. package/lib/module/assets/icons/Gmail.png +0 -0
  384. package/lib/module/assets/icons/Linkedinicon.png +0 -0
  385. package/lib/module/assets/icons/Redditicon.png +0 -0
  386. package/lib/module/assets/icons/YouTubeicon2.png +0 -0
  387. package/lib/module/assets/icons/YouTubeicon3.png +0 -0
  388. package/lib/module/assets/icons/farcaster.png +0 -0
  389. package/lib/module/assets/icons/instagram.png +0 -0
  390. package/lib/module/assets/icons/pinterest.png +0 -0
  391. package/lib/module/assets/icons/swerv_logo.png +0 -0
  392. package/lib/module/assets/icons/twitter.jpg +0 -0
  393. package/lib/module/assets/images/Checkbox.svg +0 -3
  394. package/lib/module/assets/images/EnochE.svg +0 -19
  395. package/lib/module/assets/images/Enochicon1.png +0 -0
  396. package/lib/module/assets/images/Face_ID_logo.png +0 -0
  397. package/lib/module/assets/images/Facebookicon.png +0 -0
  398. package/lib/module/assets/images/Gmail.png +0 -0
  399. package/lib/module/assets/images/Googlelogo.png +0 -0
  400. package/lib/module/assets/images/Linkedinicon.png +0 -0
  401. package/lib/module/assets/images/Onairoslogo.png +0 -0
  402. package/lib/module/assets/images/Personalityprofile.svg +0 -3
  403. package/lib/module/assets/images/Personalitytraits.svg +0 -3
  404. package/lib/module/assets/images/Redditicon.png +0 -0
  405. package/lib/module/assets/images/Userpreferences.svg +0 -3
  406. package/lib/module/assets/images/YouTubeicon3.png +0 -0
  407. package/lib/module/assets/images/arrow.svg +0 -20
  408. package/lib/module/assets/images/basicproficon.svg +0 -43
  409. package/lib/module/assets/images/basicprofile.svg +0 -3
  410. package/lib/module/assets/images/checkmark.svg +0 -4
  411. package/lib/module/assets/images/contentanalysis.svg +0 -3
  412. package/lib/module/assets/images/contenticon.svg +0 -23
  413. package/lib/module/assets/images/persona1.png +0 -0
  414. package/lib/module/assets/images/persona2.png +0 -0
  415. package/lib/module/assets/images/persona3.png +0 -0
  416. package/lib/module/assets/images/persona4.png +0 -0
  417. package/lib/module/assets/images/persona5.png +0 -0
  418. package/lib/module/assets/images/personalityicon.svg +0 -18
  419. package/lib/module/assets/images/x-close.svg +0 -3
  420. package/lib/module/components/BodyText.js +0 -20
  421. package/lib/module/components/BodyText.js.map +0 -1
  422. package/lib/module/components/BrandMark.js +0 -37
  423. package/lib/module/components/BrandMark.js.map +0 -1
  424. package/lib/module/components/CodeInput.js +0 -23
  425. package/lib/module/components/CodeInput.js.map +0 -1
  426. package/lib/module/components/EmailInput.js +0 -23
  427. package/lib/module/components/EmailInput.js.map +0 -1
  428. package/lib/module/components/ExistingUserDataConfirmation.js +0 -465
  429. package/lib/module/components/ExistingUserDataConfirmation.js.map +0 -1
  430. package/lib/module/components/GoogleButton.js +0 -48
  431. package/lib/module/components/GoogleButton.js.map +0 -1
  432. package/lib/module/components/HeadingGroup.js +0 -36
  433. package/lib/module/components/HeadingGroup.js.map +0 -1
  434. package/lib/module/components/ModalHeader.js +0 -92
  435. package/lib/module/components/ModalHeader.js.map +0 -1
  436. package/lib/module/components/ModalSheet.js +0 -39
  437. package/lib/module/components/ModalSheet.js.map +0 -1
  438. package/lib/module/components/OnairosSignInButton.js +0 -120
  439. package/lib/module/components/OnairosSignInButton.js.map +0 -1
  440. package/lib/module/components/PersonaImage.js +0 -53
  441. package/lib/module/components/PersonaImage.js.map +0 -1
  442. package/lib/module/components/PersonaLoadingScreen.js +0 -148
  443. package/lib/module/components/PersonaLoadingScreen.js.map +0 -1
  444. package/lib/module/components/PersonalizationConsentScreen.js +0 -309
  445. package/lib/module/components/PersonalizationConsentScreen.js.map +0 -1
  446. package/lib/module/components/PinCreationScreen.js +0 -386
  447. package/lib/module/components/PinCreationScreen.js.map +0 -1
  448. package/lib/module/components/PlatformConnectorsStep.js +0 -820
  449. package/lib/module/components/PlatformConnectorsStep.js.map +0 -1
  450. package/lib/module/components/PlatformToggle.js +0 -173
  451. package/lib/module/components/PlatformToggle.js.map +0 -1
  452. package/lib/module/components/PrimaryButton.js +0 -172
  453. package/lib/module/components/PrimaryButton.js.map +0 -1
  454. package/lib/module/components/SignInMatchAnimation.js +0 -189
  455. package/lib/module/components/SignInMatchAnimation.js.map +0 -1
  456. package/lib/module/components/SignInStep.js +0 -171
  457. package/lib/module/components/SignInStep.js.map +0 -1
  458. package/lib/module/components/VerificationStep.js +0 -146
  459. package/lib/module/components/VerificationStep.js.map +0 -1
  460. package/lib/module/components/WelcomeScreen.js +0 -393
  461. package/lib/module/components/WelcomeScreen.js.map +0 -1
  462. package/lib/module/components/icons/Basicproficon.js +0 -30
  463. package/lib/module/components/icons/Basicproficon.js.map +0 -1
  464. package/lib/module/components/icons/Basicprofile.js +0 -14
  465. package/lib/module/components/icons/Basicprofile.js.map +0 -1
  466. package/lib/module/components/icons/Checkbox.js +0 -14
  467. package/lib/module/components/icons/Checkbox.js.map +0 -1
  468. package/lib/module/components/icons/Checkmark.js +0 -20
  469. package/lib/module/components/icons/Checkmark.js.map +0 -1
  470. package/lib/module/components/icons/Contentanalysis.js +0 -14
  471. package/lib/module/components/icons/Contentanalysis.js.map +0 -1
  472. package/lib/module/components/icons/Contenticon.js +0 -32
  473. package/lib/module/components/icons/Contenticon.js.map +0 -1
  474. package/lib/module/components/icons/EnochE.js +0 -34
  475. package/lib/module/components/icons/EnochE.js.map +0 -1
  476. package/lib/module/components/icons/Personalityicon.js +0 -23
  477. package/lib/module/components/icons/Personalityicon.js.map +0 -1
  478. package/lib/module/components/icons/Personalityprofile.js +0 -14
  479. package/lib/module/components/icons/Personalityprofile.js.map +0 -1
  480. package/lib/module/components/icons/Personalitytraits.js +0 -14
  481. package/lib/module/components/icons/Personalitytraits.js.map +0 -1
  482. package/lib/module/components/icons/Userpreferences.js +0 -14
  483. package/lib/module/components/icons/Userpreferences.js.map +0 -1
  484. package/lib/module/components/icons/index.js +0 -13
  485. package/lib/module/components/icons/index.js.map +0 -1
  486. package/lib/module/config/api.js +0 -26
  487. package/lib/module/config/api.js.map +0 -1
  488. package/lib/module/context/AuthContext.js +0 -335
  489. package/lib/module/context/AuthContext.js.map +0 -1
  490. package/lib/module/hooks/useConnectedAccounts.js +0 -106
  491. package/lib/module/hooks/useConnectedAccounts.js.map +0 -1
  492. package/lib/module/hooks/useUserConnections.js +0 -140
  493. package/lib/module/hooks/useUserConnections.js.map +0 -1
  494. package/lib/module/services/apiClient.js +0 -298
  495. package/lib/module/services/apiClient.js.map +0 -1
  496. package/lib/module/services/authService.js +0 -905
  497. package/lib/module/services/authService.js.map +0 -1
  498. package/lib/module/services/biometricPinService.js +0 -173
  499. package/lib/module/services/biometricPinService.js.map +0 -1
  500. package/lib/module/services/connectedAccountsService.js +0 -255
  501. package/lib/module/services/connectedAccountsService.js.map +0 -1
  502. package/lib/module/services/googleAuthService.js +0 -258
  503. package/lib/module/services/googleAuthService.js.map +0 -1
  504. package/lib/module/services/imageCompressionService.js +0 -250
  505. package/lib/module/services/imageCompressionService.js.map +0 -1
  506. package/lib/module/services/jwtStorageService.js +0 -239
  507. package/lib/module/services/jwtStorageService.js.map +0 -1
  508. package/lib/module/services/mobileTrainingService.js +0 -172
  509. package/lib/module/services/mobileTrainingService.js.map +0 -1
  510. package/lib/module/services/pinEncryptionService.js +0 -75
  511. package/lib/module/services/pinEncryptionService.js.map +0 -1
  512. package/lib/module/services/pinStorageUtils.js +0 -93
  513. package/lib/module/services/pinStorageUtils.js.map +0 -1
  514. package/lib/module/services/storageService.js +0 -383
  515. package/lib/module/services/storageService.js.map +0 -1
  516. package/lib/module/services/trainingApiHelpers.js +0 -67
  517. package/lib/module/services/trainingApiHelpers.js.map +0 -1
  518. package/lib/module/services/userConnectionsService.js +0 -476
  519. package/lib/module/services/userConnectionsService.js.map +0 -1
  520. package/lib/module/services/youtubeMigrationService.js +0 -404
  521. package/lib/module/services/youtubeMigrationService.js.map +0 -1
  522. package/lib/module/theme/index.js +0 -244
  523. package/lib/module/theme/index.js.map +0 -1
  524. package/lib/module/utils/eventUtils.js +0 -270
  525. package/lib/module/utils/eventUtils.js.map +0 -1
  526. package/lib/module/utils/haptics.js +0 -59
  527. package/lib/module/utils/haptics.js.map +0 -1
  528. package/lib/module/utils/imagePreloader.js +0 -3
  529. package/lib/module/utils/imagePreloader.js.map +0 -1
  530. package/lib/typescript/components/BodyText.d.ts +0 -10
  531. package/lib/typescript/components/BodyText.d.ts.map +0 -1
  532. package/lib/typescript/components/BrandMark.d.ts +0 -11
  533. package/lib/typescript/components/BrandMark.d.ts.map +0 -1
  534. package/lib/typescript/components/CodeInput.d.ts +0 -10
  535. package/lib/typescript/components/CodeInput.d.ts.map +0 -1
  536. package/lib/typescript/components/EmailInput.d.ts +0 -8
  537. package/lib/typescript/components/EmailInput.d.ts.map +0 -1
  538. package/lib/typescript/components/ExistingUserDataConfirmation.d.ts +0 -12
  539. package/lib/typescript/components/ExistingUserDataConfirmation.d.ts.map +0 -1
  540. package/lib/typescript/components/GoogleButton.d.ts +0 -11
  541. package/lib/typescript/components/GoogleButton.d.ts.map +0 -1
  542. package/lib/typescript/components/HeadingGroup.d.ts +0 -11
  543. package/lib/typescript/components/HeadingGroup.d.ts.map +0 -1
  544. package/lib/typescript/components/ModalHeader.d.ts +0 -11
  545. package/lib/typescript/components/ModalHeader.d.ts.map +0 -1
  546. package/lib/typescript/components/ModalSheet.d.ts +0 -13
  547. package/lib/typescript/components/ModalSheet.d.ts.map +0 -1
  548. package/lib/typescript/components/OnairosSignInButton.d.ts +0 -13
  549. package/lib/typescript/components/OnairosSignInButton.d.ts.map +0 -1
  550. package/lib/typescript/components/PersonaImage.d.ts +0 -8
  551. package/lib/typescript/components/PersonaImage.d.ts.map +0 -1
  552. package/lib/typescript/components/PersonaLoadingScreen.d.ts +0 -10
  553. package/lib/typescript/components/PersonaLoadingScreen.d.ts.map +0 -1
  554. package/lib/typescript/components/PersonalizationConsentScreen.d.ts +0 -10
  555. package/lib/typescript/components/PersonalizationConsentScreen.d.ts.map +0 -1
  556. package/lib/typescript/components/PinCreationScreen.d.ts +0 -10
  557. package/lib/typescript/components/PinCreationScreen.d.ts.map +0 -1
  558. package/lib/typescript/components/PlatformConnectorsStep.d.ts +0 -11
  559. package/lib/typescript/components/PlatformConnectorsStep.d.ts.map +0 -1
  560. package/lib/typescript/components/PlatformToggle.d.ts +0 -20
  561. package/lib/typescript/components/PlatformToggle.d.ts.map +0 -1
  562. package/lib/typescript/components/PrimaryButton.d.ts +0 -22
  563. package/lib/typescript/components/PrimaryButton.d.ts.map +0 -1
  564. package/lib/typescript/components/SignInMatchAnimation.d.ts +0 -9
  565. package/lib/typescript/components/SignInMatchAnimation.d.ts.map +0 -1
  566. package/lib/typescript/components/SignInStep.d.ts +0 -12
  567. package/lib/typescript/components/SignInStep.d.ts.map +0 -1
  568. package/lib/typescript/components/VerificationStep.d.ts +0 -13
  569. package/lib/typescript/components/VerificationStep.d.ts.map +0 -1
  570. package/lib/typescript/components/WelcomeScreen.d.ts +0 -9
  571. package/lib/typescript/components/WelcomeScreen.d.ts.map +0 -1
  572. package/lib/typescript/components/icons/Basicproficon.d.ts +0 -5
  573. package/lib/typescript/components/icons/Basicproficon.d.ts.map +0 -1
  574. package/lib/typescript/components/icons/Basicprofile.d.ts +0 -5
  575. package/lib/typescript/components/icons/Basicprofile.d.ts.map +0 -1
  576. package/lib/typescript/components/icons/Checkbox.d.ts +0 -5
  577. package/lib/typescript/components/icons/Checkbox.d.ts.map +0 -1
  578. package/lib/typescript/components/icons/Checkmark.d.ts +0 -5
  579. package/lib/typescript/components/icons/Checkmark.d.ts.map +0 -1
  580. package/lib/typescript/components/icons/Contentanalysis.d.ts +0 -5
  581. package/lib/typescript/components/icons/Contentanalysis.d.ts.map +0 -1
  582. package/lib/typescript/components/icons/Contenticon.d.ts +0 -5
  583. package/lib/typescript/components/icons/Contenticon.d.ts.map +0 -1
  584. package/lib/typescript/components/icons/EnochE.d.ts +0 -5
  585. package/lib/typescript/components/icons/EnochE.d.ts.map +0 -1
  586. package/lib/typescript/components/icons/Personalityicon.d.ts +0 -5
  587. package/lib/typescript/components/icons/Personalityicon.d.ts.map +0 -1
  588. package/lib/typescript/components/icons/Personalityprofile.d.ts +0 -5
  589. package/lib/typescript/components/icons/Personalityprofile.d.ts.map +0 -1
  590. package/lib/typescript/components/icons/Personalitytraits.d.ts +0 -5
  591. package/lib/typescript/components/icons/Personalitytraits.d.ts.map +0 -1
  592. package/lib/typescript/components/icons/Userpreferences.d.ts +0 -5
  593. package/lib/typescript/components/icons/Userpreferences.d.ts.map +0 -1
  594. package/lib/typescript/components/icons/index.d.ts +0 -12
  595. package/lib/typescript/components/icons/index.d.ts.map +0 -1
  596. package/lib/typescript/config/api.d.ts +0 -24
  597. package/lib/typescript/config/api.d.ts.map +0 -1
  598. package/lib/typescript/context/AuthContext.d.ts +0 -34
  599. package/lib/typescript/context/AuthContext.d.ts.map +0 -1
  600. package/lib/typescript/hooks/useConnectedAccounts.d.ts +0 -11
  601. package/lib/typescript/hooks/useConnectedAccounts.d.ts.map +0 -1
  602. package/lib/typescript/hooks/useUserConnections.d.ts +0 -12
  603. package/lib/typescript/hooks/useUserConnections.d.ts.map +0 -1
  604. package/lib/typescript/services/apiClient.d.ts +0 -91
  605. package/lib/typescript/services/apiClient.d.ts.map +0 -1
  606. package/lib/typescript/services/authService.d.ts +0 -216
  607. package/lib/typescript/services/authService.d.ts.map +0 -1
  608. package/lib/typescript/services/biometricPinService.d.ts +0 -29
  609. package/lib/typescript/services/biometricPinService.d.ts.map +0 -1
  610. package/lib/typescript/services/connectedAccountsService.d.ts +0 -56
  611. package/lib/typescript/services/connectedAccountsService.d.ts.map +0 -1
  612. package/lib/typescript/services/googleAuthService.d.ts +0 -63
  613. package/lib/typescript/services/googleAuthService.d.ts.map +0 -1
  614. package/lib/typescript/services/imageCompressionService.d.ts +0 -37
  615. package/lib/typescript/services/imageCompressionService.d.ts.map +0 -1
  616. package/lib/typescript/services/jwtStorageService.d.ts +0 -86
  617. package/lib/typescript/services/jwtStorageService.d.ts.map +0 -1
  618. package/lib/typescript/services/mobileTrainingService.d.ts +0 -45
  619. package/lib/typescript/services/mobileTrainingService.d.ts.map +0 -1
  620. package/lib/typescript/services/pinEncryptionService.d.ts +0 -17
  621. package/lib/typescript/services/pinEncryptionService.d.ts.map +0 -1
  622. package/lib/typescript/services/pinStorageUtils.d.ts +0 -25
  623. package/lib/typescript/services/pinStorageUtils.d.ts.map +0 -1
  624. package/lib/typescript/services/storageService.d.ts +0 -128
  625. package/lib/typescript/services/storageService.d.ts.map +0 -1
  626. package/lib/typescript/services/trainingApiHelpers.d.ts +0 -38
  627. package/lib/typescript/services/trainingApiHelpers.d.ts.map +0 -1
  628. package/lib/typescript/services/userConnectionsService.d.ts +0 -90
  629. package/lib/typescript/services/userConnectionsService.d.ts.map +0 -1
  630. package/lib/typescript/services/youtubeMigrationService.d.ts +0 -12
  631. package/lib/typescript/services/youtubeMigrationService.d.ts.map +0 -1
  632. package/lib/typescript/theme/index.d.ts +0 -416
  633. package/lib/typescript/theme/index.d.ts.map +0 -1
  634. package/lib/typescript/utils/eventUtils.d.ts +0 -108
  635. package/lib/typescript/utils/eventUtils.d.ts.map +0 -1
  636. package/lib/typescript/utils/haptics.d.ts +0 -11
  637. package/lib/typescript/utils/haptics.d.ts.map +0 -1
  638. package/lib/typescript/utils/imagePreloader.d.ts +0 -2
  639. package/lib/typescript/utils/imagePreloader.d.ts.map +0 -1
  640. package/src/assets/fonts/EBGaramond-VariableFont_wght.ttf +0 -0
  641. package/src/assets/fonts/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  642. package/src/assets/icons/Facebookicon.png +0 -0
  643. package/src/assets/icons/Gmail.png +0 -0
  644. package/src/assets/icons/Linkedinicon.png +0 -0
  645. package/src/assets/icons/Redditicon.png +0 -0
  646. package/src/assets/icons/YouTubeicon2.png +0 -0
  647. package/src/assets/icons/YouTubeicon3.png +0 -0
  648. package/src/assets/icons/farcaster.png +0 -0
  649. package/src/assets/icons/instagram.png +0 -0
  650. package/src/assets/icons/pinterest.png +0 -0
  651. package/src/assets/icons/swerv_logo.png +0 -0
  652. package/src/assets/icons/twitter.jpg +0 -0
  653. package/src/assets/images/Checkbox.svg +0 -3
  654. package/src/assets/images/EnochE.svg +0 -19
  655. package/src/assets/images/Enochicon1.png +0 -0
  656. package/src/assets/images/Face_ID_logo.png +0 -0
  657. package/src/assets/images/Facebookicon.png +0 -0
  658. package/src/assets/images/Gmail.png +0 -0
  659. package/src/assets/images/Googlelogo.png +0 -0
  660. package/src/assets/images/Linkedinicon.png +0 -0
  661. package/src/assets/images/Onairoslogo.png +0 -0
  662. package/src/assets/images/Personalityprofile.svg +0 -3
  663. package/src/assets/images/Personalitytraits.svg +0 -3
  664. package/src/assets/images/Redditicon.png +0 -0
  665. package/src/assets/images/Userpreferences.svg +0 -3
  666. package/src/assets/images/YouTubeicon3.png +0 -0
  667. package/src/assets/images/arrow.svg +0 -20
  668. package/src/assets/images/basicproficon.svg +0 -43
  669. package/src/assets/images/basicprofile.svg +0 -3
  670. package/src/assets/images/checkmark.svg +0 -4
  671. package/src/assets/images/contentanalysis.svg +0 -3
  672. package/src/assets/images/contenticon.svg +0 -23
  673. package/src/assets/images/persona1.png +0 -0
  674. package/src/assets/images/persona2.png +0 -0
  675. package/src/assets/images/persona3.png +0 -0
  676. package/src/assets/images/persona4.png +0 -0
  677. package/src/assets/images/persona5.png +0 -0
  678. package/src/assets/images/personalityicon.svg +0 -18
  679. package/src/assets/images/x-close.svg +0 -3
  680. package/src/components/BodyText.tsx +0 -33
  681. package/src/components/BrandMark.tsx +0 -62
  682. package/src/components/CodeInput.tsx +0 -32
  683. package/src/components/EmailInput.tsx +0 -31
  684. package/src/components/ExistingUserDataConfirmation.tsx +0 -507
  685. package/src/components/GoogleButton.tsx +0 -55
  686. package/src/components/HeadingGroup.tsx +0 -49
  687. package/src/components/ModalHeader.tsx +0 -125
  688. package/src/components/ModalSheet.tsx +0 -59
  689. package/src/components/OnairosSignInButton.tsx +0 -132
  690. package/src/components/PersonaImage.tsx +0 -79
  691. package/src/components/PersonaLoadingScreen.tsx +0 -201
  692. package/src/components/PersonalizationConsentScreen.tsx +0 -410
  693. package/src/components/PinCreationScreen.tsx +0 -492
  694. package/src/components/PlatformConnectorsStep.tsx +0 -892
  695. package/src/components/PlatformToggle.tsx +0 -226
  696. package/src/components/PrimaryButton.tsx +0 -214
  697. package/src/components/SignInMatchAnimation.tsx +0 -225
  698. package/src/components/SignInStep.tsx +0 -217
  699. package/src/components/VerificationStep.tsx +0 -198
  700. package/src/components/WelcomeScreen.tsx +0 -490
  701. package/src/components/icons/Basicproficon.tsx +0 -30
  702. package/src/components/icons/Basicprofile.tsx +0 -17
  703. package/src/components/icons/Checkbox.tsx +0 -17
  704. package/src/components/icons/Checkmark.tsx +0 -24
  705. package/src/components/icons/Contentanalysis.tsx +0 -17
  706. package/src/components/icons/Contenticon.tsx +0 -30
  707. package/src/components/icons/EnochE.tsx +0 -39
  708. package/src/components/icons/Personalityicon.tsx +0 -22
  709. package/src/components/icons/Personalityprofile.tsx +0 -17
  710. package/src/components/icons/Personalitytraits.tsx +0 -17
  711. package/src/components/icons/Userpreferences.tsx +0 -17
  712. package/src/components/icons/index.ts +0 -12
  713. package/src/config/api.ts +0 -25
  714. package/src/context/AuthContext.tsx +0 -393
  715. package/src/hooks/useConnectedAccounts.ts +0 -139
  716. package/src/hooks/useUserConnections.ts +0 -166
  717. package/src/services/apiClient.ts +0 -337
  718. package/src/services/authService.ts +0 -1008
  719. package/src/services/biometricPinService.ts +0 -193
  720. package/src/services/connectedAccountsService.ts +0 -290
  721. package/src/services/googleAuthService.ts +0 -279
  722. package/src/services/imageCompressionService.ts +0 -303
  723. package/src/services/jwtStorageService.ts +0 -257
  724. package/src/services/mobileTrainingService.ts +0 -204
  725. package/src/services/pinEncryptionService.ts +0 -76
  726. package/src/services/pinStorageUtils.ts +0 -97
  727. package/src/services/storageService.ts +0 -452
  728. package/src/services/trainingApiHelpers.ts +0 -67
  729. package/src/services/userConnectionsService.ts +0 -557
  730. package/src/services/youtubeMigrationService.ts +0 -454
  731. package/src/theme/index.ts +0 -239
  732. package/src/utils/eventUtils.ts +0 -303
  733. package/src/utils/haptics.ts +0 -59
  734. package/src/utils/imagePreloader.ts +0 -2
@@ -1,2888 +1,1839 @@
1
- import React, { useCallback, useEffect, useState, useRef } from 'react';
2
- import {
3
- View,
4
- Text,
5
- Image,
6
- StyleSheet,
7
- Dimensions,
8
- TouchableOpacity,
9
- ActivityIndicator,
10
- Platform,
11
- Modal,
12
- Animated,
13
- SafeAreaView,
14
- ScrollView,
15
- Switch,
16
- Linking,
17
- KeyboardAvoidingView,
18
- Vibration,
19
- Easing,
20
- Alert,
21
- } from 'react-native';
22
- // Removed navigation dependencies for SDK compatibility
23
- import AsyncStorage from '@react-native-async-storage/async-storage';
24
- // Import components and hooks
25
- import { PinInput } from './PinInput';
26
- import { OAuthWebView } from './onboarding/OAuthWebView';
27
- // import { useConnections } from '../../hooks/useConnections'; // Commented out - file path issue
28
- // import { useAuth } from '../../context/AuthContext'; // Commented out - file path issue
29
- import { initiateOAuth, initiateNativeAuth, hasNativeSDK, isOAuthCallback } from '../services/platformAuthService';
30
- import { triggerHaptic, HapticType } from '../utils/haptics';
31
- import { getOnairosUsername } from '../services/authService';
32
- import { io, Socket } from 'socket.io-client';
33
- import { setTemporaryPin, clearTemporaryPin } from '../services/pinStorageUtils';
34
- import { getEncryptedPinForAPI } from '../services/pinEncryptionService';
35
- import { startEnochTrainingWithYouTubeCheck } from '../services/mobileTrainingService';
36
-
37
- // Import types
38
- // import type { ConnectionStatus } from '../../hooks/useConnections'; // Commented out - file path issue
39
- // Removed App-specific navigation types
40
-
41
- const { height, width } = Dimensions.get('window');
42
-
43
- export interface UniversalOnboardingProps {
44
- visible: boolean;
45
- onClose: () => void;
46
- AppName: string;
47
- requestData?: any;
48
- returnLink?: string;
49
- onComplete?: (apiUrl: string, token: string, userData: any) => void;
50
- embedd?: boolean;
51
- debug?: boolean;
52
- test?: boolean;
53
- testMode?: boolean;
54
- preferredPlatform?: string;
55
- primaryAuthOnly?: boolean;
56
- }
57
-
58
- // ConnectionStatus interface is now imported from hooks/useConnections
59
-
60
- export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
61
- visible,
62
- onClose,
63
- AppName,
64
- requestData,
65
- returnLink,
66
- onComplete,
67
- embedd = false,
68
- debug = false,
69
- test = false,
70
- preferredPlatform,
71
- primaryAuthOnly = false,
72
- }) => {
73
- const [step, setStep] = useState<'connect' | 'pin' | 'persona' | 'oauth' | 'privacy' | 'connections'>(primaryAuthOnly ? 'persona' : 'connect');
74
- const [connections, setConnections] = useState<any>({});
75
- const [pin, setPin] = useState<string>('');
76
- const [selectedTier, setSelectedTier] = useState<'Small' | 'Medium' | 'Large'>('Medium');
77
- // Training state is now handled by TrainingModal
78
- const [slideAnim] = useState(new Animated.Value(height * 0.5));
79
- const [platformToggles, setPlatformToggles] = useState<{[key: string]: boolean}>({});
80
- const [oauthUrl, setOauthUrl] = useState<string>('');
81
- const [currentPlatform, setCurrentPlatform] = useState<string>('');
82
- const [email, setEmail] = useState<string>('');
83
- const [longPressTimer, setLongPressTimer] = useState<ReturnType<typeof setTimeout> | null>(null);
84
- const [modalVisible, setModalVisible] = useState(visible);
85
-
86
- // Debug logging for modal visibility
87
- useEffect(() => {
88
- console.log('🔍 UniversalOnboarding: visible prop changed to:', visible);
89
- console.log('🔍 UniversalOnboarding: modalVisible state is:', modalVisible);
90
- console.log('🔍 UniversalOnboarding: current step is:', step);
91
- console.log('🔍 UniversalOnboarding: primaryAuthOnly is:', primaryAuthOnly);
92
- }, [visible, modalVisible, step, primaryAuthOnly]);
93
- const isCompletingRef = useRef(false);
94
- const [connectionsCount, setConnectionsCount] = useState<number>(5); // Simulated connections count
95
-
96
- // Add state for showing additional platforms
97
- const [showAdditionalPlatforms, setShowAdditionalPlatforms] = useState<boolean>(false);
98
- const [additionalPlatformsOpacity] = useState(new Animated.Value(0));
99
-
100
- // Add ref for ScrollView to control scroll position
101
- const scrollViewRef = useRef<ScrollView>(null);
102
- // Get the authenticated user from auth context
103
- // const { user } = useAuth(); // Hook not available
104
- const user = null;
105
-
106
- // State for storing the correct username
107
- const [username, setUsername] = useState<string>('');
108
-
109
- // Real training state variables (replacing fake persona state)
110
- const [personaProgress, setPersonaProgress] = useState(0);
111
- const [personaStatus, setPersonaStatus] = useState('Initializing...');
112
- const [isPersonaComplete, setIsPersonaComplete] = useState(false);
113
- const [socketConnected, setSocketConnected] = useState(false);
114
- const [hasError, setHasError] = useState(false);
115
- const [userTraits, setUserTraits] = useState<any>(null);
116
- const [inferenceResults, setInferenceResults] = useState<any>(null);
117
- const [userToken, setUserToken] = useState<string | null>(null);
118
- const [userInfo, setUserInfo] = useState<any>(null);
119
- const [animatedDots, setAnimatedDots] = useState('');
120
- const socketRef = useRef<Socket | null>(null);
121
- const dotsAnimationRef = useRef<ReturnType<typeof setInterval> | null>(null);
122
-
123
- // Existing user state
124
- const [isExistingUser, setIsExistingUser] = useState(false);
125
- const [existingUserToken, setExistingUserToken] = useState<string | null>(null);
126
- const [existingUserInfo, setExistingUserInfo] = useState<any>(null);
127
-
128
- // Data scenario states
129
- const [dataScenario, setDataScenario] = useState<'SUFFICIENT' | 'LIMITED_DATA' | 'NO_DATA' | 'CONNECTIONS_REQUIRED' | null>(null);
130
- const [dataDetails, setDataDetails] = useState<any>(null);
131
- const [showDataWarning, setShowDataWarning] = useState(false);
132
-
133
- // ✅ NEW: Background training state
134
- const [isBackgroundTrainingStarted, setIsBackgroundTrainingStarted] = useState(false);
135
- const [backgroundTrainingProgress, setBackgroundTrainingProgress] = useState('');
136
- const [backgroundSocketId, setBackgroundSocketId] = useState<string | null>(null);
137
-
138
- // Function to store connected platforms
139
- const storeConnectedPlatform = async (platformId: string) => {
140
- try {
141
- const storedPlatforms = await AsyncStorage.getItem('connectedPlatforms');
142
- let platforms: string[] = storedPlatforms ? JSON.parse(storedPlatforms) : [];
143
-
144
- // Add platform if not already in the list
145
- if (!platforms.includes(platformId)) {
146
- platforms.push(platformId);
147
- await AsyncStorage.setItem('connectedPlatforms', JSON.stringify(platforms));
148
- console.log('📱 Stored connected platform:', platformId, 'Total platforms:', platforms);
149
- }
150
- } catch (error) {
151
- console.error('Error storing connected platform:', error);
152
- }
153
- };
154
-
155
- // Function to remove connected platform from storage
156
- const removeConnectedPlatform = async (platformId: string) => {
157
- try {
158
- const storedPlatforms = await AsyncStorage.getItem('connectedPlatforms');
159
- let platforms: string[] = storedPlatforms ? JSON.parse(storedPlatforms) : [];
160
-
161
- // Remove platform from the list
162
- platforms = platforms.filter(platform => platform !== platformId);
163
- await AsyncStorage.setItem('connectedPlatforms', JSON.stringify(platforms));
164
- console.log('📱 Removed connected platform:', platformId, 'Remaining platforms:', platforms);
165
- } catch (error) {
166
- console.error('Error removing connected platform:', error);
167
- }
168
- };
169
-
170
- // Function to handle disconnect confirmation
171
- const handleDisconnectPlatform = (platformId: string, platformName: string) => {
172
- Alert.alert(
173
- 'Disconnect Platform',
174
- `Are you sure you want to disconnect ${platformName}?`,
175
- [
176
- {
177
- text: 'No',
178
- style: 'cancel',
179
- },
180
- {
181
- text: 'Yes',
182
- style: 'destructive',
183
- onPress: async () => {
184
- try {
185
- // Update local state to show disconnected
186
- setConnectionStatuses(prev => ({
187
- ...prev,
188
- [platformId]: 'disconnected'
189
- }));
190
-
191
- setConnections(prev => {
192
- const newConnections = { ...prev };
193
- delete newConnections[platformId];
194
- return newConnections;
195
- });
196
-
197
- setPlatformToggles(prev => ({
198
- ...prev,
199
- [platformId]: false
200
- }));
201
-
202
- // Remove from storage
203
- await removeConnectedPlatform(platformId);
204
-
205
- // Call the disconnect function from the hook
206
- await disconnectPlatform();
207
-
208
- console.log('🔌 Disconnected platform:', platformId);
209
- } catch (error) {
210
- console.error('Error disconnecting platform:', error);
211
- }
212
- },
213
- },
214
- ]
215
- );
216
- };
217
-
218
- // Function to start animated dots
219
- const startDotsAnimation = () => {
220
- if (dotsAnimationRef.current) {
221
- clearInterval(dotsAnimationRef.current);
222
- }
223
-
224
- let dotCount = 0;
225
- dotsAnimationRef.current = setInterval(() => {
226
- dotCount = (dotCount + 1) % 4; // 0, 1, 2, 3, then back to 0
227
- if (dotCount === 0) {
228
- setAnimatedDots('');
229
- } else {
230
- setAnimatedDots('.'.repeat(dotCount));
231
- }
232
- }, 500); // Change every 500ms
233
- };
234
-
235
- // Function to stop animated dots
236
- const stopDotsAnimation = () => {
237
- if (dotsAnimationRef.current) {
238
- clearInterval(dotsAnimationRef.current);
239
- dotsAnimationRef.current = null;
240
- }
241
- setAnimatedDots('');
242
- };
243
-
244
- // Split platforms into main and additional
245
- const mainPlatforms = [
246
- { id: 'pinterest', name: 'Pinterest', color: '#E60023' },
247
- { id: 'youtube', name: 'YouTube', color: '#FFFFFF' },
248
- { id: 'linkedin', name: 'LinkedIn', color: '#0077B5' },
249
- ];
250
-
251
- const additionalPlatforms = [
252
- { id: 'reddit', name: 'Reddit', color: '#FFFFFF' },
253
- { id: 'gmail', name: 'Gmail', color: '#EA4335' },
254
- ];
255
-
256
- // Keep the original platforms array for compatibility
257
- const platforms = [...mainPlatforms, ...additionalPlatforms];
258
-
259
- // const {
260
- // connectPlatform,
261
- // disconnectPlatform,
262
- // getConnectionStatus,
263
- // isConnecting,
264
- // } = useConnections(); // Hook not available
265
- const connectPlatform = async () => {};
266
- const disconnectPlatform = async () => {};
267
- const getConnectionStatus = () => 'disconnected';
268
- const isConnecting = false;
269
- const isConnected = () => false;
270
-
271
- // Track connection statuses and currently connecting platform
272
- const [connectionStatuses, setConnectionStatuses] = useState<Record<string, string>>({});
273
- const [connectingPlatform, setConnectingPlatform] = useState<string | null>(null);
274
-
275
- // Function to get the platform icon based on platform ID
276
- const getPlatformIcon = (platformId: string) => {
277
- switch (platformId) {
278
- case 'instagram':
279
- return require('../assets/icons/instagram.png');
280
- case 'youtube':
281
- return require('../assets/icons/YouTubeicon2.png');
282
- case 'reddit':
283
- return require('../assets/icons/Redditicon.png');
284
- case 'pinterest':
285
- return require('../assets/icons/pinterest.png');
286
- case 'facebook':
287
- return require('../assets/icons/Facebookicon.png');
288
- case 'linkedin':
289
- return require('../assets/icons/Linkedinicon.png');
290
- case 'gmail':
291
- return require('../assets/icons/Gmail.png');
292
- }
293
- };
294
-
295
- // Track if the modal has ever been visible to prevent initial onClose call
296
- const hasBeenVisibleRef = useRef(false);
297
-
298
- useEffect(() => {
299
- if (visible) {
300
- hasBeenVisibleRef.current = true; // Mark that modal has been visible
301
- isCompletingRef.current = false; // Reset flag when becoming visible
302
- setModalVisible(true);
303
- loadInitialStatus();
304
- // Animate in
305
- Animated.spring(slideAnim, {
306
- toValue: 0,
307
- friction: 8,
308
- tension: 40,
309
- useNativeDriver: true,
310
- }).start();
311
-
312
- const subscription = Linking.addEventListener('url', handleUrl);
313
- return () => {
314
- subscription.remove();
315
- };
316
- } else if (hasBeenVisibleRef.current && !isCompletingRef.current) {
317
- // Only animate out if NOT completing - prevents flicker during successful completion
318
- Animated.timing(slideAnim, {
319
- toValue: height,
320
- useNativeDriver: true,
321
- duration: 500, // Longer duration for a more elegant exit
322
- easing: Easing.bezier(0.16, 1, 0.3, 1), // Custom bezier curve for a luxurious, smooth exit
323
- }).start(() => {
324
- // Use a timeout to prevent animation state updates during unmount
325
- setTimeout(() => {
326
- setModalVisible(false);
327
- onClose(); // Call onClose from props
328
- }, 16);
329
- });
330
- } else if (hasBeenVisibleRef.current && isCompletingRef.current) {
331
- // If completing, hide modal immediately without any animation or delay
332
- setModalVisible(false);
333
- // Don't call onClose() when completing - let parent handle the navigation
334
- isCompletingRef.current = false; // Reset for next time
335
- }
336
- }, [visible, onClose]); // Removed isCompleting from dependency array
337
-
338
- // Cleanup socket connection and dots animation when component unmounts or becomes invisible
339
- useEffect(() => {
340
- return () => {
341
- if (socketRef.current) {
342
- console.log('🔌 Cleaning up socket connection...');
343
- socketRef.current.disconnect();
344
- socketRef.current = null;
345
- }
346
- stopDotsAnimation();
347
- };
348
- }, [visible]);
349
-
350
- // Set up deep link listener for OAuth callbacks
351
- const handleUrl = useCallback(({url}: {url: string}) => {
352
- if (isOAuthCallback(url)) {
353
- handleOAuthCallback(url);
354
- }
355
- }, []);
356
-
357
- // Load user data and authentication token when modal becomes visible
358
- useEffect(() => {
359
- const loadUserData = async () => {
360
- try {
361
- // Check for existing user info first
362
- const existingToken = await AsyncStorage.getItem('existing_user_token');
363
- const existingInfo = await AsyncStorage.getItem('existing_user_info');
364
-
365
- if (existingToken && existingInfo) {
366
- console.log('🔍 Found existing user info - user wants to add more data');
367
- setIsExistingUser(true);
368
- setExistingUserToken(existingToken);
369
- setExistingUserInfo(JSON.parse(existingInfo));
370
-
371
- // Clear the temporary storage
372
- await AsyncStorage.removeItem('existing_user_token');
373
- await AsyncStorage.removeItem('existing_user_info');
374
- }
375
-
376
- const token = await AsyncStorage.getItem('onairos_jwt_token') || await AsyncStorage.getItem('auth_token');
377
- setUserToken(token);
378
-
379
- if (token) {
380
- // Use provided username or get stored username
381
- const storedUsername = await getOnairosUsername();
382
- const fallbackUsername = storedUsername || user?.email?.split('@')[0] || user?.name || 'mobile_user';
383
-
384
- console.log('🔍 Using username for training:', fallbackUsername);
385
- setUserInfo({
386
- username: fallbackUsername,
387
- email: user?.email || null,
388
- id: null // Will be filled by backend during training
389
- });
390
- setUsername(fallbackUsername);
391
- }
392
- } catch (error) {
393
- console.error('Error loading user data:', error);
394
- // Fallback user info
395
- const fallbackUsername = user?.email?.split('@')[0] || user?.name || 'mobile_user';
396
- setUserInfo({
397
- username: fallbackUsername,
398
- email: user?.email || null,
399
- id: null
400
- });
401
- setUsername(fallbackUsername);
402
- }
403
- };
404
-
405
- if (visible) {
406
- loadUserData();
407
- }
408
- }, [visible, user]);
409
-
410
- const loadInitialStatus = async () => {
411
- try {
412
- // Get the stored Onairos username first
413
- const storedUsername = await getOnairosUsername();
414
- const fallbackUsername = user?.email || user?.name || `user_${Math.floor(Math.random() * 10000)}`;
415
- const finalUsername = storedUsername || fallbackUsername;
416
-
417
- console.log('🔍 Loading username for data connections:', {
418
- storedUsername,
419
- fallbackUsername,
420
- finalUsername
421
- });
422
-
423
- setUsername(finalUsername);
424
-
425
- // Initialize connection statuses
426
- const connectionStatus = await getConnectionStatus();
427
- console.log('Initial connection status:', connectionStatus);
428
-
429
- // Update the main connections state
430
- setConnections(connectionStatus);
431
-
432
- // Convert connections object to status strings
433
- const statuses: Record<string, string> = {};
434
- Object.keys(connectionStatus).forEach(platform => {
435
- statuses[platform] = connectionStatus[platform]?.connected ? 'connected' : 'disconnected';
436
- });
437
- setConnectionStatuses(statuses);
438
-
439
- // Initialize platform toggles based on connection statuses
440
- const toggles: {[key: string]: boolean} = {};
441
- Object.keys(connectionStatus).forEach(platform => {
442
- toggles[platform] = connectionStatus[platform]?.connected || false;
443
- });
444
- setPlatformToggles(toggles);
445
-
446
- console.log('Connection statuses set:', statuses);
447
- console.log('Platform toggles set:', toggles);
448
- } catch (error) {
449
- console.error('Error loading initial connection status:', error);
450
- // Set empty objects as fallback
451
- setConnections({});
452
- setConnectionStatuses({});
453
- setPlatformToggles({});
454
-
455
- // Still set a fallback username even if other loading fails
456
- const fallbackUsername = user?.email || user?.name || `user_${Math.floor(Math.random() * 10000)}`;
457
- setUsername(fallbackUsername);
458
- }
459
- };
460
-
461
- const handleClose = () => {
462
- const currentlyCompleting = isCompletingRef.current; // Capture ref's current value
463
-
464
- // Animate out with a luxurious, smooth motion
465
- Animated.timing(slideAnim, {
466
- toValue: height, // Full slide down
467
- useNativeDriver: true,
468
- duration: 500, // Longer duration for a more elegant exit
469
- easing: Easing.bezier(0.16, 1, 0.3, 1), // Custom bezier curve for a luxurious, smooth exit
470
- }).start(() => {
471
- // Small delay before closing modal to ensure animation completes fully
472
- setTimeout(() => {
473
- setModalVisible(false);
474
- if (!currentlyCompleting) { // Use the captured value
475
- onClose();
476
- }
477
- // Do NOT reset isCompletingRef.current here. The useEffect watching props.visible is responsible for that.
478
- }, 100);
479
- });
480
- };
481
-
482
- const handleConnectPlatform = async (platformId: string) => {
483
- // Trigger haptic feedback when connect button is pressed
484
- triggerHaptic(HapticType.BUTTON_PRESS);
485
-
486
- setConnectingPlatform(platformId);
487
-
488
- try {
489
- // Check if the platform has a native SDK
490
- if (hasNativeSDK(platformId)) {
491
- // Use native SDK authentication
492
- setCurrentPlatform(platformId);
493
- const success = await initiateNativeAuth(platformId, username);
494
-
495
- if (success) {
496
- // Update connections state
497
- setConnections(prev => ({
498
- ...prev,
499
- [platformId]: { userName: username, connected: true }
500
- }));
501
-
502
- // Update platform toggles
503
- setPlatformToggles(prev => ({
504
- ...prev,
505
- [platformId]: true
506
- }));
507
-
508
- // Update connection statuses
509
- setConnectionStatuses(prev => ({
510
- ...prev,
511
- [platformId]: 'connected'
512
- }));
513
-
514
- // Store the connected platform
515
- await storeConnectedPlatform(platformId);
516
- }
517
- } else {
518
- // For other platforms, use the web OAuth flow
519
- setCurrentPlatform(platformId);
520
- const oauthUrl = await initiateOAuth(platformId, username);
521
-
522
- if (oauthUrl) {
523
- setOauthUrl(oauthUrl);
524
- setStep('oauth');
525
- }
526
- }
527
- } catch (error) {
528
- console.error(`Error connecting to ${platformId}:`, error);
529
- } finally {
530
- setConnectingPlatform(null);
531
- }
532
- };
533
-
534
- const togglePlatform = useCallback(async (platformId: string) => {
535
- // If toggling on, initiate the OAuth flow for the platform
536
- if (!platformToggles[platformId]) {
537
- try {
538
- // Special case for Instagram which uses the existing flow
539
- if (platformId === 'instagram') {
540
- setPlatformToggles(prev => ({
541
- ...prev,
542
- [platformId]: !prev[platformId]
543
- }));
544
- setConnections(prev => ({
545
- ...prev,
546
- [platformId]: { userName: `${platformId}_user`, connected: true }
547
- }));
548
- // Store the connected platform
549
- await storeConnectedPlatform(platformId);
550
- return;
551
- }
552
-
553
- // Check if the platform has a native SDK
554
- if (hasNativeSDK(platformId)) {
555
- // Use native SDK authentication
556
- setCurrentPlatform(platformId);
557
- const success = await initiateNativeAuth(platformId, username);
558
-
559
- if (success) {
560
- // Update connections state
561
- setConnections(prev => ({
562
- ...prev,
563
- [platformId]: { userName: username, connected: true }
564
- }));
565
-
566
- // Update platform toggles
567
- setPlatformToggles(prev => ({
568
- ...prev,
569
- [platformId]: true
570
- }));
571
-
572
- // Store the connected platform
573
- await storeConnectedPlatform(platformId);
574
- }
575
- return;
576
- }
577
-
578
- // For other platforms, use the web OAuth flow
579
- setCurrentPlatform(platformId);
580
- const oauthUrl = await initiateOAuth(platformId, username);
581
-
582
- // If oauthUrl is null, it means we should use native SDK (which should have been caught above)
583
- if (oauthUrl) {
584
- setOauthUrl(oauthUrl);
585
- setStep('oauth');
586
- }
587
- } catch (error) {
588
- console.error(`Error initiating OAuth for ${platformId}:`, error);
589
- // If there's an error, don't toggle the platform
590
- return;
591
- }
592
- } else {
593
- // If toggling off, just update the state
594
- setPlatformToggles(prev => ({
595
- ...prev,
596
- [platformId]: !prev[platformId]
597
- }));
598
-
599
- // Remove connection
600
- setConnections(prev => {
601
- const newConnections = { ...prev };
602
- delete newConnections[platformId];
603
- return newConnections;
604
- });
605
- }
606
- }, [platformToggles, username]);
607
-
608
- /**
609
- * Handles OAuth callback URLs
610
- */
611
- const handleOAuthCallback = useCallback((url: string) => {
612
- try {
613
- // Extract the authorization code from the URL
614
- const parsedUrl = new URL(url);
615
- const code = parsedUrl.searchParams.get('code');
616
- const platform = parsedUrl.searchParams.get('platform') || currentPlatform;
617
-
618
- if (code && platform) {
619
- // Update connections state
620
- setConnections(prev => ({
621
- ...prev,
622
- [platform]: { userName: username, connected: true }
623
- }));
624
-
625
- // Update platform toggles
626
- setPlatformToggles(prev => ({
627
- ...prev,
628
- [platform]: true
629
- }));
630
-
631
- // Return to the connect step
632
- setStep('connect');
633
- }
634
- } catch (error) {
635
- console.error('Error handling OAuth callback:', error);
636
- }
637
- }, [currentPlatform, username]);
638
-
639
- /**
640
- * Handles successful OAuth authentication from the OAuthWebView
641
- */
642
- const handleOAuthSuccess = useCallback(async (code: string) => {
643
- console.log(`🎉 OAuth success for ${currentPlatform} - backend callback completed`);
644
- console.log(`📝 Received result:`, code);
645
-
646
- if (currentPlatform) {
647
- console.log(`✅ Updating connection state for ${currentPlatform}`);
648
-
649
- // Update connections state
650
- setConnections(prev => ({
651
- ...prev,
652
- [currentPlatform]: { userName: username, connected: true }
653
- }));
654
-
655
- // Update platform toggles
656
- setPlatformToggles(prev => ({
657
- ...prev,
658
- [currentPlatform]: true
659
- }));
660
-
661
- // Update connection statuses
662
- setConnectionStatuses(prev => ({
663
- ...prev,
664
- [currentPlatform]: 'connected'
665
- }));
666
-
667
- // Store the connected platform
668
- await storeConnectedPlatform(currentPlatform);
669
-
670
- console.log(`💾 ${currentPlatform} connection stored successfully`);
671
- }
672
-
673
- // Close the OAuth window by returning to the connect step
674
- console.log('🔄 Returning to connect step');
675
- setOauthUrl('');
676
- setStep('connect');
677
- }, [currentPlatform, username, storeConnectedPlatform]);
678
-
679
- const handlePinSubmit = useCallback(async (userPin: string) => {
680
- setPin(userPin);
681
-
682
- // Store PIN temporarily for training component access
683
- setTemporaryPin(userPin);
684
-
685
- console.log('🔐 [PIN SUBMIT] PIN submitted, checking for background training...');
686
-
687
- // NEW: Check if background training is already running
688
- if (isBackgroundTrainingStarted && backgroundSocketId) {
689
- console.log('✅ [PIN SUBMIT] Background training detected, continuing with PIN validation...');
690
-
691
- // Continue with existing training by sending PIN validation
692
- try {
693
- await continueBackgroundTrainingWithPin(userPin);
694
- setStep('persona'); // Move to persona step to show training progress
695
- } catch (error) {
696
- console.error('❌ [PIN SUBMIT] Failed to continue background training:', error);
697
- // ❌ DISABLED: Don't start new training when PIN is submitted
698
- // Just show persona step with error state
699
- setStep('persona');
700
- setPersonaStatus('Error: Failed to continue training');
701
- setHasError(true);
702
- }
703
- } else {
704
- console.log('ℹ️ [PIN SUBMIT] No background training detected');
705
- // DISABLED: Don't start training when PIN is submitted
706
- // Training should only start during connector→PIN transition
707
- setStep('persona');
708
- setPersonaStatus('No training in progress. Please restart the flow.');
709
- setHasError(true);
710
- }
711
-
712
- console.log('🔐 [PIN SUBMIT] PIN stored securely, moved to persona step');
713
- }, [isBackgroundTrainingStarted, backgroundSocketId]);
714
-
715
- // Start real Enoch training via API (restored for backwards compatibility)
716
- const startEnochTraining = async (socketId: string, authToken?: string) => {
717
- try {
718
- setPersonaStatus('Starting training...');
719
- setPersonaProgress(15);
720
-
721
- // Use provided token or get from AsyncStorage to avoid state timing issues
722
- const token = authToken || await AsyncStorage.getItem('onairos_jwt_token') || await AsyncStorage.getItem('auth_token');
723
-
724
- if (!token) {
725
- console.error(' No authentication token available');
726
- throw new Error('No authentication token available');
727
- }
728
-
729
- console.log('🚀 Starting Enoch training with socketId:', socketId);
730
- console.log('🔑 Using auth token:', token ? `${token.substring(0, 20)}...` : 'None');
731
-
732
- // Get stored Onairos username for API calls
733
- const storedUsername = await getOnairosUsername();
734
- const finalUsername = storedUsername || userInfo?.username || userInfo?.name || username || 'mobile_user';
735
-
736
- // Get connected platforms information
737
- const connectedPlatforms = await AsyncStorage.getItem('connectedPlatforms');
738
- const platformsList = connectedPlatforms ? JSON.parse(connectedPlatforms) : [];
739
-
740
- console.log('📱 Connected platforms for training:', platformsList);
741
-
742
- // Get encrypted PIN for training (if available)
743
- const encryptedPin = await getEncryptedPinForAPI().catch(error => {
744
- console.warn('⚠️ Could not get encrypted PIN for training:', error);
745
- return null;
746
- });
747
-
748
- // Prepare user data for training - match backend expected format
749
- const trainingData = {
750
- socketId,
751
- username: finalUsername,
752
- email: userInfo?.email || null,
753
- modelKey: null,
754
- connectedPlatforms: platformsList,
755
- platformConnections: connections,
756
- ...(encryptedPin && {
757
- encryptedPin: encryptedPin,
758
- hasPinData: true
759
- })
760
- };
761
-
762
- console.log('📤 Sending training data to /mobile-training/enoch:', trainingData);
763
-
764
- // Use the new training function that includes YouTube migration check
765
- const result = await startEnochTrainingWithYouTubeCheck(trainingData);
766
- console.log('📡 Training API response:', result);
767
-
768
- // Handle CONNECTIONS_REQUIRED scenario (pre-training validation)
769
- if (result.requiresConnections || result.code === 'CONNECTIONS_REQUIRED') {
770
- console.log('🔗 Connections required detected from HTTP response');
771
- setDataScenario('CONNECTIONS_REQUIRED');
772
- setDataDetails(result);
773
- setShowDataWarning(true);
774
- setPersonaStatus('Connections required');
775
- setHasError(true);
776
- stopDotsAnimation();
777
- return;
778
- }
779
-
780
- if (result.success) {
781
- console.log('🚀 Training Started:', result.message);
782
- console.log('🎯 Training Features:', result.features);
783
-
784
- // Log the new features from the spec
785
- if (result.features) {
786
- console.log('✅ Inference enabled:', result.features.inference);
787
- console.log('💾 Storage method:', result.features.storage);
788
- console.log('🔒 Compression enabled:', result.features.compression);
789
- console.log('🔐 Encryption enabled:', result.features.encryption);
790
- console.log('📊 Training type:', result.features.type);
791
- console.log('🗄️ Databases:', result.features.databases);
792
- console.log('📈 Query scores enabled:', result.features.queryScores);
793
- }
794
-
795
- setPersonaStatus('Training model...');
796
- setPersonaProgress(25);
797
- } else {
798
- console.error('❌ Training start failed:', result.error);
799
- setPersonaStatus(`Error: ${result.error || 'Training failed to start'}`);
800
- setHasError(true);
801
- stopDotsAnimation();
802
- }
803
- } catch (error) {
804
- console.error('❌ Training start error:', error);
805
- setPersonaStatus(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
806
- setHasError(true);
807
- stopDotsAnimation();
808
- }
809
- };
810
-
811
- // NEW: Continue background training with PIN validation
812
- const continueBackgroundTrainingWithPin = async (userPin: string) => {
813
- try {
814
- console.log('🔐 [BACKGROUND] Continuing existing background training with PIN validation');
815
- console.log('🔑 Using auth token:', `${userToken?.substring(0, 20)}...`);
816
-
817
- // Check if we have a valid socket ID
818
- if (!backgroundSocketId) {
819
- throw new Error('No background socket ID available');
820
- }
821
-
822
- // REMOVED: Don't send new training signal - training already started!
823
- // We should just validate the PIN with the existing training, not start new training
824
-
825
- console.log('🔐 [BACKGROUND] PIN validation - continuing with existing training session');
826
- console.log('🔌 [BACKGROUND] Using existing socket ID:', backgroundSocketId);
827
-
828
- // Just log that we're continuing - the existing training should handle PIN internally
829
- console.log('✅ [BACKGROUND] PIN submitted for existing training session');
830
-
831
- } catch (error) {
832
- console.error('❌ [BACKGROUND] Failed to continue background training:', error);
833
- throw error;
834
- }
835
- };
836
-
837
- // Replace fake persona creation with real training setup
838
- const startPersonaCreation = async () => {
839
- setPersonaProgress(0);
840
- setPersonaStatus('Initializing');
841
- setIsPersonaComplete(false);
842
- setHasError(false);
843
-
844
- // Start the dots animation
845
- startDotsAnimation();
846
-
847
- try {
848
- console.log('🚀 Starting persona creation...');
849
-
850
- // Ensure we have a valid authentication token before starting
851
- console.log('🔐 Step 1: Ensuring authentication token...');
852
- const authToken = await ensureAuthToken();
853
-
854
- if (!authToken) {
855
- throw new Error('Failed to create or retrieve authentication token');
856
- }
857
-
858
- // Set the token in state
859
- setUserToken(authToken);
860
- console.log(' Authentication token set in state:', `${authToken.substring(0, 20)}...`);
861
- console.log('🔍 Token length:', authToken.length);
862
-
863
- // Also verify it was stored properly
864
- const storedToken = await AsyncStorage.getItem('onairos_jwt_token');
865
- console.log('🔑 Token stored verification:', storedToken ? `${storedToken.substring(0, 20)}...` : 'Not stored');
866
-
867
- // If userInfo is not set, try to reload it
868
- if (!userInfo) {
869
- console.log('🔄 UserInfo not available, attempting to reload...');
870
- const storedUsername = await getOnairosUsername();
871
- const fallbackUsername = storedUsername || user?.email?.split('@')[0] || user?.name || 'mobile_user';
872
-
873
- const newUserInfo = {
874
- username: fallbackUsername,
875
- email: user?.email || null,
876
- id: null
877
- };
878
-
879
- setUserInfo(newUserInfo);
880
- setUsername(fallbackUsername);
881
-
882
- console.log('🧑‍💻 Set user info:', newUserInfo);
883
-
884
- // Give a moment for state to update
885
- await new Promise<void>(resolve => setTimeout(() => resolve(), 200));
886
- }
887
-
888
- // Check again after potential reload
889
- const currentUserInfo = userInfo || {
890
- username: username || user?.email?.split('@')[0] || user?.name || 'mobile_user',
891
- email: user?.email || null,
892
- id: null
893
- };
894
-
895
- if (!authToken) {
896
- console.error(' No authentication token available after ensureAuthToken');
897
- throw new Error('No authentication token available');
898
- }
899
-
900
- console.log('🔧 Setting up socket connection for real training...');
901
- console.log('🧑‍💻 User info available:', currentUserInfo);
902
- console.log('🔑 Token available:', !!authToken);
903
- console.log('🌐 Attempting to connect to: https://api2.onairos.uk');
904
- console.log('📱 Device platform:', Platform.OS);
905
- console.log('🔗 Network connectivity check starting...');
906
-
907
- setPersonaStatus('Connecting');
908
- setPersonaProgress(5);
909
-
910
- // Initialize socket connection with enhanced configuration
911
- socketRef.current = io('https://api2.onairos.uk', {
912
- transports: ['websocket', 'polling'], // Add polling as fallback
913
- autoConnect: false,
914
- timeout: 15000, // Increase timeout to 15 seconds
915
- reconnection: true, // Enable reconnection with limits
916
- reconnectionAttempts: 3,
917
- reconnectionDelay: 1000,
918
- forceNew: true // Force a new connection
919
- });
920
-
921
- console.log('🔌 Socket instance created:', {
922
- connected: socketRef.current.connected,
923
- id: socketRef.current.id,
924
- disconnected: socketRef.current.disconnected
925
- });
926
-
927
- // Socket event listeners with enhanced error handling
928
- socketRef.current.on('connect', () => {
929
- console.log('✅ Socket connected for training');
930
- console.log('🔌 Socket connection details:', {
931
- id: socketRef.current?.id,
932
- connected: socketRef.current?.connected,
933
- transport: socketRef.current?.io?.engine?.transport?.name
934
- });
935
-
936
- setSocketConnected(true);
937
- setPersonaStatus('Starting training');
938
- setPersonaProgress(10);
939
-
940
- const socketId = socketRef.current?.id;
941
- console.log('🔌 Socket ID for training:', socketId);
942
-
943
- if (socketId) {
944
- // Add a small delay to ensure socket is fully ready and registered on backend
945
- console.log('🔄 Socket ready for persona display with token:', authToken ? `${authToken.substring(0, 20)}...` : 'None');
946
- console.log('⏰ Socket registered but training will not start from persona screen...');
947
- // ❌ DISABLED: Don't start training from persona screen
948
- // Training should only start during connector→PIN transition
949
- // setTimeout(() => {
950
- // console.log('🚀 Now starting training with socket ID:', socketId);
951
- // startEnochTraining(socketId, authToken);
952
- // }, 2000);
953
-
954
- // Just show the persona screen without starting training
955
- setPersonaStatus('Waiting for training to start from connector→PIN transition');
956
- } else {
957
- console.error('❌ No socket ID available after connection');
958
- setPersonaStatus('Connection error. Please try again.');
959
- setHasError(true);
960
- stopDotsAnimation();
961
- }
962
- });
963
-
964
- socketRef.current.on('disconnect', (reason) => {
965
- console.log('❌ Socket disconnected, reason:', reason);
966
- console.log('🔍 Disconnect details:', {
967
- reason: reason,
968
- wasConnected: socketConnected,
969
- socketId: socketRef.current?.id
970
- });
971
- setSocketConnected(false);
972
- });
973
-
974
- socketRef.current.on('reconnect', (attemptNumber) => {
975
- console.log('🔄 Socket reconnected after', attemptNumber, 'attempts');
976
- setSocketConnected(true);
977
- });
978
-
979
- socketRef.current.on('reconnect_attempt', (attemptNumber) => {
980
- console.log('🔄 Socket reconnection attempt:', attemptNumber);
981
- setPersonaStatus(`Reconnecting... (${attemptNumber}/3)`);
982
- });
983
-
984
- socketRef.current.on('reconnect_error', (error) => {
985
- console.error('❌ Socket reconnection error:', error);
986
- });
987
-
988
- socketRef.current.on('reconnect_failed', () => {
989
- console.error('❌ Socket reconnection failed after all attempts');
990
- setPersonaStatus('Connection failed. Please try again.');
991
- setHasError(true);
992
- stopDotsAnimation();
993
- });
994
-
995
- socketRef.current.on('connect_error', (error) => {
996
- console.error('❌ Socket connection error:', error);
997
- console.error('❌ Socket error details:', {
998
- message: error.message,
999
- name: error.name,
1000
- stack: error.stack,
1001
- errorObject: error
1002
- });
1003
- setPersonaStatus('Connection error. Please try again.');
1004
- setHasError(true);
1005
- stopDotsAnimation();
1006
- });
1007
-
1008
- socketRef.current.on('trainingCompleted', (data) => {
1009
- console.log('✅ Training Complete:', data);
1010
- setPersonaStatus('Running test inference');
1011
- setPersonaProgress(60);
1012
- });
1013
-
1014
- socketRef.current.on('inferenceCompleted', (data) => {
1015
- console.log('🧠 Inference Complete:', data);
1016
- setPersonaStatus('Uploading to S3');
1017
- setPersonaProgress(80);
1018
- setUserTraits(data.traits);
1019
- setInferenceResults(data.inferenceResults);
1020
- });
1021
-
1022
- socketRef.current.on('modelStandby', (data) => {
1023
- console.log('🎉 All Complete:', data);
1024
-
1025
- // Log completion details based on new spec
1026
- if (data.completed) {
1027
- console.log('✅ Training completed:', data.message);
1028
- console.log('💾 Storage method:', data.storage);
1029
- console.log('🔐 Encryption enabled:', data.encryption);
1030
- console.log('🧠 Inference enabled:', data.inference);
1031
-
1032
- // Log database info for Enoch mode
1033
- if (data.databases && Array.isArray(data.databases)) {
1034
- console.log('🗄️ Databases used:', data.databases.join(', '));
1035
- }
1036
-
1037
- // Log testing mode
1038
- if (data.testing) {
1039
- console.log('🧪 Testing mode enabled');
1040
- }
1041
- }
1042
-
1043
- setIsPersonaComplete(true);
1044
- setPersonaStatus('Complete!');
1045
- setPersonaProgress(100);
1046
- stopDotsAnimation();
1047
- });
1048
-
1049
- socketRef.current.on('trainingUpdate', (data) => {
1050
- console.log('📊 Training update:', data);
1051
-
1052
- // Handle YouTube token expiry
1053
- if (data.error && data.error.includes('YouTube access token has expired')) {
1054
- console.log('🔄 YouTube token expired, triggering reconnection...');
1055
- setDataScenario(null);
1056
- setShowDataWarning(false);
1057
- setPersonaStatus('YouTube token expired - reconnecting...');
1058
-
1059
- // Try to refresh tokens first before full reconnection
1060
- setTimeout(async () => {
1061
- try {
1062
- // Import the refresh function
1063
- const { refreshYouTubeTokens } = await import('../services/platformAuthService');
1064
-
1065
- console.log('🔄 Attempting to refresh YouTube tokens...');
1066
- const refreshSuccess = await refreshYouTubeTokens();
1067
-
1068
- if (refreshSuccess) {
1069
- console.log('✅ YouTube tokens refreshed, but training restart disabled');
1070
- setPersonaStatus('YouTube tokens refreshed - but training only starts from connector→PIN');
1071
-
1072
- // DISABLED: Don't restart training from persona screen
1073
- // Training should only start during connector→PIN transition
1074
- // if (socketRef.current?.id) {
1075
- // startEnochTraining(socketRef.current.id, userToken ?? undefined);
1076
- // }
1077
-
1078
- setHasError(true); // Show error so user can restart flow properly
1079
- } else {
1080
- console.log('❌ Token refresh failed, attempting full reconnection...');
1081
- setPersonaStatus('Token refresh failed - please restart from connector screen');
1082
-
1083
- // ❌ DISABLED: Don't restart training from persona screen
1084
- // Fall back to full reconnection
1085
- try {
1086
- await handleConnectPlatform('youtube');
1087
- console.log('✅ YouTube reconnected, but training restart disabled');
1088
- setPersonaStatus('YouTube reconnected - please restart from connector screen');
1089
-
1090
- // DISABLED: Don't restart training from persona screen
1091
- // Training should only start during connector→PIN transition
1092
- // if (socketRef.current?.id) {
1093
- // startEnochTraining(socketRef.current.id, userToken ?? undefined);
1094
- // }
1095
-
1096
- setHasError(true); // Show error so user can restart flow properly
1097
- } catch (reconnectError) {
1098
- console.error('❌ YouTube reconnection failed:', reconnectError);
1099
- setPersonaStatus('YouTube reconnection failed. Please restart from connector screen.');
1100
- setHasError(true);
1101
- stopDotsAnimation();
1102
- }
1103
- }
1104
- } catch (error) {
1105
- console.error(' YouTube token refresh/reconnection error:', error);
1106
- setPersonaStatus('YouTube connection failed. Please try again.');
1107
- setHasError(true);
1108
- stopDotsAnimation();
1109
- }
1110
- }, 1000);
1111
- return;
1112
- }
1113
-
1114
- // Handle connections required scenario (pre-training validation)
1115
- if (data.requiresConnections || data.code === 'CONNECTIONS_REQUIRED') {
1116
- console.log('🔗 Connections required detected from socket');
1117
- setDataScenario('CONNECTIONS_REQUIRED');
1118
- setDataDetails(data);
1119
- setShowDataWarning(true);
1120
- setPersonaStatus('Connections required');
1121
- setHasError(true);
1122
- stopDotsAnimation();
1123
- return;
1124
- }
1125
-
1126
- // Handle data scenario responses
1127
- if (data.code === 'NO_DATA') {
1128
- console.log('🚫 No data scenario detected');
1129
- setDataScenario('NO_DATA');
1130
- setDataDetails(data.details);
1131
- setShowDataWarning(true);
1132
- setPersonaStatus('No interaction data found');
1133
- setHasError(true);
1134
- stopDotsAnimation();
1135
- return;
1136
- }
1137
-
1138
- if (data.code === 'LIMITED_DATA') {
1139
- console.log('ℹ️ Limited data scenario detected');
1140
- setDataScenario('LIMITED_DATA');
1141
- setDataDetails(data.details);
1142
- setShowDataWarning(true);
1143
- setPersonaStatus('Creating your persona with available data');
1144
- // Don't set hasError - training continues
1145
- return;
1146
- }
1147
-
1148
- // Handle regular training updates
1149
- if (data.error) {
1150
- console.error('❌ Training update error:', data.error);
1151
- setPersonaStatus(`Error: ${data.error}`);
1152
- setHasError(true);
1153
- stopDotsAnimation();
1154
- } else if (data.progress) {
1155
- setPersonaProgress(data.progress);
1156
- setPersonaStatus(data.status || 'Training in progress');
1157
- // Clear data warning if training progresses normally
1158
- if (showDataWarning && data.progress > 30) {
1159
- setShowDataWarning(false);
1160
- }
1161
- }
1162
- });
1163
-
1164
- // Connect to socket with timeout
1165
- console.log('🔌 Attempting to connect to socket...');
1166
- console.log('🔌 Socket config:', {
1167
- url: 'https://api2.onairos.uk',
1168
- transports: ['websocket'],
1169
- autoConnect: false,
1170
- timeout: 10000,
1171
- reconnection: false
1172
- });
1173
- socketRef.current.connect();
1174
- console.log('🔌 Socket connect() called - waiting for connection...');
1175
-
1176
- // Add immediate state check
1177
- setTimeout(() => {
1178
- console.log('🔍 Socket state after 1 second:', {
1179
- connected: socketRef.current?.connected,
1180
- disconnected: socketRef.current?.disconnected,
1181
- id: socketRef.current?.id,
1182
- engineConnected: socketRef.current?.io?.engine?.readyState
1183
- });
1184
- }, 1000);
1185
-
1186
- setTimeout(() => {
1187
- console.log('🔍 Socket state after 5 seconds:', {
1188
- connected: socketRef.current?.connected,
1189
- disconnected: socketRef.current?.disconnected,
1190
- id: socketRef.current?.id,
1191
- engineConnected: socketRef.current?.io?.engine?.readyState
1192
- });
1193
- }, 5000);
1194
-
1195
- // Set a timeout for socket connection with enhanced fallback
1196
- setTimeout(() => {
1197
- if (!socketConnected && socketRef.current && !socketRef.current.connected) {
1198
- console.error('❌ Socket connection timeout after 20 seconds');
1199
- console.error('🔍 Socket state:', {
1200
- connected: socketRef.current.connected,
1201
- disconnected: socketRef.current.disconnected,
1202
- id: socketRef.current.id
1203
- });
1204
-
1205
- setPersonaStatus('Connection timeout. Please check your internet and try again.');
1206
- setHasError(true);
1207
- stopDotsAnimation();
1208
-
1209
- // Cleanup socket
1210
- if (socketRef.current) {
1211
- socketRef.current.disconnect();
1212
- socketRef.current = null;
1213
- }
1214
- }
1215
- }, 20000); // Increased timeout to 20 seconds
1216
-
1217
- } catch (error) {
1218
- console.error('❌ Error in startPersonaCreation:', error);
1219
- setPersonaStatus('Failed to initialize training. Please try again.');
1220
- setHasError(true);
1221
- stopDotsAnimation();
1222
- }
1223
- };
1224
-
1225
- // Get user-friendly status message based on progress (updated for real training)
1226
- const getPersonaStatusMessage = (progress: number) => {
1227
- if (hasError) return 'Something went wrong. Please try again.';
1228
- if (isPersonaComplete) return 'Your persona is ready! 🎉';
1229
-
1230
- let baseMessage = '';
1231
- if (progress < 20) {
1232
- baseMessage = 'Keeping your data private';
1233
- } else if (progress < 40) {
1234
- baseMessage = 'Trying to understand your mind';
1235
- } else if (progress < 60) {
1236
- baseMessage = 'You\'re more interesting than I expected';
1237
- } else if (progress < 80) {
1238
- baseMessage = 'Finalizing your unique persona';
1239
- } else if (progress < 95) {
1240
- baseMessage = 'Almost done';
1241
- } else {
1242
- baseMessage = 'Just a few more seconds';
1243
- }
1244
-
1245
- return baseMessage + animatedDots;
1246
- };
1247
-
1248
- // Handle persona completion - now continues the onboarding flow instead of going to separate training
1249
- const handlePersonaComplete = async () => {
1250
- console.log('🎉 Real persona creation completed successfully');
1251
- // Cleanup socket connection
1252
- if (socketRef.current) {
1253
- console.log('🔌 Disconnecting training socket...');
1254
- socketRef.current.disconnect();
1255
- socketRef.current = null;
1256
- }
1257
- // Clear temporary PIN since training is complete
1258
- clearTemporaryPin();
1259
- // Set the completion flag BEFORE calling onComplete
1260
- isCompletingRef.current = true;
1261
-
1262
- // Get the real authentication token instead of using placeholder
1263
- try {
1264
- const realToken = await AsyncStorage.getItem('onairos_jwt_token') ||
1265
- await AsyncStorage.getItem('enoch_token') ||
1266
- await AsyncStorage.getItem('auth_token');
1267
-
1268
- if (realToken) {
1269
- console.log('✅ Using real authentication token for onComplete:', `${realToken.substring(0, 30)}...`);
1270
- // Call onComplete with the REAL token that was created during ensureAuthToken
1271
- onComplete?.('https://api2.onairos.uk', realToken, { email: userInfo?.email || '', ...userInfo });
1272
- } else {
1273
- console.error('❌ No real authentication token found after training completion');
1274
- // Fallback to placeholder token if no real token exists
1275
- onComplete?.('https://api2.onairos.uk', 'training-complete-token', { email: userInfo?.email || '', ...userInfo });
1276
- }
1277
- } catch (error) {
1278
- console.error('❌ Error retrieving real token for onComplete:', error);
1279
- // Fallback to placeholder token on error
1280
- onComplete?.('https://api2.onairos.uk', 'training-complete-token', { email: userInfo?.email || '', ...userInfo });
1281
- }
1282
- };
1283
-
1284
- // Ensure authentication token exists before training
1285
- const ensureAuthToken = async () => {
1286
- try {
1287
- // Check if we already have an authentication token (prioritize Enoch-specific tokens)
1288
- let existingToken = await AsyncStorage.getItem('onairos_jwt_token') ||
1289
- await AsyncStorage.getItem('enoch_token') ||
1290
- await AsyncStorage.getItem('auth_token');
1291
-
1292
- if (existingToken && existingToken.trim().length > 20) {
1293
- console.log('✅ Authentication token already exists:', `${existingToken.substring(0, 20)}...`);
1294
- console.log('🔍 Token length:', existingToken.length);
1295
- console.log('🔍 Token format check:', existingToken.includes('.') ? 'Looks like JWT' : 'Not JWT format');
1296
- return existingToken;
1297
- }
1298
-
1299
- console.log('🔐 Creating Enoch authentication token for universal onboarding...');
1300
-
1301
- // Create user accounts and get proper Enoch token
1302
- const fallbackEmail = user?.email || `${username || 'user'}@onairos.temp`;
1303
- const fallbackUsername = username || user?.name?.split('@')[0] || 'mobile_user';
1304
-
1305
- console.log('🔐 Using credentials:', { email: fallbackEmail, username: fallbackUsername });
1306
-
1307
- // Step 1: Create Enoch user first
1308
- console.log('🔐 Step 1: Creating Enoch user...');
1309
- try {
1310
- const enochRegisterResponse = await fetch('https://api2.onairos.uk/enoch/users/register', {
1311
- method: 'POST',
1312
- headers: {
1313
- 'Content-Type': 'application/json'
1314
- },
1315
- body: JSON.stringify({
1316
- email: fallbackEmail,
1317
- name: fallbackUsername,
1318
- photoUrl: ''
1319
- })
1320
- });
1321
-
1322
- console.log('📡 Enoch register response status:', enochRegisterResponse.status);
1323
- const enochResponseData = await enochRegisterResponse.json();
1324
- console.log('🔗 Enoch user creation response:', enochResponseData);
1325
- } catch (enochError) {
1326
- console.warn('⚠️ Enoch user creation failed (continuing):', enochError);
1327
- }
1328
-
1329
- // Step 2: Create Onairos account to get JWT token (this is the critical step)
1330
- console.log('🔐 Step 2: Creating Onairos account...');
1331
- const onairosSignupResponse = await fetch('https://api2.onairos.uk/register/enoch', {
1332
- method: 'POST',
1333
- headers: {
1334
- 'Content-Type': 'application/json'
1335
- },
1336
- body: JSON.stringify({
1337
- email: fallbackEmail,
1338
- username: fallbackUsername,
1339
- name: fallbackUsername
1340
- })
1341
- });
1342
-
1343
- console.log('📡 Onairos register response status:', onairosSignupResponse.status);
1344
-
1345
- if (!onairosSignupResponse.ok) {
1346
- const errorText = await onairosSignupResponse.text();
1347
- console.error('❌ Onairos signup failed:', errorText);
1348
- throw new Error(`Onairos signup failed: ${onairosSignupResponse.status} ${errorText}`);
1349
- }
1350
-
1351
- const onairosResponseData = await onairosSignupResponse.json();
1352
- console.log('🔗 Onairos account creation response:', onairosResponseData);
1353
-
1354
- // Extract the token from the response
1355
- let authToken = null;
1356
- if (onairosResponseData.token) {
1357
- authToken = onairosResponseData.token;
1358
- } else if (onairosResponseData.data?.token) {
1359
- authToken = onairosResponseData.data.token;
1360
- } else if (onairosResponseData.jwt) {
1361
- authToken = onairosResponseData.jwt;
1362
- }
1363
-
1364
- if (!authToken) {
1365
- console.error('❌ No token found in response:', onairosResponseData);
1366
- throw new Error('No authentication token returned from server');
1367
- }
1368
-
1369
- // Validate the token format
1370
- if (typeof authToken !== 'string' || authToken.length < 20) {
1371
- console.error('❌ Invalid token format:', authToken);
1372
- throw new Error('Invalid token format received');
1373
- }
1374
-
1375
- // Check if it looks like a JWT
1376
- const jwtParts = authToken.split('.');
1377
- if (jwtParts.length !== 3) {
1378
- console.warn('⚠️ Token does not appear to be a valid JWT:', `${authToken.substring(0, 20)}...`);
1379
- } else {
1380
- console.log('✅ Token appears to be a valid JWT format');
1381
- }
1382
-
1383
- // Store the token in multiple locations for maximum compatibility
1384
- await AsyncStorage.setItem('onairos_jwt_token', authToken);
1385
- await AsyncStorage.setItem('auth_token', authToken);
1386
- await AsyncStorage.setItem('enoch_token', authToken);
1387
- await AsyncStorage.setItem('onairos_username', onairosResponseData.username || fallbackUsername);
1388
-
1389
- console.log('✅ Successfully stored authentication tokens');
1390
- console.log('🔑 Token preview:', `${authToken.substring(0, 20)}...`);
1391
- console.log('🔑 Token length:', authToken.length);
1392
- console.log('👤 Username stored:', onairosResponseData.username || fallbackUsername);
1393
-
1394
- return authToken;
1395
-
1396
- } catch (error) {
1397
- console.error('❌ Error ensuring authentication token:', error);
1398
- throw error;
1399
- }
1400
- };
1401
-
1402
- const canProceed = Object.values(platformToggles).some(value => value);
1403
-
1404
- const handleProceed = () => {
1405
- if (canProceed) {
1406
- // Trigger haptic feedback when continue button is pressed
1407
- triggerHaptic(HapticType.BUTTON_PRESS);
1408
-
1409
- // ✅ NEW: Start background training when transitioning from connectors to PIN screen
1410
- console.log('🚀 [TRANSITION] Starting background training during connect → pin transition');
1411
- startBackgroundTraining().catch(error => {
1412
- console.error('❌ [TRANSITION] Background training failed during transition:', error);
1413
- // Continue to PIN screen even if training fails
1414
- });
1415
-
1416
- setStep('pin');
1417
- }
1418
- };
1419
-
1420
- const handleReviewerBypass = () => {
1421
- console.log('Reviewer bypass activated');
1422
-
1423
- // Set the completion flag BEFORE calling onComplete
1424
- isCompletingRef.current = true;
1425
-
1426
- // Call onComplete with a special reviewer token that the parent can recognize
1427
- onComplete?.('https://api2.onairos.uk', 'reviewer-bypass-token', { email: 'reviewer@apple.com' });
1428
- };
1429
-
1430
- // Removed navigation dependency for SDK compatibility
1431
-
1432
- // Training is now handled by the TrainingModal component
1433
-
1434
- // ✅ NEW: Background training function - starts when PIN screen appears
1435
- const startBackgroundTraining = useCallback(async (): Promise<void> => {
1436
- try {
1437
- console.log('🚀 [BACKGROUND] Starting background training optimization...');
1438
- setBackgroundTrainingProgress('Connecting...');
1439
-
1440
- // Step 1: Ensure authentication token exists
1441
- const authToken = await ensureAuthToken();
1442
- if (!authToken) {
1443
- throw new Error('Failed to create authentication token for background training');
1444
- }
1445
-
1446
- console.log('✅ [BACKGROUND] Auth token ready:', `${authToken.substring(0, 20)}...`);
1447
- setUserToken(authToken);
1448
-
1449
- // Step 2: Set up user info if not available
1450
- if (!userInfo) {
1451
- const storedUsername = await getOnairosUsername();
1452
- const fallbackUsername = storedUsername || user?.email?.split('@')[0] || user?.name || 'mobile_user';
1453
-
1454
- const newUserInfo = {
1455
- username: fallbackUsername,
1456
- email: user?.email || null,
1457
- id: null
1458
- };
1459
-
1460
- setUserInfo(newUserInfo);
1461
- setUsername(fallbackUsername);
1462
- console.log('🧑‍💻 [BACKGROUND] User info set:', newUserInfo);
1463
- }
1464
-
1465
- // Step 3: Initialize socket connection for background training
1466
- console.log('🔌 [BACKGROUND] Setting up socket for background training...');
1467
- setBackgroundTrainingProgress('Initializing data collection...');
1468
-
1469
- const backgroundSocket = io('https://api2.onairos.uk', {
1470
- transports: ['websocket', 'polling'],
1471
- autoConnect: false,
1472
- timeout: 15000,
1473
- reconnection: true,
1474
- reconnectionAttempts: 3,
1475
- reconnectionDelay: 1000,
1476
- forceNew: true
1477
- });
1478
-
1479
- // Set up socket event listeners for background training
1480
- backgroundSocket.on('connect', async () => {
1481
- console.log('✅ [BACKGROUND] Socket connected, starting background data collection...');
1482
- const socketId = backgroundSocket.id;
1483
- setBackgroundSocketId(socketId || null);
1484
- setBackgroundTrainingProgress('Collecting your data...');
1485
-
1486
- if (socketId) {
1487
- // Step 4: Start background training WITHOUT PIN (per backend instructions)
1488
- await startBackgroundEnochTraining(socketId, authToken);
1489
- }
1490
- });
1491
-
1492
- backgroundSocket.on('trainingUpdate', (data) => {
1493
- if (data.progress) {
1494
- setBackgroundTrainingProgress(data.status || 'Processing...');
1495
- console.log('📊 [BACKGROUND] Training progress:', data.progress, '%');
1496
- }
1497
- });
1498
-
1499
- backgroundSocket.on('disconnect', () => {
1500
- console.log('❌ [BACKGROUND] Socket disconnected');
1501
- });
1502
-
1503
- // Store socket reference for later use
1504
- socketRef.current = backgroundSocket;
1505
-
1506
- // Connect the socket to start background training
1507
- backgroundSocket.connect();
1508
-
1509
- console.log('✅ [BACKGROUND] Background training initiated successfully');
1510
-
1511
- } catch (error) {
1512
- console.error('❌ [BACKGROUND] Background training failed:', error);
1513
- setBackgroundTrainingProgress('');
1514
- throw error;
1515
- }
1516
- }, [userInfo, username, user]);
1517
-
1518
- // ✅ NEW: Background training API call - starts data collection without PIN
1519
- const startBackgroundEnochTraining = async (socketId: string, authToken: string) => {
1520
- try {
1521
- console.log('🚀 [BACKGROUND] Starting background training with socketId:', socketId);
1522
- console.log('🔑 Using auth token:', `${authToken.substring(0, 20)}...`);
1523
-
1524
- // Prepare background training data
1525
- const backgroundData = {
1526
- socketId,
1527
- username: userInfo?.username || userInfo?.name || username || 'mobile_user',
1528
- email: userInfo?.email || null,
1529
- modelKey: null,
1530
- connectedPlatforms: [], // No connected platforms for background training
1531
- platformConnections: {}, // No platform connections for background training
1532
- // No encrypted PIN for background training
1533
- };
1534
-
1535
- console.log('📤 Sending background training data to /mobile-training/enoch:', backgroundData);
1536
-
1537
- // Use the new training function that includes YouTube migration check
1538
- const backgroundResult = await startEnochTrainingWithYouTubeCheck(backgroundData);
1539
- console.log('📡 Background training API response:', backgroundResult);
1540
-
1541
- // Handle CONNECTIONS_REQUIRED scenario (pre-training validation)
1542
- if (backgroundResult.requiresConnections || backgroundResult.code === 'CONNECTIONS_REQUIRED') {
1543
- console.log('🔗 Connections required detected from background training');
1544
- setDataScenario('CONNECTIONS_REQUIRED');
1545
- setDataDetails(backgroundResult);
1546
- setShowDataWarning(true);
1547
- setPersonaStatus('Connections required');
1548
- setHasError(true);
1549
- stopDotsAnimation();
1550
- return;
1551
- }
1552
-
1553
- if (backgroundResult.success) {
1554
- console.log('🚀 Background training started:', backgroundResult.message);
1555
- console.log('🎯 Background training Features:', backgroundResult.features);
1556
-
1557
- // ✅ CRITICAL: Set background training started flag
1558
- setIsBackgroundTrainingStarted(true);
1559
-
1560
- // Log the new features from the spec
1561
- if (backgroundResult.features) {
1562
- console.log('✅ Inference enabled:', backgroundResult.features.inference);
1563
- console.log('💾 Storage method:', backgroundResult.features.storage);
1564
- console.log('🔒 Compression enabled:', backgroundResult.features.compression);
1565
- console.log('🔐 Encryption enabled:', backgroundResult.features.encryption);
1566
- console.log('📊 Training type:', backgroundResult.features.type);
1567
- console.log('🗄️ Databases:', backgroundResult.features.databases);
1568
- console.log('📈 Query scores enabled:', backgroundResult.features.queryScores);
1569
- }
1570
-
1571
- setPersonaStatus('Background training model...');
1572
- setPersonaProgress(25);
1573
- } else {
1574
- console.error('❌ Background training start failed:', backgroundResult.error);
1575
- setPersonaStatus(`Error: ${backgroundResult.error || 'Background training failed to start'}`);
1576
- setHasError(true);
1577
- stopDotsAnimation();
1578
- }
1579
- } catch (error) {
1580
- console.error('❌ Background training start error:', error);
1581
- setPersonaStatus(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
1582
- setHasError(true);
1583
- stopDotsAnimation();
1584
- }
1585
- };
1586
-
1587
- return (
1588
- <Modal
1589
- transparent={true}
1590
- visible={modalVisible}
1591
- animationType="none"
1592
- onRequestClose={handleClose}
1593
- >
1594
- <View style={styles.modalOverlay}>
1595
- <Animated.View
1596
- style={[
1597
- styles.bottomSheet,
1598
- { transform: [{ translateY: slideAnim }] },
1599
- ]}
1600
- >
1601
- <SafeAreaView style={styles.container}>
1602
- <View style={styles.handleContainer}>
1603
- <TouchableOpacity onPress={handleClose} style={styles.handleButton}>
1604
- <View style={styles.handle} />
1605
- </TouchableOpacity>
1606
- </View>
1607
-
1608
- {step === 'connect' && (
1609
- <View style={styles.connectContainer}>
1610
- <View style={styles.header}>
1611
- <View style={styles.headerContent}>
1612
- <View style={styles.appIcon}>
1613
- <Text style={styles.appIconText}>O</Text>
1614
- </View>
1615
- <Text style={styles.arrow}>→</Text>
1616
- <View style={styles.onairosIcon}>
1617
- <Image
1618
- source={require('../assets/images/Enochicon1.png')}
1619
- style={styles.onairosIconImage}
1620
- resizeMode="contain"
1621
- />
1622
- </View>
1623
- </View>
1624
- <View style={styles.titleContainer}>
1625
- <Text style={styles.mainTitle}>
1626
- {isExistingUser ? 'Add More Data to Enoch' : 'Connect your platforms to Enoch via Onairos'}
1627
- </Text>
1628
- <Text style={styles.privacyMessage}>
1629
- {isExistingUser
1630
- ? 'Connect additional accounts to enhance your digital personality. Your privacy is ensured.'
1631
- : 'Connect one account to get started. Each additional connection improves your match quality. Your privacy is ensured.'
1632
- }{' '}
1633
- <Text
1634
- style={styles.privacyLink}
1635
- onPress={() => setStep('privacy')}
1636
- >
1637
- How it's used →
1638
- </Text>
1639
- </Text>
1640
-
1641
- {/* Existing User Info Banner */}
1642
- {isExistingUser && existingUserInfo && (
1643
- <View style={styles.existingUserBanner}>
1644
- <Text style={styles.existingUserText}>
1645
- ✅ You already have {existingUserInfo.existingUserData?.summary?.connectionsCount || 0} connections
1646
- </Text>
1647
- <Text style={styles.existingUserSubtext}>
1648
- Add more platforms to get even better insights
1649
- </Text>
1650
- </View>
1651
- )}
1652
- {connectionStatuses['linkedin'] === 'connected' &&
1653
- Object.values(connectionStatuses).filter(status => status === 'connected').length === 1 && (
1654
- <View style={styles.linkedinRequirementContainer}>
1655
- <Text style={styles.linkedinRequirementText}>
1656
- <Text style={styles.linkedinRequirementAsterisk}>*</Text>
1657
- <Text style={styles.linkedinRequirementMessage}> LinkedIn requires pairing with an additional platform for optimal results</Text>
1658
- </Text>
1659
- </View>
1660
- )}
1661
- </View>
1662
- </View>
1663
-
1664
- <View
1665
- style={{
1666
- height: 1,
1667
- backgroundColor: '#CCCCCC',
1668
- width: '100%',
1669
- marginVertical: 0,
1670
- }}
1671
- />
1672
-
1673
- <ScrollView
1674
- ref={scrollViewRef}
1675
- style={styles.platformsScrollView}
1676
- contentContainerStyle={styles.platformsScrollContent}
1677
- showsVerticalScrollIndicator={true}
1678
- bounces={true}
1679
- alwaysBounceVertical={true}
1680
- indicatorStyle="black"
1681
- scrollEventThrottle={16}
1682
- directionalLockEnabled={true}
1683
- keyboardShouldPersistTaps="handled"
1684
- decelerationRate="normal"
1685
- contentOffset={{ x: 0, y: 60 }}
1686
- >
1687
- <View style={styles.platformsContainer}>
1688
- {primaryAuthOnly ? (
1689
- // Only show email option when primaryAuthOnly is true
1690
- <View style={styles.platformItem}>
1691
- <View style={styles.platformInfo}>
1692
- <View style={[styles.platformIcon, { backgroundColor: '#4285F4' }]}>
1693
- <Text style={styles.platformIconText}>@</Text>
1694
- </View>
1695
- <Text style={styles.platformName}>Email</Text>
1696
- </View>
1697
-
1698
- <TouchableOpacity
1699
- onPress={async () => {
1700
- // Trigger haptic feedback when sign in button is pressed
1701
- triggerHaptic(HapticType.BUTTON_PRESS);
1702
-
1703
- // Mark email as connected for primaryAuth flow
1704
- setConnections(prev => ({
1705
- ...prev,
1706
- email: { userName: email || 'user@example.com', connected: true }
1707
- }));
1708
- setConnectionStatuses(prev => ({
1709
- ...prev,
1710
- email: 'connected'
1711
- }));
1712
- setPlatformToggles(prev => ({
1713
- ...prev,
1714
- email: true
1715
- }));
1716
-
1717
- // Store the connected platform
1718
- await storeConnectedPlatform('email');
1719
-
1720
- setStep('connections');
1721
- }}
1722
- style={[
1723
- styles.footerButtonConfirm,
1724
- { paddingVertical: 8, paddingHorizontal: 16 }
1725
- ]}
1726
- >
1727
- <Text style={styles.footerButtonTextConfirm}>
1728
- Sign in
1729
- </Text>
1730
- </TouchableOpacity>
1731
- </View>
1732
- ) : (
1733
- // Show main platforms first
1734
- <>
1735
- {mainPlatforms.map(platform => {
1736
- const isConnected = connectionStatuses[platform.id] === 'connected';
1737
- const isPlatformConnecting = connectingPlatform === platform.id;
1738
-
1739
- return (
1740
- <View key={platform.id} style={styles.platformItem}>
1741
- <View style={styles.platformInfo}>
1742
- {platform.id === 'linkedin' ? (
1743
- // Use square container for LinkedIn
1744
- <View style={[styles.linkedinPlatformIcon, { backgroundColor: platform.color }]}>
1745
- <Image
1746
- source={getPlatformIcon(platform.id)}
1747
- style={styles.platformIconImage}
1748
- resizeMode="contain"
1749
- />
1750
- </View>
1751
- ) : platform.id === 'pinterest' ? (
1752
- // Use smaller circular container for Pinterest
1753
- <View style={[styles.pinterestPlatformIcon, { backgroundColor: platform.color }]}>
1754
- <Image
1755
- source={getPlatformIcon(platform.id)}
1756
- style={styles.pinterestIconImage}
1757
- resizeMode="contain"
1758
- />
1759
- </View>
1760
- ) : (
1761
- <View style={[styles.platformIcon, { backgroundColor: platform.color }]}>
1762
- <Image
1763
- source={getPlatformIcon(platform.id)}
1764
- style={
1765
- platform.id === 'youtube' ? styles.youtubeIconImage :
1766
- styles.platformIconImage
1767
- }
1768
- resizeMode="contain"
1769
- />
1770
- </View>
1771
- )}
1772
- <Text style={styles.platformName}>
1773
- {platform.name}
1774
- {platform.id === 'linkedin' && connectionStatuses['linkedin'] === 'connected' &&
1775
- Object.values(connectionStatuses).filter(status => status === 'connected').length === 1 && <Text style={styles.asterisk}>*</Text>}
1776
- </Text>
1777
- </View>
1778
-
1779
- <TouchableOpacity
1780
- onPress={() => {
1781
- if (isConnected) {
1782
- // Handle disconnect
1783
- handleDisconnectPlatform(platform.id, platform.name);
1784
- } else {
1785
- // Handle connect
1786
- handleConnectPlatform(platform.id);
1787
- }
1788
- }}
1789
- onLongPress={platform.id === 'youtube' ? () => {
1790
- // Start a timer for haptic feedback at 2 seconds
1791
- const hapticTimer = setTimeout(() => {
1792
- // Provide haptic feedback
1793
- Vibration.vibrate(100);
1794
- }, 2000);
1795
-
1796
- // Start a timer for 4 seconds for the bypass
1797
- const bypassTimer = setTimeout(() => {
1798
- handleReviewerBypass();
1799
- }, 4000);
1800
-
1801
- // Store the timer
1802
- setLongPressTimer(bypassTimer);
1803
- } : undefined}
1804
- onPressOut={platform.id === 'youtube' ? () => {
1805
- // Clear the timer if the press is released before 4 seconds
1806
- if (longPressTimer) {
1807
- clearTimeout(longPressTimer);
1808
- setLongPressTimer(null);
1809
- }
1810
- } : undefined}
1811
- style={[
1812
- isConnected ? styles.footerButtonConnected : styles.footerButtonConfirm,
1813
- { paddingVertical: 8, paddingHorizontal: 16 }
1814
- ]}
1815
- disabled={isPlatformConnecting}
1816
- >
1817
- {isPlatformConnecting ? (
1818
- <ActivityIndicator size="small" color="#fff" />
1819
- ) : (
1820
- <Text style={isConnected ? styles.footerButtonTextConnected : styles.footerButtonTextConfirm}>
1821
- {isConnected ? 'Connected' : 'Connect'}
1822
- </Text>
1823
- )}
1824
- </TouchableOpacity>
1825
- </View>
1826
- );
1827
- })}
1828
-
1829
- {/* Add additional platforms toggle button */}
1830
- <TouchableOpacity
1831
- style={styles.additionalPlatformsToggle}
1832
- onPress={() => {
1833
- triggerHaptic(HapticType.BUTTON_PRESS);
1834
- const newShowState = !showAdditionalPlatforms;
1835
-
1836
- if (newShowState) {
1837
- // Expanding - show platforms first, then animate in and scroll
1838
- setShowAdditionalPlatforms(true);
1839
- Animated.timing(additionalPlatformsOpacity, {
1840
- toValue: 1,
1841
- duration: 300,
1842
- useNativeDriver: true,
1843
- }).start();
1844
-
1845
- // Auto-scroll to reveal additional platforms when expanding
1846
- setTimeout(() => {
1847
- scrollViewRef.current?.scrollTo({
1848
- y: 220, // Scroll down to reveal additional platforms and hint at Gmail
1849
- animated: true,
1850
- });
1851
- }, 100);
1852
- } else {
1853
- // Collapsing - animate out first, then hide platforms and scroll back up
1854
- Animated.timing(additionalPlatformsOpacity, {
1855
- toValue: 0,
1856
- duration: 300,
1857
- useNativeDriver: true,
1858
- }).start(() => {
1859
- setShowAdditionalPlatforms(false);
1860
- });
1861
-
1862
- // Scroll back up smoothly when collapsing
1863
- setTimeout(() => {
1864
- scrollViewRef.current?.scrollTo({
1865
- y: 0, // Scroll back to top
1866
- animated: true,
1867
- });
1868
- }, 100);
1869
- }
1870
- }}
1871
- >
1872
- <Text style={styles.additionalPlatformsText}>
1873
- {showAdditionalPlatforms ? '− Hide additional platforms' : '+ Add additional platforms'}
1874
- </Text>
1875
- </TouchableOpacity>
1876
-
1877
- {/* Show additional platforms when expanded */}
1878
- {showAdditionalPlatforms && (
1879
- <Animated.View style={{ opacity: additionalPlatformsOpacity }}>
1880
- {additionalPlatforms.map(platform => {
1881
- const isConnected = connectionStatuses[platform.id] === 'connected';
1882
- const isPlatformConnecting = connectingPlatform === platform.id;
1883
-
1884
- return (
1885
- <View key={platform.id} style={styles.platformItem}>
1886
- <View style={styles.platformInfo}>
1887
- {platform.id === 'gmail' ? (
1888
- // Use the same wrapper for size/centering, but transparent background
1889
- <View style={[styles.platformIcon, { backgroundColor: 'transparent' }]}>
1890
- <Image
1891
- source={getPlatformIcon(platform.id)}
1892
- style={styles.gmailIconImage}
1893
- resizeMode="contain"
1894
- />
1895
- </View>
1896
- ) : (
1897
- <View style={[styles.platformIcon, { backgroundColor: platform.color }]}>
1898
- <Image
1899
- source={getPlatformIcon(platform.id)}
1900
- style={
1901
- platform.id === 'reddit' ? styles.redditIconImage :
1902
- styles.platformIconImage
1903
- }
1904
- resizeMode="contain"
1905
- />
1906
- </View>
1907
- )}
1908
- <Text style={styles.platformName}>{platform.name}</Text>
1909
- </View>
1910
-
1911
- <TouchableOpacity
1912
- onPress={() => {
1913
- if (isConnected) {
1914
- // Handle disconnect
1915
- handleDisconnectPlatform(platform.id, platform.name);
1916
- } else {
1917
- // Handle connect
1918
- handleConnectPlatform(platform.id);
1919
- }
1920
- }}
1921
- style={[
1922
- isConnected ? styles.footerButtonConnected : styles.footerButtonConfirm,
1923
- { paddingVertical: 8, paddingHorizontal: 16 }
1924
- ]}
1925
- disabled={isPlatformConnecting}
1926
- >
1927
- {isPlatformConnecting ? (
1928
- <ActivityIndicator size="small" color="#fff" />
1929
- ) : (
1930
- <Text style={isConnected ? styles.footerButtonTextConnected : styles.footerButtonTextConfirm}>
1931
- {isConnected ? 'Connected' : 'Connect'}
1932
- </Text>
1933
- )}
1934
- </TouchableOpacity>
1935
- </View>
1936
- );
1937
- })}
1938
- </Animated.View>
1939
- )}
1940
- </>
1941
- )}
1942
- </View>
1943
- </ScrollView>
1944
-
1945
- <View style={styles.footer}>
1946
- <TouchableOpacity
1947
- style={styles.footerButtonCancel}
1948
- onPress={handleClose}
1949
- >
1950
- <Text style={styles.footerButtonText}>Cancel</Text>
1951
- </TouchableOpacity>
1952
-
1953
- <TouchableOpacity
1954
- style={[
1955
- styles.footerButtonConfirm,
1956
- !canProceed && styles.footerButtonDisabled,
1957
- ]}
1958
- onPress={handleProceed}
1959
- disabled={!canProceed}
1960
- >
1961
- <Text style={styles.footerButtonTextConfirm}>Continue</Text>
1962
- </TouchableOpacity>
1963
- </View>
1964
- </View>
1965
- )}
1966
-
1967
- {step === 'pin' && (
1968
- <PinInput
1969
- onSubmit={handlePinSubmit}
1970
- minLength={8}
1971
- requireSpecialChar
1972
- requireNumber
1973
- onBack={() => setStep('connect')}
1974
- enableBiometricStorage={true}
1975
- // ✅ REMOVED: Background training now starts during transition, not on component mount
1976
- // onBackgroundTrainingStart={startBackgroundTraining}
1977
- showBackgroundProgress={true}
1978
- backgroundProgressText={backgroundTrainingProgress || "Training is starting in the background..."}
1979
- />
1980
- )}
1981
-
1982
- {step === 'persona' && (
1983
- <View style={styles.personaContainer}>
1984
- <View style={styles.personaHeaderWithBack}>
1985
- <TouchableOpacity
1986
- style={[
1987
- styles.personaBackButton,
1988
- // Highlight the back button when there's insufficient data
1989
- (dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') && styles.highlightedBackButton
1990
- ]}
1991
- onPress={() => setStep('connect')} // Changed from 'pin' to 'connect' to go back to platform connections
1992
- >
1993
- <Text style={[
1994
- { fontSize: 24 },
1995
- // Make the arrow more prominent when insufficient data
1996
- (dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') && styles.highlightedBackArrow
1997
- ]}>←</Text>
1998
- </TouchableOpacity>
1999
- <Text style={styles.personaTitle}>
2000
- {isPersonaComplete ? 'Your persona is ready! 🎉' : 'Creating your persona'}
2001
- </Text>
2002
- </View>
2003
- <Text style={styles.personaSubtitle}>
2004
- {isPersonaComplete
2005
- ? 'We\'ve created a personalized experience just for you.'
2006
- : 'This will only take a moment. We\'re personalizing your experience.'
2007
- }
2008
- </Text>
2009
-
2010
- <View style={styles.personaProgressContainer}>
2011
- <View style={styles.personaProgressBar}>
2012
- <View
2013
- style={[
2014
- styles.personaProgressFill,
2015
- { width: `${personaProgress}%` },
2016
- hasError && styles.progressError
2017
- ]}
2018
- />
2019
- </View>
2020
- <Text style={styles.personaProgressText}>{Math.round(personaProgress)}%</Text>
2021
- {!isPersonaComplete && (
2022
- <Text style={styles.personaStatusText}>{getPersonaStatusMessage(personaProgress)}</Text>
2023
- )}
2024
- </View>
2025
-
2026
- {/* Data Warning Component */}
2027
- {showDataWarning && dataScenario && (
2028
- <View style={[
2029
- styles.dataWarningContainer,
2030
- (dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') ? styles.dataConnectionsRequired : styles.dataInfoContainer
2031
- ]}>
2032
- <View style={styles.dataWarningHeader}>
2033
- <Text style={styles.dataWarningIcon}>
2034
- {(dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') ? '‼️' : 'ℹ️'}
2035
- </Text>
2036
- <Text style={[
2037
- styles.dataWarningTitle,
2038
- { color: (dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') ? '#000000' : '#0369A1' }
2039
- ]}>
2040
- {dataScenario === 'CONNECTIONS_REQUIRED'
2041
- ? 'Not enough data to personalize your experience'
2042
- : dataScenario === 'NO_DATA'
2043
- ? 'Not enough data to personalize your experience'
2044
- : 'Working with your available data'
2045
- }
2046
- </Text>
2047
- </View>
2048
-
2049
- <Text style={[
2050
- styles.dataWarningMessage,
2051
- { color: (dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') ? '#374151' : '#0C4A6E' }
2052
- ]}>
2053
- {dataScenario === 'CONNECTIONS_REQUIRED'
2054
- ? 'To provide the best experience possible, please go back and add an additional platform.'
2055
- : dataScenario === 'NO_DATA'
2056
- ? 'To provide the best experience possible, please go back and add an additional platform.'
2057
- : 'We\'re building your persona with the information currently available.'
2058
- }
2059
- </Text>
2060
-
2061
- {/* Add back button call-to-action for NO_DATA and CONNECTIONS_REQUIRED scenarios */}
2062
- {(dataScenario === 'NO_DATA' || dataScenario === 'CONNECTIONS_REQUIRED') && (
2063
- <TouchableOpacity
2064
- style={styles.goBackButton}
2065
- onPress={() => setStep('connect')}
2066
- activeOpacity={0.8}
2067
- >
2068
- <Text style={styles.goBackButtonText}>← Add another platform</Text>
2069
- </TouchableOpacity>
2070
- )}
2071
-
2072
- {dataDetails?.suggestions && dataScenario !== 'NO_DATA' && (
2073
- <View style={[
2074
- styles.dataWarningSuggestions,
2075
- { borderTopColor: 'rgba(3, 105, 161, 0.2)' }
2076
- ]}>
2077
- <Text style={[
2078
- styles.dataWarningSuggestionsTitle,
2079
- { color: '#0369A1' }
2080
- ]}>
2081
- For higher model quality:
2082
- </Text>
2083
- {dataDetails.suggestions.slice(0, 2).map((suggestion: string, index: number) => {
2084
- // Override specific suggestions with new text
2085
- let displayText = suggestion;
2086
- if (suggestion.toLowerCase().includes('better model quality') ||
2087
- suggestion.toLowerCase().includes('interact with more content')) {
2088
- displayText = 'Connect additional platforms';
2089
- } else if (suggestion.toLowerCase().includes('like/dislike more videos') ||
2090
- suggestion.toLowerCase().includes('posts, or content')) {
2091
- displayText = 'Interact and generate more content (like/dislike more videos, etc.)';
2092
- }
2093
-
2094
- return (
2095
- <Text key={index} style={[
2096
- styles.dataWarningSuggestionItem,
2097
- { color: '#0C4A6E' }
2098
- ]}>
2099
- • {displayText}
2100
- </Text>
2101
- );
2102
- })}
2103
- </View>
2104
- )}
2105
-
2106
- {dataScenario === 'LIMITED_DATA' && (
2107
- <Text style={[
2108
- styles.dataWarningContinueNote,
2109
- { color: '#0369A1' }
2110
- ]}>
2111
-
2112
- </Text>
2113
- )}
2114
- </View>
2115
- )}
2116
-
2117
- {/* YouTube Reconnection Indicator */}
2118
- {personaStatus.includes('YouTube token expired') && (
2119
- <View style={[styles.dataWarningContainer, styles.dataInfoContainer]}>
2120
- <View style={styles.dataWarningHeader}>
2121
- <Text style={styles.dataWarningIcon}>🔄</Text>
2122
- <Text style={[styles.dataWarningTitle, { color: '#0369A1' }]}>
2123
- Refreshing YouTube connection
2124
- </Text>
2125
- </View>
2126
- <Text style={[styles.dataWarningMessage, { color: '#0C4A6E' }]}>
2127
- Your YouTube access expired. We're automatically reconnecting...
2128
- </Text>
2129
- </View>
2130
- )}
2131
-
2132
- {isPersonaComplete && (
2133
- <View style={styles.personaCompleteContainer}>
2134
- <TouchableOpacity
2135
- style={styles.personaContinueButton}
2136
- onPress={() => {
2137
- console.log('🎯 Continue button pressed - completing onboarding');
2138
- handlePersonaComplete();
2139
- }}
2140
- activeOpacity={0.8}
2141
- >
2142
- <Text style={styles.personaContinueButtonText}>Continue</Text>
2143
- </TouchableOpacity>
2144
- </View>
2145
- )}
2146
-
2147
- {hasError && !isPersonaComplete && !showDataWarning && (
2148
- <View style={styles.personaCompleteContainer}>
2149
- <Text style={styles.personaCompleteTitle}>Something went wrong</Text>
2150
- <Text style={styles.personaCompleteSubtitle}>
2151
- Please try again or contact support if the issue persists.
2152
- </Text>
2153
- <TouchableOpacity
2154
- style={[styles.personaContinueButton, { backgroundColor: '#FF3B30' }]}
2155
- onPress={() => {
2156
- console.log('🔄 Retry button pressed - redirecting to connector screen');
2157
- setHasError(false);
2158
- // ❌ DISABLED: Don't start new training on retry
2159
- // Training should only start during connector→PIN transition
2160
- // startPersonaCreation();
2161
-
2162
- // Instead, go back to connector screen so user can restart proper flow
2163
- setStep('connect');
2164
- setPersonaStatus('Initializing');
2165
- setPersonaProgress(0);
2166
- }}
2167
- activeOpacity={0.8}
2168
- >
2169
- <Text style={styles.personaContinueButtonText}>Restart</Text>
2170
- </TouchableOpacity>
2171
- </View>
2172
- )}
2173
- </View>
2174
- )}
2175
-
2176
-
2177
-
2178
- {step === 'oauth' && oauthUrl && (
2179
- <View style={{ marginTop: -17 }}>
2180
- <OAuthWebView
2181
- url={oauthUrl}
2182
- platform={currentPlatform}
2183
- onClose={() => {
2184
- setStep('connect');
2185
- setOauthUrl('');
2186
- }}
2187
- onSuccess={handleOAuthSuccess}
2188
- onComplete={() => setStep('connect')}
2189
- />
2190
- </View>
2191
- )}
2192
-
2193
- {step === 'privacy' && (
2194
- <View style={styles.modalContent}>
2195
- <View style={styles.privacyHeader}>
2196
- <TouchableOpacity
2197
- style={styles.backButton}
2198
- onPress={() => setStep('connect')}
2199
- >
2200
- <Text style={styles.backButtonText}>←</Text>
2201
- </TouchableOpacity>
2202
- <Text style={styles.headerTitle}>How Enoch uses personal data</Text>
2203
- <View style={styles.backButtonSpacer} />
2204
- </View>
2205
-
2206
- <View style={styles.privacyDetailsContainer}>
2207
- <Text style={styles.privacyDetailsTitle}></Text>
2208
-
2209
- <View style={styles.privacyBulletPoint}>
2210
- <Text style={styles.bulletPoint}>•</Text>
2211
- <Text style={styles.privacyBulletText}>
2212
- Enoch legally accesses your platform data with explicit permission for this event only - never stored post-session and auto-deleted.
2213
- </Text>
2214
- </View>
2215
-
2216
- <View style={styles.privacyBulletPoint}>
2217
- <Text style={styles.bulletPoint}>•</Text>
2218
- <Text style={styles.privacyBulletText}>
2219
- Enoch NEVER sells your data. You are a user, not a commodity.
2220
- </Text>
2221
- </View>
2222
-
2223
- <View style={styles.privacyBulletPoint}>
2224
- <Text style={styles.bulletPoint}>•</Text>
2225
- <Text style={styles.privacyBulletText}>
2226
- Data collected builds your Onairos persona, enabling personalized experiences across future products while prioritizing your data sovereignty.
2227
- </Text>
2228
- </View>
2229
- </View>
2230
-
2231
- <TouchableOpacity
2232
- style={styles.footerButtonConfirm}
2233
- onPress={() => {
2234
- triggerHaptic(HapticType.BUTTON_PRESS);
2235
- setStep('connect');
2236
- }}
2237
- >
2238
- <Text style={styles.footerButtonTextConfirm}>Got it</Text>
2239
- </TouchableOpacity>
2240
- </View>
2241
- )}
2242
-
2243
- {step === 'connections' && (
2244
- <View style={styles.modalContent}>
2245
- <View style={styles.privacyHeader}>
2246
- <TouchableOpacity
2247
- style={styles.backButton}
2248
- onPress={() => setStep(primaryAuthOnly ? 'connections' : 'connect')}
2249
- >
2250
- <Text style={styles.backButtonText}>←</Text>
2251
- </TouchableOpacity>
2252
- <Text style={styles.headerTitle}>Connections Found</Text>
2253
- <View style={styles.backButtonSpacer} />
2254
- </View>
2255
-
2256
- <View style={styles.privacyDetailsContainer}>
2257
- <Text style={styles.connectionsCountText}>
2258
- We found {connectionsCount} potential connections for you!
2259
- </Text>
2260
-
2261
- <View style={styles.privacyBulletPoint}>
2262
- <Text style={styles.bulletPoint}>•</Text>
2263
- <Text style={styles.privacyBulletText}>
2264
- Continue to see your personalized matches
2265
- </Text>
2266
- </View>
2267
-
2268
- <View style={styles.privacyBulletPoint}>
2269
- <Text style={styles.bulletPoint}>•</Text>
2270
- <Text style={styles.privacyBulletText}>
2271
- Your privacy is protected - we only show compatible profiles
2272
- </Text>
2273
- </View>
2274
-
2275
- <View style={styles.privacyBulletPoint}>
2276
- <Text style={styles.bulletPoint}>•</Text>
2277
- <Text style={styles.privacyBulletText}>
2278
- Ready to start building meaningful connections?
2279
- </Text>
2280
- </View>
2281
- </View>
2282
-
2283
- <TouchableOpacity
2284
- style={styles.footerButtonConfirm}
2285
- onPress={async () => {
2286
- triggerHaptic(HapticType.BUTTON_PRESS);
2287
- // Complete the onboarding flow
2288
- isCompletingRef.current = true;
2289
-
2290
- // Check if this is an existing user adding more data
2291
- if (isExistingUser && existingUserToken) {
2292
- console.log('🔑 EXISTING USER: UniversalOnboarding complete, returning existing user token:', `${existingUserToken.substring(0, 20)}...`);
2293
- onComplete?.('https://api2.onairos.uk', existingUserToken, { email: userInfo?.email || '', ...userInfo });
2294
- } else {
2295
- // For new users, retrieve the stored JWT token from TrainingModal
2296
- const storedToken = await AsyncStorage.getItem('onairos_jwt_token') ||
2297
- await AsyncStorage.getItem('auth_token') ||
2298
- await AsyncStorage.getItem('enoch_token');
2299
-
2300
- const storedEmail = await AsyncStorage.getItem('user_email');
2301
-
2302
- if (storedToken) {
2303
- console.log('🔑 NEW USER: UniversalOnboarding complete, returning stored JWT token:', `${storedToken.substring(0, 20)}...`);
2304
- onComplete?.('https://api2.onairos.uk', storedToken, { email: storedEmail || userInfo?.email || '', ...userInfo });
2305
- } else {
2306
- console.warn('⚠️ NEW USER: No stored JWT token found, using fallback');
2307
- onComplete?.('https://api2.onairos.uk', 'connections-complete-token', { email: userInfo?.email || '', ...userInfo });
2308
- }
2309
- }
2310
- }}
2311
- >
2312
- <Text style={styles.footerButtonTextConfirm}>Continue to Setup</Text>
2313
- </TouchableOpacity>
2314
- </View>
2315
- )}
2316
- </SafeAreaView>
2317
- </Animated.View>
2318
- </View>
2319
- </Modal>
2320
- );
2321
- };
2322
-
2323
- const styles = StyleSheet.create({
2324
- modalOverlay: {
2325
- flex: 1,
2326
- justifyContent: 'flex-end',
2327
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
2328
- },
2329
- bottomSheet: {
2330
- backgroundColor: '#FFFFFF',
2331
- borderTopLeftRadius: 20,
2332
- borderTopRightRadius: 20,
2333
- paddingTop: 8,
2334
- paddingBottom: Platform.OS === 'ios' ? 20 : 0,
2335
- minHeight: height * .86,
2336
- width: '100%',
2337
- },
2338
- container: {
2339
- flex: 1,
2340
- },
2341
- handleContainer: {
2342
- alignItems: 'center',
2343
- paddingVertical: 8,
2344
- },
2345
- handleButton: {
2346
- padding: 10,
2347
- width: 60,
2348
- },
2349
- handle: {
2350
- width: 40,
2351
- height: 5,
2352
- borderRadius: 3,
2353
- backgroundColor: '#E0E0E0',
2354
- },
2355
- header: {
2356
- alignItems: 'center',
2357
- marginBottom: 20,
2358
- paddingHorizontal: 24,
2359
- },
2360
- headerContent: {
2361
- flexDirection: 'row',
2362
- alignItems: 'center',
2363
- justifyContent: 'center',
2364
- marginBottom: 24,
2365
- },
2366
- appIcon: {
2367
- width: 48,
2368
- height: 48,
2369
- borderRadius: 24,
2370
- backgroundColor: '#F5F5F5',
2371
- alignItems: 'center',
2372
- justifyContent: 'center',
2373
- },
2374
- appIconText: {
2375
- fontSize: 24,
2376
- fontWeight: 'bold',
2377
- color: '#333',
2378
- },
2379
- arrow: {
2380
- marginHorizontal: 16,
2381
- fontSize: 24,
2382
- color: '#666',
2383
- },
2384
- onairosIcon: {
2385
- width: 54,
2386
- height: 54,
2387
- borderRadius: 32,
2388
- backgroundColor: 'transparent',
2389
- alignItems: 'center',
2390
- justifyContent: 'center',
2391
- },
2392
- onairosIconText: {
2393
- fontSize: 24,
2394
- fontWeight: 'bold',
2395
- color: '#fff',
2396
- },
2397
- onairosIconImage: {
2398
- width: 54,
2399
- height: 54,
2400
- },
2401
- titleContainer: {
2402
- alignItems: 'center',
2403
- },
2404
- mainTitle: {
2405
- fontSize: 24,
2406
- fontWeight: '600',
2407
- color: '#333',
2408
- textAlign: 'center',
2409
- marginBottom: 12,
2410
- },
2411
- privacyMessage: {
2412
- fontSize: 14,
2413
- color: '#666',
2414
- textAlign: 'center',
2415
- marginBottom: 20,
2416
- },
2417
- privacyLink: {
2418
- color: '#000000',
2419
- fontWeight: '600',
2420
- textDecorationLine: 'underline',
2421
- },
2422
- boldText: {
2423
- fontWeight: 'bold',
2424
- },
2425
- content: {
2426
- flex: 1,
2427
- paddingHorizontal: 24,
2428
- },
2429
- platformsScrollView: {
2430
- flex: 1,
2431
- width: '100%',
2432
- },
2433
- platformsScrollContent: {
2434
- paddingBottom: 0,
2435
- paddingHorizontal: 24,
2436
- },
2437
- platformsContainer: {
2438
- marginTop: 16,
2439
- },
2440
- platformItem: {
2441
- flexDirection: 'row',
2442
- alignItems: 'center',
2443
- justifyContent: 'space-between',
2444
- paddingVertical: 16,
2445
- paddingHorizontal: 8,
2446
- borderBottomWidth: 1,
2447
- borderBottomColor: '#E5E5E5',
2448
- },
2449
- platformInfo: {
2450
- flexDirection: 'row',
2451
- alignItems: 'center',
2452
- },
2453
- platformIcon: {
2454
- width: 40,
2455
- height: 40,
2456
- borderRadius: 20,
2457
- backgroundColor: '#F5F5F5',
2458
- alignItems: 'center',
2459
- justifyContent: 'center',
2460
- marginRight: 16,
2461
- },
2462
- platformIconText: {
2463
- fontSize: 18,
2464
- fontWeight: 'bold',
2465
- color: '#333',
2466
- textAlign: 'center',
2467
- textAlignVertical: 'center',
2468
- },
2469
- platformIconImage: {
2470
- width: 24,
2471
- height: 24,
2472
- },
2473
- youtubeIconImage: {
2474
- width: 58,
2475
- height: 58,
2476
- },
2477
- redditIconImage: {
2478
- width: 34,
2479
- height: 34,
2480
- },
2481
- pinterestIconImage: {
2482
- width: 48,
2483
- height: 48,
2484
- },
2485
- gmailIconImage: {
2486
- width: 32,
2487
- height: 32,
2488
- },
2489
- linkedinPlatformIcon: {
2490
- width: 40,
2491
- height: 40,
2492
- borderRadius: 8,
2493
- backgroundColor: '#F5F5F5',
2494
- alignItems: 'center',
2495
- justifyContent: 'center',
2496
- marginRight: 16,
2497
- },
2498
- pinterestPlatformIcon: {
2499
- width: 34,
2500
- height: 34,
2501
- borderRadius: 17,
2502
- backgroundColor: '#F5F5F5',
2503
- alignItems: 'center',
2504
- justifyContent: 'center',
2505
- marginRight: 16,
2506
- },
2507
- platformName: {
2508
- fontSize: 16,
2509
- color: '#333',
2510
- textAlignVertical: 'center',
2511
- },
2512
- footer: {
2513
- flexDirection: 'row',
2514
- alignItems: 'center',
2515
- justifyContent: 'space-between',
2516
- padding: 24,
2517
- borderTopWidth: 1,
2518
- borderTopColor: '#E5E5E5',
2519
- },
2520
- footerButtonCancel: {
2521
- paddingVertical: 12,
2522
- paddingHorizontal: 16,
2523
- },
2524
- footerButtonText: {
2525
- color: '#666',
2526
- fontSize: 16,
2527
- },
2528
- footerButtonConfirm: {
2529
- paddingVertical: 12,
2530
- paddingHorizontal: 24,
2531
- backgroundColor: '#000000',
2532
- borderRadius: 8,
2533
- marginTop: 20,
2534
- },
2535
- footerButtonConnected: {
2536
- paddingVertical: 12,
2537
- paddingHorizontal: 24,
2538
- backgroundColor: '#E9C46A',
2539
- borderRadius: 8,
2540
- },
2541
- footerButtonDisabled: {
2542
- backgroundColor: '#E5E5E5',
2543
- },
2544
- footerButtonTextConfirm: {
2545
- color: '#fff',
2546
- fontSize: 16,
2547
- fontWeight: '600',
2548
- },
2549
- footerButtonTextConnected: {
2550
- color: '#000',
2551
- fontSize: 16,
2552
- fontWeight: '600',
2553
- },
2554
- connectContainer: {
2555
- flex: 1,
2556
- },
2557
- modalContent: {
2558
- flex: 1,
2559
- backgroundColor: '#FFFFFF',
2560
- borderTopLeftRadius: 20,
2561
- borderTopRightRadius: 20,
2562
- padding: 24,
2563
- },
2564
- backButton: {
2565
- padding: 8,
2566
- width: 40,
2567
- },
2568
- backButtonText: {
2569
- fontSize: 24,
2570
- color: '#000000',
2571
- },
2572
- backButtonSpacer: {
2573
- width: 40,
2574
- },
2575
- headerTitle: {
2576
- fontSize: 18,
2577
- fontWeight: '600',
2578
- color: '#333',
2579
- textAlign: 'center',
2580
- },
2581
- privacyHeader: {
2582
- flexDirection: 'row',
2583
- alignItems: 'center',
2584
- justifyContent: 'space-between',
2585
- paddingHorizontal: 16,
2586
- paddingVertical: 20,
2587
- backgroundColor: '#FFFFFF',
2588
- borderBottomWidth: 1,
2589
- borderBottomColor: '#F0F0F0',
2590
- },
2591
- privacyDetailsContainer: {
2592
- paddingHorizontal: 24,
2593
- paddingVertical: 8,
2594
- flex: 1,
2595
- marginTop: 20,
2596
- },
2597
- privacyDetailsTitle: {
2598
- fontSize: 20,
2599
- fontWeight: '600',
2600
- color: '#333',
2601
- marginBottom: 0,
2602
- },
2603
- privacyBulletPoint: {
2604
- flexDirection: 'row',
2605
- marginBottom: 16,
2606
- alignItems: 'flex-start',
2607
- },
2608
- bulletPoint: {
2609
- fontSize: 18,
2610
- marginRight: 8,
2611
- color: '#333',
2612
- },
2613
- privacyBulletText: {
2614
- fontSize: 16,
2615
- color: '#333',
2616
- flex: 1,
2617
- lineHeight: 24,
2618
- },
2619
- connectionsCountText: {
2620
- fontSize: 18,
2621
- fontWeight: '600',
2622
- color: '#333',
2623
- marginBottom: 24,
2624
- },
2625
- personaContainer: {
2626
- flex: 1,
2627
- padding: 16,
2628
- backgroundColor: '#fff',
2629
- justifyContent: 'flex-start',
2630
- },
2631
- personaHeaderWithBack: {
2632
- flexDirection: 'row',
2633
- alignItems: 'center',
2634
- justifyContent: 'space-between',
2635
- marginBottom: 16,
2636
- paddingVertical: 8,
2637
- },
2638
- personaBackButton: {
2639
- padding: 8,
2640
- },
2641
- personaHeader: {
2642
- alignItems: 'center',
2643
- marginBottom: 32,
2644
- paddingHorizontal: 24,
2645
- },
2646
- personaTitle: {
2647
- fontSize: 20,
2648
- fontWeight: '600',
2649
- color: '#333',
2650
- textAlign: 'center',
2651
- flex: 1,
2652
- marginBottom: 8,
2653
- },
2654
- personaSubtitle: {
2655
- fontSize: 14,
2656
- color: '#666',
2657
- marginBottom: 24,
2658
- textAlign: 'center',
2659
- },
2660
- personaProgressContainer: {
2661
- marginBottom: 24,
2662
- paddingHorizontal: 0,
2663
- },
2664
- personaProgressBar: {
2665
- height: 8,
2666
- backgroundColor: '#F5F5F5',
2667
- borderRadius: 4,
2668
- overflow: 'hidden',
2669
- marginBottom: 12,
2670
- },
2671
- personaProgressFill: {
2672
- height: '100%',
2673
- backgroundColor: '#1BA9D4',
2674
- borderRadius: 4,
2675
- },
2676
- personaProgressText: {
2677
- fontSize: 16,
2678
- fontWeight: '600',
2679
- color: '#333',
2680
- textAlign: 'center',
2681
- marginBottom: 8,
2682
- },
2683
- personaStatusText: {
2684
- fontSize: 14,
2685
- color: '#666',
2686
- textAlign: 'center',
2687
- },
2688
- personaCompleteContainer: {
2689
- alignItems: 'center',
2690
- paddingHorizontal: 0,
2691
- },
2692
- personaCompleteTitle: {
2693
- fontSize: 20,
2694
- fontWeight: '600',
2695
- color: '#333',
2696
- marginBottom: 8,
2697
- textAlign: 'center',
2698
- },
2699
- personaCompleteSubtitle: {
2700
- fontSize: 14,
2701
- color: '#666',
2702
- textAlign: 'center',
2703
- marginBottom: 40,
2704
- },
2705
- personaContinueButton: {
2706
- backgroundColor: '#000000',
2707
- paddingVertical: 16,
2708
- paddingHorizontal: 48,
2709
- borderRadius: 16,
2710
- alignItems: 'center',
2711
- width: '100%',
2712
- marginTop: 32,
2713
- shadowColor: '#000',
2714
- shadowOffset: { width: 0, height: 2 },
2715
- shadowOpacity: 0.1,
2716
- shadowRadius: 4,
2717
- elevation: 3,
2718
- },
2719
- personaContinueButtonText: {
2720
- color: '#fff',
2721
- fontSize: 16,
2722
- fontWeight: '600',
2723
- },
2724
- progressError: {
2725
- backgroundColor: '#FF3B30',
2726
- },
2727
- dataWarningContainer: {
2728
- padding: 16,
2729
- borderRadius: 12,
2730
- marginTop: 16,
2731
- borderWidth: 1,
2732
- shadowColor: '#000',
2733
- shadowOffset: { width: 0, height: 2 },
2734
- shadowOpacity: 0.1,
2735
- shadowRadius: 4,
2736
- elevation: 3,
2737
- },
2738
- dataWarningHeader: {
2739
- flexDirection: 'row',
2740
- alignItems: 'center',
2741
- marginBottom: 8,
2742
- },
2743
- dataWarningIcon: {
2744
- fontSize: 20,
2745
- marginRight: 8,
2746
- },
2747
- dataWarningTitle: {
2748
- fontSize: 16,
2749
- fontWeight: '600',
2750
- flex: 1,
2751
- },
2752
- dataWarningMessage: {
2753
- fontSize: 14,
2754
- marginBottom: 0,
2755
- lineHeight: 20,
2756
- },
2757
- dataWarningSuggestions: {
2758
- marginTop: 12,
2759
- paddingTop: 12,
2760
- borderTopWidth: 1,
2761
- },
2762
- dataWarningSuggestionsTitle: {
2763
- fontSize: 14,
2764
- fontWeight: '600',
2765
- marginBottom: 6,
2766
- },
2767
- dataWarningSuggestionItem: {
2768
- fontSize: 13,
2769
- marginBottom: 4,
2770
- lineHeight: 18,
2771
- },
2772
- dataWarningContinueNote: {
2773
- fontSize: 13,
2774
- fontWeight: '500',
2775
- marginTop: 12,
2776
- fontStyle: 'italic',
2777
- },
2778
- dataWarningError: {
2779
- backgroundColor: '#FFF5F5',
2780
- borderColor: '#FEB2B2',
2781
- },
2782
- dataInfoContainer: {
2783
- backgroundColor: '#F0F9FF',
2784
- borderColor: '#BAE6FD',
2785
- },
2786
- dataConnectionsRequired: {
2787
- backgroundColor: '#F5F5F5',
2788
- borderColor: '#D1D5DB',
2789
- shadowColor: '#6B7280',
2790
- shadowOffset: { width: 0, height: 0 },
2791
- shadowOpacity: 0.15,
2792
- shadowRadius: 8,
2793
- },
2794
- highlightedBackButton: {
2795
- backgroundColor: '#F5F5F5',
2796
- borderRadius: 20,
2797
- borderWidth: 2,
2798
- borderColor: '#000000',
2799
- shadowColor: '#000000',
2800
- shadowOffset: { width: 0, height: 0 },
2801
- shadowOpacity: 0.3,
2802
- shadowRadius: 8,
2803
- elevation: 5,
2804
- },
2805
- highlightedBackArrow: {
2806
- color: '#000000',
2807
- fontWeight: 'bold',
2808
- },
2809
- goBackButton: {
2810
- backgroundColor: '#000000',
2811
- paddingVertical: 12,
2812
- paddingHorizontal: 16,
2813
- borderRadius: 8,
2814
- marginTop: 12,
2815
- alignItems: 'center',
2816
- shadowColor: '#000000',
2817
- shadowOffset: { width: 0, height: 0 },
2818
- shadowOpacity: 0.3,
2819
- shadowRadius: 6,
2820
- elevation: 3,
2821
- },
2822
- goBackButtonText: {
2823
- color: '#FFFFFF',
2824
- fontSize: 14,
2825
- fontWeight: '600',
2826
- },
2827
- additionalPlatformsToggle: {
2828
- paddingVertical: 16,
2829
- paddingHorizontal: 8,
2830
- alignItems: 'center',
2831
- borderBottomWidth: 1,
2832
- borderBottomColor: '#E5E5E5',
2833
- },
2834
- additionalPlatformsText: {
2835
- fontSize: 14,
2836
- color: '#666',
2837
- fontWeight: '500',
2838
- },
2839
- asterisk: {
2840
- color: '#FF6B6B',
2841
- fontSize: 16,
2842
- fontWeight: 'bold',
2843
- marginLeft: 2,
2844
- },
2845
-
2846
- linkedinRequirementContainer: {
2847
- backgroundColor: '#F8F9FA',
2848
- borderWidth: 1,
2849
- borderColor: '#E5E7EB',
2850
- borderRadius: 8,
2851
- paddingHorizontal: 12,
2852
- paddingVertical: 8,
2853
- marginTop: 8,
2854
- marginHorizontal: 4,
2855
- },
2856
- linkedinRequirementText: {
2857
- fontSize: 12,
2858
- fontStyle: 'italic',
2859
- textAlign: 'center',
2860
- },
2861
- linkedinRequirementAsterisk: {
2862
- color: '#FF6B6B',
2863
- fontWeight: 'bold',
2864
- },
2865
- linkedinRequirementMessage: {
2866
- color: '#6B7280',
2867
- },
2868
- existingUserBanner: {
2869
- backgroundColor: '#F0F9FF',
2870
- borderWidth: 1,
2871
- borderColor: '#0EA5E9',
2872
- borderRadius: 12,
2873
- paddingHorizontal: 16,
2874
- paddingVertical: 12,
2875
- marginTop: 12,
2876
- },
2877
- existingUserText: {
2878
- fontSize: 14,
2879
- fontWeight: '600',
2880
- color: '#0369A1',
2881
- marginBottom: 4,
2882
- },
2883
- existingUserSubtext: {
2884
- fontSize: 12,
2885
- color: '#0C4A6E',
2886
- lineHeight: 16,
2887
- },
2888
- });
1
+ import React, { useCallback, useEffect, useState, useRef } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ ActivityIndicator,
8
+ Dimensions,
9
+ Platform,
10
+ Modal,
11
+ Animated,
12
+ SafeAreaView,
13
+ TouchableWithoutFeedback,
14
+ ScrollView,
15
+ Image,
16
+ Switch,
17
+ Linking,
18
+ Alert,
19
+ TextInput,
20
+ } from 'react-native';
21
+ import Icon from 'react-native-vector-icons/MaterialIcons';
22
+ import { PlatformList } from './PlatformList';
23
+ import { PinInput } from './PinInput';
24
+ import { TrainingModal } from './TrainingModal';
25
+ import { DataRequestScreen } from './DataRequestScreen';
26
+ import { OAuthWebView } from './onboarding/OAuthWebView';
27
+ import { useConnections } from '../hooks/useConnections';
28
+ import { COLORS, DEEP_LINK_CONFIG } from '../constants';
29
+ import { initiateOAuth, initiateNativeAuth, hasNativeSDK, isOAuthCallback, testApiConnectivity, handleOAuthCallbackUrl, refreshYouTubeTokens, requestEmailVerification, verifyEmailCode, checkEmailVerificationStatus, disconnectPlatform } from '../services/platformAuthService';
30
+ import type { UniversalOnboardingProps, ConnectionStatus, TestModeOptions } from '../types';
31
+
32
+ // Optional Opacity SDK imports with error handling
33
+ let opacityInit: any = null;
34
+ let OpacityEnvironment: any = null;
35
+ let opacityGet: any = null;
36
+
37
+ try {
38
+ const opacitySDK = require('@opacity-labs/react-native-opacity');
39
+ opacityInit = opacitySDK.init;
40
+ OpacityEnvironment = opacitySDK.OpacityEnvironment;
41
+ opacityGet = opacitySDK.get;
42
+ } catch (error) {
43
+ console.warn('Opacity SDK not available:', error);
44
+ // Opacity SDK will be disabled if not available
45
+ }
46
+
47
+ const { height, width } = Dimensions.get('window');
48
+
49
+ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
50
+ visible,
51
+ onClose,
52
+ AppName,
53
+ appIcon,
54
+ requestData,
55
+ returnLink,
56
+ onComplete,
57
+ embedd = false,
58
+ debug = false,
59
+ testMode = false,
60
+ preferredPlatform,
61
+ inferenceData,
62
+ auto = false,
63
+ partner,
64
+ }) => {
65
+ const [step, setStep] = useState<'email' | 'verify' | 'dataRequest' | 'connect' | 'pin' | 'training' | 'oauth' | 'success'>('email');
66
+ const [connections, setConnections] = useState<ConnectionStatus>({});
67
+ const [pin, setPin] = useState<string>('');
68
+ const [selectedTier, setSelectedTier] = useState<'Small' | 'Medium' | 'Large'>('Medium');
69
+ const [training, setTraining] = useState<{
70
+ progress: number;
71
+ eta: string;
72
+ }>({ progress: 0, eta: '' });
73
+ const [slideAnim] = useState(new Animated.Value(height));
74
+ const [platformToggles, setPlatformToggles] = useState<{[key: string]: boolean}>({});
75
+ const [oauthUrl, setOauthUrl] = useState<string>('');
76
+ const [currentPlatform, setCurrentPlatform] = useState<string>('');
77
+ const [username, setUsername] = useState<string>('Avatar');
78
+ const [isConnectingPlatform, setIsConnectingPlatform] = useState<boolean>(false);
79
+ const [showLoginWebView, setShowLoginWebView] = useState<boolean>(false);
80
+ const [email, setEmail] = useState<string>('');
81
+ const [verificationCode, setVerificationCode] = useState<string>('');
82
+ const [isVerifyingCode, setIsVerifyingCode] = useState<boolean>(false);
83
+
84
+ const [isExistingUser, setIsExistingUser] = useState<boolean>(false);
85
+
86
+ // Add refs for cleanup and code inputs
87
+ const successTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
88
+ const isMountedRef = useRef<boolean>(true);
89
+ const codeInputRefs = useRef<Array<TextInput | null>>([]);
90
+
91
+ // Add state for showing additional platforms
92
+ const [showAllPlatforms, setShowAllPlatforms] = useState<boolean>(false);
93
+
94
+
95
+ // Parse test mode options
96
+ const testModeOptions = typeof testMode === 'object' ? testMode : {} as any;
97
+ const isTestMode = testMode === true || (typeof testMode === 'object' && testMode !== null);
98
+ const showTestControls = (debug || isTestMode) && requestData;
99
+
100
+ // Simple 2-flow system
101
+ const isExistingUserFlow = testModeOptions.existingUser || false;
102
+ const isNewUserFlow = testModeOptions.newUser || false;
103
+
104
+ const platforms = [
105
+ { id: 'instagram', name: 'Instagram', icon: require('../assets/images/instagram.png') },
106
+ { id: 'youtube', name: 'YouTube', icon: require('../assets/images/youtube.png') },
107
+ { id: 'email', name: 'Gmail', icon: require('../assets/images/email.png') },
108
+ { id: 'reddit', name: 'Reddit', icon: require('../assets/images/reddit.png') },
109
+ { id: 'pinterest', name: 'Pinterest', icon: require('../assets/images/pinterest.png') },
110
+ ];
111
+
112
+ // Handle preferredPlatform to show ONLY preferred platforms (up to 2)
113
+ const getDisplayPlatforms = () => {
114
+ if (!preferredPlatform) {
115
+ // Default behavior: show first 3 platforms initially
116
+ return showAllPlatforms ? platforms : platforms.slice(0, 3);
117
+ }
118
+
119
+ const preferredArray = Array.isArray(preferredPlatform) ? preferredPlatform : [preferredPlatform];
120
+ const maxPreferred = Math.min(preferredArray.length, 2); // Limit to 2 preferred platforms max
121
+
122
+ // Show ONLY the preferred platforms (in specified order)
123
+ return preferredArray.slice(0, maxPreferred)
124
+ .map(id => platforms.find(p => p.id === id))
125
+ .filter(Boolean) as typeof platforms;
126
+ };
127
+
128
+ const platformsToDisplay = getDisplayPlatforms();
129
+
130
+ // Calculate additional platforms for "Show More" button
131
+ const additionalPlatforms = preferredPlatform ? [] : platforms.slice(3);
132
+
133
+ const {
134
+ connectPlatform,
135
+ disconnectPlatform,
136
+ getConnectionStatus,
137
+ isConnecting,
138
+ } = useConnections();
139
+
140
+ useEffect(() => {
141
+ // Set mounted flag
142
+ isMountedRef.current = true;
143
+
144
+ if (visible) {
145
+ loadInitialStatus();
146
+ // Animate in
147
+ Animated.spring(slideAnim, {
148
+ toValue: 0,
149
+ useNativeDriver: true,
150
+ bounciness: 0,
151
+ }).start();
152
+
153
+ // Set up deep link listener for OAuth callbacks
154
+ // Using the subscription pattern for React Native's Linking API
155
+ const subscription = Linking.addListener('url', ({ url }) => {
156
+ if (isOAuthCallback(url)) {
157
+ handleOAuthCallback(url);
158
+ }
159
+ });
160
+
161
+ // Check for initial URL (app was opened via deep link)
162
+ Linking.getInitialURL().then(initialUrl => {
163
+ if (initialUrl && isOAuthCallback(initialUrl)) {
164
+ handleOAuthCallback(initialUrl);
165
+ }
166
+ });
167
+
168
+ // Initialize platform toggles
169
+ const initialToggles: { [key: string]: boolean } = {};
170
+ platforms.forEach((platform) => {
171
+ initialToggles[platform.id] = false;
172
+ });
173
+ setPlatformToggles(initialToggles);
174
+
175
+ // Debug mode for Expo Go
176
+ if (debug || Platform.OS === 'web') {
177
+ console.log('Debug mode enabled - Using mock data for onboarding');
178
+ console.log('Configuration:', {
179
+ auto,
180
+ partner,
181
+ hasInferenceData: !!inferenceData,
182
+ inferenceDataType: typeof inferenceData,
183
+ });
184
+
185
+ // Pre-populate with mock connections in debug mode
186
+ if (testMode || Platform.OS === 'web') {
187
+ setConnections({
188
+ instagram: { userName: 'instagram_user', connected: true },
189
+ youtube: { userName: 'youtube_user', connected: true },
190
+ });
191
+ }
192
+ }
193
+
194
+ // If there's a preferred platform, pre-connect
195
+ if (preferredPlatform && debug) {
196
+ const preferredArray = Array.isArray(preferredPlatform) ? preferredPlatform : [preferredPlatform];
197
+ const newConnections: any = {};
198
+ preferredArray.slice(0, 2).forEach(platform => {
199
+ newConnections[platform] = { userName: `${platform}_user`, connected: true };
200
+ });
201
+ setConnections(prev => ({
202
+ ...prev,
203
+ ...newConnections
204
+ }));
205
+ }
206
+
207
+ // Return cleanup function
208
+ return () => {
209
+ // Remove event listener using the subscription
210
+ subscription.remove();
211
+ };
212
+ } else {
213
+ // Animate out
214
+ Animated.timing(slideAnim, {
215
+ toValue: height,
216
+ duration: 250,
217
+ useNativeDriver: true,
218
+ }).start(() => {
219
+ // Reset state if needed
220
+ });
221
+ }
222
+ }, [visible, preferredPlatform]);
223
+
224
+ // Cleanup effect for unmounting
225
+ useEffect(() => {
226
+ return () => {
227
+ // Set mounted flag to false
228
+ isMountedRef.current = false;
229
+
230
+ // Clear any pending timeouts
231
+ if (successTimeoutRef.current) {
232
+ clearTimeout(successTimeoutRef.current);
233
+ successTimeoutRef.current = null;
234
+ }
235
+ };
236
+ }, []);
237
+
238
+ const handleClose = () => {
239
+ // Clear any pending timeouts before closing
240
+ if (successTimeoutRef.current) {
241
+ clearTimeout(successTimeoutRef.current);
242
+ successTimeoutRef.current = null;
243
+ }
244
+
245
+ // Set mounted flag to false
246
+ isMountedRef.current = false;
247
+
248
+ // Animate out and then call onClose
249
+ Animated.timing(slideAnim, {
250
+ toValue: height,
251
+ duration: 250,
252
+ useNativeDriver: true,
253
+ }).start(() => {
254
+ // Only call onClose if component is still meant to be mounted
255
+ // This prevents the "User closed onboarding" error
256
+ onClose();
257
+ });
258
+ };
259
+
260
+ const loadInitialStatus = useCallback(async () => {
261
+ try {
262
+ console.log('🔄 Loading initial connection status...');
263
+ const status = await getConnectionStatus();
264
+ console.log('✅ Connection status loaded:', status);
265
+ setConnections(status || {});
266
+ } catch (error) {
267
+ console.error('❌ Failed to load connection status:', error);
268
+ // Set empty connections to prevent crashes
269
+ setConnections({});
270
+ }
271
+ }, [getConnectionStatus]);
272
+
273
+ const togglePlatform = useCallback(async (platformId: string) => {
274
+ if (!platformToggles[platformId]) {
275
+ // Attempt to connect platform
276
+ try {
277
+ setIsConnectingPlatform(true);
278
+ console.log(`🔌 Initiating connection for ${platformId}`);
279
+
280
+ // Test API connectivity first
281
+ console.log('🔍 Testing API connectivity...');
282
+ const connectivityTest = await testApiConnectivity();
283
+
284
+ if (!connectivityTest.success) {
285
+ console.error('❌ API connectivity test failed:', connectivityTest.error);
286
+ Alert.alert('Network Error', `${connectivityTest.error}\n\nPlease check your internet connection and try again.`);
287
+ return;
288
+ }
289
+
290
+ console.log('✅ API connectivity confirmed');
291
+
292
+ // Instagram: Use Opacity SDK exclusively
293
+ if (platformId === 'instagram') {
294
+ // Check if Opacity SDK is available
295
+ if (!opacityInit || !OpacityEnvironment || !opacityGet) {
296
+ console.error('❌ Opacity SDK not available for Instagram');
297
+ throw new Error('Instagram connection requires the Opacity SDK. Please ensure @opacity-labs/react-native-opacity is properly installed and configured.');
298
+ }
299
+
300
+ console.log('🔌 Initializing Opacity SDK for Instagram...');
301
+
302
+ // Initialize Opacity SDK with your API key
303
+ const apiKey = 'OsamaTest-7bde2407-7360-462a-86b4-b26d7f890cbb';
304
+
305
+ await opacityInit({
306
+ apiKey,
307
+ environment: OpacityEnvironment.Production,
308
+ shouldShowErrorsInWebView: true,
309
+ });
310
+
311
+ console.log('✅ Opacity SDK initialized successfully');
312
+ console.log('📱 Fetching Instagram profile...');
313
+
314
+ // Fetch Instagram profile using Opacity SDK
315
+ const profile = await opacityGet('flow:instagram:profile');
316
+
317
+ if (profile && typeof profile === 'object') {
318
+ console.log('✅ Instagram profile retrieved:', profile);
319
+
320
+ // Extract username from profile or use fallback
321
+ const instagramUsername = profile.username || profile.name || username;
322
+
323
+ // Update platform toggle state
324
+ setPlatformToggles(prev => ({
325
+ ...prev,
326
+ [platformId]: true
327
+ }));
328
+
329
+ // Update connections state with Instagram data
330
+ setConnections(prev => ({
331
+ ...prev,
332
+ [platformId]: {
333
+ userName: instagramUsername,
334
+ connected: true,
335
+ profileData: profile // Store additional profile data
336
+ }
337
+ }));
338
+
339
+ console.log(`✅ Instagram successfully connected for user: ${instagramUsername}`);
340
+
341
+ } else {
342
+ throw new Error('Invalid or empty Instagram profile data returned from Opacity SDK');
343
+ }
344
+ } else {
345
+ // For all other platforms (non-Instagram), check if they have native SDK
346
+ if (hasNativeSDK(platformId)) {
347
+ console.log(`📱 Using native SDK for ${platformId}`);
348
+ // Use native SDK for authentication
349
+ const success = await initiateNativeAuth(platformId, username);
350
+ if (success) {
351
+ console.log(`✅ Native authentication successful for ${platformId}`);
352
+ // Update platform toggle state
353
+ setPlatformToggles(prev => ({
354
+ ...prev,
355
+ [platformId]: true
356
+ }));
357
+
358
+ // Update connections state
359
+ setConnections(prev => ({
360
+ ...prev,
361
+ [platformId]: { userName: username, connected: true }
362
+ }));
363
+ } else {
364
+ throw new Error(`Native authentication failed for ${platformId}`);
365
+ }
366
+ } else {
367
+ // Use OAuth WebView flow
368
+ console.log(`🌐 Initiating OAuth flow for ${platformId}`);
369
+
370
+ const oauthUrl = await initiateOAuth(platformId, username, AppName);
371
+
372
+ if (oauthUrl) {
373
+ console.log(`✅ Received OAuth URL for ${platformId}:`, oauthUrl);
374
+ setCurrentPlatform(platformId);
375
+ setOauthUrl(oauthUrl);
376
+ setStep('oauth');
377
+ } else {
378
+ console.error(`❌ No OAuth URL returned for ${platformId}`);
379
+ throw new Error(`Failed to get authorization URL for ${platformId}. Please try again.`);
380
+ }
381
+ }
382
+ }
383
+ } catch (error) {
384
+ console.error(`❌ Error connecting ${platformId}:`, error);
385
+
386
+ // Provide user-friendly error messages based on platform
387
+ let errorMessage = 'Unknown error occurred';
388
+ if (error instanceof Error) {
389
+ if (platformId === 'instagram') {
390
+ if (error.message.includes('Initialize')) {
391
+ errorMessage = 'Failed to initialize Instagram connection. Please check your internet connection.';
392
+ } else if (error.message.includes('profile')) {
393
+ errorMessage = 'Unable to retrieve Instagram profile. Please try again or check your Instagram account permissions.';
394
+ } else {
395
+ errorMessage = error.message;
396
+ }
397
+ } else {
398
+ errorMessage = error.message;
399
+ }
400
+ }
401
+
402
+ Alert.alert(
403
+ `${platformId.charAt(0).toUpperCase() + platformId.slice(1)} Connection Failed`,
404
+ errorMessage,
405
+ [{ text: 'OK', style: 'default' }]
406
+ );
407
+ } finally {
408
+ setIsConnectingPlatform(false);
409
+ }
410
+ } else {
411
+ // Disconnect platform
412
+ console.log(`🔌 Disconnecting ${platformId}`);
413
+ setPlatformToggles(prev => ({
414
+ ...prev,
415
+ [platformId]: false
416
+ }));
417
+
418
+ // Update connections state
419
+ setConnections(prev => {
420
+ const newConnections = { ...prev };
421
+ delete newConnections[platformId];
422
+ return newConnections;
423
+ });
424
+ }
425
+ }, [platformToggles, username, AppName]);
426
+
427
+ /**
428
+ * Handles OAuth callback URLs
429
+ */
430
+ const handleOAuthCallback = useCallback((url: string) => {
431
+ console.log('🔗 OAuth callback received:', url);
432
+
433
+ const result = handleOAuthCallbackUrl(url);
434
+
435
+ if (result.success && result.platform && result.code) {
436
+ console.log(`✅ OAuth successful for ${result.platform}`);
437
+
438
+ // Update connections state
439
+ setConnections(prev => ({
440
+ ...prev,
441
+ [result.platform!]: { userName: username, connected: true }
442
+ }));
443
+
444
+ // Update platform toggles
445
+ setPlatformToggles(prev => ({
446
+ ...prev,
447
+ [result.platform!]: true
448
+ }));
449
+
450
+ // Close OAuth window and return to connect step
451
+ setOauthUrl('');
452
+ setCurrentPlatform('');
453
+ setStep('connect');
454
+
455
+ console.log(`🎉 ${result.platform} successfully connected via OAuth`);
456
+ } else {
457
+ console.error('❌ OAuth callback failed or incomplete');
458
+ }
459
+ }, [username]);
460
+
461
+ /**
462
+ * Handles completion of the OAuth flow
463
+ */
464
+ const handleOAuthSuccess = useCallback((code: string) => {
465
+ console.log(`OAuth success for ${currentPlatform} with code: ${code}`);
466
+
467
+ // Update connections for the current platform
468
+ if (currentPlatform) {
469
+ // Update connections state
470
+ setConnections(prev => ({
471
+ ...prev,
472
+ [currentPlatform]: { userName: username, connected: true }
473
+ }));
474
+
475
+ // Update platform toggles
476
+ setPlatformToggles(prev => ({
477
+ ...prev,
478
+ [currentPlatform]: true
479
+ }));
480
+
481
+ console.log(`Successfully connected ${currentPlatform} for user ${username}`);
482
+ }
483
+
484
+ // Close OAuth window and return to connect step
485
+ setOauthUrl('');
486
+ setCurrentPlatform('');
487
+ setStep('connect');
488
+ }, [currentPlatform, username]);
489
+
490
+
491
+
492
+ // Function to check for existing account (spoofed for now)
493
+ const checkExistingAccount = useCallback(async () => {
494
+ console.log('Checking for existing account...');
495
+ // TODO: Implement actual logic to check cookies/storage for existing account
496
+ // For now, this is spoofed and doesn't do anything
497
+ return false;
498
+ }, []);
499
+
500
+ // Function to handle email submission
501
+ const handleEmailSubmit = useCallback(async () => {
502
+ console.log('🚀 handleEmailSubmit called with email:', email);
503
+ console.log('🧪 testMode value:', testMode);
504
+ console.log('🧪 isTestMode computed:', isTestMode);
505
+
506
+ try {
507
+ if (!email || !email.trim()) {
508
+ console.log('❌ No email provided');
509
+ Alert.alert('Error', 'Please enter your email address');
510
+ return;
511
+ }
512
+
513
+ // Basic email validation
514
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
515
+ if (!emailRegex.test(email.trim())) {
516
+ console.log('❌ Invalid email format');
517
+ Alert.alert('Error', 'Please enter a valid email address');
518
+ return;
519
+ }
520
+
521
+ console.log('📧 Email validation passed, proceeding...');
522
+
523
+ // Check if we should skip API calls entirely (only for specific test scenarios)
524
+ const shouldSkipApiCalls = (typeof testMode === 'object' && testMode !== null && testMode.skipApiCalls === true);
525
+
526
+ if (shouldSkipApiCalls) {
527
+ console.log('🧪 Test mode with skipApiCalls: true - Skipping API call, proceeding to verification');
528
+ setStep('verify');
529
+ return;
530
+ }
531
+
532
+ // Add loading state to prevent multiple submissions
533
+ if (isVerifyingCode) {
534
+ console.log('⚠️ Email verification already in progress');
535
+ return;
536
+ }
537
+
538
+ setIsVerifyingCode(true);
539
+ console.log('🔄 Starting email verification process...');
540
+ console.log('📡 Will make API call with testMode flag:', isTestMode);
541
+
542
+ // Wrap the entire API call in a timeout to prevent hanging
543
+ const timeoutPromise = new Promise((_, reject) => {
544
+ setTimeout(() => reject(new Error('Request timeout')), 10000); // 10 second timeout
545
+ });
546
+
547
+ try {
548
+ // Safety check for function availability with detailed debugging
549
+ console.log('🔍 Checking requestEmailVerification function availability...');
550
+ console.log('🔍 Type of requestEmailVerification:', typeof requestEmailVerification);
551
+ console.log('🔍 requestEmailVerification function:', requestEmailVerification);
552
+
553
+ if (typeof requestEmailVerification !== 'function') {
554
+ console.error('❌ requestEmailVerification function not available');
555
+ console.error('❌ Available functions from platformAuthService:', {
556
+ requestEmailVerification: typeof requestEmailVerification,
557
+ verifyEmailCode: typeof verifyEmailCode,
558
+ checkEmailVerificationStatus: typeof checkEmailVerificationStatus,
559
+ });
560
+ // In development, just proceed anyway
561
+ console.log('🧪 Proceeding without API call in development mode');
562
+ setStep('verify');
563
+ return;
564
+ }
565
+
566
+ console.log('✅ requestEmailVerification function is available');
567
+ console.log('🔄 Making API call to requestEmailVerification...');
568
+
569
+ // Race between API call and timeout
570
+ const result = await Promise.race([
571
+ requestEmailVerification(email.trim(), isTestMode),
572
+ timeoutPromise
573
+ ]) as any;
574
+
575
+ console.log('📡 Email verification API result:', result);
576
+
577
+ if (result && result.success) {
578
+ console.log('✅ Verification code requested successfully');
579
+ setStep('verify');
580
+ } else {
581
+ console.warn('⚠️ Email verification request failed, but proceeding anyway:', result);
582
+ // In development mode, proceed even if API fails
583
+ setStep('verify');
584
+ }
585
+ } catch (verificationError) {
586
+ console.error('❌ Error in email verification API call:', verificationError);
587
+ // In development mode, proceed even if API fails
588
+ console.log('🧪 API failed but proceeding to verification step in development mode');
589
+ setStep('verify');
590
+ } finally {
591
+ setIsVerifyingCode(false);
592
+ }
593
+ } catch (error) {
594
+ console.error('❌ Unexpected error in email submission:', error);
595
+ setIsVerifyingCode(false);
596
+
597
+ // In development mode, still try to proceed
598
+ console.log('🧪 Error occurred but attempting to proceed to verification step');
599
+ try {
600
+ setStep('verify');
601
+ } catch (stepError) {
602
+ console.error('❌ Failed to set step to verify:', stepError);
603
+ Alert.alert('Error', 'An unexpected error occurred. Please try again.');
604
+ }
605
+ }
606
+ }, [email, isVerifyingCode, debug, testMode, isTestMode]);
607
+
608
+ // Function to handle verification code submission
609
+ const handleVerificationSubmit = useCallback(async () => {
610
+ if (!verificationCode.trim() || verificationCode.trim().length !== 6) {
611
+ Alert.alert('Error', 'Please enter a 6-digit verification code');
612
+ return;
613
+ }
614
+
615
+ setIsVerifyingCode(true);
616
+
617
+ try {
618
+ // Safety check for function availability
619
+ if (typeof verifyEmailCode !== 'function') {
620
+ throw new Error('Email verification service not available');
621
+ }
622
+
623
+ // Test Mode: Use specific flows
624
+ if (isTestMode) {
625
+ console.log('🧪 Test mode verification - simulating success');
626
+
627
+ if (isExistingUserFlow) {
628
+ // Flow 1: Existing User → Data Request → Close (return API URL)
629
+ console.log('🧪 Test Flow 1: Existing User → Show Data Request');
630
+ setIsExistingUser(true);
631
+ setStep('dataRequest');
632
+ return;
633
+ } else if (isNewUserFlow) {
634
+ // Flow 2: New User → Platform Connect → PIN → Training
635
+ console.log('🧪 Test Flow 2: New User → Platform Connect');
636
+ const emailPrefix = email.trim().split('@')[0] || 'TestUser';
637
+ setUsername(emailPrefix);
638
+ setStep('connect');
639
+ return;
640
+ }
641
+ }
642
+
643
+ // Real API call (production) or test mode
644
+ const result = await verifyEmailCode(email.trim(), verificationCode.trim(), isTestMode);
645
+
646
+ if (result.success) {
647
+ console.log('✅ Email verification successful');
648
+
649
+ // Check if user exists in backend (properly typed now)
650
+ const existingUser = result.existingUser || false;
651
+ setIsExistingUser(existingUser);
652
+
653
+ if (existingUser) {
654
+ console.log('👤 Existing user detected, showing data request screen');
655
+ setStep('dataRequest');
656
+ } else {
657
+ console.log('🆕 New user, proceeding to platform connection');
658
+ // Safely set username from email prefix
659
+ try {
660
+ const emailPrefix = email.trim().split('@')[0];
661
+ if (emailPrefix && emailPrefix.length > 0) {
662
+ setUsername(emailPrefix);
663
+ } else {
664
+ setUsername('User'); // Fallback username
665
+ }
666
+ } catch (usernameError) {
667
+ console.warn('Failed to extract username from email, using fallback:', usernameError);
668
+ setUsername('User');
669
+ }
670
+ setStep('connect');
671
+ }
672
+ } else {
673
+ Alert.alert('Verification Failed', result.error || 'Invalid verification code');
674
+ }
675
+ } catch (error) {
676
+ console.error('❌ Error verifying code:', error);
677
+ Alert.alert('Error', 'Failed to verify code');
678
+ } finally {
679
+ setIsVerifyingCode(false);
680
+ }
681
+ }, [email, verificationCode]);
682
+
683
+ const handlePinSubmit = useCallback(async (userPin: string) => {
684
+ setPin(userPin);
685
+ setStep('training');
686
+
687
+ // Save session data for "Never Connect Again" functionality
688
+ try {
689
+ const sessionData = {
690
+ pin: userPin,
691
+ connections,
692
+ platformToggles,
693
+ selectedTier,
694
+ username,
695
+ timestamp: Date.now(),
696
+ appName: AppName,
697
+ inferenceData: auto ? inferenceData : undefined,
698
+ partner,
699
+ };
700
+
701
+ // Store session data in secure storage for future use
702
+ console.log('Saving session data for future "Never Connect Again" functionality:', sessionData);
703
+
704
+ // TODO: Implement actual secure storage of session data
705
+ // This would typically involve:
706
+ // 1. Storing encrypted session data locally
707
+ // 2. Setting cookies in WebView for onairos.uk domain
708
+ // 3. Storing authentication tokens securely
709
+
710
+ // For now, we'll simulate this with console logging
711
+ console.log('Session data saved - future apps will detect existing account');
712
+
713
+ } catch (error) {
714
+ console.error('Failed to save session data:', error);
715
+ }
716
+ }, [connections, selectedTier, platformToggles, username, AppName, auto, inferenceData, partner]);
717
+
718
+ const handleTrainingComplete = useCallback(async () => {
719
+ console.log('🎉 Training completed successfully');
720
+ console.log('🔍 Auto mode enabled:', auto);
721
+ console.log('🔍 Inference data available:', !!inferenceData);
722
+
723
+ try {
724
+ if (auto && inferenceData) {
725
+ console.log('🤖 Auto mode: Making API request to get URL and perform inference');
726
+
727
+ // First, get the API URL from backend
728
+ const apiUrlResponse = await fetch('https://api2.onairos.uk/', {
729
+ method: 'POST',
730
+ headers: {
731
+ 'Content-Type': 'application/json',
732
+ },
733
+ body: JSON.stringify({
734
+ Info: {
735
+ storage: 'secure',
736
+ appId: AppName,
737
+ confirmations: Object.keys(requestData || {}),
738
+ EncryptedUserPin: pin, // Use the actual PIN from user
739
+ account: email.trim(),
740
+ proofMode: false,
741
+ }
742
+ })
743
+ });
744
+
745
+ if (!apiUrlResponse.ok) {
746
+ throw new Error(`Failed to get API URL: ${apiUrlResponse.status}`);
747
+ }
748
+
749
+ const { apiUrl, token } = await apiUrlResponse.json();
750
+ console.log('✅ Received API URL:', apiUrl);
751
+ console.log('✅ Received token:', token?.substring(0, 20) + '...');
752
+
753
+ // Now make the inference call with the provided data
754
+ const inferenceResponse = await fetch(apiUrl, {
755
+ method: 'POST',
756
+ headers: {
757
+ 'Content-Type': 'application/json',
758
+ 'Authorization': `Bearer ${token}`,
759
+ },
760
+ body: JSON.stringify({
761
+ ...inferenceData,
762
+ userEmail: email.trim(),
763
+ appName: AppName,
764
+ timestamp: new Date().toISOString(),
765
+ })
766
+ });
767
+
768
+ if (!inferenceResponse.ok) {
769
+ throw new Error(`Inference API failed: ${inferenceResponse.status}`);
770
+ }
771
+
772
+ const inferenceResults = await inferenceResponse.json();
773
+ console.log('✅ Auto mode inference results:', inferenceResults);
774
+
775
+ // Close the modal first
776
+ handleClose();
777
+
778
+ // Complete onboarding with inference results
779
+ setTimeout(() => {
780
+ onComplete(apiUrl, token, {
781
+ pin,
782
+ connections,
783
+ platformToggles,
784
+ selectedTier,
785
+ tierData: requestData?.[selectedTier],
786
+ sessionSaved: true,
787
+ // Add inference data if auto mode is enabled
788
+ ...(auto && inferenceData && { inferenceData }),
789
+ // Add partner info for special partners
790
+ ...(partner && { partner: partner === 'couplebible' ? 'CoupleBible' : partner }),
791
+ autoMode: true,
792
+ inferenceResults,
793
+ apiUrl,
794
+ token,
795
+ });
796
+ }, 100);
797
+
798
+ } else {
799
+ console.log('📋 Standard mode: Returning API URL for manual use');
800
+
801
+ // Prepare completion data
802
+ const completionData = {
803
+ pin,
804
+ connections,
805
+ platformToggles,
806
+ selectedTier,
807
+ tierData: requestData?.[selectedTier],
808
+ sessionSaved: true,
809
+ // Add inference data if auto mode is enabled
810
+ ...(auto && inferenceData && { inferenceData }),
811
+ // Add partner info for special partners
812
+ ...(partner && { partner: partner === 'couplebible' ? 'CoupleBible' : partner }),
813
+ autoMode: false,
814
+ };
815
+
816
+ console.log('Completion data prepared:', completionData);
817
+
818
+ // Close the modal first
819
+ handleClose();
820
+
821
+ // Then call the completion callback
822
+ setTimeout(() => {
823
+ onComplete('https://api2.onairos.uk', 'dummy-token', completionData);
824
+ }, 100);
825
+ }
826
+ } catch (error) {
827
+ console.error('❌ Error in training complete:', error);
828
+
829
+ // Fallback to standard mode
830
+ const completionData = {
831
+ pin,
832
+ connections,
833
+ platformToggles,
834
+ selectedTier,
835
+ tierData: requestData?.[selectedTier],
836
+ sessionSaved: true,
837
+ // Add inference data if auto mode is enabled
838
+ ...(auto && inferenceData && { inferenceData }),
839
+ // Add partner info for special partners
840
+ ...(partner && { partner: partner === 'couplebible' ? 'CoupleBible' : partner }),
841
+ autoMode: false,
842
+ error: error instanceof Error ? error.message : 'Unknown error',
843
+ };
844
+
845
+ console.log('Fallback completion data:', completionData);
846
+
847
+ // Close the modal first
848
+ handleClose();
849
+
850
+ // Then call the completion callback
851
+ setTimeout(() => {
852
+ onComplete('https://api2.onairos.uk', 'dummy-token', completionData);
853
+ }, 100);
854
+ }
855
+ }, [pin, connections, platformToggles, selectedTier, requestData, auto, inferenceData, partner, handleClose, onComplete, AppName, email]);
856
+
857
+ const handleDataRequestAccept = useCallback(async () => {
858
+ console.log('Data request accepted for existing user');
859
+ console.log('🔍 Auto mode enabled:', auto);
860
+ console.log('🔍 Inference data available:', !!inferenceData);
861
+
862
+ try {
863
+ if (auto && inferenceData) {
864
+ console.log('🤖 Auto mode: Making API request to get URL and perform inference');
865
+
866
+ // First, get the API URL from backend
867
+ const apiUrlResponse = await fetch('https://api2.onairos.uk/', {
868
+ method: 'POST',
869
+ headers: {
870
+ 'Content-Type': 'application/json',
871
+ },
872
+ body: JSON.stringify({
873
+ Info: {
874
+ storage: 'secure', // or whatever storage type
875
+ appId: AppName,
876
+ confirmations: Object.keys(requestData || {}),
877
+ EncryptedUserPin: 'temp-pin', // This would come from user PIN in real flow
878
+ account: email.trim(),
879
+ proofMode: false,
880
+ }
881
+ })
882
+ });
883
+
884
+ if (!apiUrlResponse.ok) {
885
+ throw new Error(`Failed to get API URL: ${apiUrlResponse.status}`);
886
+ }
887
+
888
+ const { apiUrl, token } = await apiUrlResponse.json();
889
+ console.log('✅ Received API URL:', apiUrl);
890
+ console.log('✅ Received token:', token?.substring(0, 20) + '...');
891
+
892
+ // Now make the inference call with the provided data
893
+ const inferenceResponse = await fetch(apiUrl, {
894
+ method: 'POST',
895
+ headers: {
896
+ 'Content-Type': 'application/json',
897
+ 'Authorization': `Bearer ${token}`,
898
+ },
899
+ body: JSON.stringify({
900
+ ...inferenceData,
901
+ userEmail: email.trim(),
902
+ appName: AppName,
903
+ timestamp: new Date().toISOString(),
904
+ })
905
+ });
906
+
907
+ if (!inferenceResponse.ok) {
908
+ throw new Error(`Inference API failed: ${inferenceResponse.status}`);
909
+ }
910
+
911
+ const inferenceResults = await inferenceResponse.json();
912
+ console.log(' Auto mode inference results:', inferenceResults);
913
+
914
+ // Complete onboarding with inference results
915
+ onComplete(apiUrl, token, {
916
+ existingAccount: true,
917
+ email: email.trim(),
918
+ dataRequestAccepted: true,
919
+ requestData,
920
+ autoMode: true,
921
+ inferenceResults,
922
+ apiUrl,
923
+ token,
924
+ });
925
+
926
+ } else {
927
+ console.log('📋 Standard mode: Returning API URL for manual use');
928
+
929
+ // Standard mode: just return the API URL
930
+ onComplete('https://api2.onairos.uk', 'existing-session-token', {
931
+ existingAccount: true,
932
+ email: email.trim(),
933
+ dataRequestAccepted: true,
934
+ requestData,
935
+ autoMode: false,
936
+ });
937
+ }
938
+ } catch (error) {
939
+ console.error('❌ Error in data request accept:', error);
940
+
941
+ // Fallback to standard mode
942
+ onComplete('https://api2.onairos.uk', 'existing-session-token', {
943
+ existingAccount: true,
944
+ email: email.trim(),
945
+ dataRequestAccepted: true,
946
+ requestData,
947
+ autoMode: false,
948
+ error: error instanceof Error ? error.message : 'Unknown error',
949
+ });
950
+ }
951
+ }, [email, onComplete, requestData, auto, inferenceData, AppName]);
952
+
953
+ const handleDataRequestDecline = useCallback(() => {
954
+ console.log('Data request declined');
955
+ handleClose();
956
+ }, [handleClose]);
957
+
958
+ const canProceedToPin = useCallback(() => {
959
+ // Test mode: Always allow proceeding (simulates platform connections)
960
+ if (isTestMode || testModeOptions.skipRealConnections) {
961
+ console.log('🧪 Test mode: Allowing proceed without real platform connections');
962
+ return true;
963
+ }
964
+
965
+ // Production: Check if at least one platform is connected
966
+ const hasPlatformConnected = Object.values(platformToggles).some(value => value === true);
967
+
968
+ // Auto mode validation
969
+ if (auto && partner !== 'couplebible' && !inferenceData) {
970
+ console.warn('Auto mode enabled but no inference data provided (and partner is not couplebible)');
971
+ return false;
972
+ }
973
+
974
+ return hasPlatformConnected;
975
+ }, [platformToggles, auto, partner, inferenceData, isTestMode, testModeOptions]);
976
+
977
+ const handleProceed = () => {
978
+ console.log('Proceeding to next step');
979
+ // Show success screen first
980
+ setStep('success');
981
+
982
+ // Clear any existing timeout
983
+ if (successTimeoutRef.current) {
984
+ clearTimeout(successTimeoutRef.current);
985
+ }
986
+
987
+ // After a delay, proceed to PIN
988
+ successTimeoutRef.current = setTimeout(() => {
989
+ // Only proceed if component is still mounted and visible
990
+ if (isMountedRef.current && visible) {
991
+ setStep('pin');
992
+ }
993
+ successTimeoutRef.current = null;
994
+ }, 3000);
995
+ };
996
+
997
+ return (
998
+ <Modal
999
+ visible={visible}
1000
+ transparent
1001
+ animationType="none"
1002
+ statusBarTranslucent
1003
+ onRequestClose={handleClose}
1004
+ >
1005
+ <TouchableWithoutFeedback onPress={handleClose}>
1006
+ <View style={styles.modalOverlay}>
1007
+ <TouchableWithoutFeedback onPress={e => e.stopPropagation()}>
1008
+ <Animated.View
1009
+ style={[
1010
+ styles.bottomSheet,
1011
+ {
1012
+ transform: [{ translateY: slideAnim }],
1013
+ }
1014
+ ]}
1015
+ >
1016
+ <SafeAreaView style={styles.container}>
1017
+ <View style={styles.handleContainer}>
1018
+ <View style={styles.handle} />
1019
+ </View>
1020
+
1021
+ {step === 'email' && (
1022
+ <View style={styles.emailInputContainer}>
1023
+ <View style={styles.emailHeader}>
1024
+ <View style={styles.onairosIcon}>
1025
+ <Image
1026
+ source={require('../assets/images/onairos_logo.png')}
1027
+ style={styles.onairosLogo}
1028
+ resizeMode="contain"
1029
+ />
1030
+ </View>
1031
+ <Text style={styles.emailTitle}>Welcome to Onairos</Text>
1032
+ <Text style={styles.emailSubtitle}>Enter your email to get started</Text>
1033
+ </View>
1034
+
1035
+ <View style={styles.emailInputSection}>
1036
+ <TextInput
1037
+ style={styles.emailInput}
1038
+ value={email}
1039
+ onChangeText={setEmail}
1040
+ placeholder="Enter your email address"
1041
+ keyboardType="email-address"
1042
+ autoCapitalize="none"
1043
+ autoCorrect={false}
1044
+ autoFocus
1045
+ />
1046
+
1047
+ <TouchableOpacity
1048
+ style={[styles.emailSubmitButton, !email.trim() && styles.emailSubmitButtonDisabled]}
1049
+ onPress={handleEmailSubmit}
1050
+ disabled={!email.trim()}
1051
+ >
1052
+ <Text style={styles.emailSubmitButtonText}>Continue</Text>
1053
+ </TouchableOpacity>
1054
+ </View>
1055
+ </View>
1056
+ )}
1057
+
1058
+ {step === 'verify' && (
1059
+ <View style={styles.emailInputContainer}>
1060
+ <View style={styles.emailHeader}>
1061
+ <View style={styles.onairosIcon}>
1062
+ <Image
1063
+ source={require('../assets/images/onairos_logo.png')}
1064
+ style={styles.onairosLogo}
1065
+ resizeMode="contain"
1066
+ />
1067
+ </View>
1068
+ <Text style={styles.emailTitle}>Enter Verification Code</Text>
1069
+ <Text style={styles.emailSubtitle}>
1070
+ We've sent a 6-digit code to {email}
1071
+ </Text>
1072
+ {isTestMode && (
1073
+ <Text style={styles.developmentNote}>
1074
+ 🔍 Test Mode: Any 6-digit code will work
1075
+ </Text>
1076
+ )}
1077
+ </View>
1078
+
1079
+ <View style={styles.emailInputSection}>
1080
+ <View style={styles.codeInputContainer}>
1081
+ {[0, 1, 2, 3, 4, 5].map((index) => (
1082
+ <TextInput
1083
+ key={index}
1084
+ ref={(ref) => (codeInputRefs.current[index] = ref)}
1085
+ style={[
1086
+ styles.codeDigit,
1087
+ verificationCode.length === index && styles.codeDigitActive
1088
+ ]}
1089
+ value={verificationCode[index] || ''}
1090
+ onChangeText={(text) => {
1091
+ if (text.length <= 1 && /^\d*$/.test(text)) {
1092
+ const newCode = verificationCode.split('');
1093
+ newCode[index] = text;
1094
+ const updatedCode = newCode.join('').slice(0, 6);
1095
+ setVerificationCode(updatedCode);
1096
+
1097
+ // Auto-focus next input
1098
+ if (text && index < 5) {
1099
+ codeInputRefs.current[index + 1]?.focus();
1100
+ }
1101
+ }
1102
+ }}
1103
+ onKeyPress={({ nativeEvent }) => {
1104
+ // Handle backspace to move to previous input
1105
+ if (nativeEvent.key === 'Backspace' && !verificationCode[index] && index > 0) {
1106
+ codeInputRefs.current[index - 1]?.focus();
1107
+ }
1108
+ }}
1109
+ keyboardType="number-pad"
1110
+ maxLength={1}
1111
+ textAlign="center"
1112
+ autoFocus={index === 0}
1113
+ />
1114
+ ))}
1115
+ </View>
1116
+
1117
+ <TouchableOpacity
1118
+ style={[
1119
+ styles.emailSubmitButton,
1120
+ (verificationCode.length !== 6 || isVerifyingCode) && styles.emailSubmitButtonDisabled
1121
+ ]}
1122
+ onPress={handleVerificationSubmit}
1123
+ disabled={verificationCode.length !== 6 || isVerifyingCode}
1124
+ >
1125
+ {isVerifyingCode ? (
1126
+ <ActivityIndicator size="small" color="#fff" />
1127
+ ) : (
1128
+ <Text style={styles.emailSubmitButtonText}>Verify</Text>
1129
+ )}
1130
+ </TouchableOpacity>
1131
+
1132
+ <TouchableOpacity
1133
+ style={styles.backButton}
1134
+ onPress={() => setStep('email')}
1135
+ >
1136
+ <Text style={styles.backButtonText}>← Back to email</Text>
1137
+ </TouchableOpacity>
1138
+ </View>
1139
+ </View>
1140
+ )}
1141
+
1142
+ {step === 'connect' && (
1143
+ <>
1144
+ {/* Header with Onairos icon and arrow to app icon */}
1145
+ <View style={styles.header}>
1146
+ <View style={styles.headerContent}>
1147
+ <View style={styles.onairosIcon}>
1148
+ <Image
1149
+ source={require('../assets/images/onairos_logo.png')}
1150
+ style={styles.onairosLogo}
1151
+ resizeMode="contain"
1152
+ />
1153
+ </View>
1154
+ <Icon name="arrow-forward" size={24} color="#666" style={styles.arrow} />
1155
+ <View style={styles.appIcon}>
1156
+ {appIcon ? (
1157
+ <Image
1158
+ source={appIcon}
1159
+ style={styles.appIconImage}
1160
+ resizeMode="contain"
1161
+ />
1162
+ ) : (
1163
+ <Text style={styles.appIconText}>
1164
+ {AppName.charAt(0)}
1165
+ </Text>
1166
+ )}
1167
+ </View>
1168
+ </View>
1169
+
1170
+
1171
+ </View>
1172
+
1173
+ <ScrollView
1174
+ style={styles.content}
1175
+ contentContainerStyle={styles.scrollContent}
1176
+ showsVerticalScrollIndicator={true}
1177
+ bounces={true}
1178
+ scrollEnabled={true}
1179
+ nestedScrollEnabled={true}
1180
+ keyboardShouldPersistTaps="handled"
1181
+ >
1182
+ {/* Main title and description */}
1183
+ <View style={styles.titleContainer}>
1184
+ <Text style={styles.mainTitle}>
1185
+ Let {AppName} learn about you from your data and apps
1186
+ </Text>
1187
+ <Text style={styles.privacyMessage}>
1188
+ None of your app data is shared with ANYONE
1189
+ </Text>
1190
+ {(debug || testMode) && (
1191
+ <Text style={styles.developmentNote}>
1192
+ 🧪 Test Mode: You can proceed without connecting any platforms
1193
+ </Text>
1194
+ )}
1195
+ </View>
1196
+
1197
+ {/* Platform connection options */}
1198
+ <View style={styles.platformsContainer}>
1199
+ {platformsToDisplay.map((platform) => (
1200
+ <TouchableOpacity
1201
+ key={platform.id}
1202
+ style={styles.platformItem}
1203
+ onPress={() => togglePlatform(platform.id)}
1204
+ disabled={isConnectingPlatform}
1205
+ >
1206
+ <View style={styles.platformInfo}>
1207
+ <Image
1208
+ source={platform.icon}
1209
+ style={styles.platformIcon}
1210
+ resizeMode="contain"
1211
+ />
1212
+ <Text style={styles.platformName}>
1213
+ {platform.name}
1214
+ </Text>
1215
+ </View>
1216
+
1217
+ {isConnectingPlatform && currentPlatform === platform.id ? (
1218
+ <ActivityIndicator size="small" color={COLORS.primary} />
1219
+ ) : (
1220
+ <View style={[
1221
+ styles.platformToggle,
1222
+ platformToggles[platform.id] && styles.platformToggleActive
1223
+ ]}>
1224
+ <View style={[
1225
+ styles.platformToggleThumb,
1226
+ platformToggles[platform.id] && styles.platformToggleThumbActive
1227
+ ]} />
1228
+ </View>
1229
+ )}
1230
+ </TouchableOpacity>
1231
+ ))}
1232
+
1233
+ {/* Show more/less platforms button */}
1234
+ {additionalPlatforms.length > 0 && (
1235
+ <TouchableOpacity
1236
+ style={styles.expandButton}
1237
+ onPress={() => setShowAllPlatforms(!showAllPlatforms)}
1238
+ >
1239
+ <Icon
1240
+ name={showAllPlatforms ? "expand_less" : "add"}
1241
+ size={24}
1242
+ color={COLORS.primary}
1243
+ />
1244
+ <Text style={styles.expandButtonText}>
1245
+ {showAllPlatforms
1246
+ ? "Show Less"
1247
+ : `${additionalPlatforms.length} More Connectors`
1248
+ }
1249
+ </Text>
1250
+ </TouchableOpacity>
1251
+ )}
1252
+ </View>
1253
+
1254
+ {/* Test mode controls - Simple 2-flow system */}
1255
+ {showTestControls && (
1256
+ <View style={styles.testModeContainer}>
1257
+ <Text style={styles.testModeTitle}>🧪 Test Mode - 2 Main Flows</Text>
1258
+
1259
+ <TouchableOpacity
1260
+ style={styles.testExistingUserButton}
1261
+ onPress={() => {
1262
+ // Flow 1: Existing User
1263
+ setIsExistingUser(true);
1264
+ setStep('dataRequest');
1265
+ }}
1266
+ >
1267
+ <Icon name="person" size={20} color="#28a745" />
1268
+ <Text style={styles.testExistingUserButtonText}>
1269
+ Flow 1: Existing User (Email Code Data Request Close)
1270
+ </Text>
1271
+ </TouchableOpacity>
1272
+
1273
+ <TouchableOpacity
1274
+ style={styles.testSkipToTrainingButton}
1275
+ onPress={() => {
1276
+ // Flow 2: New User - Skip to connect step
1277
+ setStep('connect');
1278
+ }}
1279
+ >
1280
+ <Icon name="person-add" size={20} color="#17a2b8" />
1281
+ <Text style={styles.testSkipToTrainingButtonText}>
1282
+ Flow 2: New User (Connect → PIN → Training)
1283
+ </Text>
1284
+ </TouchableOpacity>
1285
+
1286
+ <TouchableOpacity
1287
+ style={styles.testDataRequestButton}
1288
+ onPress={() => setStep('dataRequest')}
1289
+ >
1290
+ <Icon name="preview" size={20} color={COLORS.primary} />
1291
+ <Text style={styles.testDataRequestButtonText}>
1292
+ Preview Data Request Screen
1293
+ </Text>
1294
+ </TouchableOpacity>
1295
+ </View>
1296
+ )}
1297
+ </ScrollView>
1298
+
1299
+ <View style={styles.footer}>
1300
+ <TouchableOpacity
1301
+ style={styles.footerButtonCancel}
1302
+ onPress={handleClose}
1303
+ >
1304
+ <Text style={styles.footerButtonText}>Cancel</Text>
1305
+ </TouchableOpacity>
1306
+
1307
+ <TouchableOpacity
1308
+ style={[
1309
+ styles.footerButtonConfirm,
1310
+ !canProceedToPin() && styles.footerButtonDisabled
1311
+ ]}
1312
+ onPress={handleProceed}
1313
+ disabled={!canProceedToPin()}
1314
+ >
1315
+ <Text style={styles.footerButtonTextConfirm}>Connect</Text>
1316
+ </TouchableOpacity>
1317
+ </View>
1318
+ </>
1319
+ )}
1320
+
1321
+ {step === 'success' && (
1322
+ <View style={styles.successContainer}>
1323
+ <View style={styles.successContent}>
1324
+ {/* Big green checkmark */}
1325
+ <View style={styles.successIcon}>
1326
+ <Icon name="check" size={48} color="#fff" />
1327
+ </View>
1328
+
1329
+ <Text style={styles.successTitle}>Never Connect Again!</Text>
1330
+ <Text style={styles.successSubtitle}>
1331
+ Your login session has been saved
1332
+ </Text>
1333
+
1334
+ <View style={styles.successMessage}>
1335
+ <Text style={styles.successMessageText}>
1336
+ Your Onairos account and platform connections are now saved in your browser cookies.
1337
+ Next time you use any app with Onairos, you'll be automatically signed in without
1338
+ needing to reconnect your accounts.
1339
+ </Text>
1340
+ </View>
1341
+
1342
+ {/* Auto-progress indicator */}
1343
+ <View style={styles.progressIndicator}>
1344
+ <ActivityIndicator size="small" color="#4CAF50" />
1345
+ <Text style={styles.progressText}>Continuing...</Text>
1346
+ </View>
1347
+ </View>
1348
+ </View>
1349
+ )}
1350
+
1351
+ {step === 'pin' && (
1352
+ <PinInput
1353
+ onSubmit={handlePinSubmit}
1354
+ minLength={8}
1355
+ requireSpecialChar
1356
+ requireNumber
1357
+ onBack={() => setStep('connect')}
1358
+ />
1359
+ )}
1360
+
1361
+ {step === 'training' && (
1362
+ <TrainingModal
1363
+ visible={step === 'training'}
1364
+ progress={training.progress}
1365
+ eta={training.eta}
1366
+ onCancel={handleClose}
1367
+ onComplete={handleTrainingComplete}
1368
+ modelKey="onairosTrainingModel"
1369
+ username={username}
1370
+ test={isTestMode}
1371
+ />
1372
+ )}
1373
+
1374
+ {step === 'dataRequest' && (
1375
+ <DataRequestScreen
1376
+ onAccept={handleDataRequestAccept}
1377
+ onDecline={handleDataRequestDecline}
1378
+ requestData={requestData || {}}
1379
+ AppName={AppName}
1380
+ appIcon={appIcon}
1381
+ />
1382
+ )}
1383
+
1384
+ {step === 'oauth' && oauthUrl && (
1385
+ <OAuthWebView
1386
+ url={oauthUrl}
1387
+ platform={currentPlatform}
1388
+ onClose={() => {
1389
+ setStep('connect');
1390
+ setOauthUrl('');
1391
+ }}
1392
+ onSuccess={handleOAuthSuccess}
1393
+ onComplete={() => setStep('connect')}
1394
+ />
1395
+ )}
1396
+
1397
+
1398
+
1399
+ </SafeAreaView>
1400
+ </Animated.View>
1401
+ </TouchableWithoutFeedback>
1402
+ </View>
1403
+ </TouchableWithoutFeedback>
1404
+
1405
+
1406
+ </Modal>
1407
+ );
1408
+ };
1409
+
1410
+ const styles = StyleSheet.create({
1411
+ modalOverlay: {
1412
+ flex: 1,
1413
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
1414
+ justifyContent: 'flex-end',
1415
+ alignItems: 'center',
1416
+ },
1417
+ bottomSheet: {
1418
+ backgroundColor: '#fff',
1419
+ width: width,
1420
+ height: height * 0.8,
1421
+ borderTopLeftRadius: 24,
1422
+ borderTopRightRadius: 24,
1423
+ overflow: 'hidden',
1424
+ },
1425
+ container: {
1426
+ flex: 1,
1427
+ backgroundColor: '#fff',
1428
+ },
1429
+ handleContainer: {
1430
+ width: '100%',
1431
+ alignItems: 'center',
1432
+ paddingTop: 12,
1433
+ paddingBottom: 8,
1434
+ },
1435
+ handle: {
1436
+ width: 40,
1437
+ height: 5,
1438
+ borderRadius: 3,
1439
+ backgroundColor: '#E0E0E0',
1440
+ },
1441
+ header: {
1442
+ padding: 24,
1443
+ alignItems: 'center',
1444
+ },
1445
+ headerContent: {
1446
+ flexDirection: 'row',
1447
+ alignItems: 'center',
1448
+ justifyContent: 'center',
1449
+ marginBottom: 16,
1450
+ },
1451
+ appIcon: {
1452
+ width: 48,
1453
+ height: 48,
1454
+ borderRadius: 16,
1455
+ backgroundColor: '#F5F5F5',
1456
+ alignItems: 'center',
1457
+ justifyContent: 'center',
1458
+ },
1459
+ appIconText: {
1460
+ fontSize: 24,
1461
+ color: '#000',
1462
+ },
1463
+ appIconImage: {
1464
+ width: 32,
1465
+ height: 32,
1466
+ },
1467
+ arrow: {
1468
+ marginHorizontal: 16,
1469
+ },
1470
+ onairosIcon: {
1471
+ width: 48,
1472
+ height: 48,
1473
+ borderRadius: 16,
1474
+ backgroundColor: '#F5F5F5',
1475
+ alignItems: 'center',
1476
+ justifyContent: 'center',
1477
+ },
1478
+ onairosIconText: {
1479
+ fontSize: 24,
1480
+ color: '#000',
1481
+ },
1482
+ onairosLogo: {
1483
+ width: 32,
1484
+ height: 32,
1485
+ },
1486
+ titleContainer: {
1487
+ marginBottom: 20,
1488
+ },
1489
+ mainTitle: {
1490
+ fontSize: 20,
1491
+ fontWeight: '600',
1492
+ color: '#000',
1493
+ textAlign: 'center',
1494
+ marginBottom: 12,
1495
+ },
1496
+ privacyMessage: {
1497
+ fontSize: 14,
1498
+ color: '#666',
1499
+ textAlign: 'center',
1500
+ marginBottom: 12,
1501
+ },
1502
+ content: {
1503
+ flex: 1,
1504
+ paddingHorizontal: 24,
1505
+ },
1506
+ scrollContent: {
1507
+ flexGrow: 1,
1508
+ paddingBottom: 20,
1509
+ },
1510
+ platformsContainer: {
1511
+ marginTop: 16,
1512
+ },
1513
+ platformItem: {
1514
+ flexDirection: 'row',
1515
+ justifyContent: 'space-between',
1516
+ alignItems: 'center',
1517
+ padding: 12,
1518
+ backgroundColor: '#fff',
1519
+ borderRadius: 12,
1520
+ marginBottom: 8,
1521
+ borderWidth: 1,
1522
+ borderColor: '#eee',
1523
+ },
1524
+ platformInfo: {
1525
+ flexDirection: 'row',
1526
+ alignItems: 'center',
1527
+ flex: 1,
1528
+ },
1529
+ platformIcon: {
1530
+ width: 24,
1531
+ height: 24,
1532
+ marginRight: 12,
1533
+ },
1534
+ platformName: {
1535
+ fontSize: 16,
1536
+ fontWeight: '500',
1537
+ color: '#000',
1538
+ },
1539
+ footer: {
1540
+ flexDirection: 'row',
1541
+ alignItems: 'center',
1542
+ justifyContent: 'space-between',
1543
+ padding: 24,
1544
+ borderTopWidth: 1,
1545
+ borderTopColor: '#eee',
1546
+ backgroundColor: '#fff',
1547
+ },
1548
+ footerButtonCancel: {
1549
+ paddingVertical: 8,
1550
+ paddingHorizontal: 16,
1551
+ },
1552
+ footerButtonConfirm: {
1553
+ paddingVertical: 16,
1554
+ paddingHorizontal: 32,
1555
+ borderRadius: 16,
1556
+ backgroundColor: '#fff',
1557
+ borderWidth: 1,
1558
+ borderColor: '#000',
1559
+ },
1560
+ footerButtonDisabled: {
1561
+ opacity: 0.5,
1562
+ },
1563
+ footerButtonText: {
1564
+ color: '#666',
1565
+ fontSize: 16,
1566
+ },
1567
+ footerButtonTextConfirm: {
1568
+ color: '#000',
1569
+ fontSize: 16,
1570
+ fontWeight: '600',
1571
+ },
1572
+
1573
+ successContainer: {
1574
+ flex: 1,
1575
+ justifyContent: 'center',
1576
+ alignItems: 'center',
1577
+ },
1578
+ successContent: {
1579
+ backgroundColor: '#fff',
1580
+ padding: 24,
1581
+ borderRadius: 16,
1582
+ alignItems: 'center',
1583
+ },
1584
+ successIcon: {
1585
+ backgroundColor: '#4CAF50',
1586
+ borderRadius: 24,
1587
+ padding: 12,
1588
+ marginBottom: 16,
1589
+ },
1590
+ successTitle: {
1591
+ fontSize: 22,
1592
+ fontWeight: '600',
1593
+ color: '#000',
1594
+ textAlign: 'center',
1595
+ marginBottom: 16,
1596
+ },
1597
+ successSubtitle: {
1598
+ fontSize: 14,
1599
+ color: '#666',
1600
+ textAlign: 'center',
1601
+ marginBottom: 16,
1602
+ },
1603
+ successMessage: {
1604
+ backgroundColor: '#f0f0f0',
1605
+ padding: 16,
1606
+ borderRadius: 8,
1607
+ marginBottom: 16,
1608
+ },
1609
+ successMessageText: {
1610
+ fontSize: 14,
1611
+ color: '#666',
1612
+ },
1613
+ platformToggle: {
1614
+ width: 50,
1615
+ height: 28,
1616
+ borderRadius: 14,
1617
+ borderWidth: 1,
1618
+ borderColor: '#ddd',
1619
+ backgroundColor: '#f0f0f0',
1620
+ justifyContent: 'center',
1621
+ paddingHorizontal: 2,
1622
+ },
1623
+ platformToggleActive: {
1624
+ borderColor: '#4CAF50',
1625
+ backgroundColor: '#4CAF50',
1626
+ },
1627
+ platformToggleThumb: {
1628
+ width: 22,
1629
+ height: 22,
1630
+ borderRadius: 11,
1631
+ backgroundColor: '#fff',
1632
+ shadowColor: '#000',
1633
+ shadowOffset: { width: 0, height: 1 },
1634
+ shadowOpacity: 0.2,
1635
+ shadowRadius: 2,
1636
+ elevation: 2,
1637
+ },
1638
+ platformToggleThumbActive: {
1639
+ alignSelf: 'flex-end',
1640
+ },
1641
+ // Dark mode styles
1642
+ darkPlatformItem: {
1643
+ backgroundColor: '#333',
1644
+ borderColor: '#555',
1645
+ },
1646
+ darkText: {
1647
+ color: '#fff',
1648
+ },
1649
+ darkSubText: {
1650
+ color: '#ccc',
1651
+ },
1652
+ progressIndicator: {
1653
+ flexDirection: 'row',
1654
+ alignItems: 'center',
1655
+ marginTop: 16,
1656
+ },
1657
+ progressText: {
1658
+ fontSize: 16,
1659
+ fontWeight: '500',
1660
+ color: '#000',
1661
+ marginLeft: 8,
1662
+ },
1663
+ // Email input styles
1664
+ emailInputContainer: {
1665
+ flex: 1,
1666
+ justifyContent: 'flex-start',
1667
+ alignItems: 'center',
1668
+ padding: 24,
1669
+ paddingTop: 60,
1670
+ },
1671
+ emailHeader: {
1672
+ alignItems: 'center',
1673
+ marginBottom: 32,
1674
+ },
1675
+ emailTitle: {
1676
+ fontSize: 24,
1677
+ fontWeight: '600',
1678
+ color: '#000',
1679
+ textAlign: 'center',
1680
+ marginTop: 16,
1681
+ marginBottom: 8,
1682
+ },
1683
+ emailSubtitle: {
1684
+ fontSize: 16,
1685
+ color: '#666',
1686
+ textAlign: 'center',
1687
+ },
1688
+ emailInputSection: {
1689
+ width: '100%',
1690
+ maxWidth: 320,
1691
+ },
1692
+ emailInput: {
1693
+ borderWidth: 1,
1694
+ borderColor: '#ddd',
1695
+ borderRadius: 12,
1696
+ padding: 16,
1697
+ fontSize: 16,
1698
+ marginBottom: 16,
1699
+ backgroundColor: '#fff',
1700
+ },
1701
+ emailSubmitButton: {
1702
+ backgroundColor: '#4CAF50',
1703
+ paddingVertical: 16,
1704
+ paddingHorizontal: 32,
1705
+ borderRadius: 12,
1706
+ alignItems: 'center',
1707
+ },
1708
+ emailSubmitButtonDisabled: {
1709
+ opacity: 0.5,
1710
+ },
1711
+ emailSubmitButtonText: {
1712
+ color: '#fff',
1713
+ fontSize: 16,
1714
+ fontWeight: '600',
1715
+ },
1716
+ // Verification code styles
1717
+ developmentNote: {
1718
+ fontSize: 14,
1719
+ color: '#FF9800',
1720
+ textAlign: 'center',
1721
+ marginTop: 8,
1722
+ backgroundColor: '#FFF3E0',
1723
+ padding: 8,
1724
+ borderRadius: 4,
1725
+ },
1726
+ codeInputContainer: {
1727
+ flexDirection: 'row',
1728
+ justifyContent: 'space-between',
1729
+ marginBottom: 24,
1730
+ paddingHorizontal: 20,
1731
+ },
1732
+ codeDigit: {
1733
+ width: 45,
1734
+ height: 55,
1735
+ borderWidth: 2,
1736
+ borderColor: '#ddd',
1737
+ borderRadius: 8,
1738
+ fontSize: 24,
1739
+ fontWeight: '600',
1740
+ color: '#000',
1741
+ backgroundColor: '#fff',
1742
+ },
1743
+ codeDigitActive: {
1744
+ borderColor: '#4CAF50',
1745
+ },
1746
+ backButton: {
1747
+ paddingVertical: 12,
1748
+ alignItems: 'center',
1749
+ },
1750
+ backButtonText: {
1751
+ color: '#666',
1752
+ fontSize: 16,
1753
+ },
1754
+ // Expand button styles
1755
+ expandButton: {
1756
+ flexDirection: 'row',
1757
+ alignItems: 'center',
1758
+ justifyContent: 'center',
1759
+ padding: 12,
1760
+ backgroundColor: '#f8f9fa',
1761
+ borderRadius: 12,
1762
+ borderWidth: 1,
1763
+ borderColor: '#e9ecef',
1764
+ marginTop: 8,
1765
+ },
1766
+ expandButtonText: {
1767
+ fontSize: 14,
1768
+ fontWeight: '500',
1769
+ color: COLORS.primary,
1770
+ marginLeft: 8,
1771
+ },
1772
+ // Test mode styles
1773
+ testModeContainer: {
1774
+ marginTop: 16,
1775
+ paddingHorizontal: 16,
1776
+ backgroundColor: '#f8f9fa',
1777
+ borderRadius: 12,
1778
+ padding: 16,
1779
+ borderWidth: 1,
1780
+ borderColor: '#e9ecef',
1781
+ },
1782
+ testModeTitle: {
1783
+ fontSize: 16,
1784
+ fontWeight: '600',
1785
+ color: '#495057',
1786
+ marginBottom: 12,
1787
+ textAlign: 'center',
1788
+ },
1789
+ testDataRequestButton: {
1790
+ flexDirection: 'row',
1791
+ alignItems: 'center',
1792
+ justifyContent: 'center',
1793
+ padding: 12,
1794
+ backgroundColor: '#fff3cd',
1795
+ borderRadius: 12,
1796
+ borderWidth: 1,
1797
+ borderColor: '#ffeaa7',
1798
+ marginBottom: 8,
1799
+ },
1800
+ testDataRequestButtonText: {
1801
+ fontSize: 14,
1802
+ fontWeight: '500',
1803
+ color: '#856404',
1804
+ marginLeft: 8,
1805
+ },
1806
+ testExistingUserButton: {
1807
+ flexDirection: 'row',
1808
+ alignItems: 'center',
1809
+ justifyContent: 'center',
1810
+ padding: 12,
1811
+ backgroundColor: '#d4edda',
1812
+ borderRadius: 12,
1813
+ borderWidth: 1,
1814
+ borderColor: '#c3e6cb',
1815
+ marginBottom: 8,
1816
+ },
1817
+ testExistingUserButtonText: {
1818
+ fontSize: 14,
1819
+ fontWeight: '500',
1820
+ color: '#155724',
1821
+ marginLeft: 8,
1822
+ },
1823
+ testSkipToTrainingButton: {
1824
+ flexDirection: 'row',
1825
+ alignItems: 'center',
1826
+ justifyContent: 'center',
1827
+ padding: 12,
1828
+ backgroundColor: '#d1ecf1',
1829
+ borderRadius: 12,
1830
+ borderWidth: 1,
1831
+ borderColor: '#bee5eb',
1832
+ },
1833
+ testSkipToTrainingButtonText: {
1834
+ fontSize: 14,
1835
+ fontWeight: '500',
1836
+ color: '#0c5460',
1837
+ marginLeft: 8,
1838
+ },
1839
+ });