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