@oxyhq/services 5.13.0 → 5.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) hide show
  1. package/README.md +71 -0
  2. package/lib/commonjs/core/HttpClient.js +238 -0
  3. package/lib/commonjs/core/HttpClient.js.map +1 -0
  4. package/lib/commonjs/core/OxyServices.js +530 -332
  5. package/lib/commonjs/core/OxyServices.js.map +1 -1
  6. package/lib/commonjs/core/RequestManager.js +199 -0
  7. package/lib/commonjs/core/RequestManager.js.map +1 -0
  8. package/lib/commonjs/core/index.js +38 -1
  9. package/lib/commonjs/core/index.js.map +1 -1
  10. package/lib/commonjs/i18n/index.js +37 -1
  11. package/lib/commonjs/i18n/index.js.map +1 -1
  12. package/lib/commonjs/i18n/locales/ar-SA.json +128 -0
  13. package/lib/commonjs/i18n/locales/ca-ES.json +128 -0
  14. package/lib/commonjs/i18n/locales/de-DE.json +128 -0
  15. package/lib/commonjs/i18n/locales/en-US.json +85 -12
  16. package/lib/commonjs/i18n/locales/es-ES.json +58 -6
  17. package/lib/commonjs/i18n/locales/fr-FR.json +128 -0
  18. package/lib/commonjs/i18n/locales/it-IT.json +128 -0
  19. package/lib/commonjs/i18n/locales/ja-JP.json +127 -0
  20. package/lib/commonjs/i18n/locales/ko-KR.json +128 -0
  21. package/lib/commonjs/i18n/locales/pt-PT.json +128 -0
  22. package/lib/commonjs/i18n/locales/zh-CN.json +128 -0
  23. package/lib/commonjs/index.js +36 -0
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/ui/components/Avatar.js +94 -27
  26. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  27. package/lib/commonjs/ui/components/FollowButton.js +1 -0
  28. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  29. package/lib/commonjs/ui/components/FontLoader.js +22 -42
  30. package/lib/commonjs/ui/components/FontLoader.js.map +1 -1
  31. package/lib/commonjs/ui/components/OxyProvider.js +5 -8
  32. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  33. package/lib/commonjs/ui/components/StepBasedScreen.js +64 -44
  34. package/lib/commonjs/ui/components/StepBasedScreen.js.map +1 -1
  35. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +14 -35
  36. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
  37. package/lib/commonjs/ui/components/internal/PinInput.js +2 -2
  38. package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
  39. package/lib/commonjs/ui/components/internal/TextField.js +13 -8
  40. package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
  41. package/lib/commonjs/ui/context/OxyContext.js +443 -371
  42. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  43. package/lib/commonjs/ui/hooks/useSessionSocket.js +80 -22
  44. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  45. package/lib/commonjs/ui/index.js +4 -1
  46. package/lib/commonjs/ui/index.js.map +1 -1
  47. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +32 -2
  48. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +101 -59
  50. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/FileManagementScreen.js +3 -2
  52. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +75 -117
  54. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SignInScreen.js +43 -50
  56. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/SignUpScreen.js +14 -16
  58. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  59. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +188 -142
  60. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  61. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +10 -10
  62. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  63. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +2 -4
  64. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  65. package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js +45 -25
  66. package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js.map +1 -1
  67. package/lib/commonjs/ui/screens/steps/RecoverResetPasswordStep.js +88 -53
  68. package/lib/commonjs/ui/screens/steps/RecoverResetPasswordStep.js.map +1 -1
  69. package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js +79 -58
  70. package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js.map +1 -1
  71. package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js +61 -52
  72. package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js.map +1 -1
  73. package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js +218 -39
  74. package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js.map +1 -1
  75. package/lib/commonjs/ui/screens/steps/SignInTotpStep.js +77 -50
  76. package/lib/commonjs/ui/screens/steps/SignInTotpStep.js.map +1 -1
  77. package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js +424 -71
  78. package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js.map +1 -1
  79. package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js +55 -30
  80. package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js.map +1 -1
  81. package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js +64 -46
  82. package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js.map +1 -1
  83. package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js +84 -146
  84. package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js.map +1 -1
  85. package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js +113 -34
  86. package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js.map +1 -1
  87. package/lib/commonjs/ui/stores/accountStore.js +237 -0
  88. package/lib/commonjs/ui/stores/accountStore.js.map +1 -0
  89. package/lib/commonjs/ui/stores/authStore.js +17 -20
  90. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  91. package/lib/commonjs/ui/styles/authStyles.js +16 -8
  92. package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
  93. package/lib/commonjs/ui/styles/index.js +11 -0
  94. package/lib/commonjs/ui/styles/index.js.map +1 -1
  95. package/lib/commonjs/ui/styles/spacing.js +51 -0
  96. package/lib/commonjs/ui/styles/spacing.js.map +1 -0
  97. package/lib/commonjs/utils/asyncUtils.js +9 -22
  98. package/lib/commonjs/utils/asyncUtils.js.map +1 -1
  99. package/lib/commonjs/utils/cache.js +259 -0
  100. package/lib/commonjs/utils/cache.js.map +1 -0
  101. package/lib/commonjs/utils/index.js +99 -0
  102. package/lib/commonjs/utils/index.js.map +1 -1
  103. package/lib/commonjs/utils/languageUtils.js +159 -0
  104. package/lib/commonjs/utils/languageUtils.js.map +1 -0
  105. package/lib/commonjs/utils/requestUtils.js +217 -0
  106. package/lib/commonjs/utils/requestUtils.js.map +1 -0
  107. package/lib/commonjs/utils/sessionUtils.js +191 -0
  108. package/lib/commonjs/utils/sessionUtils.js.map +1 -0
  109. package/lib/commonjs/utils/validationUtils.js +1 -1
  110. package/lib/module/core/HttpClient.js +232 -0
  111. package/lib/module/core/HttpClient.js.map +1 -0
  112. package/lib/module/core/OxyServices.js +528 -326
  113. package/lib/module/core/OxyServices.js.map +1 -1
  114. package/lib/module/core/RequestManager.js +194 -0
  115. package/lib/module/core/RequestManager.js.map +1 -0
  116. package/lib/module/core/index.js +2 -0
  117. package/lib/module/core/index.js.map +1 -1
  118. package/lib/module/i18n/index.js +37 -1
  119. package/lib/module/i18n/index.js.map +1 -1
  120. package/lib/module/i18n/locales/ar-SA.json +128 -0
  121. package/lib/module/i18n/locales/ca-ES.json +128 -0
  122. package/lib/module/i18n/locales/de-DE.json +128 -0
  123. package/lib/module/i18n/locales/en-US.json +85 -12
  124. package/lib/module/i18n/locales/es-ES.json +58 -6
  125. package/lib/module/i18n/locales/fr-FR.json +128 -0
  126. package/lib/module/i18n/locales/it-IT.json +128 -0
  127. package/lib/module/i18n/locales/ja-JP.json +127 -0
  128. package/lib/module/i18n/locales/ko-KR.json +128 -0
  129. package/lib/module/i18n/locales/pt-PT.json +128 -0
  130. package/lib/module/i18n/locales/zh-CN.json +128 -0
  131. package/lib/module/index.js +2 -0
  132. package/lib/module/index.js.map +1 -1
  133. package/lib/module/ui/components/Avatar.js +94 -27
  134. package/lib/module/ui/components/Avatar.js.map +1 -1
  135. package/lib/module/ui/components/FollowButton.js +1 -0
  136. package/lib/module/ui/components/FollowButton.js.map +1 -1
  137. package/lib/module/ui/components/FontLoader.js +23 -43
  138. package/lib/module/ui/components/FontLoader.js.map +1 -1
  139. package/lib/module/ui/components/OxyProvider.js +6 -8
  140. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  141. package/lib/module/ui/components/StepBasedScreen.js +65 -45
  142. package/lib/module/ui/components/StepBasedScreen.js.map +1 -1
  143. package/lib/module/ui/components/internal/GroupedPillButtons.js +14 -35
  144. package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
  145. package/lib/module/ui/components/internal/PinInput.js +2 -2
  146. package/lib/module/ui/components/internal/PinInput.js.map +1 -1
  147. package/lib/module/ui/components/internal/TextField.js +13 -8
  148. package/lib/module/ui/components/internal/TextField.js.map +1 -1
  149. package/lib/module/ui/context/OxyContext.js +442 -370
  150. package/lib/module/ui/context/OxyContext.js.map +1 -1
  151. package/lib/module/ui/hooks/useSessionSocket.js +80 -22
  152. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  153. package/lib/module/ui/index.js +4 -2
  154. package/lib/module/ui/index.js.map +1 -1
  155. package/lib/module/ui/screens/AccountSettingsScreen.js +33 -2
  156. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  157. package/lib/module/ui/screens/AccountSwitcherScreen.js +102 -60
  158. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  159. package/lib/module/ui/screens/FileManagementScreen.js +3 -2
  160. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  161. package/lib/module/ui/screens/LanguageSelectorScreen.js +73 -117
  162. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  163. package/lib/module/ui/screens/SignInScreen.js +44 -51
  164. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  165. package/lib/module/ui/screens/SignUpScreen.js +14 -16
  166. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  167. package/lib/module/ui/screens/WelcomeNewUserScreen.js +187 -143
  168. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  169. package/lib/module/ui/screens/internal/SignInPasswordStep.js +10 -10
  170. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  171. package/lib/module/ui/screens/internal/SignInUsernameStep.js +2 -4
  172. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  173. package/lib/module/ui/screens/steps/RecoverRequestStep.js +45 -25
  174. package/lib/module/ui/screens/steps/RecoverRequestStep.js.map +1 -1
  175. package/lib/module/ui/screens/steps/RecoverResetPasswordStep.js +89 -54
  176. package/lib/module/ui/screens/steps/RecoverResetPasswordStep.js.map +1 -1
  177. package/lib/module/ui/screens/steps/RecoverSuccessStep.js +80 -59
  178. package/lib/module/ui/screens/steps/RecoverSuccessStep.js.map +1 -1
  179. package/lib/module/ui/screens/steps/RecoverVerifyStep.js +62 -53
  180. package/lib/module/ui/screens/steps/RecoverVerifyStep.js.map +1 -1
  181. package/lib/module/ui/screens/steps/SignInPasswordStep.js +219 -40
  182. package/lib/module/ui/screens/steps/SignInPasswordStep.js.map +1 -1
  183. package/lib/module/ui/screens/steps/SignInTotpStep.js +78 -51
  184. package/lib/module/ui/screens/steps/SignInTotpStep.js.map +1 -1
  185. package/lib/module/ui/screens/steps/SignInUsernameStep.js +426 -73
  186. package/lib/module/ui/screens/steps/SignInUsernameStep.js.map +1 -1
  187. package/lib/module/ui/screens/steps/SignUpIdentityStep.js +55 -30
  188. package/lib/module/ui/screens/steps/SignUpIdentityStep.js.map +1 -1
  189. package/lib/module/ui/screens/steps/SignUpSecurityStep.js +65 -47
  190. package/lib/module/ui/screens/steps/SignUpSecurityStep.js.map +1 -1
  191. package/lib/module/ui/screens/steps/SignUpSummaryStep.js +84 -146
  192. package/lib/module/ui/screens/steps/SignUpSummaryStep.js.map +1 -1
  193. package/lib/module/ui/screens/steps/SignUpWelcomeStep.js +114 -35
  194. package/lib/module/ui/screens/steps/SignUpWelcomeStep.js.map +1 -1
  195. package/lib/module/ui/stores/accountStore.js +229 -0
  196. package/lib/module/ui/stores/accountStore.js.map +1 -0
  197. package/lib/module/ui/stores/authStore.js +17 -20
  198. package/lib/module/ui/stores/authStore.js.map +1 -1
  199. package/lib/module/ui/styles/authStyles.js +16 -8
  200. package/lib/module/ui/styles/authStyles.js.map +1 -1
  201. package/lib/module/ui/styles/index.js +1 -0
  202. package/lib/module/ui/styles/index.js.map +1 -1
  203. package/lib/module/ui/styles/spacing.js +48 -0
  204. package/lib/module/ui/styles/spacing.js.map +1 -0
  205. package/lib/module/utils/asyncUtils.js +10 -22
  206. package/lib/module/utils/asyncUtils.js.map +1 -1
  207. package/lib/module/utils/cache.js +250 -0
  208. package/lib/module/utils/cache.js.map +1 -0
  209. package/lib/module/utils/index.js +7 -0
  210. package/lib/module/utils/index.js.map +1 -1
  211. package/lib/module/utils/languageUtils.js +151 -0
  212. package/lib/module/utils/languageUtils.js.map +1 -0
  213. package/lib/module/utils/requestUtils.js +210 -0
  214. package/lib/module/utils/requestUtils.js.map +1 -0
  215. package/lib/module/utils/sessionUtils.js +180 -0
  216. package/lib/module/utils/sessionUtils.js.map +1 -0
  217. package/lib/module/utils/validationUtils.js +1 -1
  218. package/lib/typescript/core/HttpClient.d.ts +64 -0
  219. package/lib/typescript/core/HttpClient.d.ts.map +1 -0
  220. package/lib/typescript/core/OxyServices.d.ts +86 -73
  221. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  222. package/lib/typescript/core/RequestManager.d.ts +67 -0
  223. package/lib/typescript/core/RequestManager.d.ts.map +1 -0
  224. package/lib/typescript/core/index.d.ts +2 -0
  225. package/lib/typescript/core/index.d.ts.map +1 -1
  226. package/lib/typescript/i18n/index.d.ts.map +1 -1
  227. package/lib/typescript/index.d.ts +2 -0
  228. package/lib/typescript/index.d.ts.map +1 -1
  229. package/lib/typescript/models/interfaces.d.ts +15 -0
  230. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  231. package/lib/typescript/models/session.d.ts +1 -0
  232. package/lib/typescript/models/session.d.ts.map +1 -1
  233. package/lib/typescript/ui/components/Avatar.d.ts +6 -7
  234. package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
  235. package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
  236. package/lib/typescript/ui/components/FontLoader.d.ts +3 -3
  237. package/lib/typescript/ui/components/FontLoader.d.ts.map +1 -1
  238. package/lib/typescript/ui/components/OxyProvider.d.ts +2 -2
  239. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  240. package/lib/typescript/ui/components/StepBasedScreen.d.ts.map +1 -1
  241. package/lib/typescript/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
  242. package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
  243. package/lib/typescript/ui/context/OxyContext.d.ts +5 -0
  244. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  245. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  246. package/lib/typescript/ui/index.d.ts +2 -2
  247. package/lib/typescript/ui/index.d.ts.map +1 -1
  248. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  249. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  250. package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts +3 -3
  251. package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  252. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  253. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
  254. package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  255. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
  256. package/lib/typescript/ui/screens/steps/RecoverRequestStep.d.ts.map +1 -1
  257. package/lib/typescript/ui/screens/steps/RecoverResetPasswordStep.d.ts.map +1 -1
  258. package/lib/typescript/ui/screens/steps/RecoverSuccessStep.d.ts.map +1 -1
  259. package/lib/typescript/ui/screens/steps/RecoverVerifyStep.d.ts.map +1 -1
  260. package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts +2 -0
  261. package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts.map +1 -1
  262. package/lib/typescript/ui/screens/steps/SignInTotpStep.d.ts.map +1 -1
  263. package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts.map +1 -1
  264. package/lib/typescript/ui/screens/steps/SignUpIdentityStep.d.ts.map +1 -1
  265. package/lib/typescript/ui/screens/steps/SignUpSecurityStep.d.ts.map +1 -1
  266. package/lib/typescript/ui/screens/steps/SignUpSummaryStep.d.ts.map +1 -1
  267. package/lib/typescript/ui/screens/steps/SignUpWelcomeStep.d.ts.map +1 -1
  268. package/lib/typescript/ui/stores/accountStore.d.ts +34 -0
  269. package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -0
  270. package/lib/typescript/ui/stores/authStore.d.ts +7 -3
  271. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  272. package/lib/typescript/ui/styles/authStyles.d.ts +19 -2
  273. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
  274. package/lib/typescript/ui/styles/index.d.ts +1 -0
  275. package/lib/typescript/ui/styles/index.d.ts.map +1 -1
  276. package/lib/typescript/ui/styles/spacing.d.ts +43 -0
  277. package/lib/typescript/ui/styles/spacing.d.ts.map +1 -0
  278. package/lib/typescript/utils/asyncUtils.d.ts +2 -0
  279. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
  280. package/lib/typescript/utils/cache.d.ts +128 -0
  281. package/lib/typescript/utils/cache.d.ts.map +1 -0
  282. package/lib/typescript/utils/index.d.ts +4 -0
  283. package/lib/typescript/utils/index.d.ts.map +1 -1
  284. package/lib/typescript/utils/languageUtils.d.ts +38 -0
  285. package/lib/typescript/utils/languageUtils.d.ts.map +1 -0
  286. package/lib/typescript/utils/requestUtils.d.ts +122 -0
  287. package/lib/typescript/utils/requestUtils.d.ts.map +1 -0
  288. package/lib/typescript/utils/sessionUtils.d.ts +55 -0
  289. package/lib/typescript/utils/sessionUtils.d.ts.map +1 -0
  290. package/lib/typescript/utils/validationUtils.d.ts +1 -1
  291. package/package.json +1 -1
  292. package/src/core/HttpClient.ts +277 -0
  293. package/src/core/OxyServices.ts +461 -352
  294. package/src/core/RequestManager.ts +240 -0
  295. package/src/core/index.ts +10 -0
  296. package/src/i18n/index.ts +36 -0
  297. package/src/i18n/locales/ar-SA.json +128 -0
  298. package/src/i18n/locales/ca-ES.json +128 -0
  299. package/src/i18n/locales/de-DE.json +128 -0
  300. package/src/i18n/locales/en-US.json +85 -12
  301. package/src/i18n/locales/es-ES.json +58 -6
  302. package/src/i18n/locales/fr-FR.json +128 -0
  303. package/src/i18n/locales/it-IT.json +128 -0
  304. package/src/i18n/locales/ja-JP.json +127 -0
  305. package/src/i18n/locales/ko-KR.json +128 -0
  306. package/src/i18n/locales/pt-PT.json +128 -0
  307. package/src/i18n/locales/zh-CN.json +128 -0
  308. package/src/index.ts +10 -0
  309. package/src/models/interfaces.ts +19 -0
  310. package/src/models/session.ts +1 -1
  311. package/src/ui/components/Avatar.tsx +151 -35
  312. package/src/ui/components/FollowButton.tsx +1 -0
  313. package/src/ui/components/FontLoader.tsx +17 -37
  314. package/src/ui/components/OxyProvider.tsx +14 -13
  315. package/src/ui/components/StepBasedScreen.tsx +66 -43
  316. package/src/ui/components/internal/GroupedPillButtons.tsx +15 -31
  317. package/src/ui/components/internal/PinInput.tsx +2 -2
  318. package/src/ui/components/internal/TextField.tsx +7 -6
  319. package/src/ui/context/OxyContext.tsx +441 -326
  320. package/src/ui/hooks/useSessionSocket.ts +72 -18
  321. package/src/ui/index.ts +4 -1
  322. package/src/ui/screens/AccountSettingsScreen.tsx +34 -2
  323. package/src/ui/screens/AccountSwitcherScreen.tsx +102 -68
  324. package/src/ui/screens/FileManagementScreen.tsx +16 -16
  325. package/src/ui/screens/LanguageSelectorScreen.tsx +86 -143
  326. package/src/ui/screens/SignInScreen.tsx +59 -43
  327. package/src/ui/screens/SignUpScreen.tsx +14 -15
  328. package/src/ui/screens/WelcomeNewUserScreen.tsx +153 -105
  329. package/src/ui/screens/internal/SignInPasswordStep.tsx +4 -6
  330. package/src/ui/screens/internal/SignInUsernameStep.tsx +1 -1
  331. package/src/ui/screens/steps/RecoverRequestStep.tsx +34 -24
  332. package/src/ui/screens/steps/RecoverResetPasswordStep.tsx +65 -36
  333. package/src/ui/screens/steps/RecoverSuccessStep.tsx +71 -47
  334. package/src/ui/screens/steps/RecoverVerifyStep.tsx +60 -50
  335. package/src/ui/screens/steps/SignInPasswordStep.tsx +190 -32
  336. package/src/ui/screens/steps/SignInTotpStep.tsx +68 -34
  337. package/src/ui/screens/steps/SignInUsernameStep.tsx +446 -63
  338. package/src/ui/screens/steps/SignUpIdentityStep.tsx +49 -35
  339. package/src/ui/screens/steps/SignUpSecurityStep.tsx +56 -39
  340. package/src/ui/screens/steps/SignUpSummaryStep.tsx +99 -89
  341. package/src/ui/screens/steps/SignUpWelcomeStep.tsx +88 -20
  342. package/src/ui/stores/accountStore.ts +285 -0
  343. package/src/ui/stores/authStore.ts +16 -19
  344. package/src/ui/styles/authStyles.ts +16 -8
  345. package/src/ui/styles/index.ts +1 -0
  346. package/src/ui/styles/spacing.ts +46 -0
  347. package/src/utils/asyncUtils.ts +10 -24
  348. package/src/utils/cache.ts +264 -0
  349. package/src/utils/index.ts +19 -0
  350. package/src/utils/languageUtils.ts +174 -0
  351. package/src/utils/requestUtils.ts +234 -0
  352. package/src/utils/sessionUtils.ts +206 -0
  353. package/src/utils/validationUtils.ts +1 -1
