@oxyhq/services 10.2.10 → 10.3.0

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 (220) hide show
  1. package/lib/commonjs/index.js.map +1 -1
  2. package/lib/commonjs/ui/components/ActingAsBanner.js +2 -1
  3. package/lib/commonjs/ui/components/ActingAsBanner.js.map +1 -1
  4. package/lib/commonjs/ui/components/FollowButton.js +107 -1
  5. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  6. package/lib/commonjs/ui/components/StepBasedScreen.js +19 -10
  7. package/lib/commonjs/ui/components/StepBasedScreen.js.map +1 -1
  8. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +38 -122
  9. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  10. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +9 -7
  11. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  12. package/lib/commonjs/ui/hooks/useDeviceAccounts.js +20 -25
  13. package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +1 -1
  14. package/lib/commonjs/ui/hooks/useFollow.js +7 -0
  15. package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
  16. package/lib/commonjs/ui/hooks/useFollow.types.js +4 -0
  17. package/lib/commonjs/ui/hooks/useProfileEditing.js +19 -10
  18. package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
  19. package/lib/commonjs/ui/screens/AppInfoScreen.js +1 -2
  20. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  21. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +1 -2
  22. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  23. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +11 -13
  24. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  25. package/lib/commonjs/ui/screens/FeedbackScreen.js +2 -3
  26. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  27. package/lib/commonjs/ui/screens/HelpSupportScreen.js +2 -2
  28. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
  29. package/lib/commonjs/ui/screens/HistoryViewScreen.js +2 -2
  30. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  31. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +2 -3
  32. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +2 -2
  34. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/ManageAccountScreen.js +1 -2
  36. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/NotificationsScreen.js +2 -2
  38. package/lib/commonjs/ui/screens/NotificationsScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +2 -2
  40. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/PreferencesScreen.js +2 -2
  42. package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/ProfileScreen.js +4 -3
  44. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +3 -4
  46. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +2 -2
  48. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/trust/TrustFAQScreen.js +2 -2
  50. package/lib/commonjs/ui/screens/trust/TrustFAQScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +2 -2
  52. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  53. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  54. package/lib/commonjs/ui/stores/followStore.js +68 -0
  55. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  56. package/lib/commonjs/ui/utils/avatarUtils.js +1 -1
  57. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/ui/components/ActingAsBanner.js +2 -1
  60. package/lib/module/ui/components/ActingAsBanner.js.map +1 -1
  61. package/lib/module/ui/components/FollowButton.js +109 -3
  62. package/lib/module/ui/components/FollowButton.js.map +1 -1
  63. package/lib/module/ui/components/StepBasedScreen.js +19 -10
  64. package/lib/module/ui/components/StepBasedScreen.js.map +1 -1
  65. package/lib/module/ui/context/hooks/useAuthOperations.js +38 -122
  66. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  67. package/lib/module/ui/hooks/mutations/useAccountMutations.js +9 -7
  68. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  69. package/lib/module/ui/hooks/useDeviceAccounts.js +20 -25
  70. package/lib/module/ui/hooks/useDeviceAccounts.js.map +1 -1
  71. package/lib/module/ui/hooks/useFollow.js +7 -0
  72. package/lib/module/ui/hooks/useFollow.js.map +1 -1
  73. package/lib/module/ui/hooks/useFollow.types.js +2 -0
  74. package/lib/module/ui/hooks/useProfileEditing.js +19 -10
  75. package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
  76. package/lib/module/ui/screens/AppInfoScreen.js +1 -1
  77. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  78. package/lib/module/ui/screens/ConnectedAppsScreen.js +1 -1
  79. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  80. package/lib/module/ui/screens/EditProfileFieldScreen.js +10 -11
  81. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  82. package/lib/module/ui/screens/FeedbackScreen.js +1 -1
  83. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  84. package/lib/module/ui/screens/HelpSupportScreen.js +1 -1
  85. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
  86. package/lib/module/ui/screens/HistoryViewScreen.js +1 -1
  87. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  88. package/lib/module/ui/screens/LanguageSelectorScreen.js +1 -1
  89. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  90. package/lib/module/ui/screens/LegalDocumentsScreen.js +1 -1
  91. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
  92. package/lib/module/ui/screens/ManageAccountScreen.js +1 -1
  93. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  94. package/lib/module/ui/screens/NotificationsScreen.js +1 -1
  95. package/lib/module/ui/screens/NotificationsScreen.js.map +1 -1
  96. package/lib/module/ui/screens/PaymentGatewayScreen.js +1 -1
  97. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  98. package/lib/module/ui/screens/PreferencesScreen.js +1 -1
  99. package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
  100. package/lib/module/ui/screens/ProfileScreen.js +5 -4
  101. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  102. package/lib/module/ui/screens/SavesCollectionsScreen.js +2 -3
  103. package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
  104. package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
  105. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  106. package/lib/module/ui/screens/trust/TrustFAQScreen.js +1 -1
  107. package/lib/module/ui/screens/trust/TrustFAQScreen.js.map +1 -1
  108. package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
  109. package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  110. package/lib/module/ui/stores/authStore.js.map +1 -1
  111. package/lib/module/ui/stores/followStore.js +68 -0
  112. package/lib/module/ui/stores/followStore.js.map +1 -1
  113. package/lib/module/ui/utils/avatarUtils.js +1 -1
  114. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  115. package/lib/typescript/commonjs/index.d.ts +1 -0
  116. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/ui/components/ActingAsBanner.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts +24 -4
  119. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/ui/components/StepBasedScreen.d.ts +5 -3
  121. package/lib/typescript/commonjs/ui/components/StepBasedScreen.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +1 -1
  123. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +52 -6
  125. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts +3 -1
  128. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/hooks/useFollow.types.d.ts +2 -0
  130. package/lib/typescript/commonjs/ui/hooks/useFollow.types.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts +22 -18
  132. package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  134. package/lib/typescript/commonjs/ui/screens/SavesCollectionsScreen.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/ui/stores/authStore.d.ts +2 -10
  136. package/lib/typescript/commonjs/ui/stores/authStore.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/ui/stores/followStore.d.ts +2 -1
  138. package/lib/typescript/commonjs/ui/stores/followStore.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/ui/types/navigation.d.ts +0 -1
  140. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts +2 -1
  142. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
  143. package/lib/typescript/module/index.d.ts +1 -0
  144. package/lib/typescript/module/index.d.ts.map +1 -1
  145. package/lib/typescript/module/ui/components/ActingAsBanner.d.ts.map +1 -1
  146. package/lib/typescript/module/ui/components/FollowButton.d.ts +24 -4
  147. package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/components/StepBasedScreen.d.ts +5 -3
  149. package/lib/typescript/module/ui/components/StepBasedScreen.d.ts.map +1 -1
  150. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +1 -1
  151. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +52 -6
  153. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  154. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +1 -1
  155. package/lib/typescript/module/ui/hooks/useFollow.d.ts +3 -1
  156. package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
  157. package/lib/typescript/module/ui/hooks/useFollow.types.d.ts +2 -0
  158. package/lib/typescript/module/ui/hooks/useFollow.types.d.ts.map +1 -1
  159. package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts +22 -18
  160. package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
  161. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  162. package/lib/typescript/module/ui/screens/SavesCollectionsScreen.d.ts.map +1 -1
  163. package/lib/typescript/module/ui/stores/authStore.d.ts +2 -10
  164. package/lib/typescript/module/ui/stores/authStore.d.ts.map +1 -1
  165. package/lib/typescript/module/ui/stores/followStore.d.ts +2 -1
  166. package/lib/typescript/module/ui/stores/followStore.d.ts.map +1 -1
  167. package/lib/typescript/module/ui/types/navigation.d.ts +0 -1
  168. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  169. package/lib/typescript/module/ui/utils/avatarUtils.d.ts +2 -1
  170. package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
  171. package/package.json +3 -2
  172. package/src/index.ts +1 -0
  173. package/src/ui/components/ActingAsBanner.tsx +2 -4
  174. package/src/ui/components/FollowButton.tsx +154 -10
  175. package/src/ui/components/StepBasedScreen.tsx +37 -22
  176. package/src/ui/context/hooks/useAuthOperations.ts +51 -144
  177. package/src/ui/hooks/mutations/useAccountMutations.ts +9 -8
  178. package/src/ui/hooks/useDeviceAccounts.ts +20 -24
  179. package/src/ui/hooks/useFollow.ts +8 -1
  180. package/src/ui/hooks/useFollow.types.ts +3 -0
  181. package/src/ui/hooks/useProfileEditing.ts +43 -30
  182. package/src/ui/screens/AppInfoScreen.tsx +1 -1
  183. package/src/ui/screens/ConnectedAppsScreen.tsx +1 -1
  184. package/src/ui/screens/EditProfileFieldScreen.tsx +24 -14
  185. package/src/ui/screens/FeedbackScreen.tsx +1 -1
  186. package/src/ui/screens/HelpSupportScreen.tsx +1 -1
  187. package/src/ui/screens/HistoryViewScreen.tsx +1 -1
  188. package/src/ui/screens/LanguageSelectorScreen.tsx +1 -1
  189. package/src/ui/screens/LegalDocumentsScreen.tsx +1 -1
  190. package/src/ui/screens/ManageAccountScreen.tsx +1 -1
  191. package/src/ui/screens/NotificationsScreen.tsx +1 -1
  192. package/src/ui/screens/PaymentGatewayScreen.tsx +1 -1
  193. package/src/ui/screens/PreferencesScreen.tsx +1 -1
  194. package/src/ui/screens/ProfileScreen.tsx +4 -4
  195. package/src/ui/screens/SavesCollectionsScreen.tsx +3 -6
  196. package/src/ui/screens/trust/TrustCenterScreen.tsx +1 -1
  197. package/src/ui/screens/trust/TrustFAQScreen.tsx +1 -1
  198. package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
  199. package/src/ui/stores/authStore.ts +4 -13
  200. package/src/ui/stores/followStore.ts +47 -1
  201. package/src/ui/types/navigation.ts +0 -4
  202. package/src/ui/utils/avatarUtils.ts +3 -2
  203. package/lib/commonjs/ui/styles/spacing.js +0 -68
  204. package/lib/commonjs/ui/styles/spacing.js.map +0 -1
  205. package/lib/commonjs/ui/utils/themeUtils.js +0 -37
  206. package/lib/commonjs/ui/utils/themeUtils.js.map +0 -1
  207. package/lib/module/ui/styles/spacing.js +0 -16
  208. package/lib/module/ui/styles/spacing.js.map +0 -1
  209. package/lib/module/ui/utils/themeUtils.js +0 -13
  210. package/lib/module/ui/utils/themeUtils.js.map +0 -1
  211. package/lib/typescript/commonjs/ui/styles/spacing.d.ts +0 -13
  212. package/lib/typescript/commonjs/ui/styles/spacing.d.ts.map +0 -1
  213. package/lib/typescript/commonjs/ui/utils/themeUtils.d.ts +0 -11
  214. package/lib/typescript/commonjs/ui/utils/themeUtils.d.ts.map +0 -1
  215. package/lib/typescript/module/ui/styles/spacing.d.ts +0 -13
  216. package/lib/typescript/module/ui/styles/spacing.d.ts.map +0 -1
  217. package/lib/typescript/module/ui/utils/themeUtils.d.ts +0 -11
  218. package/lib/typescript/module/ui/utils/themeUtils.d.ts.map +0 -1
  219. package/src/ui/styles/spacing.ts +0 -22
  220. package/src/ui/utils/themeUtils.ts +0 -18
