@oxyhq/services 5.7.5 → 5.8.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 (303) hide show
  1. package/README.md +76 -76
  2. package/lib/commonjs/core/index.js +177 -102
  3. package/lib/commonjs/core/index.js.map +1 -1
  4. package/lib/commonjs/index.js +69 -28
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/ui/components/Avatar.js +15 -6
  7. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  8. package/lib/commonjs/ui/components/FollowButton.js +100 -12
  9. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  10. package/lib/commonjs/ui/components/GroupedItem.js +58 -13
  11. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  12. package/lib/commonjs/ui/components/GroupedSection.js +7 -1
  13. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
  14. package/lib/commonjs/ui/components/Header.js +356 -0
  15. package/lib/commonjs/ui/components/Header.js.map +1 -0
  16. package/lib/commonjs/ui/components/OxyProvider.js +28 -7
  17. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  18. package/lib/commonjs/ui/components/index.js +7 -0
  19. package/lib/commonjs/ui/components/index.js.map +1 -1
  20. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +1 -1
  21. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
  22. package/lib/commonjs/ui/components/internal/TextField.js +606 -546
  23. package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
  24. package/lib/commonjs/ui/components/internal/TextField.md +436 -0
  25. package/lib/commonjs/ui/context/OxyContext.js +181 -199
  26. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  27. package/lib/commonjs/ui/hooks/index.js +6 -0
  28. package/lib/commonjs/ui/hooks/index.js.map +1 -1
  29. package/lib/commonjs/ui/hooks/useFollow.js +59 -2
  30. package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
  31. package/lib/commonjs/ui/hooks/useSessionSocket.js +5 -2
  32. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  33. package/lib/commonjs/ui/navigation/OxyRouter.js +11 -1
  34. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  35. package/lib/commonjs/ui/screens/AccountCenterScreen.js +6 -6
  36. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/AccountManagementDemo.js +3 -3
  38. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
  39. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +241 -598
  40. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +1160 -406
  42. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +135 -237
  44. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/AppInfoScreen.js +246 -463
  46. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/FeedbackScreen.js +3 -3
  48. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +808 -650
  50. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/ProfileScreen.js +214 -37
  52. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +51 -72
  54. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SessionManagementScreen.js +11 -29
  56. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/SignInScreen.js +30 -303
  58. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  59. package/lib/commonjs/ui/screens/SignUpScreen.js +4 -4
  60. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  61. package/lib/commonjs/ui/screens/UserLinksScreen.js +90 -0
  62. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -0
  63. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +19 -31
  64. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  65. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +7 -10
  66. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  67. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +11 -5
  68. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  69. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +11 -4
  70. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  71. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js +9 -6
  72. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  73. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +3 -30
  74. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  75. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +37 -46
  76. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  77. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +9 -12
  78. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  79. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +9 -12
  80. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  81. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +9 -12
  82. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  83. package/lib/commonjs/ui/stores/authStore.js +36 -6
  84. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  85. package/lib/commonjs/ui/stores/followStore.js +106 -1
  86. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  87. package/lib/commonjs/ui/styles/authStyles.js +337 -0
  88. package/lib/commonjs/ui/styles/authStyles.js.map +1 -0
  89. package/lib/commonjs/ui/styles/index.js +11 -0
  90. package/lib/commonjs/ui/styles/index.js.map +1 -1
  91. package/lib/module/core/index.js +177 -41
  92. package/lib/module/core/index.js.map +1 -1
  93. package/lib/module/index.js +24 -4
  94. package/lib/module/index.js.map +1 -1
  95. package/lib/module/ui/components/Avatar.js +15 -6
  96. package/lib/module/ui/components/Avatar.js.map +1 -1
  97. package/lib/module/ui/components/FollowButton.js +101 -13
  98. package/lib/module/ui/components/FollowButton.js.map +1 -1
  99. package/lib/module/ui/components/GroupedItem.js +59 -14
  100. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  101. package/lib/module/ui/components/GroupedSection.js +7 -1
  102. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  103. package/lib/module/ui/components/Header.js +351 -0
  104. package/lib/module/ui/components/Header.js.map +1 -0
  105. package/lib/module/ui/components/OxyProvider.js +30 -9
  106. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  107. package/lib/module/ui/components/index.js +1 -0
  108. package/lib/module/ui/components/index.js.map +1 -1
  109. package/lib/module/ui/components/internal/GroupedPillButtons.js +1 -1
  110. package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
  111. package/lib/module/ui/components/internal/TextField.js +607 -547
  112. package/lib/module/ui/components/internal/TextField.js.map +1 -1
  113. package/lib/module/ui/components/internal/TextField.md +436 -0
  114. package/lib/module/ui/context/OxyContext.js +180 -198
  115. package/lib/module/ui/context/OxyContext.js.map +1 -1
  116. package/lib/module/ui/hooks/index.js +1 -1
  117. package/lib/module/ui/hooks/index.js.map +1 -1
  118. package/lib/module/ui/hooks/useFollow.js +57 -1
  119. package/lib/module/ui/hooks/useFollow.js.map +1 -1
  120. package/lib/module/ui/hooks/useSessionSocket.js +5 -2
  121. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  122. package/lib/module/ui/navigation/OxyRouter.js +11 -1
  123. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  124. package/lib/module/ui/screens/AccountCenterScreen.js +6 -6
  125. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  126. package/lib/module/ui/screens/AccountManagementDemo.js +3 -3
  127. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
  128. package/lib/module/ui/screens/AccountOverviewScreen.js +242 -597
  129. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  130. package/lib/module/ui/screens/AccountSettingsScreen.js +1161 -407
  131. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  132. package/lib/module/ui/screens/AccountSwitcherScreen.js +135 -237
  133. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  134. package/lib/module/ui/screens/AppInfoScreen.js +248 -465
  135. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  136. package/lib/module/ui/screens/FeedbackScreen.js +3 -3
  137. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  138. package/lib/module/ui/screens/PaymentGatewayScreen.js +809 -651
  139. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  140. package/lib/module/ui/screens/ProfileScreen.js +214 -37
  141. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  142. package/lib/module/ui/screens/RecoverAccountScreen.js +53 -74
  143. package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
  144. package/lib/module/ui/screens/SessionManagementScreen.js +11 -29
  145. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  146. package/lib/module/ui/screens/SignInScreen.js +32 -305
  147. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  148. package/lib/module/ui/screens/SignUpScreen.js +5 -5
  149. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  150. package/lib/module/ui/screens/UserLinksScreen.js +85 -0
  151. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -0
  152. package/lib/module/ui/screens/internal/SignInPasswordStep.js +19 -31
  153. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  154. package/lib/module/ui/screens/internal/SignInUsernameStep.js +7 -10
  155. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  156. package/lib/module/ui/screens/internal/SignUpIdentityStep.js +11 -5
  157. package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  158. package/lib/module/ui/screens/internal/SignUpSecurityStep.js +11 -4
  159. package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  160. package/lib/module/ui/screens/karma/KarmaAboutScreen.js +9 -6
  161. package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  162. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +3 -30
  163. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  164. package/lib/module/ui/screens/karma/KarmaFAQScreen.js +37 -46
  165. package/lib/module/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  166. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +9 -12
  167. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  168. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +9 -12
  169. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  170. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +9 -12
  171. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  172. package/lib/module/ui/stores/authStore.js +36 -6
  173. package/lib/module/ui/stores/authStore.js.map +1 -1
  174. package/lib/module/ui/stores/followStore.js +106 -1
  175. package/lib/module/ui/stores/followStore.js.map +1 -1
  176. package/lib/module/ui/styles/authStyles.js +332 -0
  177. package/lib/module/ui/styles/authStyles.js.map +1 -0
  178. package/lib/module/ui/styles/index.js +1 -0
  179. package/lib/module/ui/styles/index.js.map +1 -1
  180. package/lib/typescript/core/index.d.ts +68 -24
  181. package/lib/typescript/core/index.d.ts.map +1 -1
  182. package/lib/typescript/index.d.ts +13 -3
  183. package/lib/typescript/index.d.ts.map +1 -1
  184. package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
  185. package/lib/typescript/ui/components/FollowButton.d.ts +1 -0
  186. package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
  187. package/lib/typescript/ui/components/GroupedItem.d.ts +6 -0
  188. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  189. package/lib/typescript/ui/components/GroupedSection.d.ts +6 -0
  190. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -1
  191. package/lib/typescript/ui/components/Header.d.ts +24 -0
  192. package/lib/typescript/ui/components/Header.d.ts.map +1 -0
  193. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  194. package/lib/typescript/ui/components/index.d.ts +1 -0
  195. package/lib/typescript/ui/components/index.d.ts.map +1 -1
  196. package/lib/typescript/ui/components/internal/TextField.d.ts +31 -16
  197. package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
  198. package/lib/typescript/ui/context/OxyContext.d.ts +5 -2
  199. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  200. package/lib/typescript/ui/hooks/index.d.ts +1 -1
  201. package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
  202. package/lib/typescript/ui/hooks/useFollow.d.ts +20 -0
  203. package/lib/typescript/ui/hooks/useFollow.d.ts.map +1 -1
  204. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  205. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
  206. package/lib/typescript/ui/navigation/types.d.ts +9 -2
  207. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  208. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  209. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  210. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  211. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  212. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  213. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  214. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +5 -1
  215. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
  216. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  217. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  218. package/lib/typescript/ui/screens/UserLinksScreen.d.ts +15 -0
  219. package/lib/typescript/ui/screens/UserLinksScreen.d.ts.map +1 -0
  220. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +1 -1
  221. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
  222. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -1
  223. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -1
  224. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +1 -1
  225. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +1 -1
  226. package/lib/typescript/ui/screens/karma/KarmaAboutScreen.d.ts.map +1 -1
  227. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
  228. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts.map +1 -1
  229. package/lib/typescript/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +1 -1
  230. package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
  231. package/lib/typescript/ui/screens/karma/KarmaRulesScreen.d.ts.map +1 -1
  232. package/lib/typescript/ui/stores/authStore.d.ts +3 -1
  233. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  234. package/lib/typescript/ui/stores/followStore.d.ts +10 -0
  235. package/lib/typescript/ui/stores/followStore.d.ts.map +1 -1
  236. package/lib/typescript/ui/styles/authStyles.d.ts +326 -0
  237. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -0
  238. package/lib/typescript/ui/styles/index.d.ts +1 -0
  239. package/lib/typescript/ui/styles/index.d.ts.map +1 -1
  240. package/package.json +1 -4
  241. package/src/core/index.ts +195 -41
  242. package/src/index.ts +64 -4
  243. package/src/ui/components/Avatar.tsx +11 -5
  244. package/src/ui/components/FollowButton.tsx +95 -11
  245. package/src/ui/components/GroupedItem.tsx +57 -9
  246. package/src/ui/components/GroupedSection.tsx +12 -0
  247. package/src/ui/components/Header.tsx +405 -0
  248. package/src/ui/components/OxyProvider.tsx +37 -15
  249. package/src/ui/components/index.ts +1 -0
  250. package/src/ui/components/internal/GroupedPillButtons.tsx +1 -1
  251. package/src/ui/components/internal/TextField.md +436 -0
  252. package/src/ui/components/internal/TextField.tsx +720 -620
  253. package/src/ui/context/OxyContext.tsx +211 -195
  254. package/src/ui/hooks/index.ts +1 -1
  255. package/src/ui/hooks/useFollow.ts +63 -0
  256. package/src/ui/hooks/useSessionSocket.ts +5 -2
  257. package/src/ui/navigation/OxyRouter.tsx +11 -1
  258. package/src/ui/navigation/types.ts +10 -2
  259. package/src/ui/screens/AccountCenterScreen.tsx +5 -5
  260. package/src/ui/screens/AccountManagementDemo.tsx +9 -9
  261. package/src/ui/screens/AccountOverviewScreen.tsx +265 -414
  262. package/src/ui/screens/AccountSettingsScreen.tsx +1173 -403
  263. package/src/ui/screens/AccountSwitcherScreen.tsx +158 -202
  264. package/src/ui/screens/AppInfoScreen.tsx +270 -497
  265. package/src/ui/screens/FeedbackScreen.tsx +3 -3
  266. package/src/ui/screens/PaymentGatewayScreen.tsx +668 -365
  267. package/src/ui/screens/ProfileScreen.tsx +196 -33
  268. package/src/ui/screens/RecoverAccountScreen.tsx +46 -74
  269. package/src/ui/screens/SessionManagementScreen.tsx +14 -22
  270. package/src/ui/screens/SignInScreen.tsx +27 -294
  271. package/src/ui/screens/SignUpScreen.tsx +5 -5
  272. package/src/ui/screens/UserLinksScreen.tsx +96 -0
  273. package/src/ui/screens/internal/SignInPasswordStep.tsx +11 -22
  274. package/src/ui/screens/internal/SignInUsernameStep.tsx +3 -10
  275. package/src/ui/screens/internal/SignUpIdentityStep.tsx +2 -5
  276. package/src/ui/screens/internal/SignUpSecurityStep.tsx +3 -4
  277. package/src/ui/screens/karma/KarmaAboutScreen.tsx +9 -2
  278. package/src/ui/screens/karma/KarmaCenterScreen.tsx +1 -20
  279. package/src/ui/screens/karma/KarmaFAQScreen.tsx +40 -24
  280. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +9 -3
  281. package/src/ui/screens/karma/KarmaRewardsScreen.tsx +9 -3
  282. package/src/ui/screens/karma/KarmaRulesScreen.tsx +9 -3
  283. package/src/ui/stores/authStore.ts +34 -7
  284. package/src/ui/stores/followStore.ts +102 -1
  285. package/src/ui/styles/authStyles.ts +352 -0
  286. package/src/ui/styles/index.ts +1 -0
  287. package/lib/commonjs/core/auth-manager.js +0 -440
  288. package/lib/commonjs/core/auth-manager.js.map +0 -1
  289. package/lib/commonjs/core/use-auth.js +0 -244
  290. package/lib/commonjs/core/use-auth.js.map +0 -1
  291. package/lib/module/core/auth-manager.js +0 -432
  292. package/lib/module/core/auth-manager.js.map +0 -1
  293. package/lib/module/core/use-auth.js +0 -235
  294. package/lib/module/core/use-auth.js.map +0 -1
  295. package/lib/typescript/core/auth-manager.d.ts +0 -136
  296. package/lib/typescript/core/auth-manager.d.ts.map +0 -1
  297. package/lib/typescript/core/use-auth.d.ts +0 -79
  298. package/lib/typescript/core/use-auth.d.ts.map +0 -1
  299. package/src/__tests__/middleware.test.ts +0 -105
  300. package/src/__tests__/setup.ts +0 -10
  301. package/src/__tests__/zero-config-auth.test.ts +0 -607
  302. package/src/core/auth-manager.ts +0 -500
  303. package/src/core/use-auth.tsx +0 -245