@@ -5,16 +5,37 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useOxy = exports.default = exports.OxyProvider = exports.OxyContextProvider = void 0;
7
7
  var _react = require("react");
8
- var _reactNative = require("react-native");
9
8
  var _core = require("../../core");
9
+ var _sessionUtils = require("../../utils/sessionUtils");
10
10
  var _deviceManager = require("../../utils/deviceManager");
11
11
  var _useSessionSocket = require("../hooks/useSessionSocket");
12
12
  var _sonner = require("../../lib/sonner");
13
13
  var _authStore = require("../stores/authStore");
14
+ var _languageUtils = require("../../utils/languageUtils");
14
15
  var _jsxRuntime = require("react/jsx-runtime");
15
16
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } // Define the context shape
16
17
  // NOTE: We intentionally avoid importing useFollow here to prevent a require cycle.
17
18
  // If consumers relied on `const { useFollow } = useOxy()`, we provide a lazy proxy below.
19
+ // Empty follow hook fallback
20
+ const createEmptyFollowHook = () => {
21
+ const emptyResult = {
22
+ isFollowing: false,
23
+ isLoading: false,
24
+ error: null,
25
+ toggleFollow: async () => {},
26
+ setFollowStatus: () => {},
27
+ fetchStatus: async () => {},
28
+ clearError: () => {},
29
+ followerCount: null,
30
+ followingCount: null,
31
+ isLoadingCounts: false,
32
+ fetchUserCounts: async () => {},
33
+ setFollowerCount: () => {},
34
+ setFollowingCount: () => {}
35
+ };
36
+ return () => emptyResult;
37
+ };
38
+
18
39
  // Create the context with default values