@@ -1,38 +1,51 @@
1
1
  import { useCallback } from 'react';
2
- import { useI18n } from './useI18n';
3
2
  import { useUpdateProfile } from './mutations/useAccountMutations';
4
3
  import { useAuthStore } from '../stores/authStore';
4
+ import type { UserProfileUpdate } from '@oxyhq/contracts';
5
+
6
+ interface ProfileLocation {
7
+ id: string;
8
+ name: string;
9
+ label?: string;
10
+ coordinates?: { lat: number; lon: number };
11
+ }
12
+
13
+ interface ProfileLinkMetadata {
14
+ url: string;
15
+ title?: string;
16
+ description?: string;
17
+ image?: string;
18
+ id: string;
19
+ }
5
20
 
6
21
  export interface ProfileUpdateData {
7
- displayName?: string;
22
+ firstName?: string;
8
23
  lastName?: string;
9
24
  username?: string;
10
25
  email?: string;
11
26
  bio?: string;
12
27
  location?: string;
13
- locations?: Array<{
14
- id: string;
15
- name: string;
16
- label?: string;
17
- coordinates?: { lat: number; lon: number };
18
- }>;
28
+ locations?: ProfileLocation[];
19
29
  links?: string[];
20
- linksMetadata?: Array<{
21
- url: string;
22
- title?: string;
23
- description?: string;
24
- image?: string;
25
- id: string;
26
- }>;
30
+ linksMetadata?: ProfileLinkMetadata[];
27
31
  avatar?: string;
28
32
  }