@@ -14,7 +14,6 @@ interface SignInUsernameStepProps {
14
14
  isAddAccountMode: boolean;
15
15
  user: any;
16
16
  errorMessage: string;
17
- inputScaleAnim: Animated.Value;
18
17
  isInputFocused: boolean;
19
18
  username: string;
20
19
  validationStatus: 'idle' | 'validating' | 'valid' | 'invalid';
@@ -36,7 +35,6 @@ const SignInUsernameStep: React.FC<SignInUsernameStepProps> = ({
36
35
  isAddAccountMode,
37
36
  user,
38
37
  errorMessage,
39
- inputScaleAnim,
40
38
  isInputFocused,
41
39
  username,
42
40
  validationStatus,
@@ -88,14 +86,11 @@ const SignInUsernameStep: React.FC<SignInUsernameStepProps> = ({
88
86
  </Text>
89
87
  </View>
90
88
  )}
91
- <Animated.View style={[
92
- styles.modernInputContainer,
93
- { transform: [{ scale: inputScaleAnim }] }
94
- ]}>
89
+ <View style={styles.modernInputContainer}>
95
90
  <TextField
96
91
  ref={inputRef}
97
92
  label="Username"
98
- icon="person-outline"
93
+ leading={<Ionicons name="person-outline" size={24} color={colors.secondaryText} />}
99
94
  value={username}
100
95
  onChangeText={handleUsernameChange}
101
96
  onFocus={handleInputFocus}
@@ -103,16 +98,14 @@ const SignInUsernameStep: React.FC<SignInUsernameStepProps> = ({
103
98
  autoCapitalize="none"
104
99
  autoCorrect={false}
105
100
  testID="username-input"
106
- colors={colors}
107
101
  variant="filled"
108
102
  error={validationStatus === 'invalid' ? errorMessage : undefined}
109
103
  loading={validationStatus === 'validating'}
110
104
  success={validationStatus === 'valid'}
111
- validMessage={validationStatus === 'valid' && userProfile ? `Welcome back, ${userProfile.displayName || userProfile.name || username}!` : undefined}
112
105
  onSubmitEditing={handleUsernameContinue}
113
106
  autoFocus
114
107
  />
115
- </Animated.View>
108
+ </View>
116
109
  <GroupedPillButtons
117
110
  buttons={[
118
111
  {
@@ -55,7 +55,7 @@ const SignUpIdentityStep: React.FC<SignUpIdentityStepProps> = ({
55
55
  </View>
56
56
  <TextField
57
57
  ref={inputRef}
58
- icon="person-outline"
58
+ leading={<Ionicons name="person-outline" size={24} color={colors.secondaryText} />}
59
59
  label="Username"
60
60
  value={formData.username}
61
61
  onChangeText={(text) => {
@@ -65,18 +65,16 @@ const SignUpIdentityStep: React.FC<SignUpIdentityStepProps> = ({
65
65
  autoCapitalize="none"
66
66
  autoCorrect={false}
67
67
  testID="username-input"
68
- colors={colors}
69
68
  variant="filled"
70
69
  error={validationState.status === 'invalid' ? validationState.message : undefined}
71
70
  loading={validationState.status === 'validating'}
72
71
  success={validationState.status === 'valid'}
73
- validMessage={validationState.status === 'valid' ? 'Looks good!' : undefined}
74
72
  onSubmitEditing={handleIdentityNext}
75
73
  autoFocus
76
74
  />
77
75
  <ValidationMessage validationState={validationState} colors={colors} styles={styles} />
78
76
  <TextField
79
- icon="mail-outline"
77
+ leading={<Ionicons name="mail-outline" size={24} color={colors.secondaryText} />}
80
78
  label="Email"
81
79
  value={formData.email}
82
80
  onChangeText={(text) => {
@@ -86,7 +84,6 @@ const SignUpIdentityStep: React.FC<SignUpIdentityStepProps> = ({
86
84
  autoCapitalize="none"
87
85
  autoCorrect={false}
88
86
  testID="email-input"
89
- colors={colors}
90
87
  variant="filled"
91
88
  error={formData.email && !validateEmail(formData.email) ? 'Please enter a valid email address' : undefined}
92
89
  />
@@ -1,5 +1,6 @@
1
1
  import React, { useRef, useCallback, useEffect } from 'react';
2
2
  import { View, Text, Animated, TextInput } from 'react-native';
3
+ import { Ionicons } from '@expo/vector-icons';
3
4
  import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
4
5
  import TextField from '../../components/internal/TextField';
5
6
 
@@ -78,7 +79,7 @@ const SignUpSecurityStep: React.FC<SignUpSecurityStepProps> = ({
78
79
  </View>
79
80
  <TextField
80
81
  ref={passwordRef}
81
- icon="lock-closed-outline"
82
+ leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
82
83
  label="Password"
83
84
  value={formData.password}
84
85
  onChangeText={text => updateField('password', text)}
@@ -86,7 +87,6 @@ const SignUpSecurityStep: React.FC<SignUpSecurityStepProps> = ({
86
87
  autoCapitalize="none"
87
88
  autoCorrect={false}
88
89
  testID="password-input"
89
- colors={colors}
90
90
  variant="filled"
91
91
  error={formData.password && typeof formData.password === 'string' && !validatePassword(formData.password) ? `Password must be at least ${PASSWORD_MIN_LENGTH} characters` : undefined}
92
92
  onSubmitEditing={() => confirmPasswordRef.current?.focus()}
@@ -95,7 +95,7 @@ const SignUpSecurityStep: React.FC<SignUpSecurityStepProps> = ({
95
95
  <Text style={[styles.passwordHint, { color: colors.secondaryText }]}>Password must be at least {PASSWORD_MIN_LENGTH} characters long</Text>
96
96
  <TextField
97
97
  ref={confirmPasswordRef}
98
- icon="lock-closed-outline"
98
+ leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
99
99
  label="Confirm Password"
100
100
  value={formData.confirmPassword}
101
101
  onChangeText={text => updateField('confirmPassword', text)}
@@ -103,7 +103,6 @@ const SignUpSecurityStep: React.FC<SignUpSecurityStepProps> = ({
103
103
  autoCapitalize="none"
104
104
  autoCorrect={false}
105
105
  testID="confirm-password-input"
106
- colors={colors}
107
106
  variant="filled"
108
107
  error={formData.confirmPassword && typeof formData.confirmPassword === 'string' && !validatePasswordsMatch(formData.password, formData.confirmPassword) ? 'Passwords do not match' : undefined}
109
108
  onSubmitEditing={handleSecurityNext}
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { View, Text, StyleSheet, ScrollView, Platform } from 'react-native';
3
3
  import { BaseScreenProps } from '../../navigation/types';
4
+ import { Header } from '../../components';
4
5
 
5
6
  const KarmaAboutScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
6
7
  const isDarkTheme = theme === 'dark';
@@ -10,7 +11,13 @@ const KarmaAboutScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
10
11
 
11
12
  return (
12
13
  <View style={[styles.container, { backgroundColor }]}>
13
- <Text style={[styles.title, { color: textColor }]}>About Karma</Text>
14
+ <Header
15
+ title="About Karma"
16
+ subtitle="Learn about the karma system"
17
+ theme={theme}
18
+ onBack={goBack}
19
+ elevation="subtle"
20
+ />
14
21
  <ScrollView contentContainerStyle={styles.contentContainer}>
15
22
  <Text style={[styles.paragraph, { color: textColor }]}>Karma is a recognition of your positive actions in the Oxy Ecosystem. It cannot be sent or received directly, only earned by contributing to the community.</Text>
16
23
  <Text style={[styles.section, { color: primaryColor }]}>How to Earn Karma</Text>
@@ -37,7 +44,7 @@ const styles = StyleSheet.create({
37
44
  margin: 24,
38
45
  marginBottom: 24,
39
46
  },
40
- contentContainer: { padding: 24 },
47
+ contentContainer: { padding: 24, paddingTop: 20 },
41
48
  section: { fontSize: 18, fontWeight: 'bold', marginTop: 24, marginBottom: 8 },
42
49
  paragraph: { fontSize: 16, marginBottom: 12 },
43
50
  });
@@ -16,7 +16,6 @@ import Avatar from '../../components/Avatar';
16
16
  import { Ionicons } from '@expo/vector-icons';
17
17
 
18
18
  const KarmaCenterScreen: React.FC<BaseScreenProps> = ({
19
- onClose,
20
19
  theme,
21
20
  navigate,
22
21
  goBack,
@@ -137,11 +136,6 @@ const KarmaCenterScreen: React.FC<BaseScreenProps> = ({
137
136
  </View>
138
137
  {error && <Text style={{ color: '#D32F2F', marginTop: 16, textAlign: 'center' }}>{error}</Text>}
139
138
  </ScrollView>
140
- <View style={styles.footer}>
141
- <TouchableOpacity style={styles.closeButton} onPress={onClose}>
142
- <Text style={[styles.closeButtonText, { color: primaryColor }]}>Close</Text>
143
- </TouchableOpacity>
144
- </View>
145
139
  </View>
146
140
  );
147
141
  };
@@ -247,20 +241,7 @@ const styles = StyleSheet.create({
247
241
  fontSize: 13,
248
242
  marginTop: 2,
249
243
  },
250
- footer: {
251
- padding: 16,
252
- borderTopWidth: 1,
253
- borderTopColor: '#E0E0E0',
254
- alignItems: 'center',
255
- },
256
- closeButton: {
257
- paddingVertical: 8,
258
- paddingHorizontal: 16,
259
- },
260
- closeButtonText: {
261
- fontSize: 16,
262
- fontWeight: '600',
263
- },
244
+
264
245
  message: {
265
246
  fontSize: 16,
266
247
  textAlign: 'center',
@@ -2,6 +2,7 @@ import React, { useState, useMemo, useCallback } from 'react';
2
2
  import { View, Text, StyleSheet, ScrollView, Platform, TouchableOpacity, TextInput, LayoutAnimation, UIManager } from 'react-native';
3
3
  import { BaseScreenProps } from '../../navigation/types';
4
4
  import { Ionicons } from '@expo/vector-icons';
5
+ import { Header } from '../../components';
5
6
 
6
7
  if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
7
8
  UIManager.setLayoutAnimationEnabledExperimental(true);
@@ -79,7 +80,14 @@ const KarmaFAQScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
79
80
 
80
81
  return (
81
82
  <View style={[styles.container, { backgroundColor: themeStyles.backgroundColor }]}>
82
- <Text style={[styles.title, { color: themeStyles.textColor }]}>Karma FAQ</Text>
83
+ <Header
84
+ title="Karma FAQ"
85
+ subtitle="Frequently asked questions about karma"
86
+ subtitleVariant="muted"
87
+ theme={theme}
88
+ onBack={goBack}
89
+ elevation="subtle"
90
+ />
83
91
  <View style={[styles.searchBar, { backgroundColor: themeStyles.inputBg, borderColor: themeStyles.inputBorder }]}>
84
92
  <Ionicons name="search-outline" size={20} color={themeStyles.primaryColor} style={{ marginRight: 8 }} />
85
93
  <TextInput
@@ -91,31 +99,37 @@ const KarmaFAQScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
91
99
  returnKeyType="search"
92
100
  />
93
101
  </View>
94
- <ScrollView contentContainerStyle={styles.contentContainer} keyboardShouldPersistTaps="handled">
102
+ <ScrollView contentContainerStyle={styles.contentContainer}>
95
103
  {filteredFaqs.length === 0 ? (
96
- <Text style={[styles.noResults, { color: themeStyles.textColor }]}>No results found.</Text>
104
+ <Text style={[styles.noResults, { color: themeStyles.textColor }]}>
105
+ No FAQ items found matching "{search}"
106
+ </Text>
97
107
  ) : (
98
- filteredFaqs.map((item, idx) => {
99
- const isOpen = expanded === idx;
100
- return (
101
- <TouchableOpacity
102
- key={idx}
103
- style={[styles.card, { backgroundColor: themeStyles.cardColor, shadowColor: themeStyles.isDarkTheme ? '#000' : '#d169e5' }]}
104
- activeOpacity={0.95}
105
- onPress={() => handleToggle(idx)}
106
- >
107
- <View style={styles.questionRow}>
108
- <Ionicons name={isOpen ? 'chevron-down' : 'chevron-forward'} size={22} color={themeStyles.primaryColor} style={{ marginRight: 8 }} />
109
- <Text style={[styles.question, { color: themeStyles.primaryColor }]}>{item.q}</Text>
110
- </View>
111
- {isOpen && (
112
- <Text style={[styles.answer, { color: themeStyles.textColor }]}>{item.a}</Text>
113
- )}
114
- </TouchableOpacity>
115
- );
116
- })
108
+ filteredFaqs.map((faq, idx) => (
109
+ <TouchableOpacity
110
+ key={idx}
111
+ style={[styles.card, { backgroundColor: themeStyles.cardColor }]}
112
+ onPress={() => handleToggle(idx)}
113
+ activeOpacity={0.7}
114
+ >
115
+ <View style={styles.questionRow}>
116
+ <Text style={[styles.question, { color: themeStyles.textColor }]}>
117
+ {faq.q}
118
+ </Text>
119
+ <Ionicons
120
+ name={expanded === idx ? 'chevron-up' : 'chevron-down'}
121
+ size={20}
122
+ color={themeStyles.primaryColor}
123
+ />
124
+ </View>
125
+ {expanded === idx && (
126
+ <Text style={[styles.answer, { color: themeStyles.textColor }]}>
127
+ {faq.a}
128
+ </Text>
129
+ )}
130
+ </TouchableOpacity>
131
+ ))
117
132
  )}
118
- <Text style={[styles.paragraph, { color: themeStyles.textColor, marginTop: 32, textAlign: 'center' }]}>Still have questions? Contact support!</Text>
119
133
  </ScrollView>
120
134
  </View>
121
135
  );
@@ -146,7 +160,7 @@ const styles = StyleSheet.create({
146
160
  fontSize: 16,
147
161
  height: 44,
148
162
  },
149
- contentContainer: { padding: 24, paddingBottom: 40 },
163
+ contentContainer: { padding: 24, paddingTop: 20, paddingBottom: 40 },
150
164
  card: {
151
165
  borderRadius: 18,
152
166
  padding: 20,
@@ -159,11 +173,13 @@ const styles = StyleSheet.create({
159
173
  questionRow: {
160
174
  flexDirection: 'row',
161
175
  alignItems: 'center',
176
+ justifyContent: 'space-between',
162
177
  marginBottom: 8,
163
178
  },
164
179
  question: {
165
180
  fontSize: 17,
166
181
  fontWeight: 'bold',
182
+ flex: 1,
167
183
  },
168
184
  answer: {
169
185
  fontSize: 16,
@@ -3,6 +3,7 @@ import { View, Text, StyleSheet, ScrollView, ActivityIndicator, TouchableOpacity
3
3
  import { BaseScreenProps } from '../../navigation/types';
4
4
  import { useOxy } from '../../context/OxyContext';
5
5
  import Avatar from '../../components/Avatar';
6
+ import { Header } from '../../components';
6
7
 
7
8
  const KarmaLeaderboardScreen: React.FC<BaseScreenProps> = ({ goBack, theme, navigate }) => {
8
9
  const { oxyServices } = useOxy();
@@ -26,7 +27,13 @@ const KarmaLeaderboardScreen: React.FC<BaseScreenProps> = ({ goBack, theme, navi
26
27
 
27
28
  return (
28
29
  <View style={[styles.container, { backgroundColor }]}>
29
- <Text style={[styles.title, { color: textColor }]}>Karma Leaderboard</Text>
30
+ <Header
31
+ title="Karma Leaderboard"
32
+ subtitle="Top contributors in the community"
33
+ theme={theme}
34
+ onBack={goBack}
35
+ elevation="subtle"
36
+ />
30
37
  {isLoading ? (
31
38
  <ActivityIndicator size="large" color={primaryColor} style={{ marginTop: 40 }} />
32
39
  ) : error ? (
@@ -58,8 +65,7 @@ const KarmaLeaderboardScreen: React.FC<BaseScreenProps> = ({ goBack, theme, navi
58
65
 
59
66
  const styles = StyleSheet.create({
60
67
  container: { flex: 1 },
61
- title: { fontSize: 24, fontWeight: 'bold', margin: 24, textAlign: 'center' },
62
- listContainer: { paddingBottom: 40 },
68
+ listContainer: { paddingBottom: 40, paddingTop: 20 },
63
69
  row: {
64
70
  flexDirection: 'row',
65
71
  alignItems: 'center',
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { View, Text, StyleSheet, ScrollView, Platform } from 'react-native';
3
3
  import { BaseScreenProps } from '../../navigation/types';
4
+ import { Header } from '../../components';
4
5
 
5
6
  const KarmaRewardsScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
6
7
  const isDarkTheme = theme === 'dark';
@@ -11,7 +12,13 @@ const KarmaRewardsScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
11
12
  // Placeholder: In a real app, fetch rewards from API
12
13
  return (
13
14
  <View style={[styles.container, { backgroundColor }]}>
14
- <Text style={[styles.title, { color: textColor }]}>Karma Rewards</Text>
15
+ <Header
16
+ title="Karma Rewards"
17
+ subtitle="Unlock special features and recognition"
18
+ theme={theme}
19
+ onBack={goBack}
20
+ elevation="subtle"
21
+ />
15
22
  <ScrollView contentContainerStyle={styles.contentContainer}>
16
23
  <Text style={[styles.paragraph, { color: textColor }]}>Unlock special features and recognition by earning karma!</Text>
17
24
  <View style={styles.rewardBox}>
@@ -34,8 +41,7 @@ const KarmaRewardsScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
34
41
 
35
42
  const styles = StyleSheet.create({
36
43
  container: { flex: 1 },
37
- title: { fontSize: 24, fontWeight: 'bold', margin: 24, textAlign: 'center' },
38
- contentContainer: { padding: 24 },
44
+ contentContainer: { padding: 24, paddingTop: 20 },
39
45
  rewardBox: {
40
46
  backgroundColor: '#f7eaff',
41
47
  borderRadius: 16,
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
2
2
  import { View, Text, StyleSheet, ScrollView, ActivityIndicator } from 'react-native';
3
3
  import { BaseScreenProps } from '../../navigation/types';
4
4
  import { useOxy } from '../../context/OxyContext';
5
+ import { Header } from '../../components';
5
6
 
6
7
  const KarmaRulesScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
7
8
  const { oxyServices } = useOxy();
@@ -25,7 +26,13 @@ const KarmaRulesScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
25
26
 
26
27
  return (
27
28
  <View style={[styles.container, { backgroundColor }]}>
28
- <Text style={[styles.title, { color: textColor }]}>Karma Rules</Text>
29
+ <Header
30
+ title="Karma Rules"
31
+ subtitle="How to earn karma points"
32
+ theme={theme}
33
+ onBack={goBack}
34
+ elevation="subtle"
35
+ />
29
36
  {isLoading ? (
30
37
  <ActivityIndicator size="large" color={primaryColor} style={{ marginTop: 40 }} />
31
38
  ) : error ? (
@@ -49,8 +56,7 @@ const KarmaRulesScreen: React.FC<BaseScreenProps> = ({ goBack, theme }) => {
49
56
 
50
57
  const styles = StyleSheet.create({
51
58
  container: { flex: 1 },
52
- title: { fontSize: 24, fontWeight: 'bold', margin: 24, textAlign: 'center' },
53
- listContainer: { paddingBottom: 40 },
59
+ listContainer: { paddingBottom: 40, paddingTop: 20 },
54
60
  ruleRow: {
55
61
  paddingVertical: 14,
56
62
  paddingHorizontal: 24,
@@ -6,39 +6,66 @@ interface AuthState {
6
6
  isAuthenticated: boolean;
7
7
  isLoading: boolean;
8
8
  error: string | null;
9
+ lastUserFetch: number | null; // Timestamp of last user fetch for caching
9
10
  loginStart: () => void;
10
11
  loginSuccess: (user: User) => void;
11
12
  loginFailure: (error: string) => void;
12
13
  logout: () => void;
13
- fetchUser: (oxyServices: any) => Promise<void>;
14
+ fetchUser: (oxyServices: any, forceRefresh?: boolean) => Promise<void>;
14
15
  updateUser: (updates: Partial<User>, oxyServices: any) => Promise<void>;
16
+ setUser: (user: User) => void; // Direct user setter for caching
15
17
  }
16
18
 
17
- export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>) => void) => ({
19
+ export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>) => void, get: () => AuthState) => ({
18
20
  user: null,
19
21
  isAuthenticated: false,
20
22
  isLoading: false,
21
23
  error: null,
24
+ lastUserFetch: null,
22
25
  loginStart: () => set({ isLoading: true, error: null }),
23
- loginSuccess: (user: User) => set({ isLoading: false, isAuthenticated: true, user }),
26
+ loginSuccess: (user: User) => set({ isLoading: false, isAuthenticated: true, user, lastUserFetch: Date.now() }),
24
27
  loginFailure: (error: string) => set({ isLoading: false, error }),
25
- logout: () => set({ user: null, isAuthenticated: false }),
26
- fetchUser: async (oxyServices) => {
28
+ logout: () => set({ user: null, isAuthenticated: false, lastUserFetch: null }),
29
+ setUser: (user: User) => set({ user, lastUserFetch: Date.now() }),
30
+ fetchUser: async (oxyServices, forceRefresh = false) => {
31
+ const state = get();
32
+ const now = Date.now();
33
+ const cacheAge = state.lastUserFetch ? now - state.lastUserFetch : Infinity;
34
+ const cacheValid = cacheAge < 5 * 60 * 1000; // 5 minutes cache
35
+
36
+ // Use cached data if available and not forcing refresh
37
+ if (!forceRefresh && state.user && cacheValid) {
38
+ console.log('AuthStore: Using cached user data (age:', cacheAge, 'ms)');
39
+ return;
40
+ }
41
+
27
42
  set({ isLoading: true, error: null });
28
43
  try {
44
+ console.log('AuthStore: Fetching user data...');
29
45
  const user = await oxyServices.getCurrentUser();
30
- set({ user, isLoading: false, isAuthenticated: true });
46
+ console.log('AuthStore: Received user data:', {
47
+ hasUser: !!user,
48
+ userLinksMetadata: user?.linksMetadata,
49
+ userLinks: user?.links,
50
+ userWebsite: user?.website
51
+ });
52
+ set({ user, isLoading: false, isAuthenticated: true, lastUserFetch: now });
31
53
  } catch (error: any) {
54
+ console.error('AuthStore: Error fetching user:', error);
32
55
  set({ error: error.message || 'Failed to fetch user', isLoading: false });
33
56
  }
34
57
  },
35
58
  updateUser: async (updates, oxyServices) => {
36
59
  set({ isLoading: true, error: null });
37
60
  try {
61
+ console.log('AuthStore: Updating user with:', updates);
38
62
  await oxyServices.updateProfile(updates);
63
+ console.log('AuthStore: Profile updated successfully');
39
64
  // Immediately fetch the latest user data after update
40
- await useAuthStore.getState().fetchUser(oxyServices);
65
+ await useAuthStore.getState().fetchUser(oxyServices, true); // Force refresh
66
+ console.log('AuthStore: User data refreshed');
41
67
  } catch (error: any) {
68
+ console.error('AuthStore: Error updating user:', error);
42
69
  set({ error: error.message || 'Failed to update user', isLoading: false });
43
70
  }
44
71
  },
@@ -6,11 +6,21 @@ interface FollowState {
6
6
  loadingUsers: Record<string, boolean>;
7
7
  fetchingUsers: Record<string, boolean>;
8
8
  errors: Record<string, string | null>;
9
+ // Follower counts for each user
10
+ followerCounts: Record<string, number>;
11
+ followingCounts: Record<string, number>;
12
+ // Loading states for counts
13
+ loadingCounts: Record<string, boolean>;
9
14
  setFollowingStatus: (userId: string, isFollowing: boolean) => void;
10
15
  clearFollowError: (userId: string) => void;
11
16
  resetFollowState: () => void;
12
17
  fetchFollowStatus: (userId: string, oxyServices: OxyServices) => Promise<void>;
13
18
  toggleFollowUser: (userId: string, oxyServices: OxyServices, isCurrentlyFollowing: boolean) => Promise<void>;
19
+ // New methods for follower counts
20
+ setFollowerCount: (userId: string, count: number) => void;
21
+ setFollowingCount: (userId: string, count: number) => void;
22
+ updateCountsFromFollowAction: (targetUserId: string, action: 'follow' | 'unfollow', counts: { followers: number; following: number }, currentUserId?: string) => void;
23
+ fetchUserCounts: (userId: string, oxyServices: OxyServices) => Promise<void>;
14
24
  }
15
25
 
16
26
  export const useFollowStore = create<FollowState>((set: any, get: any) => ({
@@ -18,6 +28,9 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
18
28
  loadingUsers: {},
19
29
  fetchingUsers: {},
20
30
  errors: {},
31
+ followerCounts: {},
32
+ followingCounts: {},
33
+ loadingCounts: {},
21
34
  setFollowingStatus: (userId: string, isFollowing: boolean) => set((state: FollowState) => ({
22
35
  followingUsers: { ...state.followingUsers, [userId]: isFollowing },
23
36
  errors: { ...state.errors, [userId]: null },
@@ -30,6 +43,9 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
30
43
  loadingUsers: {},
31
44
  fetchingUsers: {},
32
45
  errors: {},
46
+ followerCounts: {},
47
+ followingCounts: {},
48
+ loadingCounts: {},
33
49
  }),
34
50
  fetchFollowStatus: async (userId: string, oxyServices: OxyServices) => {
35
51
  set((state: FollowState) => ({
@@ -56,7 +72,7 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
56
72
  errors: { ...state.errors, [userId]: null },
57
73
  }));
58
74
  try {
59
- let response;
75
+ let response: any;
60
76
  let newFollowState;
61
77
  if (isCurrentlyFollowing) {
62
78
  response = await oxyServices.unfollowUser(userId);
@@ -65,11 +81,44 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
65
81
  response = await oxyServices.followUser(userId);
66
82
  newFollowState = true;
67
83
  }
84
+
85
+ // Update follow status
68
86
  set((state: FollowState) => ({
69
87
  followingUsers: { ...state.followingUsers, [userId]: newFollowState },
70
88
  loadingUsers: { ...state.loadingUsers, [userId]: false },
71
89
  errors: { ...state.errors, [userId]: null },
72
90
  }));
91
+
92
+ // Update counts if the response includes them
93
+ // The API returns counts for both users:
94
+ // - followers: target user's follower count (the user being followed)
95
+ // - following: current user's following count (the user doing the following)
96
+ if (response && response.counts) {
97
+ const { counts } = response;
98
+
99
+ // Get current user ID from oxyServices
100
+ const currentUserId = oxyServices.getCurrentUserId();
101
+
102
+ set((state: FollowState) => {
103
+ const updates: any = {};
104
+
105
+ // Update target user's follower count (the user being followed)
106
+ updates.followerCounts = {
107
+ ...state.followerCounts,
108
+ [userId]: counts.followers
109
+ };
110
+
111
+ // Update current user's following count (the user doing the following)
112
+ if (currentUserId) {
113
+ updates.followingCounts = {
114
+ ...state.followingCounts,
115
+ [currentUserId]: counts.following
116
+ };
117
+ }
118
+
119
+ return updates;
120
+ });
121
+ }
73
122
  } catch (error: any) {
74
123
  set((state: FollowState) => ({
75
124
  loadingUsers: { ...state.loadingUsers, [userId]: false },
@@ -77,4 +126,56 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
77
126
  }));
78
127
  }
79
128
  },
129
+ setFollowerCount: (userId: string, count: number) => set((state: FollowState) => ({
130
+ followerCounts: { ...state.followerCounts, [userId]: count },
131
+ })),
132
+ setFollowingCount: (userId: string, count: number) => set((state: FollowState) => ({
133
+ followingCounts: { ...state.followingCounts, [userId]: count },
134
+ })),
135
+ updateCountsFromFollowAction: (targetUserId: string, action: 'follow' | 'unfollow', counts: { followers: number; following: number }, currentUserId?: string) => {
136
+ set((state: FollowState) => {
137
+ const updates: any = {};
138
+
139
+ // Update target user's follower count (the user being followed)
140
+ updates.followerCounts = {
141
+ ...state.followerCounts,
142
+ [targetUserId]: counts.followers
143
+ };
144
+
145
+ // Update current user's following count (the user doing the following)
146
+ if (currentUserId) {
147
+ updates.followingCounts = {
148
+ ...state.followingCounts,
149
+ [currentUserId]: counts.following
150
+ };
151
+ }
152
+
153
+ return updates;
154
+ });
155
+ },
156
+ fetchUserCounts: async (userId: string, oxyServices: OxyServices) => {
157
+ set((state: FollowState) => ({
158
+ loadingCounts: { ...state.loadingCounts, [userId]: true },
159
+ }));
160
+ try {
161
+ const user = await oxyServices.getUserById(userId);
162
+ if (user && user._count) {
163
+ set((state: FollowState) => ({
164
+ followerCounts: {
165
+ ...state.followerCounts,
166
+ [userId]: user._count.followers || 0
167
+ },
168
+ followingCounts: {
169
+ ...state.followingCounts,
170
+ [userId]: user._count.following || 0
171
+ },
172
+ loadingCounts: { ...state.loadingCounts, [userId]: false },
173
+ }));
174
+ }
175
+ } catch (error: any) {
176
+ set((state: FollowState) => ({
177
+ loadingCounts: { ...state.loadingCounts, [userId]: false },
178
+ }));
179
+ }
180
+ },
80
181
  }));