@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,5 +1,6 @@
1
- import React, { useCallback, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
2
2
  import { View, Text, TouchableOpacity, StyleSheet, Platform, Animated, ScrollView } from 'react-native';
3
+ import AnimatedReanimated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
3
4
  import type { BaseScreenProps } from '../navigation/types';
4
5
  import { useOxy } from '../context/OxyContext';
5
6
  import Avatar from '../components/Avatar';
@@ -10,6 +11,41 @@ import { useThemeColors } from '../styles';
10
11
  import GroupedPillButtons from '../components/internal/GroupedPillButtons';
11
12
  import { useI18n } from '../hooks/useI18n';
12
13
 
14
+ const GAP = 12;
15
+ const INNER_GAP = 8;
16
+
17
+ // Individual animated progress dot
18
+ const AnimatedProgressDot: React.FC<{
19
+ isActive: boolean;
20
+ colors: any;
21
+ styles: any;
22
+ }> = ({ isActive, colors, styles }) => {
23
+ const width = useSharedValue(isActive ? 12 : 6);
24
+ const backgroundColor = useSharedValue(isActive ? colors.primary : colors.border);
25
+
26
+ useEffect(() => {
27
+ width.value = withTiming(isActive ? 12 : 6, { duration: 300 });
28
+ backgroundColor.value = withTiming(
29
+ isActive ? colors.primary : colors.border,
30
+ { duration: 300 }
31
+ );
32
+ }, [isActive, colors.primary, colors.border, width, backgroundColor]);
33
+
34
+ const animatedStyle = useAnimatedStyle(() => ({
35
+ width: width.value,
36
+ backgroundColor: backgroundColor.value,
37
+ }));
38
+
39
+ return (
40
+ <AnimatedReanimated.View
41
+ style={[
42
+ styles.progressDot,
43
+ animatedStyle,
44
+ ]}
45
+ />
46
+ );
47
+ };
48
+
13
49
  /**
14
50
  * Post-signup welcome & onboarding screen.
15
51
  * - Greets the newly registered user
@@ -33,8 +69,19 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
33
69
  const fadeAnim = useRef(new Animated.Value(1)).current;
34
70
  const slideAnim = useRef(new Animated.Value(0)).current;
35
71
  const [currentStep, setCurrentStep] = useState(0);
72
+ // Track avatar separately to ensure it updates immediately after selection
73
+ const [selectedAvatarId, setSelectedAvatarId] = useState<string | undefined>(currentUser?.avatar);
36
74
 
37
- const avatarUri = currentUser?.avatar ? oxyServices.getFileDownloadUrl(currentUser.avatar as string, 'thumb') : undefined;
75
+ // Update selectedAvatarId when user changes
76
+ useEffect(() => {
77
+ if (user?.avatar) {
78
+ setSelectedAvatarId(user.avatar);
79
+ } else if (newUser?.avatar) {
80
+ setSelectedAvatarId(newUser.avatar);
81
+ }
82
+ }, [user?.avatar, newUser?.avatar]);
83
+
84
+ const avatarUri = selectedAvatarId ? oxyServices.getFileDownloadUrl(selectedAvatarId, 'thumb') : undefined;
38
85
 
39
86
  // Steps content
40
87
  const welcomeTitle = currentUser?.username
@@ -64,7 +111,7 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
64
111
  const totalSteps = steps.length;
65
112
  const avatarStepIndex = steps.findIndex(s => s.showAvatar);
66
113
 
67
- const animateToStep = (next: number) => {
114
+ const animateToStepCallback = useCallback((next: number) => {
68
115
  Animated.timing(fadeAnim, { toValue: 0, duration: 180, useNativeDriver: Platform.OS !== 'web' }).start(() => {
69
116
  setCurrentStep(next);
70
117
  slideAnim.setValue(-40);
@@ -73,40 +120,59 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
73
120
  Animated.spring(slideAnim, { toValue: 0, useNativeDriver: Platform.OS !== 'web', friction: 9 })
74
121
  ]).start();
75
122
  });
76
- };
123
+ }, [fadeAnim, slideAnim]);
77
124
 
78
- const nextStep = useCallback(() => { if (currentStep < totalSteps - 1) animateToStep(currentStep + 1); }, [currentStep, totalSteps]);
79
- const prevStep = useCallback(() => { if (currentStep > 0) animateToStep(currentStep - 1); }, [currentStep]);
80
- const skipToAvatar = useCallback(() => { if (avatarStepIndex >= 0) animateToStep(avatarStepIndex); }, [avatarStepIndex]);
125
+ const nextStep = useCallback(() => { if (currentStep < totalSteps - 1) animateToStepCallback(currentStep + 1); }, [currentStep, totalSteps, animateToStepCallback]);
126
+ const prevStep = useCallback(() => { if (currentStep > 0) animateToStepCallback(currentStep - 1); }, [currentStep, animateToStepCallback]);
127
+ const skipToAvatar = useCallback(() => { if (avatarStepIndex >= 0) animateToStepCallback(avatarStepIndex); }, [avatarStepIndex, animateToStepCallback]);
81
128
  const finish = useCallback(() => { if (onAuthenticated && currentUser) onAuthenticated(currentUser); }, [onAuthenticated, currentUser]);
82
129
  const openAvatarPicker = useCallback(() => {
130
+ // Ensure we're on the avatar step before opening picker
131
+ if (avatarStepIndex >= 0 && currentStep !== avatarStepIndex) {
132
+ animateToStepCallback(avatarStepIndex);
133
+ }
134
+
83
135
  navigate('FileManagement', {
84
136
  selectMode: true,
85
137
  multiSelect: false,
86
138
  disabledMimeTypes: ['video/', 'audio/', 'application/pdf'],
87
- afterSelect: 'back',
139
+ afterSelect: 'none', // Don't navigate away - stay on current screen
88
140
  onSelect: async (file: any) => {
89
141
  if (!file.contentType.startsWith('image/')) {
90
142
  toast.error(t('editProfile.toasts.selectImage') || 'Please select an image file');
91
143
  return;
92
144
  }
93
145
  try {
94
- // Update file visibility to public for avatar
95
- try {
96
- await oxyServices.assetUpdateVisibility(file.id, 'public');
97
- console.log('[WelcomeNewUser] Avatar visibility updated to public');
98
- } catch (visError) {
99
- console.warn('[WelcomeNewUser] Failed to update avatar visibility, continuing anyway:', visError);
146
+ // Update file visibility to public for avatar (skip if temporary asset ID)
147
+ if (file.id && !file.id.startsWith('temp-')) {
148
+ try {
149
+ await oxyServices.assetUpdateVisibility(file.id, 'public');
150
+ console.log('[WelcomeNewUser] Avatar visibility updated to public');
151
+ } catch (visError: any) {
152
+ // Only log non-404 errors (404 means asset doesn't exist yet, which is OK)
153
+ if (visError?.response?.status !== 404) {
154
+ console.warn('[WelcomeNewUser] Failed to update avatar visibility, continuing anyway:', visError);
155
+ }
156
+ }
100
157
  }
101
158
 
159
+ // Update the avatar immediately in local state
160
+ setSelectedAvatarId(file.id);
161
+
162
+ // Update user in store
102
163
  await updateUser({ avatar: file.id }, oxyServices);
103
164
  toast.success(t('editProfile.toasts.avatarUpdated') || 'Avatar updated');
165
+
166
+ // Ensure we stay on the avatar step
167
+ if (avatarStepIndex >= 0 && currentStep !== avatarStepIndex) {
168
+ animateToStepCallback(avatarStepIndex);
169
+ }
104
170
  } catch (e: any) {
105
171
  toast.error(e.message || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
106
172
  }
107
173
  }
108
174
  });
109
- }, [navigate, updateUser, oxyServices]);
175
+ }, [navigate, updateUser, oxyServices, currentStep, avatarStepIndex, animateToStepCallback, t]);
110
176
 
111
177
  const step = steps[currentStep];
112
178
  const pillButtons = useMemo(() => {
@@ -138,33 +204,51 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
138
204
  <View style={styles.container}>
139
205
  <View style={styles.progressContainer}>
140
206
  {steps.map((s, i) => (
141
- <View key={s.key} style={[styles.progressDot, i === currentStep ? { backgroundColor: colors.primary, width: 22 } : { backgroundColor: colors.border }]} />
207
+ <AnimatedProgressDot
208
+ key={s.key}
209
+ isActive={i === currentStep}
210
+ colors={colors}
211
+ styles={styles}
212
+ />
142
213
  ))}
143
214
  </View>
144
215
  <Animated.View style={{ opacity: fadeAnim, transform: [{ translateX: slideAnim }] }}>
145
216
  <ScrollView contentContainerStyle={styles.scrollInner} showsVerticalScrollIndicator={false}>
146
- <Text style={styles.title}>{step.title}</Text>
147
- {step.body && <Text style={styles.body}>{step.body}</Text>}
148
- {Array.isArray(step.bullets) && step.bullets.length > 0 && (
149
- <View style={styles.bulletContainer}>
150
- {step.bullets.map(b => (
151
- <View key={b} style={styles.bulletRow}>
152
- <Ionicons name="ellipse" size={8} color={colors.primary} style={{ marginTop: 6 }} />
153
- <Text style={styles.bulletText}>{b}</Text>
154
- </View>
155
- ))}
217
+ <View style={styles.contentContainer}>
218
+ <View style={[styles.header, styles.sectionSpacing]}>
219
+ <Text style={[styles.title, { color: colors.text }]}>{step.title}</Text>
220
+ {step.body && <Text style={[styles.body, { color: colors.secondaryText }]}>{step.body}</Text>}
156
221
  </View>
157
- )}
158
- {step.showAvatar && (
159
- <View style={styles.avatarSection}>
160
- <Avatar size={120} name={currentUser?.username} uri={avatarUri} theme={theme} style={styles.avatar} />
161
- <TouchableOpacity style={styles.changeAvatarButton} onPress={openAvatarPicker}>
162
- <Ionicons name="image-outline" size={18} color="#FFFFFF" />
163
- <Text style={styles.changeAvatarText}>{avatarUri ? (t('welcomeNew.avatar.change') || 'Change Avatar') : (t('welcomeNew.avatar.add') || 'Add Avatar')}</Text>
164
- </TouchableOpacity>
222
+ {Array.isArray(step.bullets) && step.bullets.length > 0 && (
223
+ <View style={[styles.bulletContainer, styles.sectionSpacing]}>
224
+ {step.bullets.map(b => (
225
+ <View key={b} style={styles.bulletRow}>
226
+ <Ionicons name="ellipse" size={8} color={colors.primary} style={{ marginTop: 6 }} />
227
+ <Text style={[styles.bulletText, { color: colors.secondaryText }]}>{b}</Text>
228
+ </View>
229
+ ))}
230
+ </View>
231
+ )}
232
+ {step.showAvatar && (
233
+ <View style={[styles.avatarSection, styles.sectionSpacing]}>
234
+ <Avatar
235
+ size={120}
236
+ name={currentUser?.name?.full || currentUser?.name?.first || currentUser?.username}
237
+ uri={avatarUri}
238
+ theme={theme}
239
+ backgroundColor={colors.primary + '20'}
240
+ style={styles.avatar}
241
+ />
242
+ <TouchableOpacity style={[styles.changeAvatarButton, { backgroundColor: colors.primary }]} onPress={openAvatarPicker}>
243
+ <Ionicons name="image-outline" size={18} color="#FFFFFF" />
244
+ <Text style={styles.changeAvatarText}>{avatarUri ? (t('welcomeNew.avatar.change') || 'Change Avatar') : (t('welcomeNew.avatar.add') || 'Add Avatar')}</Text>
245
+ </TouchableOpacity>
246
+ </View>
247
+ )}
248
+ <View style={styles.sectionSpacing}>
249
+ <GroupedPillButtons buttons={pillButtons} colors={colors} />
165
250
  </View>
166
- )}
167
- <GroupedPillButtons buttons={pillButtons} colors={colors} />
251
+ </View>
168
252
  </ScrollView>
169
253
  </Animated.View>
170
254
  </View>
@@ -174,11 +258,7 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
174
258
 
175
259
  const createStyles = (theme: string) => {
176
260
  const isDark = theme === 'dark';
177
- const textColor = isDark ? '#FFFFFF' : '#000000';
178
- const secondary = isDark ? '#CCCCCC' : '#555555';
179
- const cardBg = isDark ? '#1E1E1E' : '#FFFFFF';
180
261
  const border = isDark ? '#333333' : '#E0E0E0';
181
- const primary = '#007AFF';
182
262
  return StyleSheet.create({
183
263
  container: {
184
264
  width: '100%',
@@ -186,25 +266,38 @@ const createStyles = (theme: string) => {
186
266
  },
187
267
  scrollInner: {
188
268
  paddingBottom: 12,
269
+ paddingTop: 0,
270
+ },
271
+ contentContainer: {
272
+ width: '100%',
273
+ maxWidth: 420,
274
+ alignSelf: 'center',
275
+ },
276
+ sectionSpacing: {
277
+ marginBottom: GAP,
278
+ },
279
+ header: {
280
+ alignItems: 'flex-start',
281
+ width: '100%',
282
+ gap: INNER_GAP / 2,
189
283
  },
190
284
  title: {
191
285
  fontSize: 42,
192
286
  fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
193
287
  fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
194
288
  letterSpacing: -1,
195
- color: textColor,
196
- marginBottom: 12,
289
+ textAlign: 'left',
197
290
  },
198
291
  body: {
199
292
  fontSize: 16,
200
293
  lineHeight: 22,
201
- color: secondary,
202
- marginBottom: 28,
203
- maxWidth: 620,
294
+ textAlign: 'left',
295
+ maxWidth: 320,
296
+ alignSelf: 'flex-start',
204
297
  },
205
298
  bulletContainer: {
206
- gap: 10,
207
- marginBottom: 32,
299
+ gap: INNER_GAP,
300
+ width: '100%',
208
301
  },
209
302
  bulletRow: {
210
303
  flexDirection: 'row',
@@ -215,26 +308,26 @@ const createStyles = (theme: string) => {
215
308
  flex: 1,
216
309
  fontSize: 15,
217
310
  lineHeight: 20,
218
- color: secondary,
219
311
  },
220
312
  avatarSection: {
221
313
  width: '100%',
222
314
  alignItems: 'center',
223
- marginBottom: 40,
224
315
  },
225
316
  avatar: {
226
- marginBottom: 16,
227
- borderWidth: 4,
228
- borderColor: primary + '40',
317
+ marginBottom: INNER_GAP,
229
318
  },
230
319
  changeAvatarButton: {
231
320
  flexDirection: 'row',
232
321
  alignItems: 'center',
233
- backgroundColor: primary,
234
322
  paddingHorizontal: 18,
235
323
  paddingVertical: 10,
236
- borderRadius: 35,
324
+ borderRadius: 28,
237
325
  gap: 8,
326
+ shadowOpacity: 0,
327
+ shadowRadius: 0,
328
+ shadowOffset: { width: 0, height: 0 },
329
+ elevation: 0,
330
+ ...(Platform.OS === 'web' ? { boxShadow: 'none' } : null),
238
331
  },
239
332
  changeAvatarText: {
240
333
  color: '#FFFFFF',
@@ -245,61 +338,16 @@ const createStyles = (theme: string) => {
245
338
  flexDirection: 'row',
246
339
  width: '100%',
247
340
  justifyContent: 'center',
248
- marginBottom: 24,
249
- marginTop: 4,
341
+ marginTop: 24, // Space for bottom sheet handle (~20px) + small buffer
342
+ marginBottom: 24, // Equal spacing below dots
250
343
  },
251
344
  progressDot: {
252
- height: 10,
253
- width: 10,
254
- borderRadius: 5,
255
- marginHorizontal: 6,
345
+ height: 6,
346
+ width: 6,
347
+ borderRadius: 3,
348
+ marginHorizontal: 3,
256
349
  backgroundColor: border,
257
350
  },
258
- navBar: {
259
- flexDirection: 'row',
260
- alignItems: 'center',
261
- width: '100%',
262
- gap: 12,
263
- marginTop: 8,
264
- },
265
- navButton: {
266
- flexDirection: 'row',
267
- alignItems: 'center',
268
- gap: 6,
269
- paddingHorizontal: 14,
270
- paddingVertical: 10,
271
- borderRadius: 12,
272
- },
273
- backButton: {
274
- backgroundColor: cardBg,
275
- borderWidth: 1,
276
- borderColor: border,
277
- },
278
- skipButton: {
279
- marginLeft: 'auto',
280
- backgroundColor: 'transparent',
281
- paddingHorizontal: 4,
282
- },
283
- navText: {
284
- fontSize: 14,
285
- fontWeight: '500',
286
- },
287
- primaryButton: {
288
- flexDirection: 'row',
289
- alignItems: 'center',
290
- justifyContent: 'center',
291
- gap: 8,
292
- backgroundColor: primary,
293
- paddingVertical: 18,
294
- borderRadius: 16,
295
- width: '100%',
296
- },
297
- primaryButtonText: {
298
- color: '#FFFFFF',
299
- fontSize: 16,
300
- fontWeight: '600',
301
- letterSpacing: 0.5,
302
- },
303
351
  });
304
352
  };
305
353
 
@@ -9,6 +9,7 @@ import Animated, {
9
9
  import { Ionicons } from '@expo/vector-icons';
10
10
  import Avatar from '../../components/Avatar';
11
11
  import { useI18n } from '../../hooks/useI18n';
12
+ import { useOxy } from '../../context/OxyContext';
12
13
  import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
13
14
  import TextField from '../../components/internal/TextField';
14
15
 
@@ -59,6 +60,7 @@ const SignInPasswordStep: React.FC<SignInPasswordStepProps> = ({
59
60
  }) => {
60
61
  const inputRef = useRef<TextInput>(null);
61
62
  const { t } = useI18n();
63
+ const { oxyServices } = useOxy();
62
64
 
63
65
  // Animated styles - properly memoized to prevent re-renders
64
66
  const containerAnimatedStyle = useAnimatedStyle(() => {
@@ -101,18 +103,14 @@ const SignInPasswordStep: React.FC<SignInPasswordStepProps> = ({
101
103
  containerAnimatedStyle
102
104
  ]}>
103
105
  <View style={styles.modernUserProfileContainer}>
104
- <Animated.View style={[
105
- styles.avatarContainer,
106
- logoAnimatedStyle
107
- ]}>
106
+ <Animated.View style={logoAnimatedStyle}>
108
107
  <Avatar
109
108
  name={userProfile?.displayName || userProfile?.name || username}
110
109
  size={100}
111
110
  theme={theme as 'light' | 'dark'}
112
- style={styles.modernUserAvatar}
113
111
  backgroundColor={colors.primary + '20'}
112
+ uri={userProfile?.avatar && oxyServices ? oxyServices.getFileDownloadUrl(userProfile.avatar, 'thumb') : undefined}
114
113
  />
115
- <View style={[styles.statusIndicator, { backgroundColor: colors.primary }]} />
116
114
  </Animated.View>
117
115
  <Text style={[styles.modernUserDisplayName, { color: colors.text }]}>
118
116
  {userProfile?.displayName || userProfile?.name || username}
@@ -95,7 +95,7 @@ const SignInUsernameStep: React.FC<SignInUsernameStepProps> = ({
95
95
  <View style={[styles.modernInfoCard, { backgroundColor: colors.inputBackground }]}>
96
96
  <Ionicons name="information-circle" size={20} color={colors.primary} />
97
97
  <Text style={[styles.modernInfoText, { color: colors.text }]}>
98
- {t('signin.currentlySignedInAs', { username: user?.username }) || 'Currently signed in as '}<Text style={{ fontWeight: 'bold' }}>{user?.username}</Text>
98
+ {t('signin.currentlySignedInAs') || 'Currently signed in as'} <Text style={{ fontWeight: 'bold' }}>{user?.name?.full || user?.username}</Text>
99
99
  </Text>
100
100
  </View>
101
101
  )}
@@ -9,6 +9,7 @@ import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
9
9
  import { toast } from '../../../lib/sonner';
10
10
  import type { OxyServices } from '../../../core';
11
11
  import { useI18n } from '../../hooks/useI18n';
12
+ import { stepStyles } from '../../styles/spacing';
12
13
 
13
14
  interface RecoverRequestStepProps {
14
15
  // Common props from StepBasedScreen
@@ -51,6 +52,7 @@ const RecoverRequestStep: React.FC<RecoverRequestStepProps> = ({
51
52
  }) => {
52
53
  const inputRef = useRef<any>(null);
53
54
  const { t } = useI18n();
55
+ const baseStyles = stepStyles;
54
56
 
55
57
  const handleIdentifierChange = (text: string) => {
56
58
  setIdentifier(text);
@@ -83,13 +85,15 @@ const RecoverRequestStep: React.FC<RecoverRequestStepProps> = ({
83
85
 
84
86
  return (
85
87
  <>
86
- <HighFive width={100} height={100} />
87
- <View style={styles.modernHeader}>
88
- <Text style={[styles.modernTitle, { color: colors.text }]}>{t('recover.title')}</Text>
89
- <Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>{t('recover.noEmail')}</Text>
88
+ <View style={[baseStyles.container, baseStyles.sectionSpacing, { alignItems: 'flex-start' }]}>
89
+ <HighFive width={100} height={100} />
90
+ </View>
91
+ <View style={[baseStyles.container, baseStyles.sectionSpacing, baseStyles.header]}>
92
+ <Text style={[styles.modernTitle, baseStyles.title, { color: colors.text, marginBottom: 0, marginTop: 0 }]}>{t('recover.title')}</Text>
93
+ <Text style={[styles.modernSubtitle, baseStyles.subtitle, { color: colors.secondaryText, marginBottom: 0, marginTop: 0 }]}>{t('recover.noEmail')}</Text>
90
94
  </View>
91
95
 
92
- <View style={styles.modernInputContainer}>
96
+ <View style={[baseStyles.container, baseStyles.sectionSpacing]}>
93
97
  <TextField
94
98
  ref={inputRef}
95
99
  label={t('recover.username.label')}
@@ -101,31 +105,37 @@ const RecoverRequestStep: React.FC<RecoverRequestStepProps> = ({
101
105
  testID="recover-identifier-input"
102
106
  variant="filled"
103
107
  error={errorMessage || undefined}
108
+ helperText={t('recover.username.helper') || 'Enter your username or email'}
104
109
  editable={!isLoading}
105
110
  onSubmitEditing={handleRequestWithFocus}
106
111
  autoFocus
112
+ accessibilityLabel={t('recover.username.label')}
113
+ accessibilityHint={t('recover.username.helper') || 'Enter your username or email to recover your account'}
114
+ style={{ marginBottom: 0 }}
107
115
  />
108
116
  </View>
109
117
 
110
- <GroupedPillButtons
111
- buttons={[
112
- {
113
- text: t('common.actions.back'),
114
- onPress: () => navigate('SignIn'),
115
- icon: 'arrow-back',
116
- variant: 'transparent',
117
- },
118
- {
119
- text: t('common.actions.continue'),
120
- onPress: handleRequest,
121
- icon: 'information-circle-outline',
122
- variant: 'primary',
123
- loading: isLoading,
124
- disabled: isLoading,
125
- },
126
- ]}
127
- colors={colors}
128
- />
118
+ <View style={[baseStyles.container, baseStyles.sectionSpacing, baseStyles.buttonContainer]}>
119
+ <GroupedPillButtons
120
+ buttons={[
121
+ {
122
+ text: t('common.actions.back'),
123
+ onPress: () => navigate('SignIn'),
124
+ icon: 'arrow-back',
125
+ variant: 'transparent',
126
+ },
127
+ {
128
+ text: t('common.actions.continue'),
129
+ onPress: handleRequest,
130
+ icon: 'information-circle-outline',
131
+ variant: 'primary',
132
+ loading: isLoading,
133
+ disabled: isLoading || !identifier || identifier.length < 3,
134
+ },
135
+ ]}
136
+ colors={colors}
137
+ />
138
+ </View>
129
139
  </>
130
140
  );
131
141
  };
@@ -1,10 +1,11 @@
1
1
  import type React from 'react';
2
2
  import type { RouteName } from '../../navigation/routes';
3
- import { View, Text } from 'react-native';
3
+ import { View, Text, StyleSheet, Platform } from 'react-native';
4
4
  import { Ionicons } from '@expo/vector-icons';
5
5
  import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
6
6
  import TextField from '../../components/internal/TextField';
7
7
  import { useI18n } from '../../hooks/useI18n';
8
+ import { STEP_INNER_GAP, stepStyles } from '../../styles/spacing';
8
9
 
9
10
  interface RecoverResetPasswordStepProps {
10
11
  // Common props
@@ -53,6 +54,8 @@ const RecoverResetPasswordStep: React.FC<RecoverResetPasswordStepProps> = ({
53
54
  oxyServices,
54
55
  }) => {
55
56
  const { t } = useI18n();
57
+ const baseStyles = stepStyles;
58
+ const webShadowReset = Platform.OS === 'web' ? ({ boxShadow: 'none' } as any) : null;
56
59
  const handleReset = async () => {
57
60
  if (!password || password.length < 8) {
58
61
  setErrorMessage(t('recover.password.minLength') || 'Password must be at least 8 characters long');
@@ -86,48 +89,74 @@ const RecoverResetPasswordStep: React.FC<RecoverResetPasswordStepProps> = ({
86
89
 
87
90
  return (
88
91
  <>
89
- <View style={styles.modernHeader}>
90
- <Text style={[styles.modernTitle, { color: colors.text }]}>{t('recover.newPassword')}</Text>
91
- <Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>{t('recover.title')} @{identifier}</Text>
92
+ <View style={[baseStyles.container, baseStyles.sectionSpacing, baseStyles.header]}>
93
+ <Text style={[styles.modernTitle, baseStyles.title, { color: colors.text, marginBottom: 0, marginTop: 0 }]}>{t('recover.newPassword')}</Text>
94
+ <Text style={[styles.modernSubtitle, baseStyles.subtitle, { color: colors.secondaryText, marginBottom: 0, marginTop: 0 }]}>{t('recover.title')} @{identifier}</Text>
92
95
  </View>
93
96
 
94
- <View style={styles.modernInputContainer}>
95
- <TextField
96
- label={t('common.labels.password')}
97
- leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
98
- value={password}
99
- onChangeText={setPassword}
100
- secureTextEntry
101
- autoCapitalize="none"
102
- autoCorrect={false}
103
- variant="filled"
104
- error={errorMessage || undefined}
105
- onSubmitEditing={handleReset}
106
- autoFocus
107
- />
97
+ <View style={[baseStyles.container, baseStyles.sectionSpacing]}>
98
+ <View style={[stylesheet.formCard, { backgroundColor: colors.inputBackground || colors.card || 'rgba(0,0,0,0.04)' }, webShadowReset]}>
99
+ <TextField
100
+ label={t('common.labels.password')}
101
+ leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
102
+ value={password}
103
+ onChangeText={setPassword}
104
+ secureTextEntry
105
+ autoCapitalize="none"
106
+ autoCorrect={false}
107
+ variant="filled"
108
+ error={errorMessage || undefined}
109
+ onSubmitEditing={handleReset}
110
+ autoFocus
111
+ style={{ marginBottom: 0 }}
112
+ />
108
113
 
109
- <TextField
110
- label={t('common.labels.confirmPassword')}
111
- leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
112
- value={confirmPassword}
113
- onChangeText={setConfirmPassword}
114
- secureTextEntry
115
- autoCapitalize="none"
116
- autoCorrect={false}
117
- variant="filled"
118
- onSubmitEditing={handleReset}
119
- />
114
+ <TextField
115
+ label={t('common.labels.confirmPassword')}
116
+ leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
117
+ value={confirmPassword}
118
+ onChangeText={setConfirmPassword}
119
+ secureTextEntry
120
+ autoCapitalize="none"
121
+ autoCorrect={false}
122
+ variant="filled"
123
+ onSubmitEditing={handleReset}
124
+ style={{ marginBottom: 0 }}
125
+ />
126
+ </View>
120
127
  </View>
121
128
 
122
- <GroupedPillButtons
123
- buttons={[
124
- { text: t('common.actions.back'), onPress: prevStep, icon: 'arrow-back', variant: 'transparent' },
125
- { text: t('common.actions.resetPassword'), onPress: handleReset, icon: 'key-outline', variant: 'primary', loading: isLoading, disabled: isLoading },
126
- ]}
127
- colors={colors}
128
- />
129
+ <View style={[baseStyles.container, baseStyles.sectionSpacing, baseStyles.buttonContainer]}>
130
+ <GroupedPillButtons
131
+ buttons={[
132
+ { text: t('common.actions.back'), onPress: prevStep, icon: 'arrow-back', variant: 'transparent' },
133
+ {
134
+ text: t('common.actions.resetPassword'),
135
+ onPress: handleReset,
136
+ icon: 'key-outline',
137
+ variant: 'primary',
138
+ loading: isLoading,
139
+ disabled: isLoading || !password || password.length < 8 || password !== confirmPassword
140
+ },
141
+ ]}
142
+ colors={colors}
143
+ />
144
+ </View>
129
145
  </>
130
146
  );
131
147
  };
132
148
 
133
149
  export default RecoverResetPasswordStep;
150
+
151
+ const stylesheet = StyleSheet.create({
152
+ formCard: {
153
+ width: '100%',
154
+ maxWidth: 420,
155
+ borderRadius: 28,
156
+ paddingHorizontal: 20,
157
+ paddingVertical: 18,
158
+ gap: STEP_INNER_GAP,
159
+ alignItems: 'stretch',
160
+ shadowColor: 'transparent',
161
+ },
162
+ });