29
33
 
34
+ type ProfileFieldValue = string | ProfileLocation[] | ProfileLinkMetadata[];
35
+
36
+ function isProfileLocationArray(value: ProfileFieldValue): value is ProfileLocation[] {
37
+ return Array.isArray(value) && value.every((item) => typeof item === 'object' && item !== null && 'name' in item);
38
+ }
39
+
40
+ function isProfileLinkMetadataArray(value: ProfileFieldValue): value is ProfileLinkMetadata[] {
41
+ return Array.isArray(value) && value.every((item) => typeof item === 'object' && item !== null && 'url' in item);
42
+ }
43
+
30
44
  /**
31
45
  * Hook for managing profile editing operations
32
46
  * Provides functions to update profile fields and handle saving
33
47
  */
34
48
  export const useProfileEditing = () => {
35
- const { t } = useI18n();
36
49
  const updateProfileMutation = useUpdateProfile();
37
50
 
38
51
  /**
@@ -40,7 +53,7 @@ export const useProfileEditing = () => {
40
53
  */
41
54
  const saveProfile = useCallback(async (updates: ProfileUpdateData) => {
42
55
  // Prepare update object
43
- const updateData: Record<string, any> = {};
56
+ const updateData: UserProfileUpdate = {};
44
57
 
45
58
  if (updates.username !== undefined) {
46
59
  updateData.username = updates.username;
@@ -70,11 +83,11 @@ export const useProfileEditing = () => {
70
83
  }
71
84
 
72
85
  // Handle name field
73
- if (updates.displayName !== undefined || updates.lastName !== undefined) {
86
+ if (updates.firstName !== undefined || updates.lastName !== undefined) {
74
87
  const currentUser = useAuthStore.getState().user;
75
88
  const currentName = currentUser?.name;
76
89
  updateData.name = {
77
- first: updates.displayName ?? (typeof currentName === 'object' ? currentName?.first : '') ?? '',
90
+ first: updates.firstName ?? (typeof currentName === 'object' ? currentName?.first : '') ?? '',
78
91
  last: updates.lastName ?? (typeof currentName === 'object' ? currentName?.last : '') ?? '',
79
92
  };
80
93
  }
@@ -86,33 +99,39 @@ export const useProfileEditing = () => {
86
99
  // Error toast is handled by the mutation
87
100
  return false;
88
101
  }
89
- }, [updateProfileMutation, t]);
102
+ }, [updateProfileMutation]);
90
103
 
91
104
  /**
92
105
  * Update a single profile field
93
106
  */
94
- const updateField = useCallback(async (field: string, value: any) => {
107
+ const updateField = useCallback(async (field: string, value: ProfileFieldValue) => {
95
108
  const updates: ProfileUpdateData = {};
96
109
 
97
110
  switch (field) {
98
- case 'displayName':
99
- updates.displayName = value;
111
+ case 'firstName':
112
+ if (typeof value !== 'string') return false;
113
+ updates.firstName = value;
100
114
  break;
101
115
  case 'username':
116
+ if (typeof value !== 'string') return false;
102
117
  updates.username = value;
103
118
  break;
104
119
  case 'email':
120
+ if (typeof value !== 'string') return false;
105
121
  updates.email = value;
106
122
  break;
107
123
  case 'bio':
124
+ if (typeof value !== 'string') return false;
108
125
  updates.bio = value;
109
126
  break;
110
127
  case 'location':
128
+ if (!isProfileLocationArray(value)) return false;
111
129
  updates.locations = value;
112
130
  break;
113
131
  case 'links':
132
+ if (!isProfileLinkMetadataArray(value)) return false;
114
133
  updates.linksMetadata = value;
115
- updates.links = value.map((link: any) => link.url || link);
134
+ updates.links = value.map((link) => link.url);
116
135
  break;
117
136
  default:
118
137
  return false;
@@ -129,9 +148,3 @@ export const useProfileEditing = () => {
129
148
  };
130
149
 
131
150
 
132
-
133
-
134
-
135
-
136
-
137
-
@@ -20,7 +20,7 @@ import { SettingsIcon } from '../components/SettingsIcon';
20
20
  import { useTheme } from '@oxyhq/bloom/theme';
21
21
  import { useColorScheme } from '../hooks/useColorScheme';
22
22
  import { Colors } from '../constants/theme';
23
- import { normalizeColorScheme } from '../utils/themeUtils';
23
+ import { normalizeColorScheme } from '@oxyhq/core';
24
24
  import { useOxy } from '../context/OxyContext';
25
25
  import { useI18n } from '../hooks/useI18n';
26
26
  import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list';
@@ -15,7 +15,7 @@ import { useAuthorizedApps } from '../hooks/queries/useAccountQueries';
15
15
  import { useRevokeAuthorizedApp } from '../hooks/mutations/useAccountMutations';
16
16
  import { useColorScheme } from '../hooks/useColorScheme';
17
17
  import { Colors } from '../constants/theme';
18
- import { normalizeColorScheme, normalizeTheme } from '../utils/themeUtils';
18
+ import { normalizeColorScheme, normalizeTheme } from '@oxyhq/core';
19
19
 
20
20
  /**
21
21
  * Format an ISO-8601 timestamp as a human-readable relative time. Mirrors the
@@ -14,7 +14,7 @@ import {
14
14
  import { Ionicons } from '@expo/vector-icons';
15
15
  import type { BaseScreenProps } from '../types/navigation';
16
16
  import { useTheme } from '@oxyhq/bloom/theme';
17
- import { normalizeTheme } from '../utils/themeUtils';
17
+ import { normalizeTheme } from '@oxyhq/core';
18
18
  import Header from '../components/Header';
19
19
  import { useI18n } from '../hooks/useI18n';
20
20
  import { useOxy } from '../context/OxyContext';
@@ -59,6 +59,17 @@ interface EditProfileFieldScreenProps extends BaseScreenProps {
59
59
  fieldType?: ProfileFieldType;
60
60
  }
61
61
 
62
+ type EditableListItem = {
63
+ id: string;
64
+ name?: string;
65
+ label?: string;
66
+ url?: string;
67
+ title?: string;
68
+ description?: string;
69
+ image?: string;
70
+ coordinates?: { lat: number; lon: number };
71
+ };
72
+
62
73
  /**
63
74
  * EditProfileFieldScreen - A dedicated screen for editing profile fields
64
75
  *
@@ -84,7 +95,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
84
95
  const [fieldErrors, setFieldErrors] = useState<Record<string, string | undefined>>({});
85
96
 
86
97
  // State for list fields (locations, links)
87
- const [listItems, setListItems] = useState<Array<{ id: string; [key: string]: unknown }>>([]);
98
+ const [listItems, setListItems] = useState<EditableListItem[]>([]);
88
99
  const [newItemValue, setNewItemValue] = useState('');
89
100
 
90
101
  // Get field configuration based on fieldType
@@ -96,7 +107,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
96
107
  subtitle: t('editProfile.items.displayName.subtitle') || 'This is how your name will appear to others',
97
108
  fields: [
98
109
  {
99
- key: 'displayName',
110
+ key: 'firstName',
100
111
  label: t('editProfile.items.displayName.firstName') || 'First Name',
101
112
  placeholder: t('editProfile.items.displayName.firstNamePlaceholder') || 'Enter first name',
102
113
  },
@@ -255,30 +266,29 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
255
266
 
256
267
  if (fieldConfig.isList) {
257
268
  if (fieldType === 'locations') {
258
- const locations = Array.isArray(userData.locations) ? userData.locations as Array<Record<string, unknown>> : [];
269
+ const locations = Array.isArray(userData.locations) ? userData.locations : [];
259
270
  setListItems(locations.map((loc, i) => ({
260
271
  id: String(loc.id || `location-${i}`),
261
272
  name: String(loc.name || ''),
262
273
  ...loc,
263
274
  })));
264
275
  } else if (fieldType === 'links') {
265
- const linksMetadata = Array.isArray(userData.linksMetadata) ? userData.linksMetadata as Array<Record<string, unknown>> : [];
276
+ const linksMetadata = Array.isArray(userData.linksMetadata) ? userData.linksMetadata : [];
266
277
  const links = Array.isArray(userData.links) ? userData.links : [];
267
278
  // Use linksMetadata if available, otherwise convert links array
268
279
  if (linksMetadata.length > 0) {
269
280
  setListItems(linksMetadata.map((link, i) => ({
281
+ ...link,
270
282
  id: String(link.id || `link-${i}`),
271
- url: String(link.url || link.link || ''),
283
+ url: String(link.url || ''),
272
284
  title: String(link.title || ''),
273
- ...link,
274
285
  })));
275
286
  } else {
276
287
  setListItems(links.map((item, i) => {
277
- const url = typeof item === 'string' ? item : (item.link || '');
278
288
  return {
279
289
  id: `link-${i}`,
280
- url,
281
- title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''),
290
+ url: item,
291
+ title: item.replace(/^https?:\/\//, '').replace(/\/$/, ''),
282
292
  };
283
293
  }));
284
294
  }
@@ -286,8 +296,8 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
286
296
  } else {
287
297
  const initialValues: Record<string, string> = {};
288
298
  fieldConfig.fields.forEach(field => {
289
- if (field.key === 'displayName') {
290
- initialValues[field.key] = String(userData.displayName || userData.name?.first || '');
299
+ if (field.key === 'firstName') {
300
+ initialValues[field.key] = String(userData.name?.first || '');
291
301
  } else if (field.key === 'lastName') {
292
302
  initialValues[field.key] = String(userData.lastName || userData.name?.last || '');
293
303
  } else if (field.key === 'birthday') {
@@ -390,7 +400,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
390
400
  let success = false;
391
401
  if (fieldType === 'displayName') {
392
402
  success = await saveProfile({
393
- displayName: fieldValues.displayName,
403
+ firstName: fieldValues.firstName,
394
404
  lastName: fieldValues.lastName,
395
405
  });
396
406
  } else {
@@ -496,7 +506,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
496
506
  <Text style={[styles.listTitle, { color: bloomTheme.colors.text }]}>
497
507
  {listTitle} ({listItems.length})
498
508
  </Text>
499
- {listItems.map((item: any) => (
509
+ {listItems.map((item) => (
500
510
  <View
501
511
  key={item.id}
502
512
  style={[
@@ -13,7 +13,7 @@ import {
13
13
  } from 'react-native';
14
14
  import type { BaseScreenProps } from '../types/navigation';
15
15
  import { useThemeColors } from '../styles/theme';
16
- import { normalizeTheme } from '../utils/themeUtils';
16
+ import { normalizeTheme } from '@oxyhq/core';
17
17
  import { useTheme } from '@oxyhq/bloom/theme';
18
18
  import { Ionicons } from '@expo/vector-icons';
19
19
  import { toast } from '@oxyhq/bloom';
@@ -13,7 +13,7 @@ import { useI18n } from '../hooks/useI18n';
13
13
  import { useTheme } from '@oxyhq/bloom/theme';
14
14
  import { useColorScheme } from '../hooks/useColorScheme';
15
15
  import { Colors } from '../constants/theme';
16
- import { normalizeColorScheme } from '../utils/themeUtils';
16
+ import { normalizeColorScheme } from '@oxyhq/core';
17
17
  import { SettingsListGroup, SettingsListItem } from '@oxyhq/bloom/settings-list';
18
18
 
19
19
  const HelpSupportScreen: React.FC<BaseScreenProps> = ({
@@ -11,7 +11,7 @@ import { useI18n } from '../hooks/useI18n';
11
11
  import { useTheme } from '@oxyhq/bloom/theme';
12
12
  import { useColorScheme } from '../hooks/useColorScheme';
13
13
  import { Colors } from '../constants/theme';
14
- import { normalizeColorScheme } from '../utils/themeUtils';
14
+ import { normalizeColorScheme } from '@oxyhq/core';
15
15
  import { useOxy } from '../context/OxyContext';
16
16
  import { Dialog, useDialogControl } from '@oxyhq/bloom';
17
17
 
@@ -9,7 +9,7 @@ import {
9
9
  } from 'react-native';
10
10
  import type { BaseScreenProps } from '../types/navigation';
11
11
  import { useTheme } from '@oxyhq/bloom/theme';
12
- import { normalizeTheme } from '../utils/themeUtils';
12
+ import { normalizeTheme } from '@oxyhq/core';
13
13
  import { Ionicons } from '@expo/vector-icons';
14
14
  import { toast } from '@oxyhq/bloom';
15
15
  import Header from '../components/Header';
@@ -15,7 +15,7 @@ import { useI18n } from '../hooks/useI18n';
15
15
  import { useTheme } from '@oxyhq/bloom/theme';
16
16
  import { useColorScheme } from '../hooks/useColorScheme';
17
17
  import { Colors } from '../constants/theme';
18
- import { normalizeColorScheme } from '../utils/themeUtils';
18
+ import { normalizeColorScheme } from '@oxyhq/core';
19
19
 
20
20
  const LegalDocumentsScreen: React.FC<BaseScreenProps> = ({
21
21
  onClose,
@@ -32,7 +32,7 @@ import { useUserSubscription } from '../hooks/queries/usePaymentQueries';
32
32
  import { useDeviceSessions } from '../hooks/queries/useServicesQueries';
33
33
  import { useColorScheme } from '../hooks/useColorScheme';
34
34
  import { Colors } from '../constants/theme';
35
- import { normalizeColorScheme, normalizeTheme } from '../utils/themeUtils';
35
+ import { normalizeColorScheme, normalizeTheme } from '@oxyhq/core';
36
36
  import { screenContentStyle } from '../constants/spacing';
37
37
 
38
38
  interface DeviceSessionRow {
@@ -14,7 +14,7 @@ import { useUpdateNotificationPreferences } from '../hooks/mutations/useAccountM
14
14
  import { useSettingToggles } from '../hooks/useSettingToggle';
15
15
  import { useColorScheme } from '../hooks/useColorScheme';
16
16
  import { Colors } from '../constants/theme';
17
- import { normalizeColorScheme, normalizeTheme } from '../utils/themeUtils';
17
+ import { normalizeColorScheme, normalizeTheme } from '@oxyhq/core';
18
18
 
19
19
  interface NotificationToggleValues {
20
20
  pushEnabled: boolean;
@@ -10,7 +10,7 @@ import {
10
10
  } from 'react-native';
11
11
  import type { BaseScreenProps } from '../types/navigation';
12
12
  import { useThemeColors } from '../styles/theme';
13
- import { normalizeTheme } from '../utils/themeUtils';
13
+ import { normalizeTheme } from '@oxyhq/core';
14
14
  import { Button } from '@oxyhq/bloom/button';
15
15
  import { Ionicons } from '@expo/vector-icons';
16
16
  import { useI18n } from '../hooks/useI18n';
@@ -13,7 +13,7 @@ import { useCurrentUser } from '../hooks/queries/useAccountQueries';
13
13
  import { useUpdateUserPreferences } from '../hooks/mutations/useAccountMutations';
14
14
  import { useColorScheme } from '../hooks/useColorScheme';
15
15
  import { Colors } from '../constants/theme';
16
- import { normalizeColorScheme, normalizeTheme } from '../utils/themeUtils';
16
+ import { normalizeColorScheme, normalizeTheme } from '@oxyhq/core';
17
17
 
18
18
  type ThemePreference = 'light' | 'dark' | 'system';
19
19
 
@@ -9,7 +9,7 @@ import { useFollow } from '../hooks/useFollow';
9
9
  import { Ionicons } from '@expo/vector-icons';
10
10
  import { useI18n } from '../hooks/useI18n';
11
11
  import { useOxy } from '../context/OxyContext';
12
- import { logger } from '@oxyhq/core';
12
+ import { getAccountDisplayName, logger } from '@oxyhq/core';
13
13
  import type { User } from '@oxyhq/core';
14
14
  import { extractErrorMessage } from '../utils/errorHandlers';
15
15
 
@@ -54,7 +54,7 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ userId, username, theme,
54
54
 
55
55
  const bloomTheme = useTheme();
56
56
  const styles = createStyles();
57
- const { t } = useI18n();
57
+ const { t, locale } = useI18n();
58
58
 
59
59
  // Check if current user is viewing their own profile
60
60
  // Normalize IDs by trimming whitespace to handle format mismatches
@@ -214,7 +214,7 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ userId, username, theme,
214
214
  <View style={styles.avatarWrapper} className="border-background bg-background">
215
215
  <Avatar
216
216
  uri={profile?.avatar ? oxyServices.getFileDownloadUrl(profile.avatar, 'thumb') : undefined}
217
- name={profile?.username || username}
217
+ name={profile ? getAccountDisplayName(profile, locale) : username}
218
218
  size={96}
219
219
 
220
220
  />
@@ -246,7 +246,7 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ userId, username, theme,
246
246
  {/* Profile Info */}
247
247
  <View style={styles.header}>
248
248
  <Text style={styles.displayName} className="text-foreground">
249
- {(profile && 'displayName' in profile && typeof profile.displayName === 'string' ? profile.displayName : null) || profile?.username || username || profile?.id || ''}
249
+ {profile ? getAccountDisplayName(profile, locale) : username || ''}
250
250
  </Text>
251
251
  {profile?.username && (
252
252
  <Text style={styles.subText} className="text-muted-foreground">@{profile.username}</Text>
@@ -17,7 +17,7 @@ import { useI18n } from '../hooks/useI18n';
17
17
  import { useTheme } from '@oxyhq/bloom/theme';
18
18
  import { useColorScheme } from '../hooks/useColorScheme';
19
19
  import { Colors } from '../constants/theme';
20
- import { normalizeColorScheme } from '../utils/themeUtils';
20
+ import { normalizeColorScheme } from '@oxyhq/core';
21
21
  import { useOxy } from '../context/OxyContext';
22
22
 
23
23
  interface SavedItem {
@@ -25,7 +25,6 @@ interface SavedItem {
25
25
  title: string;
26
26
  type: 'post' | 'collection';
27
27
  savedAt: Date;
28
- url?: string;
29
28
  }
30
29
 
31
30
  interface Collection {
@@ -67,14 +66,13 @@ const SavesCollectionsScreen: React.FC<BaseScreenProps> = ({
67
66
  oxyServices.getSavedItems(user.id),
68
67
  oxyServices.getCollections(user.id),
69
68
  ]);
70
- setSavedItems(saved.map((item: any) => ({
69
+ setSavedItems(saved.map((item) => ({
71
70
  id: item.id,
72
71
  title: item.title,
73
72
  type: item.itemType === 'post' ? 'post' : 'collection',
74
73
  savedAt: new Date(item.createdAt),
75
- url: item.url,
76
74
  })));
77
- setCollections(cols.map((col: any) => ({
75
+ setCollections(cols.map((col) => ({
78
76
  id: col.id,
79
77
  name: col.name,
80
78
  description: col.description,
@@ -220,4 +218,3 @@ const styles = StyleSheet.create({
220
218
  });
221
219
 
222
220
  export default React.memo(SavesCollectionsScreen);
223
-
@@ -16,7 +16,7 @@ import { useI18n } from '../../hooks/useI18n';
16
16
  import { useTheme } from '@oxyhq/bloom/theme';
17
17
  import { useColorScheme } from '../../hooks/useColorScheme';
18
18
  import { Colors } from '../../constants/theme';
19
- import { normalizeColorScheme } from '../../utils/themeUtils';
19
+ import { normalizeColorScheme } from '@oxyhq/core';
20
20
  import { darkenColor } from '../../utils/colorUtils';
21
21
  import { useOxy } from '../../context/OxyContext';
22
22
  import { getTrustTierLabel } from './trustTier';
@@ -6,7 +6,7 @@ import Header from '../../components/Header';
6
6
  import { SettingsListItem } from '@oxyhq/bloom/settings-list';
7
7
  import { useI18n } from '../../hooks/useI18n';
8
8
  import { useTheme } from '@oxyhq/bloom/theme';
9
- import { normalizeColorScheme } from '../../utils/themeUtils';
9
+ import { normalizeColorScheme } from '@oxyhq/core';
10
10
  import { useColorScheme } from '../../hooks/useColorScheme';
11
11
  import { Colors } from '../../constants/theme';
12
12
 
@@ -6,7 +6,7 @@ import Header from '../../components/Header';
6
6
  import { Ionicons } from '@expo/vector-icons';
7
7
  import { useI18n } from '../../hooks/useI18n';
8
8
  import { useTheme } from '@oxyhq/bloom/theme';
9
- import { normalizeColorScheme } from '../../utils/themeUtils';
9
+ import { normalizeColorScheme } from '@oxyhq/core';
10
10
  import { useColorScheme } from '../../hooks/useColorScheme';
11
11
  import { Colors } from '../../constants/theme';
12
12
  import { useOxy } from '../../context/OxyContext';
@@ -4,15 +4,6 @@ import type { User } from '@oxyhq/core';
4
4
 
5
5
  const debug = createDebugLogger('AuthStore');
6
6
 
7
- type LegacyUserIdentity = Partial<Omit<User, 'id' | 'publicKey' | 'username'>> & {
8
- id?: string;
9
- _id: string;
10
- publicKey: string;
11
- username: string;
12
- };
13
-
14
- type AuthStoreUserInput = User | LegacyUserIdentity;
15
-
16
7
  export interface AuthState {
17
8
  user: User | null;
18
9
  isAuthenticated: boolean;
@@ -20,11 +11,11 @@ export interface AuthState {
20
11
  error: string | null;
21
12
  lastUserFetch: number | null; // Timestamp of last user fetch for caching
22
13
 
23
- loginSuccess: (user: AuthStoreUserInput) => void;
14
+ loginSuccess: (user: User) => void;
24
15
  loginFailure: (error: string) => void;
25
16
  logout: () => void;
26
17
  fetchUser: (oxyServices: { getCurrentUser: () => Promise<User> }, forceRefresh?: boolean) => Promise<void>;
27
- setUser: (user: AuthStoreUserInput) => void; // Direct user setter for caching
18
+ setUser: (user: User) => void; // Direct user setter for caching
28
19
  }
29
20
 
30
21
  export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>) => void, get: () => AuthState) => ({
@@ -34,7 +25,7 @@ export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>)
34
25
  error: null,
35
26
  lastUserFetch: null,
36
27
 
37
- loginSuccess: (user: AuthStoreUserInput) => set({
28
+ loginSuccess: (user: User) => set({
38
29
  isLoading: false,
39
30
  isAuthenticated: true,
40
31
  user: normalizeUserIdentity(user),
@@ -46,7 +37,7 @@ export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>)
46
37
  isAuthenticated: false,
47
38
  lastUserFetch: null,
48
39
  }),
49
- setUser: (user: AuthStoreUserInput) => set({ user: normalizeUserIdentity(user), lastUserFetch: Date.now() }),
40
+ setUser: (user: User) => set({ user: normalizeUserIdentity(user), lastUserFetch: Date.now() }),
50
41
  fetchUser: async (oxyServices, forceRefresh = false) => {
51
42
  const state = get();
52
43
  const now = Date.now();
@@ -1,5 +1,5 @@
1
1
  import { create } from 'zustand';
2
- import type { OxyServices } from '@oxyhq/core';
2
+ import type { OxyServices, BulkFollowResult } from '@oxyhq/core';
3
3
 
4
4
  interface FollowState {
5
5
  followingUsers: Record<string, boolean>;
@@ -16,6 +16,8 @@ interface FollowState {
16
16
  resetFollowState: () => void;
17
17
  fetchFollowStatus: (userId: string, oxyServices: OxyServices) => Promise<void>;
18
18
  toggleFollowUser: (userId: string, oxyServices: OxyServices, isCurrentlyFollowing: boolean) => Promise<void>;
19
+ // Bulk follow — follows MANY users in one network call; never unfollows.
20
+ followManyUsers: (userIds: string[], oxyServices: OxyServices) => Promise<BulkFollowResult>;
19
21
  // New methods for follower counts
20
22
  setFollowerCount: (userId: string, count: number) => void;
21
23
  setFollowingCount: (userId: string, count: number) => void;
@@ -126,6 +128,50 @@ export const useFollowStore = create<FollowState>((set: any, get: any) => ({
126
128
  }));
127
129
  }
128
130
  },
131
+ followManyUsers: async (userIds: string[], oxyServices: OxyServices): Promise<BulkFollowResult> => {
132
+ set((state: FollowState) => {
133
+ const loadingUsers = { ...state.loadingUsers };
134
+ const errors = { ...state.errors };
135
+ for (const uid of userIds) {
136
+ loadingUsers[uid] = true;
137
+ errors[uid] = null;
138
+ }
139
+ return { loadingUsers, errors };
140
+ });
141
+ try {
142
+ const result = await oxyServices.followUsers(userIds);
143
+ set((state: FollowState) => {
144
+ const followingUsers = { ...state.followingUsers };
145
+ const loadingUsers = { ...state.loadingUsers };
146
+ const errors = { ...state.errors };
147
+ for (const uid of userIds) {
148
+ loadingUsers[uid] = false;
149
+ }
150
+ for (const entry of result.results) {
151
+ if (entry.success || entry.alreadyFollowing) {
152
+ followingUsers[entry.userId] = true;
153
+ errors[entry.userId] = null;
154
+ } else {
155
+ errors[entry.userId] = 'Failed to update follow status';
156
+ }
157
+ }
158
+ return { followingUsers, loadingUsers, errors };
159
+ });
160
+ return result;
161
+ } catch (error: unknown) {
162
+ const message = (error instanceof Error ? error.message : null) || 'Failed to update follow status';
163
+ set((state: FollowState) => {
164
+ const loadingUsers = { ...state.loadingUsers };
165
+ const errors = { ...state.errors };
166
+ for (const uid of userIds) {
167
+ loadingUsers[uid] = false;
168
+ errors[uid] = message;
169
+ }
170
+ return { loadingUsers, errors };
171
+ });
172
+ throw error;
173
+ }
174
+ },
129
175
  setFollowerCount: (userId: string, count: number) => set((state: FollowState) => ({
130
176
  followerCounts: { ...state.followerCounts, [userId]: count },
131
177
  })),
@@ -4,9 +4,6 @@ import type { RouteName } from '../navigation/routes';
4
4
  import type { User } from '@oxyhq/core';
5
5
  import type { ClientSession } from '@oxyhq/core';
6
6
 
7
- // Re-export RouteName from routes for convenience
8
- export type { RouteName };
9
-
10
7
  export interface StepController {
11
8
  canGoBack: () => boolean;
12
9
  goBack: () => void;
@@ -67,4 +64,3 @@ export interface OxyProviderProps {
67
64
  authRedirectUri?: string;
68
65
  queryClient?: QueryClient;
69
66
  }
70
-
@@ -1,5 +1,6 @@
1
1
  import { authenticatedApiCall } from '@oxyhq/core';
2
2
  import type { OxyServices, User } from '@oxyhq/core';
3
+ import type { UserProfileUpdate } from '@oxyhq/contracts';
3
4
  import { useAccountStore } from '../stores/accountStore';
4
5
  import { useAuthStore } from '../stores/authStore';
5
6
  import type { QueryClient } from '@tanstack/react-query';
@@ -37,7 +38,7 @@ export function refreshAvatarInStore(
37
38
  * @returns Promise that resolves with updated user data
38
39
  */
39
40
  export async function updateProfileWithAvatar(
40
- updates: Partial<User>,
41
+ updates: UserProfileUpdate,
41
42
  oxyServices: OxyServices,
42
43
  activeSessionId: string | null,
43
44
  queryClient: QueryClient,
@@ -60,7 +61,7 @@ export async function updateProfileWithAvatar(
60
61
  useAuthStore.getState().setUser(data);
61
62
 
62
63
  // If avatar was updated, refresh accountStore with cache-busted URL
63
- if (updates.avatar && activeSessionId) {
64
+ if (typeof updates.avatar === 'string' && activeSessionId) {
64
65
  refreshAvatarInStore(activeSessionId, updates.avatar, oxyServices);
65
66
  }
66
67