19
40
  const OxyContext = /*#__PURE__*/(0, _react.createContext)(null);
20
41
 
@@ -67,6 +88,8 @@ const getStorage = async () => {
67
88
  const getStorageKeys = (prefix = 'oxy_session') => ({
68
89
  activeSessionId: `${prefix}_active_session_id`,
69
90
  // Only store the active session ID
91
+ sessionIds: `${prefix}_session_ids`,
92
+ // Store all session IDs for quick account loading
70
93
  language: `${prefix}_language` // Store the selected language
71
94
  });
72
95
  const OxyProvider = ({
@@ -106,43 +129,87 @@ const OxyProvider = ({
106
129
  const [minimalUser, setMinimalUser] = (0, _react.useState)(null);
107
130
  const [sessions, setSessions] = (0, _react.useState)([]);
108
131
  const [activeSessionId, setActiveSessionId] = (0, _react.useState)(null);
132
+
133
+ // Track in-flight refresh to prevent duplicate calls
134
+ const refreshInFlightRef = (0, _react.useRef)(null);
109
135
  const [storage, setStorage] = (0, _react.useState)(null);
110
136
  const [currentLanguage, setCurrentLanguage] = (0, _react.useState)('en-US');
111
137
 
112
- // Normalize language codes to BCP-47 (e.g., en-US)
113
- const normalizeLanguageCode = (0, _react.useCallback)(lang => {
114
- if (!lang) return null;
115
- if (lang.includes('-')) return lang;
116
- const map = {
117
- en: 'en-US',
118
- es: 'es-ES',
119
- ca: 'ca-ES',
120
- fr: 'fr-FR',
121
- de: 'de-DE',
122
- it: 'it-IT',
123
- pt: 'pt-PT',
124
- ja: 'ja-JP',
125
- ko: 'ko-KR',
126
- zh: 'zh-CN',
127
- ar: 'ar-SA'
128
- };
129
- return map[lang] || lang;
138
+ // Storage keys (memoized to prevent infinite loops) - declared early for use in helpers
139
+ const keys = (0, _react.useMemo)(() => getStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
140
+
141
+ // Helper to apply language preference from user/server
142
+ const applyLanguagePreference = (0, _react.useCallback)(async user => {
143
+ const userLanguage = user?.language;
144
+ if (!userLanguage || !storage) return;
145
+ try {
146
+ const serverLang = (0, _languageUtils.normalizeLanguageCode)(userLanguage);
147
+ await storage.setItem(keys.language, serverLang);
148
+ setCurrentLanguage(serverLang);
149
+ } catch (e) {
150
+ if (__DEV__) {
151
+ console.warn('Failed to apply server language preference', e);
152
+ }
153
+ }
154
+ }, [storage, keys.language]);
155
+ const mapSessionsToClient = (0, _react.useCallback)((sessions, fallbackDeviceId, fallbackUserId) => {
156
+ return sessions.map(s => ({
157
+ sessionId: s.sessionId,
158
+ deviceId: s.deviceId || fallbackDeviceId || '',
159
+ expiresAt: s.expiresAt || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
160
+ lastActive: s.lastActive || new Date().toISOString(),
161
+ userId: s.user?.id || s.userId || s.user?._id?.toString() || fallbackUserId || '',
162
+ isCurrent: Boolean(s.isCurrent)
163
+ }));
130
164
  }, []);
131
- // Add a new state to track token restoration
132
- const [tokenReady, setTokenReady] = (0, _react.useState)(false);
133
165
 
134
- // Storage keys (memoized to prevent infinite loops)
135
- const keys = (0, _react.useMemo)(() => getStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
166
+ // Save all session IDs to storage for quick loading on initialization
167
+ const saveSessionIds = (0, _react.useCallback)(async sessionIds => {
168
+ if (!storage) return;
169
+ try {
170
+ const uniqueIds = Array.from(new Set(sessionIds));
171
+ await storage.setItem(keys.sessionIds, JSON.stringify(uniqueIds));
172
+ } catch (err) {
173
+ if (__DEV__) {
174
+ console.warn('Failed to save session IDs:', err);
175
+ }
176
+ }
177
+ }, [storage, keys.sessionIds]);
178
+ const updateSessions = (0, _react.useCallback)((newSessions, mergeWithExisting = false) => {
179
+ setSessions(prevSessions => {
180
+ const sessionsToProcess = mergeWithExisting ? (0, _sessionUtils.mergeSessions)(prevSessions, newSessions, activeSessionId, false) : (0, _sessionUtils.normalizeAndSortSessions)(newSessions, activeSessionId, false);
181
+
182
+ // Save all session IDs to storage
183
+ if (storage) {
184
+ const allSessionIds = sessionsToProcess.map(s => s.sessionId);
185
+ saveSessionIds(allSessionIds).catch(() => {
186
+ // Ignore errors - non-critical
187
+ });
188
+ }
189
+ return (0, _sessionUtils.sessionsArraysEqual)(prevSessions, sessionsToProcess) ? prevSessions : sessionsToProcess;
190
+ });
191
+ }, [activeSessionId, storage, saveSessionIds]);
136
192
 
137
- // Clear all storage - defined before initAuth to avoid dependency issues
193
+ // Token ready state - start optimistically so children render immediately
194
+ const [tokenReady, setTokenReady] = (0, _react.useState)(true);
195
+
196
+ // Clear all storage
138
197
  const clearAllStorage = (0, _react.useCallback)(async () => {
139
198
  if (!storage) return;
140
199
  try {
141
200
  await storage.removeItem(keys.activeSessionId);
201
+ await storage.removeItem(keys.sessionIds);
142
202
  } catch (err) {
143
- console.error('Clear storage error:', err);
203
+ if (__DEV__) {
204
+ console.error('Clear storage error:', err);
205
+ }
206
+ onError?.({
207
+ message: 'Failed to clear storage',
208
+ code: 'STORAGE_ERROR',
209
+ status: 500
210
+ });
144
211
  }
145
- }, [storage, keys]);
212
+ }, [storage, keys, onError]);
146
213
 
147
214
  // Initialize storage
148
215
  (0, _react.useEffect)(() => {
@@ -151,34 +218,80 @@ const OxyProvider = ({
151
218
  const platformStorage = await getStorage();
152
219
  setStorage(platformStorage);
153
220
  } catch (error) {
154
- console.error('Init storage failed', error);
221
+ const errorMessage = error instanceof Error ? error.message : 'Failed to initialize storage';
155
222
  _authStore.useAuthStore.setState({
156
- error: 'Failed to initialize storage'
223
+ error: errorMessage
224
+ });
225
+ onError?.({
226
+ message: errorMessage,
227
+ code: 'STORAGE_INIT_ERROR',
228
+ status: 500
157
229
  });
158
230
  }
159
231
  };
160
232
  initStorage();
161
- }, []);
233
+ }, [onError]);
162
234
 
163
235
  // Initialize authentication state
236
+ // Note: We don't set isLoading during initialization to avoid showing spinners
237
+ // Children render immediately and can check isTokenReady/isAuthenticated themselves
164
238
  (0, _react.useEffect)(() => {
165
239
  const initAuth = async () => {
166
240
  if (!storage) return;
167
- _authStore.useAuthStore.setState({
168
- isLoading: true
169
- });
241
+ // Don't set isLoading during initialization - let it happen in background
170
242
  try {
171
- setTokenReady(false);
172
-
173
243
  // Load saved language preference
174
244
  const savedLanguageRaw = await storage.getItem(keys.language);
175
- const savedLanguage = normalizeLanguageCode(savedLanguageRaw) || savedLanguageRaw;
245
+ const savedLanguage = (0, _languageUtils.normalizeLanguageCode)(savedLanguageRaw) || savedLanguageRaw;
176
246
  if (savedLanguage) {
177
247
  setCurrentLanguage(savedLanguage);
178
248
  }
179
249
 
250
+ // Load all stored session IDs and validate them
251
+ const storedSessionIdsJson = await storage.getItem(keys.sessionIds);
252
+ const storedSessionIds = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
253
+
180
254
  // Try to restore active session from storage
181
255
  const storedActiveSessionId = await storage.getItem(keys.activeSessionId);
256
+ const validSessions = [];
257
+
258
+ // If we have stored session IDs, validate them (even without active session)
259
+ if (storedSessionIds.length > 0) {
260
+ if (__DEV__) {
261
+ console.log('Loading stored sessions on init:', storedSessionIds.length);
262
+ }
263
+
264
+ // Validate each stored session ID and build session list
265
+ for (const sessionId of storedSessionIds) {
266
+ try {
267
+ const validation = await oxyServices.validateSession(sessionId, {
268
+ useHeaderValidation: true
269
+ });
270
+ if (validation.valid && validation.user) {
271
+ validSessions.push({
272
+ sessionId,
273
+ userId: validation.user.id?.toString() || '',
274
+ deviceId: '',
275
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
276
+ lastActive: new Date().toISOString(),
277
+ isCurrent: sessionId === storedActiveSessionId
278
+ });
279
+ }
280
+ } catch (e) {
281
+ // Session invalid, skip it
282
+ if (__DEV__) {
283
+ console.warn('Session validation failed for:', sessionId, e);
284
+ }
285
+ }
286
+ }
287
+
288
+ // Update sessions list with validated sessions (even if no active session)
289
+ if (validSessions.length > 0) {
290
+ updateSessions(validSessions, false);
291
+ }
292
+ }
293
+
294
+ // If we have an active session, authenticate with it
182
295
  if (storedActiveSessionId) {
183
296
  try {
184
297
  const validation = await oxyServices.validateSession(storedActiveSessionId, {
@@ -194,86 +307,66 @@ const OxyProvider = ({
194
307
  username: fullUser.username,
195
308
  avatar: fullUser.avatar
196
309
  });
197
- // Apply server language if present
198
- if (fullUser?.language) {
199
- try {
200
- const serverLang = normalizeLanguageCode(fullUser.language) || fullUser.language;
201
- await storage.setItem(keys.language, serverLang);
202
- setCurrentLanguage(serverLang);
203
- } catch (e) {
204
- console.warn('Failed to apply server language preference', e);
310
+ await applyLanguagePreference(fullUser);
311
+ try {
312
+ const deviceSessions = await oxyServices.getDeviceSessions(storedActiveSessionId);
313
+ const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, fullUser.id);
314
+ updateSessions(allDeviceSessions, true);
315
+ } catch (e) {
316
+ if (__DEV__) {
317
+ console.warn('Failed to get device sessions on init, falling back to user sessions:', e);
205
318
  }
319
+ const serverSessions = await oxyServices.getSessionsBySessionId(storedActiveSessionId);
320
+ updateSessions(mapSessionsToClient(serverSessions, undefined, fullUser.id), false);
206
321
  }
207
- const serverSessions = await oxyServices.getSessionsBySessionId(storedActiveSessionId);
208
- const clientSessions = serverSessions.map(s => ({
209
- sessionId: s.sessionId,
210
- deviceId: s.deviceId,
211
- expiresAt: s.expiresAt || new Date().toISOString(),
212
- lastActive: s.lastActive || new Date().toISOString(),
213
- userId: s.userId || fullUser.id
214
- }));
215
- setSessions(clientSessions);
216
322
  onAuthStateChange?.(fullUser);
217
323
  } else {
218
- await clearAllStorage();
324
+ // Active session invalid, remove it but keep other sessions
325
+ await storage.removeItem(keys.activeSessionId);
326
+ // Update session list to remove invalid active session
327
+ updateSessions(validSessions.filter(s => s.sessionId !== storedActiveSessionId), false);
219
328
  }
220
329
  } catch (e) {
221
- console.error('Session validation error', e);
222
- await clearAllStorage();
330
+ if (__DEV__) {
331
+ console.error('Active session validation error', e);
332
+ }
333
+ // Remove invalid active session but keep other sessions
334
+ await storage.removeItem(keys.activeSessionId);
335
+ updateSessions(validSessions.filter(s => s.sessionId !== storedActiveSessionId), false);
223
336
  }
224
337
  }
225
338
  setTokenReady(true);
226
339
  } catch (e) {
227
- console.error('Auth init error', e);
340
+ if (__DEV__) {
341
+ console.error('Auth init error', e);
342
+ }
228
343
  await clearAllStorage();
229
- } finally {
230
- _authStore.useAuthStore.setState({
231
- isLoading: false
232
- });
344
+ setTokenReady(true);
233
345
  }
234
346
  };
235
347
  initAuth();
236
- }, [storage, oxyServices, keys, onAuthStateChange, loginSuccess, clearAllStorage]);
237
-
238
- // Remove invalid session - refresh sessions from backend
239
- const removeInvalidSession = (0, _react.useCallback)(async sessionId => {
240
- // Remove from local state
241
- const filteredSessions = sessions.filter(s => s.sessionId !== sessionId);
242
- setSessions(filteredSessions);
243
-
244
- // If there are other sessions, switch to the first one
245
- if (filteredSessions.length > 0) {
246
- await switchToSession(filteredSessions[0].sessionId);
247
- } else {
248
- // No valid sessions left
249
- setActiveSessionId(null);
250
- logoutStore();
251
- setMinimalUser(null);
252
- await storage?.removeItem(keys.activeSessionId);
253
- if (onAuthStateChange) {
254
- onAuthStateChange(null);
255
- }
256
- }
257
- }, [sessions, storage, keys, onAuthStateChange, logoutStore]);
348
+ }, [storage, oxyServices, keys, onAuthStateChange, loginSuccess, clearAllStorage, applyLanguagePreference, mapSessionsToClient, updateSessions]);
258
349
 
259
350
  // Save active session ID to storage (only session ID, no user data)
260
351
  const saveActiveSessionId = (0, _react.useCallback)(async sessionId => {
261
352
  if (!storage) return;
262
353
  await storage.setItem(keys.activeSessionId, sessionId);
263
354
  }, [storage, keys.activeSessionId]);
264
-
265
- // Switch to a different session
266
355
  const switchToSession = (0, _react.useCallback)(async sessionId => {
267
356
  try {
268
- _authStore.useAuthStore.setState({
269
- isLoading: true
357
+ const validation = await oxyServices.validateSession(sessionId, {
358
+ useHeaderValidation: true
270
359
  });
271
-
272
- // Get access token for this session
360
+ if (!validation.valid) {
361
+ updateSessions(sessions.filter(s => s.sessionId !== sessionId), false);
362
+ throw new Error('Session is invalid or expired');
363
+ }
364
+ if (!validation.user) {
365
+ throw new Error('User data not available from session validation');
366
+ }
367
+ const fullUser = validation.user;
273
368
  await oxyServices.getTokenBySession(sessionId);
274
-
275
- // Load full user data
276
- const fullUser = await oxyServices.getUserBySession(sessionId);
369
+ setTokenReady(true);
277
370
  setActiveSessionId(sessionId);
278
371
  loginSuccess(fullUser);
279
372
  setMinimalUser({
@@ -282,32 +375,52 @@ const OxyProvider = ({
282
375
  avatar: fullUser.avatar
283
376
  });
284
377
  await saveActiveSessionId(sessionId);
285
- // Apply server language if present
286
- if (fullUser?.language) {
287
- try {
288
- const serverLang = normalizeLanguageCode(fullUser.language) || fullUser.language;
289
- await storage?.setItem(keys.language, serverLang);
290
- setCurrentLanguage(serverLang);
291
- } catch (e) {
292
- console.warn('Failed to apply server language after switch', e);
378
+ await applyLanguagePreference(fullUser);
379
+ oxyServices.getDeviceSessions(sessionId).then(deviceSessions => {
380
+ const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, fullUser.id);
381
+ updateSessions(allDeviceSessions, true);
382
+ }).catch(error => {
383
+ if (__DEV__) console.warn('Failed to get device sessions after switch:', error);
384
+ });
385
+ onAuthStateChange?.(fullUser);
386
+ } catch (error) {
387
+ const isInvalidSession = error?.response?.status === 401 || error?.message?.includes('Invalid or expired session') || error?.message?.includes('Session is invalid');
388
+ if (isInvalidSession) {
389
+ updateSessions(sessions.filter(s => s.sessionId !== sessionId), false);
390
+ if (sessionId === activeSessionId && sessions.length > 1) {
391
+ const otherSessions = sessions.filter(s => s.sessionId !== sessionId);
392
+ for (const otherSession of otherSessions) {
393
+ try {
394
+ const otherValidation = await oxyServices.validateSession(otherSession.sessionId, {
395
+ useHeaderValidation: true
396
+ });
397
+ if (otherValidation.valid) {
398
+ await switchToSession(otherSession.sessionId);
399
+ return;
400
+ }
401
+ } catch {
402
+ // Continue to next session
403
+ continue;
404
+ }
405
+ }
293
406
  }
294
407
  }
295
- if (onAuthStateChange) {
296
- onAuthStateChange(fullUser);
408
+ const errorMessage = error instanceof Error ? error.message : 'Failed to switch session';
409
+ if (__DEV__) {
410
+ console.error('Switch session error:', error);
297
411
  }
298
- } catch (error) {
299
- console.error('Switch session error:', error);
300
412
  _authStore.useAuthStore.setState({
301
- error: 'Failed to switch session'
413
+ error: errorMessage
302
414
  });
303
- } finally {
304
- _authStore.useAuthStore.setState({
305
- isLoading: false
415
+ onError?.({
416
+ message: errorMessage,
417
+ code: isInvalidSession ? 'INVALID_SESSION' : 'SESSION_SWITCH_ERROR',
418
+ status: isInvalidSession ? 401 : 500
306
419
  });
420
+ setTokenReady(false);
421
+ throw error; // Re-throw so calling code can handle it
307
422
  }
308
- }, [oxyServices, onAuthStateChange, loginSuccess, saveActiveSessionId]);
309
-
310
- // Login method - only store session ID, retrieve data from backend
423
+ }, [oxyServices, onAuthStateChange, loginSuccess, saveActiveSessionId, applyLanguagePreference, mapSessionsToClient, onError, activeSessionId, sessions]);
311
424
  const login = (0, _react.useCallback)(async (username, password, deviceName) => {
312
425
  if (!storage) throw new Error('Storage not initialized');
313
426
  _authStore.useAuthStore.setState({
@@ -315,59 +428,71 @@ const OxyProvider = ({
315
428
  error: null
316
429
  });
317
430
  try {
318
- // Get device fingerprint for enhanced device identification
319
431
  const deviceFingerprint = _deviceManager.DeviceManager.getDeviceFingerprint();
320
-
321
- // Get or generate persistent device info
322
432
  const deviceInfo = await _deviceManager.DeviceManager.getDeviceInfo();
323
- console.log('Auth - Using device fingerprint:', deviceFingerprint);
324
- console.log('Auth - Using device ID:', deviceInfo.deviceId);
325
433
  const response = await oxyServices.signIn(username, password, deviceName || deviceInfo.deviceName || _deviceManager.DeviceManager.getDefaultDeviceName(), deviceFingerprint);
326
434
 
327
435
  // Handle MFA requirement
328
- if (response && response.mfaRequired) {
329
- const err = new Error('Multi-factor authentication required');
330
- err.code = 'MFA_REQUIRED';
331
- err.mfaToken = response.mfaToken;
332
- err.expiresAt = response.expiresAt;
333
- throw err;
436
+ if (response && 'mfaRequired' in response && response.mfaRequired) {
437
+ const mfaError = new Error('Multi-factor authentication required');
438
+ mfaError.code = 'MFA_REQUIRED';
439
+ mfaError.mfaToken = response.mfaToken;
440
+ mfaError.expiresAt = response.expiresAt;
441
+ throw mfaError;
334
442
  }
335
-
336
- // Set as active session (only store session ID)
337
- setActiveSessionId(response.sessionId);
338
- await saveActiveSessionId(response.sessionId);
339
-
340
- // Get access token for API calls
341
- await oxyServices.getTokenBySession(response.sessionId);
342
-
343
- // Load full user data from backend
344
- const fullUser = await oxyServices.getUserBySession(response.sessionId);
345
- loginSuccess(fullUser);
346
- setMinimalUser(response.user);
347
-
348
- // Load sessions from backend
349
- const serverSessions = await oxyServices.getSessionsBySessionId(response.sessionId);
350
- const clientSessions = serverSessions.map(serverSession => ({
351
- sessionId: serverSession.sessionId,
352
- deviceId: serverSession.deviceId,
353
- expiresAt: serverSession.expiresAt || new Date().toISOString(),
354
- lastActive: serverSession.lastActive || new Date().toISOString(),
355
- userId: serverSession.userId || fullUser.id
356
- }));
357
- setSessions(clientSessions);
358
- if (onAuthStateChange) {
359
- onAuthStateChange(fullUser);
443
+ const sessionResponse = response;
444
+ await oxyServices.getTokenBySession(sessionResponse.sessionId);
445
+ const fullUser = await oxyServices.getUserBySession(sessionResponse.sessionId);
446
+ let allDeviceSessions = [];
447
+ try {
448
+ const deviceSessions = await oxyServices.getDeviceSessions(sessionResponse.sessionId);
449
+ allDeviceSessions = mapSessionsToClient(deviceSessions, sessionResponse.deviceId, fullUser.id);
450
+ } catch (error) {
451
+ if (__DEV__) {
452
+ console.warn('Failed to get device sessions, falling back to user sessions:', error);
453
+ }
454
+ const serverSessions = await oxyServices.getSessionsBySessionId(sessionResponse.sessionId);
455
+ allDeviceSessions = mapSessionsToClient(serverSessions, undefined, fullUser.id);
360
456
  }
457
+ const userUserId = fullUser.id?.toString();
458
+ const existingSession = allDeviceSessions.find(s => s.userId?.toString() === userUserId && s.sessionId !== sessionResponse.sessionId);
459
+ if (existingSession) {
460
+ try {
461
+ await oxyServices.logoutSession(sessionResponse.sessionId, sessionResponse.sessionId);
462
+ } catch (logoutError) {
463
+ if (__DEV__) {
464
+ console.warn('Failed to logout duplicate session:', logoutError);
465
+ }
466
+ }
467
+ await switchToSession(existingSession.sessionId);
468
+ loginSuccess(fullUser);
469
+ setMinimalUser(sessionResponse.user);
470
+ updateSessions(allDeviceSessions.filter(s => s.sessionId !== sessionResponse.sessionId), false);
471
+ onAuthStateChange?.(fullUser);
472
+ return fullUser;
473
+ }
474
+ setActiveSessionId(sessionResponse.sessionId);
475
+ await saveActiveSessionId(sessionResponse.sessionId);
476
+ loginSuccess(fullUser);
477
+ setMinimalUser(sessionResponse.user);
478
+ updateSessions(allDeviceSessions, true);
479
+ onAuthStateChange?.(fullUser);
361
480
  return fullUser;
362
481
  } catch (error) {
363
- loginFailure(error.message || 'Login failed');
482
+ const errorMessage = error instanceof Error ? error.message : 'Login failed';
483
+ loginFailure(errorMessage);
484
+ onError?.({
485
+ message: errorMessage,
486
+ code: 'LOGIN_ERROR',
487
+ status: 401
488
+ });
364
489
  throw error;
365
490
  } finally {
366
491
  _authStore.useAuthStore.setState({
367
492
  isLoading: false
368
493
  });
369
494
  }
370
- }, [storage, oxyServices, saveActiveSessionId, loginSuccess, setMinimalUser, onAuthStateChange, loginFailure]);
495
+ }, [storage, oxyServices, saveActiveSessionId, loginSuccess, onAuthStateChange, loginFailure, mapSessionsToClient, onError, sessions, switchToSession]);
371
496
 
372
497
  // Logout method
373
498
  const logout = (0, _react.useCallback)(async targetSessionId => {
@@ -375,18 +500,12 @@ const OxyProvider = ({
375
500
  try {
376
501
  const sessionToLogout = targetSessionId || activeSessionId;
377
502
  await oxyServices.logoutSession(activeSessionId, sessionToLogout);
378
-
379
- // Remove session from local state
380
503
  const filteredSessions = sessions.filter(s => s.sessionId !== sessionToLogout);
381
- setSessions(filteredSessions);
382
-
383
- // If logging out active session
504
+ updateSessions(filteredSessions, false);
384
505
  if (sessionToLogout === activeSessionId) {
385
506
  if (filteredSessions.length > 0) {
386
- // Switch to another session
387
507
  await switchToSession(filteredSessions[0].sessionId);
388
508
  } else {
389
- // No sessions left
390
509
  setActiveSessionId(null);
391
510
  logoutStore();
392
511
  setMinimalUser(null);
@@ -397,74 +516,57 @@ const OxyProvider = ({
397
516
  }
398
517
  }
399
518
  } catch (error) {
400
- console.error('Logout error:', error);
519
+ const errorMessage = error instanceof Error ? error.message : 'Logout failed';
520
+ if (__DEV__) {
521
+ console.error('Logout error:', error);
522
+ }
401
523
  _authStore.useAuthStore.setState({
402
- error: 'Logout failed'
524
+ error: errorMessage
525
+ });
526
+ onError?.({
527
+ message: errorMessage,
528
+ code: 'LOGOUT_ERROR',
529
+ status: 500
403
530
  });
404
531
  }
405
- }, [activeSessionId, oxyServices, sessions, switchToSession, logoutStore, setMinimalUser, storage, keys.activeSessionId, onAuthStateChange]);
406
-
407
- // Logout all sessions
532
+ }, [activeSessionId, oxyServices, sessions, switchToSession, logoutStore, storage, keys.activeSessionId, onAuthStateChange, onError]);
408
533
  const logoutAll = (0, _react.useCallback)(async () => {
409
- console.log('logoutAll called with activeSessionId:', activeSessionId);
410
534
  if (!activeSessionId) {
411
- console.error('No active session ID found, cannot logout all');
535
+ const error = new Error('No active session found');
412
536
  _authStore.useAuthStore.setState({
413
- error: 'No active session found'
537
+ error: error.message
414
538
  });
415
- throw new Error('No active session found');
416
- }
417
- if (!oxyServices) {
418
- console.error('OxyServices not initialized');
419
- _authStore.useAuthStore.setState({
420
- error: 'Service not available'
539
+ onError?.({
540
+ message: error.message,
541
+ code: 'NO_SESSION_ERROR',
542
+ status: 404
421
543
  });
422
- throw new Error('Service not available');
544
+ throw error;
423
545
  }
424
546
  try {
425
- console.log('Calling oxyServices.logoutAllSessions with sessionId:', activeSessionId);
426
547
  await oxyServices.logoutAllSessions(activeSessionId);
427
- console.log('logoutAllSessions completed successfully');
428
-
429
- // Clear all local data
430
- setSessions([]);
548
+ updateSessions([], false);
431
549
  setActiveSessionId(null);
432
550
  logoutStore();
433
551
  setMinimalUser(null);
434
552
  await clearAllStorage();
435
- console.log('Local storage cleared');
436
- if (onAuthStateChange) {
437
- onAuthStateChange(null);
438
- console.log('Auth state change callback called');
439
- }
553
+ onAuthStateChange?.(null);
440
554
  } catch (error) {
441
- console.error('Logout all error:', error);
555
+ const errorMessage = error instanceof Error ? error.message : 'Logout all failed';
442
556
  _authStore.useAuthStore.setState({
443
- error: `Logout all failed: ${error instanceof Error ? error.message : 'Unknown error'}`
557
+ error: errorMessage
558
+ });
559
+ onError?.({
560
+ message: errorMessage,
561
+ code: 'LOGOUT_ALL_ERROR',
562
+ status: 500
444
563
  });
445
564
  throw error;
446
565
  }
447
- }, [activeSessionId, oxyServices, logoutStore, setMinimalUser, clearAllStorage, onAuthStateChange]);
566
+ }, [activeSessionId, oxyServices, logoutStore, clearAllStorage, onAuthStateChange, onError]);
448
567
 
449
- // Effect to restore token on app load or session switch
450
- (0, _react.useEffect)(() => {
451
- const restoreToken = async () => {
452
- if (activeSessionId && oxyServices) {
453
- try {
454
- await oxyServices.getTokenBySession(activeSessionId);
455
- setTokenReady(true);
456
- } catch (err) {
457
- // If token restoration fails, force logout
458
- await logout();
459
- setTokenReady(false);
460
- }
461
- } else {
462
- setTokenReady(true); // No session, so token is not needed
463
- }
464
- };
465
- restoreToken();
466
- // Only run when activeSessionId or oxyServices changes
467
- }, [activeSessionId, oxyServices, logout]);
568
+ // Token restoration is handled in initAuth and switchToSession
569
+ // No separate effect needed - children render immediately with isTokenReady available
468
570
 
469
571
  // Sign up method
470
572
  const signUp = (0, _react.useCallback)(async (username, email, password) => {
@@ -474,23 +576,24 @@ const OxyProvider = ({
474
576
  error: null
475
577
  });
476
578
  try {
477
- // Create new account using the OxyServices signUp method
478
- const response = await oxyServices.signUp(username, email, password);
479
- console.log('SignUp successful:', response);
480
-
481
- // Now log the user in to create a session
482
- // This will handle the session creation and device registration
579
+ await oxyServices.signUp(username, email, password);
483
580
  const user = await login(username, password);
484
581
  return user;
485
582
  } catch (error) {
486
- loginFailure(error.message || 'Sign up failed');
583
+ const errorMessage = error instanceof Error ? error.message : 'Sign up failed';
584
+ loginFailure(errorMessage);
585
+ onError?.({
586
+ message: errorMessage,
587
+ code: 'SIGNUP_ERROR',
588
+ status: 400
589
+ });
487
590
  throw error;
488
591
  } finally {
489
592
  _authStore.useAuthStore.setState({
490
593
  isLoading: false
491
594
  });
492
595
  }
493
- }, [storage, oxyServices, login, loginFailure]);
596
+ }, [storage, oxyServices, login, loginFailure, onError]);
494
597
 
495
598
  // Complete MFA login by verifying TOTP
496
599
  const completeMfaLogin = (0, _react.useCallback)(async (mfaToken, code) => {
@@ -515,116 +618,106 @@ const OxyProvider = ({
515
618
  username: fullUser.username,
516
619
  avatar: fullUser.avatar
517
620
  });
518
- // Apply server language if present
519
- if (fullUser?.language) {
520
- try {
521
- const serverLang = normalizeLanguageCode(fullUser.language) || fullUser.language;
522
- await storage.setItem(keys.language, serverLang);
523
- setCurrentLanguage(serverLang);
524
- } catch (e) {
525
- console.warn('Failed to apply server language on MFA login', e);
526
- }
527
- }
621
+ await applyLanguagePreference(fullUser);
528
622
 
529
- // Load sessions list
530
- const serverSessions = await oxyServices.getSessionsBySessionId(response.sessionId);
531
- const clientSessions = serverSessions.map(s => ({
532
- sessionId: s.sessionId,
533
- deviceId: s.deviceId,
534
- expiresAt: s.expiresAt || new Date().toISOString(),
535
- lastActive: s.lastActive || new Date().toISOString(),
536
- userId: s.userId || fullUser.id
537
- }));
538
- setSessions(clientSessions);
539
- // Apply server language if present
540
- if (fullUser?.language) {
541
- try {
542
- await storage.setItem(keys.language, fullUser.language);
543
- setCurrentLanguage(fullUser.language);
544
- } catch (e) {
545
- console.warn('Failed to apply server language on MFA login', e);
623
+ // Get all device sessions to support multiple accounts
624
+ try {
625
+ const deviceSessions = await oxyServices.getDeviceSessions(response.sessionId);
626
+ const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, fullUser.id);
627
+ updateSessions(allDeviceSessions, true);
628
+ } catch (error) {
629
+ // Fallback to user sessions if device sessions fail
630
+ if (__DEV__) {
631
+ console.warn('Failed to get device sessions for MFA, falling back to user sessions:', error);
546
632
  }
633
+ const serverSessions = await oxyServices.getSessionsBySessionId(response.sessionId);
634
+ const userSessions = mapSessionsToClient(serverSessions, undefined, fullUser.id);
635
+ updateSessions(userSessions, true);
547
636
  }
548
- if (onAuthStateChange) onAuthStateChange(fullUser);
637
+ onAuthStateChange?.(fullUser);
549
638
  return fullUser;
550
639
  } catch (error) {
551
- loginFailure(error.message || 'MFA verification failed');
640
+ const errorMessage = error instanceof Error ? error.message : 'MFA verification failed';
641
+ loginFailure(errorMessage);
642
+ onError?.({
643
+ message: errorMessage,
644
+ code: 'MFA_ERROR',
645
+ status: 401
646
+ });
552
647
  throw error;
553
648
  } finally {
554
649
  _authStore.useAuthStore.setState({
555
650
  isLoading: false
556
651
  });
557
652
  }
558
- }, [storage, oxyServices, loginSuccess, loginFailure, saveActiveSessionId, onAuthStateChange]);
559
-
560
- // Switch session method
653
+ }, [storage, oxyServices, loginSuccess, loginFailure, saveActiveSessionId, onAuthStateChange, applyLanguagePreference, onError]);
561
654
  const switchSession = (0, _react.useCallback)(async sessionId => {
562
655
  await switchToSession(sessionId);
563
656
  }, [switchToSession]);
564
-
565
- // Remove session method
566
657
  const removeSession = (0, _react.useCallback)(async sessionId => {
567
658
  await logout(sessionId);
568
659
  }, [logout]);
569
-
570
- // Refresh sessions method
571
660
  const refreshSessions = (0, _react.useCallback)(async () => {
572
- console.log('refreshSessions called with activeSessionId:', activeSessionId);
573
- if (!activeSessionId) {
574
- console.log('refreshSessions: No activeSessionId, returning');
575
- return;
661
+ if (!activeSessionId) return;
662
+
663
+ // If a refresh is already in progress, return the existing promise
664
+ if (refreshInFlightRef.current) {
665
+ return refreshInFlightRef.current;
576
666
  }
577
- try {
578
- console.log('refreshSessions: Calling getSessionsBySessionId...');
579
- const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
580
- console.log('refreshSessions: Server sessions received:', serverSessions);
581
-
582
- // Update local sessions with server data
583
- const updatedSessions = serverSessions.map(serverSession => ({
584
- sessionId: serverSession.sessionId,
585
- deviceId: serverSession.deviceId,
586
- expiresAt: serverSession.expiresAt || new Date().toISOString(),
587
- lastActive: serverSession.lastActive || new Date().toISOString(),
588
- userId: serverSession.userId || user?.id
589
- }));
590
- console.log('refreshSessions: Updated sessions:', updatedSessions);
591
- setSessions(updatedSessions);
592
- console.log('refreshSessions: Sessions updated in state');
593
- } catch (error) {
594
- console.error('Refresh sessions error:', error);
595
667
 
596
- // If the current session is invalid, try to find another valid session
597
- if (sessions.length > 1) {
598
- console.log('Current session invalid, trying to switch to another session...');
599
- const otherSessions = sessions.filter(s => s.sessionId !== activeSessionId);
600
- for (const session of otherSessions) {
601
- try {
602
- // Try to validate this session
603
- await oxyServices.validateSession(session.sessionId, {
604
- useHeaderValidation: true
605
- });
606
- console.log('Found valid session, switching to:', session.sessionId);
607
- await switchToSession(session.sessionId);
608
- return; // Successfully switched to another session
609
- } catch (sessionError) {
610
- console.log('Session validation failed for:', session.sessionId, sessionError);
611
- continue; // Try next session
612
- }
668
+ // Create the refresh promise
669
+ const refreshPromise = (async () => {
670
+ try {
671
+ const deviceSessions = await oxyServices.getDeviceSessions(activeSessionId);
672
+ const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, user?.id);
673
+ updateSessions(allDeviceSessions, true);
674
+ } catch (error) {
675
+ if (__DEV__) {
676
+ console.warn('Failed to refresh device sessions, falling back to user sessions:', error);
613
677
  }
614
- }
678
+ try {
679
+ const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
680
+ const userSessions = mapSessionsToClient(serverSessions, undefined, user?.id);
681
+ updateSessions(userSessions, true);
682
+ } catch (fallbackError) {
683
+ if (__DEV__) {
684
+ console.error('Refresh sessions error:', fallbackError);
685
+ }
615
686
 
616
- // If no valid sessions found, clear all sessions
617
- console.log('No valid sessions found, clearing all sessions');
618
- setSessions([]);
619
- setActiveSessionId(null);
620
- logoutStore();
621
- setMinimalUser(null);
622
- await clearAllStorage();
623
- if (onAuthStateChange) {
624
- onAuthStateChange(null);
687
+ // If the current session is invalid, try to find another valid session
688
+ if (sessions.length > 1) {
689
+ const otherSessions = sessions.filter(s => s.sessionId !== activeSessionId);
690
+ for (const session of otherSessions) {
691
+ try {
692
+ const validation = await oxyServices.validateSession(session.sessionId, {
693
+ useHeaderValidation: true
694
+ });
695
+ if (validation.valid) {
696
+ await switchToSession(session.sessionId);
697
+ return;
698
+ }
699
+ } catch {
700
+ continue;
701
+ }
702
+ }
703
+ }
704
+
705
+ // No valid sessions found, clear all
706
+ updateSessions([], false);
707
+ setActiveSessionId(null);
708
+ logoutStore();
709
+ setMinimalUser(null);
710
+ await clearAllStorage();
711
+ onAuthStateChange?.(null);
712
+ }
713
+ } finally {
714
+ // Clear the in-flight ref when done
715
+ refreshInFlightRef.current = null;
625
716
  }
626
- }
627
- }, [activeSessionId, oxyServices, user?.id, sessions, switchToSession, logoutStore, setMinimalUser, clearAllStorage, onAuthStateChange]);
717
+ })();
718
+ refreshInFlightRef.current = refreshPromise;
719
+ return refreshPromise;
720
+ }, [activeSessionId, oxyServices, user?.id, updateSessions, sessions, switchToSession, logoutStore, clearAllStorage, onAuthStateChange, mapSessionsToClient]);
628
721
 
629
722
  // Device management methods
630
723
  const getDeviceSessions = (0, _react.useCallback)(async () => {
@@ -632,58 +725,67 @@ const OxyProvider = ({
632
725
  try {
633
726
  return await oxyServices.getDeviceSessions(activeSessionId);
634
727
  } catch (error) {
635
- console.error('Get device sessions error:', error);
728
+ const errorMessage = error instanceof Error ? error.message : 'Failed to get device sessions';
729
+ onError?.({
730
+ message: errorMessage,
731
+ code: 'GET_DEVICE_SESSIONS_ERROR',
732
+ status: 500
733
+ });
636
734
  throw error;
637
735
  }
638
- }, [activeSessionId, oxyServices]);
736
+ }, [activeSessionId, oxyServices, onError]);
639
737
  const logoutAllDeviceSessions = (0, _react.useCallback)(async () => {
640
738
  if (!activeSessionId) throw new Error('No active session');
641
739
  try {
642
740
  await oxyServices.logoutAllDeviceSessions(activeSessionId);
643
-
644
- // Clear all local sessions since we logged out from all devices
645
- setSessions([]);
741
+ updateSessions([], false);
646
742
  setActiveSessionId(null);
647
743
  logoutStore();
648
744
  setMinimalUser(null);
649
745
  await clearAllStorage();
650
- if (onAuthStateChange) {
651
- onAuthStateChange(null);
652
- }
746
+ onAuthStateChange?.(null);
653
747
  } catch (error) {
654
- console.error('Logout all device sessions error:', error);
748
+ const errorMessage = error instanceof Error ? error.message : 'Failed to logout all device sessions';
749
+ onError?.({
750
+ message: errorMessage,
751
+ code: 'LOGOUT_ALL_DEVICES_ERROR',
752
+ status: 500
753
+ });
655
754
  throw error;
656
755
  }
657
- }, [activeSessionId, oxyServices, logoutStore, setMinimalUser, clearAllStorage, onAuthStateChange]);
756
+ }, [activeSessionId, oxyServices, logoutStore, clearAllStorage, onAuthStateChange, onError]);
658
757
  const updateDeviceName = (0, _react.useCallback)(async deviceName => {
659
758
  if (!activeSessionId) throw new Error('No active session');
660
759
  try {
661
760
  await oxyServices.updateDeviceName(activeSessionId, deviceName);
662
-
663
- // Update local device info
664
761
  await _deviceManager.DeviceManager.updateDeviceName(deviceName);
665
762
  } catch (error) {
666
- console.error('Update device name error:', error);
763
+ const errorMessage = error instanceof Error ? error.message : 'Failed to update device name';
764
+ onError?.({
765
+ message: errorMessage,
766
+ code: 'UPDATE_DEVICE_NAME_ERROR',
767
+ status: 500
768
+ });
667
769
  throw error;
668
770
  }
669
- }, [activeSessionId, oxyServices]);
771
+ }, [activeSessionId, oxyServices, onError]);
670
772
 
671
773
  // Language management method
672
774
  const setLanguage = (0, _react.useCallback)(async languageId => {
673
775
  if (!storage) throw new Error('Storage not initialized');
674
776
  try {
675
- // Save language preference
676
777
  await storage.setItem(keys.language, languageId);
677
778
  setCurrentLanguage(languageId);
678
- console.log(`Language changed to ${languageId}`);
679
-
680
- // TODO: Here you can add any additional logic needed for app-wide language updates
681
- // such as updating i18n configuration, refreshing translations, etc.
682
779
  } catch (error) {
683
- console.error('Error saving language preference:', error);
780
+ const errorMessage = error instanceof Error ? error.message : 'Failed to save language preference';
781
+ onError?.({
782
+ message: errorMessage,
783
+ code: 'LANGUAGE_SAVE_ERROR',
784
+ status: 500
785
+ });
684
786
  throw error;
685
787
  }
686
- }, [storage, keys.language]);
788
+ }, [storage, keys.language, onError]);
687
789
 
688
790
  // Bottom sheet control methods
689
791
  const showBottomSheet = (0, _react.useCallback)(screenOrConfig => {
@@ -698,9 +800,8 @@ const OxyProvider = ({
698
800
  } else if (bottomSheetRef.current.present) {
699
801
  if (__DEV__) console.log('Presenting bottom sheet');
700
802
  bottomSheetRef.current.present();
701
- } else {
803
+ } else if (__DEV__) {
702
804
  console.warn('No expand or present method available on bottomSheetRef');
703
- if (__DEV__) console.log('Available methods on bottomSheetRef.current:', Object.keys(bottomSheetRef.current));
704
805
  }
705
806
 
706
807
  // Then navigate to the specified screen if provided
@@ -718,10 +819,8 @@ const OxyProvider = ({
718
819
  }
719
820
  }, 100);
720
821
  }
721
- } else {
722
- console.warn('bottomSheetRef is not available');
723
- console.warn('To fix this, ensure you pass a bottomSheetRef to OxyProvider:');
724
- console.warn('<OxyProvider baseURL="..." bottomSheetRef={yourBottomSheetRef}>');
822
+ } else if (__DEV__) {
823
+ console.warn('bottomSheetRef is not available. Pass a bottomSheetRef to OxyProvider.');
725
824
  }
726
825
  }, [bottomSheetRef]);
727
826
  const hideBottomSheet = (0, _react.useCallback)(() => {
@@ -754,41 +853,22 @@ const OxyProvider = ({
754
853
  if (mod && typeof mod.useFollow === 'function') {
755
854
  return mod.useFollow(userId);
756
855
  }
757
- console.warn('useFollow module did not export a function as expected');
758
- return {
759
- isFollowing: false,
760
- isLoading: false,
761
- error: null,
762
- toggleFollow: async () => {},
763
- setFollowStatus: () => {},
764
- fetchStatus: async () => {},
765
- clearError: () => {},
766
- followerCount: null,
767
- followingCount: null,
768
- isLoadingCounts: false,
769
- fetchUserCounts: async () => {},
770
- setFollowerCount: () => {},
771
- setFollowingCount: () => {}
772
- };
856
+ if (__DEV__) {
857
+ console.warn('useFollow module did not export a function as expected');
858
+ }
859
+ return createEmptyFollowHook()(userId);
773
860
  } catch (e) {
774
- console.warn('Failed to dynamically load useFollow hook:', e);
775
- return {
776
- isFollowing: false,
777
- isLoading: false,
778
- error: null,
779
- toggleFollow: async () => {},
780
- setFollowStatus: () => {},
781
- fetchStatus: async () => {},
782
- clearError: () => {},
783
- followerCount: null,
784
- followingCount: null,
785
- isLoadingCounts: false,
786
- fetchUserCounts: async () => {},
787
- setFollowerCount: () => {},
788
- setFollowingCount: () => {}
789
- };
861
+ if (__DEV__) {
862
+ console.warn('Failed to dynamically load useFollow hook:', e);
863
+ }
864
+ return createEmptyFollowHook()(userId);
790
865
  }
791
866
  };
867
+
868
+ // Compute language metadata from currentLanguage
869
+ const languageMetadata = (0, _react.useMemo)(() => (0, _languageUtils.getLanguageMetadata)(currentLanguage), [currentLanguage]);
870
+ const languageName = (0, _react.useMemo)(() => (0, _languageUtils.getLanguageName)(currentLanguage), [currentLanguage]);
871
+ const nativeLanguageName = (0, _react.useMemo)(() => (0, _languageUtils.getNativeLanguageName)(currentLanguage), [currentLanguage]);
792
872
  const contextValue = (0, _react.useMemo)(() => ({
793
873
  user,
794
874
  minimalUser,
@@ -796,8 +876,12 @@ const OxyProvider = ({
796
876
  activeSessionId,
797
877
  isAuthenticated,
798
878
  isLoading,
879
+ isTokenReady: tokenReady,
799
880
  error,
800
881
  currentLanguage,
882
+ currentLanguageMetadata: languageMetadata,
883
+ currentLanguageName: languageName,
884
+ currentNativeLanguageName: nativeLanguageName,
801
885
  login,
802
886
  logout,
803
887
  logoutAll,
@@ -819,21 +903,9 @@ const OxyProvider = ({
819
903
  // Only depend on user ID, not the entire user object
820
904
  minimalUser?.id, sessions.length,
821
905
  // Only depend on sessions count, not the entire array
822
- activeSessionId, isAuthenticated, isLoading, error, currentLanguage, login, logout, logoutAll, signUp, completeMfaLogin, switchSession, removeSession, refreshSessions, setLanguage, getDeviceSessions, logoutAllDeviceSessions, updateDeviceName, oxyServices, bottomSheetRef, showBottomSheet, hideBottomSheet]);
823
-
824
- // Wrap children rendering to block until token is ready
825
- if (!tokenReady) {
826
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
827
- style: {
828
- flex: 1,
829
- justifyContent: 'center',
830
- alignItems: 'center'
831
- },
832
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
833
- children: "Loading authentication..."
834
- })
835
- });
836
- }
906
+ activeSessionId, isAuthenticated, isLoading, tokenReady, error, currentLanguage, languageMetadata, languageName, nativeLanguageName, login, logout, logoutAll, signUp, completeMfaLogin, switchSession, removeSession, refreshSessions, setLanguage, getDeviceSessions, logoutAllDeviceSessions, updateDeviceName, oxyServices, bottomSheetRef, showBottomSheet, hideBottomSheet]);
907
+
908
+ // Always render children - let the consuming app decide how to handle token loading state
837
909
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(OxyContext.Provider, {
838
910
  value: contextValue,
839
911
  children: children