@oxyhq/services 5.13.4 → 5.13.11

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 (170) hide show
  1. package/lib/commonjs/core/HttpClient.js +1 -1
  2. package/lib/commonjs/core/HttpClient.js.map +1 -1
  3. package/lib/commonjs/core/OxyServices.js +91 -30
  4. package/lib/commonjs/core/OxyServices.js.map +1 -1
  5. package/lib/commonjs/core/index.js +0 -7
  6. package/lib/commonjs/core/index.js.map +1 -1
  7. package/lib/commonjs/i18n/locales/en-US.json +222 -6
  8. package/lib/commonjs/index.js +0 -7
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/lib/sonner.js.map +1 -1
  11. package/lib/commonjs/ui/components/GroupedItem.js +24 -22
  12. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  13. package/lib/commonjs/ui/components/OxyProvider.js +35 -14
  14. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  15. package/lib/commonjs/ui/navigation/routes.js +36 -1
  16. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  17. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +150 -5
  18. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  19. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +475 -319
  20. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  21. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +217 -0
  22. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -0
  23. package/lib/commonjs/ui/screens/FileManagementScreen.js +911 -213
  24. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  25. package/lib/commonjs/ui/screens/HelpSupportScreen.js +131 -0
  26. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -0
  27. package/lib/commonjs/ui/screens/HistoryViewScreen.js +258 -0
  28. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -0
  29. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +211 -0
  30. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -0
  31. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +0 -1
  32. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +307 -0
  34. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -0
  35. package/lib/commonjs/ui/screens/ProfileScreen.js +1 -7
  36. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +205 -0
  38. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -0
  39. package/lib/commonjs/ui/screens/SearchSettingsScreen.js +239 -0
  40. package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -0
  41. package/lib/commonjs/ui/screens/SignInScreen.js +14 -29
  42. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  43. package/lib/commonjs/utils/asyncUtils.js +1 -0
  44. package/lib/commonjs/utils/asyncUtils.js.map +1 -1
  45. package/lib/commonjs/utils/cache.js +4 -4
  46. package/lib/commonjs/utils/cache.js.map +1 -1
  47. package/lib/commonjs/utils/index.js +0 -6
  48. package/lib/commonjs/utils/index.js.map +1 -1
  49. package/lib/module/core/HttpClient.js +1 -1
  50. package/lib/module/core/HttpClient.js.map +1 -1
  51. package/lib/module/core/OxyServices.js +90 -29
  52. package/lib/module/core/OxyServices.js.map +1 -1
  53. package/lib/module/core/index.js +1 -1
  54. package/lib/module/core/index.js.map +1 -1
  55. package/lib/module/i18n/locales/en-US.json +222 -6
  56. package/lib/module/index.js +1 -1
  57. package/lib/module/index.js.map +1 -1
  58. package/lib/module/lib/sonner.js.map +1 -1
  59. package/lib/module/ui/components/GroupedItem.js +24 -22
  60. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  61. package/lib/module/ui/components/OxyProvider.js +40 -17
  62. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  63. package/lib/module/ui/navigation/routes.js +36 -1
  64. package/lib/module/ui/navigation/routes.js.map +1 -1
  65. package/lib/module/ui/screens/AccountOverviewScreen.js +151 -6
  66. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  67. package/lib/module/ui/screens/AccountSettingsScreen.js +475 -319
  68. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  69. package/lib/module/ui/screens/AccountVerificationScreen.js +212 -0
  70. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -0
  71. package/lib/module/ui/screens/FileManagementScreen.js +913 -212
  72. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  73. package/lib/module/ui/screens/HelpSupportScreen.js +126 -0
  74. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -0
  75. package/lib/module/ui/screens/HistoryViewScreen.js +253 -0
  76. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -0
  77. package/lib/module/ui/screens/LegalDocumentsScreen.js +206 -0
  78. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -0
  79. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +0 -1
  80. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  81. package/lib/module/ui/screens/PrivacySettingsScreen.js +302 -0
  82. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -0
  83. package/lib/module/ui/screens/ProfileScreen.js +1 -7
  84. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  85. package/lib/module/ui/screens/SavesCollectionsScreen.js +200 -0
  86. package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -0
  87. package/lib/module/ui/screens/SearchSettingsScreen.js +234 -0
  88. package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -0
  89. package/lib/module/ui/screens/SignInScreen.js +14 -29
  90. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  91. package/lib/module/utils/asyncUtils.js +1 -0
  92. package/lib/module/utils/asyncUtils.js.map +1 -1
  93. package/lib/module/utils/cache.js +3 -3
  94. package/lib/module/utils/cache.js.map +1 -1
  95. package/lib/module/utils/index.js +1 -1
  96. package/lib/module/utils/index.js.map +1 -1
  97. package/lib/typescript/core/OxyServices.d.ts +35 -23
  98. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  99. package/lib/typescript/core/index.d.ts +1 -1
  100. package/lib/typescript/core/index.d.ts.map +1 -1
  101. package/lib/typescript/index.d.ts +1 -1
  102. package/lib/typescript/index.d.ts.map +1 -1
  103. package/lib/typescript/lib/sonner.d.ts +1 -0
  104. package/lib/typescript/lib/sonner.d.ts.map +1 -1
  105. package/lib/typescript/types/expo-document-picker.d.ts +36 -0
  106. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  107. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  108. package/lib/typescript/ui/navigation/routes.d.ts +1 -1
  109. package/lib/typescript/ui/navigation/routes.d.ts.map +1 -1
  110. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  111. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  112. package/lib/typescript/ui/screens/AccountVerificationScreen.d.ts +5 -0
  113. package/lib/typescript/ui/screens/AccountVerificationScreen.d.ts.map +1 -0
  114. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
  115. package/lib/typescript/ui/screens/HelpSupportScreen.d.ts +5 -0
  116. package/lib/typescript/ui/screens/HelpSupportScreen.d.ts.map +1 -0
  117. package/lib/typescript/ui/screens/HistoryViewScreen.d.ts +5 -0
  118. package/lib/typescript/ui/screens/HistoryViewScreen.d.ts.map +1 -0
  119. package/lib/typescript/ui/screens/LegalDocumentsScreen.d.ts +5 -0
  120. package/lib/typescript/ui/screens/LegalDocumentsScreen.d.ts.map +1 -0
  121. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
  122. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts +5 -0
  123. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -0
  124. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  125. package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts +5 -0
  126. package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts.map +1 -0
  127. package/lib/typescript/ui/screens/SearchSettingsScreen.d.ts +5 -0
  128. package/lib/typescript/ui/screens/SearchSettingsScreen.d.ts.map +1 -0
  129. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  130. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
  131. package/lib/typescript/utils/cache.d.ts +3 -3
  132. package/lib/typescript/utils/cache.d.ts.map +1 -1
  133. package/lib/typescript/utils/index.d.ts +1 -1
  134. package/lib/typescript/utils/index.d.ts.map +1 -1
  135. package/package.json +1 -1
  136. package/src/core/HttpClient.ts +1 -1
  137. package/src/core/OxyServices.ts +88 -30
  138. package/src/core/index.ts +1 -1
  139. package/src/i18n/locales/en-US.json +222 -6
  140. package/src/index.ts +1 -1
  141. package/src/lib/sonner.ts +1 -0
  142. package/src/types/expo-document-picker.d.ts +36 -0
  143. package/src/ui/components/GroupedItem.tsx +23 -21
  144. package/src/ui/components/OxyProvider.tsx +33 -11
  145. package/src/ui/navigation/routes.ts +42 -0
  146. package/src/ui/screens/AccountOverviewScreen.tsx +175 -5
  147. package/src/ui/screens/AccountSettingsScreen.tsx +521 -360
  148. package/src/ui/screens/AccountVerificationScreen.tsx +235 -0
  149. package/src/ui/screens/FileManagementScreen.tsx +934 -208
  150. package/src/ui/screens/HelpSupportScreen.tsx +143 -0
  151. package/src/ui/screens/HistoryViewScreen.tsx +280 -0
  152. package/src/ui/screens/LegalDocumentsScreen.tsx +220 -0
  153. package/src/ui/screens/PremiumSubscriptionScreen.tsx +0 -1
  154. package/src/ui/screens/PrivacySettingsScreen.tsx +332 -0
  155. package/src/ui/screens/ProfileScreen.tsx +1 -8
  156. package/src/ui/screens/SavesCollectionsScreen.tsx +222 -0
  157. package/src/ui/screens/SearchSettingsScreen.tsx +219 -0
  158. package/src/ui/screens/SignInScreen.tsx +19 -35
  159. package/src/utils/asyncUtils.ts +1 -0
  160. package/src/utils/cache.ts +3 -3
  161. package/src/utils/index.ts +1 -1
  162. package/lib/commonjs/ui/components/StepBasedScreen.README.md +0 -337
  163. package/lib/commonjs/ui/components/internal/TextField.md +0 -436
  164. package/lib/commonjs/ui/styles/FONTS.md +0 -126
  165. package/lib/module/ui/components/StepBasedScreen.README.md +0 -337
  166. package/lib/module/ui/components/internal/TextField.md +0 -436
  167. package/lib/module/ui/styles/FONTS.md +0 -126
  168. package/src/ui/components/StepBasedScreen.README.md +0 -337
  169. package/src/ui/components/internal/TextField.md +0 -436
  170. package/src/ui/styles/FONTS.md +0 -126
@@ -27,7 +27,7 @@ const AccountSettingsScreen = ({
27
27
  navigate
28
28
  }) => {
29
29
  const {
30
- user,
30
+ user: userFromContext,
31
31
  oxyServices,
32
32
  isLoading: authLoading,
33
33
  isAuthenticated,
@@ -38,8 +38,15 @@ const AccountSettingsScreen = ({
38
38
  t
39
39
  } = useI18n();
40
40
  const updateUser = useAuthStore(state => state.updateUser);
41
+ // Get user directly from store to ensure reactivity to avatar changes
42
+ const user = useAuthStore(state => state.user) || userFromContext;
41
43
  const [isLoading, setIsLoading] = useState(false);
42
44
  const [isSaving, setIsSaving] = useState(false);
45
+ const [isUpdatingAvatar, setIsUpdatingAvatar] = useState(false);
46
+ const [optimisticAvatarId, setOptimisticAvatarId] = useState(null);
47
+ const scrollViewRef = useRef(null);
48
+ const avatarSectionRef = useRef(null);
49
+ const [avatarSectionY, setAvatarSectionY] = useState(null);
43
50
 
44
51
  // Two-Factor (TOTP) state
45
52
  const [totpSetupUrl, setTotpSetupUrl] = useState(null);
@@ -64,8 +71,6 @@ const AccountSettingsScreen = ({
64
71
 
65
72
  // Editing states
66
73
  const [editingField, setEditingField] = useState(null);
67
-
68
- // Temporary input states for inline editing
69
74
  const [tempDisplayName, setTempDisplayName] = useState('');
70
75
  const [tempLastName, setTempLastName] = useState('');
71
76
  const [tempUsername, setTempUsername] = useState('');
@@ -96,82 +101,118 @@ const AccountSettingsScreen = ({
96
101
  }, [theme]);
97
102
 
98
103
  // Memoize animation function to prevent recreation on every render
99
- const animateSaveButton = useCallback(toValue => {
104
+ const animateSaveButton = useCallback((toValue, onComplete) => {
100
105
  Animated.spring(saveButtonScale, {
101
106
  toValue,
102
107
  useNativeDriver: Platform.OS !== 'web',
103
108
  tension: 150,
104
109
  friction: 8
105
- }).start();
110
+ }).start(onComplete ? finished => {
111
+ if (finished) {
112
+ onComplete();
113
+ }
114
+ } : undefined);
106
115
  }, [saveButtonScale]);
107
116
 
108
- // Load user data
117
+ // Track initialization to prevent unnecessary resets
118
+ const isInitializedRef = useRef(false);
119
+ const previousUserIdRef = useRef(null);
120
+ const previousAvatarRef = useRef(null);
121
+
122
+ // Load user data - only reset fields when user actually changes (not just avatar)
109
123
  useEffect(() => {
110
124
  if (user) {
111
- const userDisplayName = typeof user.name === 'string' ? user.name : user.name?.first || user.name?.full || '';
112
- const userLastName = typeof user.name === 'object' ? user.name?.last || '' : '';
113
- setDisplayName(userDisplayName);
114
- setLastName(userLastName);
115
- setUsername(user.username || '');
116
- setEmail(user.email || '');
117
- setBio(user.bio || '');
118
- setLocation(user.location || '');
125
+ const currentUserId = user.id;
126
+ const currentAvatar = typeof user.avatar === 'string' ? user.avatar : '';
127
+ const isNewUser = previousUserIdRef.current !== currentUserId;
128
+ const isAvatarOnlyUpdate = !isNewUser && previousUserIdRef.current === currentUserId && previousAvatarRef.current !== currentAvatar && previousAvatarRef.current !== null;
129
+ const shouldInitialize = !isInitializedRef.current || isNewUser;
130
+
131
+ // Only reset all fields if it's a new user or first load
132
+ // Skip reset if it's just an avatar update
133
+ if (shouldInitialize && !isAvatarOnlyUpdate) {
134
+ const userDisplayName = typeof user.name === 'string' ? user.name : user.name?.first || user.name?.full || '';
135
+ const userLastName = typeof user.name === 'object' ? user.name?.last || '' : '';
136
+ setDisplayName(userDisplayName);
137
+ setLastName(userLastName);
138
+ setUsername(user.username || '');
139
+ setEmail(user.email || '');
140
+ setBio(user.bio || '');
141
+ setLocation(user.location || '');
119
142
 
120
- // Handle locations - convert single location to array format
121
- if (user.locations && Array.isArray(user.locations)) {
122
- setTempLocations(user.locations.map((loc, index) => ({
123
- id: loc.id || `existing-${index}`,
124
- name: loc.name,
125
- label: loc.label,
126
- coordinates: loc.coordinates
127
- })));
128
- } else if (user.location) {
129
- // Convert single location string to array format
130
- setTempLocations([{
131
- id: 'existing-0',
132
- name: user.location,
133
- label: 'Location'
134
- }]);
135
- } else {
136
- setTempLocations([]);
143
+ // Handle locations - convert single location to array format
144
+ if (user.locations && Array.isArray(user.locations)) {
145
+ setTempLocations(user.locations.map((loc, index) => ({
146
+ id: loc.id || `existing-${index}`,
147
+ name: loc.name,
148
+ label: loc.label,
149
+ coordinates: loc.coordinates
150
+ })));
151
+ } else if (user.location) {
152
+ // Convert single location string to array format
153
+ setTempLocations([{
154
+ id: 'existing-0',
155
+ name: user.location,
156
+ label: 'Location'
157
+ }]);
158
+ } else {
159
+ setTempLocations([]);
160
+ }
161
+
162
+ // Handle links - simple and direct like other fields
163
+ if (user.linksMetadata && Array.isArray(user.linksMetadata)) {
164
+ const urls = user.linksMetadata.map(l => l.url);
165
+ setLinks(urls);
166
+ const metadataWithIds = user.linksMetadata.map((link, index) => ({
167
+ ...link,
168
+ id: link.id || `existing-${index}`
169
+ }));
170
+ setTempLinksWithMetadata(metadataWithIds);
171
+ } else if (Array.isArray(user.links)) {
172
+ const simpleLinks = user.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean);
173
+ setLinks(simpleLinks);
174
+ const linksWithMetadata = simpleLinks.map((url, index) => ({
175
+ url,
176
+ title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''),
177
+ description: `Link to ${url}`,
178
+ image: undefined,
179
+ id: `existing-${index}`
180
+ }));
181
+ setTempLinksWithMetadata(linksWithMetadata);
182
+ } else if (user.website) {
183
+ setLinks([user.website]);
184
+ setTempLinksWithMetadata([{
185
+ url: user.website,
186
+ title: user.website.replace(/^https?:\/\//, '').replace(/\/$/, ''),
187
+ description: `Link to ${user.website}`,
188
+ image: undefined,
189
+ id: 'existing-0'
190
+ }]);
191
+ } else {
192
+ setLinks([]);
193
+ setTempLinksWithMetadata([]);
194
+ }
195
+ isInitializedRef.current = true;
196
+ }
197
+
198
+ // Update avatar only if it changed and we're not in optimistic/updating state
199
+ // This allows the server response to update the avatar without resetting other fields
200
+ // But don't override if we have a pending optimistic update
201
+ if (currentAvatar !== avatarFileId && !isUpdatingAvatar && !optimisticAvatarId) {
202
+ setAvatarFileId(currentAvatar);
137
203
  }
138
204
 
139
- // Handle links - simple and direct like other fields
140
- if (user.linksMetadata && Array.isArray(user.linksMetadata)) {
141
- const urls = user.linksMetadata.map(l => l.url);
142
- setLinks(urls);
143
- const metadataWithIds = user.linksMetadata.map((link, index) => ({
144
- ...link,
145
- id: link.id || `existing-${index}`
146
- }));
147
- setTempLinksWithMetadata(metadataWithIds);
148
- } else if (Array.isArray(user.links)) {
149
- const simpleLinks = user.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean);
150
- setLinks(simpleLinks);
151
- const linksWithMetadata = simpleLinks.map((url, index) => ({
152
- url,
153
- title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''),
154
- description: `Link to ${url}`,
155
- image: undefined,
156
- id: `existing-${index}`
157
- }));
158
- setTempLinksWithMetadata(linksWithMetadata);
159
- } else if (user.website) {
160
- setLinks([user.website]);
161
- setTempLinksWithMetadata([{
162
- url: user.website,
163
- title: user.website.replace(/^https?:\/\//, '').replace(/\/$/, ''),
164
- description: `Link to ${user.website}`,
165
- image: undefined,
166
- id: 'existing-0'
167
- }]);
168
- } else {
169
- setLinks([]);
170
- setTempLinksWithMetadata([]);
205
+ // If we just finished updating and the server avatar matches our optimistic one, clear optimistic state
206
+ // Also clear if the server avatar matches our current avatarFileId (update completed)
207
+ if (isUpdatingAvatar === false && optimisticAvatarId) {
208
+ if (currentAvatar === optimisticAvatarId || currentAvatar === avatarFileId) {
209
+ setOptimisticAvatarId(null);
210
+ }
171
211
  }
172
- setAvatarFileId(typeof user.avatar === 'string' ? user.avatar : '');
212
+ previousUserIdRef.current = currentUserId;
213
+ previousAvatarRef.current = currentAvatar;
173
214
  }
174
- }, [user]);
215
+ }, [user, avatarFileId, isUpdatingAvatar, optimisticAvatarId]);
175
216
  const handleSave = async () => {
176
217
  if (!user) return;
177
218
  try {
@@ -188,8 +229,6 @@ const AccountSettingsScreen = ({
188
229
  links,
189
230
  linksMetadata: tempLinksWithMetadata.length > 0 ? tempLinksWithMetadata : undefined
190
231
  };
191
- console.log('Saving updates:', updates);
192
- console.log('Links metadata being saved:', tempLinksWithMetadata);
193
232
 
194
233
  // Handle name field
195
234
  if (displayName || lastName) {
@@ -242,33 +281,74 @@ const AccountSettingsScreen = ({
242
281
  toast.info?.(t('editProfile.toasts.avatarUnchanged') || 'Avatar unchanged');
243
282
  return;
244
283
  }
284
+
285
+ // Optimistically update UI immediately
286
+ setOptimisticAvatarId(file.id);
245
287
  setAvatarFileId(file.id);
246
- toast.success(t('editProfile.toasts.avatarSelected') || 'Avatar selected');
288
+
247
289
  // Auto-save avatar immediately (does not close edit profile screen)
248
290
  (async () => {
249
291
  try {
250
- console.log('[AccountSettings] Auto-saving avatar', file.id);
251
- setIsSaving(true);
292
+ setIsUpdatingAvatar(true);
252
293
 
253
294
  // Update file visibility to public for avatar
254
295
  try {
255
296
  await oxyServices.assetUpdateVisibility(file.id, 'public');
256
- console.log('[AccountSettings] Avatar visibility updated to public');
257
297
  } catch (visError) {
258
- console.warn('[AccountSettings] Failed to update avatar visibility, continuing anyway:', visError);
259
298
  // Continue with avatar update even if visibility update fails
260
299
  }
261
- await updateUser({
300
+
301
+ // Update on server directly without using updateUser (which triggers fetchUser)
302
+ // This prevents the entire component from re-rendering
303
+ await oxyServices.updateProfile({
262
304
  avatar: file.id
263
- }, oxyServices);
264
- // Force refresh current user cache (updateUser already does a fetch with force=true internally)
265
- // Extra safeguard: ensure avatarFileId reflects saved id (already set) and trigger any dependent UI.
305
+ });
306
+
307
+ // Update the user object in store directly without triggering fetchUser
308
+ // This prevents isLoading from being set to true, which would show loading screen
309
+ const currentUser = useAuthStore.getState().user;
310
+ if (currentUser) {
311
+ useAuthStore.setState({
312
+ user: {
313
+ ...currentUser,
314
+ avatar: file.id
315
+ }
316
+ // Don't update lastUserFetch to avoid cache issues
317
+ });
318
+ }
319
+
320
+ // Update local state - keep avatarFileId set to the new value
321
+ // Don't clear optimisticAvatarId yet - let it persist until user object updates
322
+ // This ensures the avatar displays correctly
323
+ setAvatarFileId(file.id);
266
324
  toast.success(t('editProfile.toasts.avatarUpdated') || 'Avatar updated');
325
+
326
+ // Scroll to avatar section after a brief delay to ensure UI is updated
327
+ requestAnimationFrame(() => {
328
+ requestAnimationFrame(() => {
329
+ if (avatarSectionY !== null) {
330
+ scrollViewRef.current?.scrollTo({
331
+ y: Math.max(0, avatarSectionY - 100),
332
+ // Offset to show section near top
333
+ animated: true
334
+ });
335
+ } else {
336
+ // Fallback: scroll to approximate position
337
+ scrollViewRef.current?.scrollTo({
338
+ y: 200,
339
+ // Approximate position of avatar section
340
+ animated: true
341
+ });
342
+ }
343
+ });
344
+ });
267
345
  } catch (e) {
268
- console.error('[AccountSettings] Failed to auto-save avatar', e);
346
+ // Revert optimistic update on error
347
+ setAvatarFileId(typeof user?.avatar === 'string' ? user.avatar : '');
348
+ setOptimisticAvatarId(null);
269
349
  toast.error(e.message || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
270
350
  } finally {
271
- setIsSaving(false);
351
+ setIsUpdatingAvatar(false);
272
352
  }
273
353
  })();
274
354
  },
@@ -276,7 +356,7 @@ const AccountSettingsScreen = ({
276
356
  disabledMimeTypes: ['video/', 'audio/', 'application/pdf']
277
357
  }
278
358
  });
279
- }, [showBottomSheet, oxyServices, avatarFileId, updateUser]);
359
+ }, [showBottomSheet, oxyServices, avatarFileId, updateUser, user]);
280
360
  const startEditing = (type, currentValue) => {
281
361
  switch (type) {
282
362
  case 'displayName':
@@ -335,11 +415,10 @@ const AccountSettingsScreen = ({
335
415
  break;
336
416
  }
337
417
 
338
- // Brief delay for animation, then reset and close editing
339
- setTimeout(() => {
340
- animateSaveButton(1);
418
+ // Complete animation, then reset and close editing
419
+ animateSaveButton(1, () => {
341
420
  setEditingField(null);
342
- }, 150);
421
+ });
343
422
  };
344
423
  const cancelEditing = () => {
345
424
  setEditingField(null);
@@ -353,11 +432,9 @@ const AccountSettingsScreen = ({
353
432
  }
354
433
  try {
355
434
  setIsFetchingMetadata(true);
356
- console.log('Fetching metadata for URL:', url);
357
435
 
358
436
  // Use the backend API to fetch metadata
359
437
  const metadata = await oxyServices.fetchLinkMetadata(url);
360
- console.log('Received metadata:', metadata);
361
438
  const result = {
362
439
  ...metadata,
363
440
  id: Date.now().toString()
@@ -367,7 +444,6 @@ const AccountSettingsScreen = ({
367
444
  linkMetadataCache.set(cacheKey, result);
368
445
  return result;
369
446
  } catch (error) {
370
- console.error('Error fetching metadata:', error);
371
447
  // Fallback to basic metadata
372
448
  const fallback = {
373
449
  url: url.startsWith('http') ? url : 'https://' + url,
@@ -405,7 +481,6 @@ const AccountSettingsScreen = ({
405
481
  locationSearchCache.set(cacheKey, data);
406
482
  setLocationSearchResults(data);
407
483
  } catch (error) {
408
- console.error('Error searching locations:', error);
409
484
  setLocationSearchResults([]);
410
485
  } finally {
411
486
  setIsSearchingLocations(false);
@@ -440,9 +515,7 @@ const AccountSettingsScreen = ({
440
515
  const addLink = async () => {
441
516
  if (!newLinkUrl.trim()) return;
442
517
  const url = newLinkUrl.trim();
443
- console.log('Adding link:', url);
444
518
  const metadata = await fetchLinkMetadata(url);
445
- console.log('Final metadata for adding:', metadata);
446
519
  setTempLinksWithMetadata(prev => [...prev, metadata]);
447
520
  setNewLinkUrl('');
448
521
  setIsAddingLink(false);
@@ -518,7 +591,7 @@ const AccountSettingsScreen = ({
518
591
  style: {
519
592
  padding: 16,
520
593
  backgroundColor: '#fff',
521
- borderRadius: 12
594
+ borderRadius: 16
522
595
  },
523
596
  children: /*#__PURE__*/_jsx(QRCode, {
524
597
  value: totpSetupUrl,
@@ -647,7 +720,7 @@ const AccountSettingsScreen = ({
647
720
  style: [styles.editingFieldLabel, {
648
721
  color: themeStyles.isDarkTheme ? '#FFFFFF' : '#1A1A1A'
649
722
  }],
650
- children: "Edit Display Name"
723
+ children: "Edit Full Name"
651
724
  })
652
725
  }), /*#__PURE__*/_jsxs(View, {
653
726
  style: {
@@ -1079,7 +1152,7 @@ const AccountSettingsScreen = ({
1079
1152
  };
1080
1153
  return /*#__PURE__*/_jsx(View, {
1081
1154
  style: [styles.editingFieldContainer, {
1082
- backgroundColor: themeStyles.backgroundColor
1155
+ backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#FFFFFF'
1083
1156
  }],
1084
1157
  children: /*#__PURE__*/_jsx(View, {
1085
1158
  style: styles.editingFieldContent,
@@ -1089,20 +1162,20 @@ const AccountSettingsScreen = ({
1089
1162
  style: styles.editingFieldHeader,
1090
1163
  children: /*#__PURE__*/_jsx(Text, {
1091
1164
  style: [styles.editingFieldLabel, {
1092
- color: themeStyles.isDarkTheme ? '#FFFFFF' : '#1A1A1A'
1165
+ color: themeStyles.isDarkTheme ? '#FFFFFF' : '#000000'
1093
1166
  }],
1094
- children: `Enter ${config.label.toLowerCase()}:`
1167
+ children: config.label
1095
1168
  })
1096
1169
  }), /*#__PURE__*/_jsx(TextInput, {
1097
1170
  style: [config.multiline ? styles.editingFieldTextArea : styles.editingFieldInput, {
1098
- backgroundColor: themeStyles.isDarkTheme ? '#333' : '#fff',
1099
- color: themeStyles.isDarkTheme ? '#fff' : '#000',
1100
- borderColor: themeStyles.primaryColor
1171
+ backgroundColor: themeStyles.isDarkTheme ? '#1C1C1E' : '#F2F2F7',
1172
+ color: themeStyles.isDarkTheme ? '#FFFFFF' : '#000000',
1173
+ borderColor: themeStyles.isDarkTheme ? '#38383A' : '#E5E5EA'
1101
1174
  }],
1102
1175
  value: tempValue,
1103
1176
  onChangeText: setTempValue,
1104
1177
  placeholder: config.placeholder,
1105
- placeholderTextColor: themeStyles.isDarkTheme ? '#aaa' : '#999',
1178
+ placeholderTextColor: themeStyles.isDarkTheme ? '#636366' : '#8E8E93',
1106
1179
  multiline: config.multiline,
1107
1180
  numberOfLines: config.multiline ? 6 : 1,
1108
1181
  keyboardType: config.keyboardType,
@@ -1116,7 +1189,7 @@ const AccountSettingsScreen = ({
1116
1189
  if (authLoading || !isAuthenticated) {
1117
1190
  return /*#__PURE__*/_jsx(View, {
1118
1191
  style: [styles.container, {
1119
- backgroundColor: themeStyles.backgroundColor,
1192
+ backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#F5F5F7',
1120
1193
  justifyContent: 'center'
1121
1194
  }],
1122
1195
  children: /*#__PURE__*/_jsx(ActivityIndicator, {
@@ -1127,19 +1200,21 @@ const AccountSettingsScreen = ({
1127
1200
  }
1128
1201
  return /*#__PURE__*/_jsxs(View, {
1129
1202
  style: [styles.container, {
1130
- backgroundColor: themeStyles.backgroundColor
1203
+ backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#F5F5F7'
1131
1204
  }],
1132
1205
  children: [editingField ? /*#__PURE__*/_jsxs(View, {
1133
1206
  style: [styles.editingHeader, {
1134
- backgroundColor: '#FFFFFF',
1135
- borderBottomColor: themeStyles.isDarkTheme ? '#38383A' : '#E9ECEF'
1207
+ backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#FFFFFF',
1208
+ borderBottomColor: themeStyles.isDarkTheme ? '#38383A' : '#E5E5EA'
1136
1209
  }],
1137
1210
  children: [/*#__PURE__*/_jsxs(View, {
1138
1211
  style: styles.editingHeaderContent,
1139
1212
  children: [/*#__PURE__*/_jsx(TouchableOpacity, {
1140
- style: styles.editingBackButton,
1213
+ style: [styles.editingBackButton, {
1214
+ backgroundColor: themeStyles.isDarkTheme ? '#1C1C1E' : '#F2F2F7'
1215
+ }],
1141
1216
  onPress: cancelEditing,
1142
- children: /*#__PURE__*/_jsx(OxyIcon, {
1217
+ children: /*#__PURE__*/_jsx(Ionicons, {
1143
1218
  name: "chevron-back",
1144
1219
  size: 20,
1145
1220
  color: themeStyles.primaryColor
@@ -1148,7 +1223,8 @@ const AccountSettingsScreen = ({
1148
1223
  style: styles.editingTitleContainer
1149
1224
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
1150
1225
  style: [styles.editingSaveButton, {
1151
- opacity: isSaving ? 0.5 : 1
1226
+ opacity: isSaving ? 0.5 : 1,
1227
+ backgroundColor: themeStyles.isDarkTheme ? '#1C1C1E' : '#F2F2F7'
1152
1228
  }],
1153
1229
  onPress: () => saveField(editingField),
1154
1230
  disabled: isSaving,
@@ -1164,14 +1240,18 @@ const AccountSettingsScreen = ({
1164
1240
  })]
1165
1241
  }), /*#__PURE__*/_jsxs(View, {
1166
1242
  style: styles.editingHeaderBottom,
1167
- children: [/*#__PURE__*/_jsx(OxyIcon, {
1168
- name: editingField === 'displayName' ? 'person' : editingField === 'username' ? 'at' : editingField === 'email' ? 'mail' : editingField === 'bio' ? 'document-text' : editingField === 'location' ? 'location' : editingField === 'links' ? 'link' : 'person',
1169
- size: 56,
1170
- color: editingField === 'displayName' ? '#007AFF' : editingField === 'username' ? '#5856D6' : editingField === 'email' ? '#FF9500' : editingField === 'bio' ? '#34C759' : editingField === 'location' ? '#FF3B30' : editingField === 'links' ? '#32D74B' : '#007AFF',
1171
- style: styles.editingBottomIcon
1243
+ children: [/*#__PURE__*/_jsx(View, {
1244
+ style: [styles.editingIconContainer, {
1245
+ backgroundColor: editingField === 'displayName' ? '#007AFF20' : editingField === 'username' ? '#5856D620' : editingField === 'email' ? '#FF950020' : editingField === 'bio' ? '#34C75920' : editingField === 'location' ? '#FF3B3020' : editingField === 'links' ? '#32D74B20' : '#007AFF20'
1246
+ }],
1247
+ children: /*#__PURE__*/_jsx(Ionicons, {
1248
+ name: editingField === 'displayName' ? 'person' : editingField === 'username' ? 'at' : editingField === 'email' ? 'mail' : editingField === 'bio' ? 'document-text' : editingField === 'location' ? 'location' : editingField === 'links' ? 'link' : 'person',
1249
+ size: 28,
1250
+ color: editingField === 'displayName' ? '#007AFF' : editingField === 'username' ? '#5856D6' : editingField === 'email' ? '#FF9500' : editingField === 'bio' ? '#34C759' : editingField === 'location' ? '#FF3B30' : editingField === 'links' ? '#32D74B' : '#007AFF'
1251
+ })
1172
1252
  }), /*#__PURE__*/_jsx(Text, {
1173
1253
  style: [styles.editingBottomTitle, {
1174
- color: themeStyles.isDarkTheme ? '#FFFFFF' : '#1A1A1A'
1254
+ color: themeStyles.isDarkTheme ? '#FFFFFF' : '#000000'
1175
1255
  }],
1176
1256
  children: editingField === 'displayName' ? t('editProfile.items.displayName.title') || 'Display Name' : editingField === 'username' ? t('editProfile.items.username.title') || 'Username' : editingField === 'email' ? t('editProfile.items.email.title') || 'Email' : editingField === 'bio' ? t('editProfile.items.bio.title') || 'Bio' : editingField === 'location' ? t('editProfile.items.locations.title') || 'Location' : editingField === 'links' ? t('editProfile.items.links.title') || 'Links' : 'Field'
1177
1257
  })]
@@ -1188,6 +1268,7 @@ const AccountSettingsScreen = ({
1188
1268
  },
1189
1269
  elevation: "subtle"
1190
1270
  }), /*#__PURE__*/_jsx(ScrollView, {
1271
+ ref: scrollViewRef,
1191
1272
  style: editingField ? styles.contentEditing : styles.content,
1192
1273
  children: editingField ?
1193
1274
  /*#__PURE__*/
@@ -1214,7 +1295,7 @@ const AccountSettingsScreen = ({
1214
1295
  children: /*#__PURE__*/_jsxs(View, {
1215
1296
  style: {
1216
1297
  backgroundColor: '#fff',
1217
- borderRadius: 16,
1298
+ borderRadius: 20,
1218
1299
  padding: 20,
1219
1300
  maxHeight: '80%'
1220
1301
  },
@@ -1246,7 +1327,7 @@ const AccountSettingsScreen = ({
1246
1327
  }), /*#__PURE__*/_jsx(View, {
1247
1328
  style: {
1248
1329
  backgroundColor: '#F8F9FA',
1249
- borderRadius: 8,
1330
+ borderRadius: 12,
1250
1331
  padding: 12
1251
1332
  },
1252
1333
  children: generatedBackupCodes.map((c, idx) => /*#__PURE__*/_jsx(Text, {
@@ -1272,7 +1353,7 @@ const AccountSettingsScreen = ({
1272
1353
  }), /*#__PURE__*/_jsx(View, {
1273
1354
  style: {
1274
1355
  backgroundColor: '#F8F9FA',
1275
- borderRadius: 8,
1356
+ borderRadius: 12,
1276
1357
  padding: 12
1277
1358
  },
1278
1359
  children: /*#__PURE__*/_jsx(Text, {
@@ -1304,198 +1385,265 @@ const AccountSettingsScreen = ({
1304
1385
  })]
1305
1386
  })
1306
1387
  }), /*#__PURE__*/_jsxs(View, {
1388
+ ref: avatarSectionRef,
1307
1389
  style: styles.section,
1390
+ onLayout: event => {
1391
+ const {
1392
+ y
1393
+ } = event.nativeEvent.layout;
1394
+ setAvatarSectionY(y);
1395
+ },
1308
1396
  children: [/*#__PURE__*/_jsx(Text, {
1309
- style: styles.sectionTitle,
1310
- children: t('editProfile.sections.profilePicture') || 'Profile Picture'
1311
- }), /*#__PURE__*/_jsx(GroupedSection, {
1312
- items: [{
1313
- id: 'profile-photo',
1314
- icon: avatarFileId ? undefined : 'person',
1315
- iconColor: '#007AFF',
1316
- // Use download URL (includes token + fallback) instead of raw stream for reliability
1317
- image: avatarFileId ? oxyServices.getFileDownloadUrl(avatarFileId, 'thumb') : undefined,
1318
- imageSize: 40,
1319
- title: 'Profile Photo',
1320
- subtitle: avatarFileId ? 'Tap to change your profile picture' : 'Tap to add a profile picture',
1321
- onPress: openAvatarPicker
1322
- }, ...(avatarFileId ? [{
1323
- id: 'remove-profile-photo',
1324
- icon: 'trash',
1325
- iconColor: '#FF3B30',
1326
- title: 'Remove Photo',
1327
- subtitle: 'Delete current profile picture',
1328
- onPress: handleAvatarRemove
1329
- }] : [])],
1330
- theme: theme
1397
+ style: [styles.sectionTitle, {
1398
+ color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93'
1399
+ }],
1400
+ children: t('editProfile.sections.profilePicture') || 'PROFILE PICTURE'
1401
+ }), /*#__PURE__*/_jsx(View, {
1402
+ style: styles.groupedSectionWrapper,
1403
+ children: /*#__PURE__*/_jsx(GroupedSection, {
1404
+ items: [{
1405
+ id: 'profile-photo',
1406
+ icon: avatarFileId ? undefined : 'person',
1407
+ iconColor: '#007AFF',
1408
+ // Use optimistic avatar ID if available, otherwise use saved one
1409
+ image: optimisticAvatarId || avatarFileId ? oxyServices.getFileDownloadUrl(optimisticAvatarId || avatarFileId, 'thumb') : undefined,
1410
+ imageSize: 40,
1411
+ title: 'Profile Photo',
1412
+ subtitle: isUpdatingAvatar ? 'Updating profile picture...' : avatarFileId ? 'Tap to change your profile picture' : 'Tap to add a profile picture',
1413
+ onPress: isUpdatingAvatar ? undefined : openAvatarPicker,
1414
+ disabled: isUpdatingAvatar,
1415
+ customIcon: isUpdatingAvatar ? /*#__PURE__*/_jsxs(Animated.View, {
1416
+ style: {
1417
+ position: 'relative',
1418
+ width: 40,
1419
+ height: 40
1420
+ },
1421
+ children: [(optimisticAvatarId || avatarFileId) && /*#__PURE__*/_jsx(Animated.Image, {
1422
+ source: {
1423
+ uri: oxyServices.getFileDownloadUrl(optimisticAvatarId || avatarFileId, 'thumb')
1424
+ },
1425
+ style: {
1426
+ width: 40,
1427
+ height: 40,
1428
+ borderRadius: 22,
1429
+ opacity: 0.6
1430
+ }
1431
+ }), /*#__PURE__*/_jsx(View, {
1432
+ style: {
1433
+ position: 'absolute',
1434
+ top: 0,
1435
+ left: 0,
1436
+ right: 0,
1437
+ bottom: 0,
1438
+ justifyContent: 'center',
1439
+ alignItems: 'center',
1440
+ backgroundColor: themeStyles.isDarkTheme ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.7)',
1441
+ borderRadius: 22
1442
+ },
1443
+ children: /*#__PURE__*/_jsx(ActivityIndicator, {
1444
+ size: "small",
1445
+ color: themeStyles.primaryColor
1446
+ })
1447
+ })]
1448
+ }) : undefined
1449
+ }, ...(avatarFileId && !isUpdatingAvatar ? [{
1450
+ id: 'remove-profile-photo',
1451
+ icon: 'trash',
1452
+ iconColor: '#FF3B30',
1453
+ title: 'Remove Photo',
1454
+ subtitle: 'Delete current profile picture',
1455
+ onPress: handleAvatarRemove
1456
+ }] : [])],
1457
+ theme: theme
1458
+ })
1331
1459
  })]
1332
1460
  }), /*#__PURE__*/_jsxs(View, {
1333
1461
  style: styles.section,
1334
1462
  children: [/*#__PURE__*/_jsx(Text, {
1335
- style: styles.sectionTitle,
1336
- children: t('editProfile.sections.basicInfo') || 'Basic Information'
1337
- }), /*#__PURE__*/_jsx(GroupedSection, {
1338
- items: [{
1339
- id: 'display-name',
1340
- icon: 'person',
1341
- iconColor: '#007AFF',
1342
- title: t('editProfile.items.displayName.title') || 'Display Name',
1343
- subtitle: [displayName, lastName].filter(Boolean).join(' ') || t('editProfile.items.displayName.add') || 'Add your display name',
1344
- onPress: () => startEditing('displayName', '')
1345
- }, {
1346
- id: 'username',
1347
- icon: 'at',
1348
- iconColor: '#5856D6',
1349
- title: t('editProfile.items.username.title') || 'Username',
1350
- subtitle: username || t('editProfile.items.username.choose') || 'Choose a username',
1351
- onPress: () => startEditing('username', username)
1352
- }, {
1353
- id: 'email',
1354
- icon: 'mail',
1355
- iconColor: '#FF9500',
1356
- title: t('editProfile.items.email.title') || 'Email',
1357
- subtitle: email || t('editProfile.items.email.add') || 'Add your email address',
1358
- onPress: () => startEditing('email', email)
1463
+ style: [styles.sectionTitle, {
1464
+ color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93'
1359
1465
  }],
1360
- theme: theme
1466
+ children: t('editProfile.sections.basicInfo') || 'BASIC INFORMATION'
1467
+ }), /*#__PURE__*/_jsx(View, {
1468
+ style: styles.groupedSectionWrapper,
1469
+ children: /*#__PURE__*/_jsx(GroupedSection, {
1470
+ items: [{
1471
+ id: 'display-name',
1472
+ icon: 'person',
1473
+ iconColor: '#007AFF',
1474
+ title: t('editProfile.items.displayName.title') || 'Display Name',
1475
+ subtitle: [displayName, lastName].filter(Boolean).join(' ') || t('editProfile.items.displayName.add') || 'Add your display name',
1476
+ onPress: () => startEditing('displayName', '')
1477
+ }, {
1478
+ id: 'username',
1479
+ icon: 'at',
1480
+ iconColor: '#5856D6',
1481
+ title: t('editProfile.items.username.title') || 'Username',
1482
+ subtitle: username || t('editProfile.items.username.choose') || 'Choose a username',
1483
+ onPress: () => startEditing('username', username)
1484
+ }, {
1485
+ id: 'email',
1486
+ icon: 'mail',
1487
+ iconColor: '#FF9500',
1488
+ title: t('editProfile.items.email.title') || 'Email',
1489
+ subtitle: email || t('editProfile.items.email.add') || 'Add your email address',
1490
+ onPress: () => startEditing('email', email)
1491
+ }],
1492
+ theme: theme
1493
+ })
1361
1494
  })]
1362
1495
  }), /*#__PURE__*/_jsxs(View, {
1363
1496
  style: styles.section,
1364
1497
  children: [/*#__PURE__*/_jsx(Text, {
1365
- style: styles.sectionTitle,
1366
- children: t('editProfile.sections.about') || 'About You'
1367
- }), /*#__PURE__*/_jsx(GroupedSection, {
1368
- items: [{
1369
- id: 'bio',
1370
- icon: 'document-text',
1371
- iconColor: '#34C759',
1372
- title: t('editProfile.items.bio.title') || 'Bio',
1373
- subtitle: bio || t('editProfile.items.bio.placeholder') || 'Tell people about yourself',
1374
- onPress: () => startEditing('bio', bio)
1375
- }, {
1376
- id: 'locations',
1377
- icon: 'location',
1378
- iconColor: '#FF3B30',
1379
- title: t('editProfile.items.locations.title') || 'Locations',
1380
- subtitle: tempLocations.length > 0 ? tempLocations.length === 1 ? t('editProfile.items.locations.count', {
1381
- count: tempLocations.length
1382
- }) || `${tempLocations.length} location added` : t('editProfile.items.locations.count_plural', {
1383
- count: tempLocations.length
1384
- }) || `${tempLocations.length} locations added` : t('editProfile.items.locations.add') || 'Add your locations',
1385
- onPress: () => startEditing('location', ''),
1386
- customContentBelow: tempLocations.length > 0 && /*#__PURE__*/_jsxs(View, {
1387
- style: styles.linksPreviewContainer,
1388
- children: [tempLocations.slice(0, 2).map((location, index) => /*#__PURE__*/_jsxs(View, {
1389
- style: styles.linkPreviewItem,
1390
- children: [/*#__PURE__*/_jsx(View, {
1391
- style: styles.linkPreviewImage,
1392
- children: /*#__PURE__*/_jsx(Text, {
1393
- style: styles.linkPreviewImageText,
1394
- children: location.name.charAt(0).toUpperCase()
1395
- })
1396
- }), /*#__PURE__*/_jsxs(View, {
1397
- style: styles.linkPreviewContent,
1398
- children: [/*#__PURE__*/_jsx(Text, {
1498
+ style: [styles.sectionTitle, {
1499
+ color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93'
1500
+ }],
1501
+ children: t('editProfile.sections.about') || 'ABOUT YOU'
1502
+ }), /*#__PURE__*/_jsx(View, {
1503
+ style: styles.groupedSectionWrapper,
1504
+ children: /*#__PURE__*/_jsx(GroupedSection, {
1505
+ items: [{
1506
+ id: 'bio',
1507
+ icon: 'document-text',
1508
+ iconColor: '#34C759',
1509
+ title: t('editProfile.items.bio.title') || 'Bio',
1510
+ subtitle: bio || t('editProfile.items.bio.placeholder') || 'Tell people about yourself',
1511
+ onPress: () => startEditing('bio', bio)
1512
+ }, {
1513
+ id: 'locations',
1514
+ icon: 'location',
1515
+ iconColor: '#FF3B30',
1516
+ title: t('editProfile.items.locations.title') || 'Locations',
1517
+ subtitle: tempLocations.length > 0 ? tempLocations.length === 1 ? t('editProfile.items.locations.count', {
1518
+ count: tempLocations.length
1519
+ }) || `${tempLocations.length} location added` : t('editProfile.items.locations.count_plural', {
1520
+ count: tempLocations.length
1521
+ }) || `${tempLocations.length} locations added` : t('editProfile.items.locations.add') || 'Add your locations',
1522
+ onPress: () => startEditing('location', ''),
1523
+ customContentBelow: tempLocations.length > 0 && /*#__PURE__*/_jsxs(View, {
1524
+ style: styles.linksPreviewContainer,
1525
+ children: [tempLocations.slice(0, 2).map((location, index) => /*#__PURE__*/_jsxs(View, {
1526
+ style: styles.linkPreviewItem,
1527
+ children: [/*#__PURE__*/_jsx(View, {
1528
+ style: styles.linkPreviewImage,
1529
+ children: /*#__PURE__*/_jsx(Text, {
1530
+ style: styles.linkPreviewImageText,
1531
+ children: location.name.charAt(0).toUpperCase()
1532
+ })
1533
+ }), /*#__PURE__*/_jsxs(View, {
1534
+ style: styles.linkPreviewContent,
1535
+ children: [/*#__PURE__*/_jsx(Text, {
1536
+ style: styles.linkPreviewTitle,
1537
+ numberOfLines: 1,
1538
+ children: location.name
1539
+ }), location.label && /*#__PURE__*/_jsx(Text, {
1540
+ style: styles.linkPreviewSubtitle,
1541
+ children: location.label
1542
+ })]
1543
+ })]
1544
+ }, location.id || index)), tempLocations.length > 2 && /*#__PURE__*/_jsxs(Text, {
1545
+ style: styles.linkPreviewMore,
1546
+ children: ["+", tempLocations.length - 2, " more"]
1547
+ })]
1548
+ })
1549
+ }, {
1550
+ id: 'links',
1551
+ icon: 'link',
1552
+ iconColor: '#32D74B',
1553
+ title: t('editProfile.items.links.title') || 'Links',
1554
+ subtitle: tempLinksWithMetadata.length > 0 ? tempLinksWithMetadata.length === 1 ? t('editProfile.items.links.count', {
1555
+ count: tempLinksWithMetadata.length
1556
+ }) || `${tempLinksWithMetadata.length} link added` : t('editProfile.items.links.count_plural', {
1557
+ count: tempLinksWithMetadata.length
1558
+ }) || `${tempLinksWithMetadata.length} links added` : t('editProfile.items.links.add') || 'Add your links',
1559
+ onPress: () => startEditing('links', ''),
1560
+ multiRow: true,
1561
+ customContentBelow: tempLinksWithMetadata.length > 0 && /*#__PURE__*/_jsxs(View, {
1562
+ style: styles.linksPreviewContainer,
1563
+ children: [tempLinksWithMetadata.slice(0, 2).map((link, index) => /*#__PURE__*/_jsxs(View, {
1564
+ style: styles.linkPreviewItem,
1565
+ children: [link.image ? /*#__PURE__*/_jsx(Image, {
1566
+ source: {
1567
+ uri: link.image
1568
+ },
1569
+ style: styles.linkPreviewImage
1570
+ }) : /*#__PURE__*/_jsx(View, {
1571
+ style: styles.linkPreviewImage,
1572
+ children: /*#__PURE__*/_jsx(Text, {
1573
+ style: styles.linkPreviewImageText,
1574
+ children: link.title?.charAt(0).toUpperCase() || link.url.charAt(0).toUpperCase()
1575
+ })
1576
+ }), /*#__PURE__*/_jsx(Text, {
1399
1577
  style: styles.linkPreviewTitle,
1400
1578
  numberOfLines: 1,
1401
- children: location.name
1402
- }), location.label && /*#__PURE__*/_jsx(Text, {
1403
- style: styles.linkPreviewSubtitle,
1404
- children: location.label
1579
+ children: link.title || link.url
1405
1580
  })]
1581
+ }, link.id || index)), tempLinksWithMetadata.length > 2 && /*#__PURE__*/_jsxs(Text, {
1582
+ style: styles.linkPreviewMore,
1583
+ children: ["+", tempLinksWithMetadata.length - 2, " more"]
1406
1584
  })]
1407
- }, location.id || index)), tempLocations.length > 2 && /*#__PURE__*/_jsxs(Text, {
1408
- style: styles.linkPreviewMore,
1409
- children: ["+", tempLocations.length - 2, " more"]
1410
- })]
1411
- })
1412
- }, {
1413
- id: 'links',
1414
- icon: 'link',
1415
- iconColor: '#32D74B',
1416
- title: t('editProfile.items.links.title') || 'Links',
1417
- subtitle: tempLinksWithMetadata.length > 0 ? tempLinksWithMetadata.length === 1 ? t('editProfile.items.links.count', {
1418
- count: tempLinksWithMetadata.length
1419
- }) || `${tempLinksWithMetadata.length} link added` : t('editProfile.items.links.count_plural', {
1420
- count: tempLinksWithMetadata.length
1421
- }) || `${tempLinksWithMetadata.length} links added` : t('editProfile.items.links.add') || 'Add your links',
1422
- onPress: () => startEditing('links', ''),
1423
- multiRow: true,
1424
- customContentBelow: tempLinksWithMetadata.length > 0 && /*#__PURE__*/_jsxs(View, {
1425
- style: styles.linksPreviewContainer,
1426
- children: [tempLinksWithMetadata.slice(0, 2).map((link, index) => /*#__PURE__*/_jsxs(View, {
1427
- style: styles.linkPreviewItem,
1428
- children: [link.image ? /*#__PURE__*/_jsx(Image, {
1429
- source: {
1430
- uri: link.image
1431
- },
1432
- style: styles.linkPreviewImage
1433
- }) : /*#__PURE__*/_jsx(View, {
1434
- style: styles.linkPreviewImage,
1435
- children: /*#__PURE__*/_jsx(Text, {
1436
- style: styles.linkPreviewImageText,
1437
- children: link.title?.charAt(0).toUpperCase() || link.url.charAt(0).toUpperCase()
1438
- })
1439
- }), /*#__PURE__*/_jsx(Text, {
1440
- style: styles.linkPreviewTitle,
1441
- numberOfLines: 1,
1442
- children: link.title || link.url
1443
- })]
1444
- }, link.id || index)), tempLinksWithMetadata.length > 2 && /*#__PURE__*/_jsxs(Text, {
1445
- style: styles.linkPreviewMore,
1446
- children: ["+", tempLinksWithMetadata.length - 2, " more"]
1447
- })]
1448
- })
1449
- }],
1450
- theme: theme
1585
+ })
1586
+ }],
1587
+ theme: theme
1588
+ })
1451
1589
  })]
1452
1590
  }), /*#__PURE__*/_jsxs(View, {
1453
1591
  style: styles.section,
1454
1592
  children: [/*#__PURE__*/_jsx(Text, {
1455
- style: styles.sectionTitle,
1456
- children: t('editProfile.sections.quickActions') || 'Quick Actions'
1457
- }), /*#__PURE__*/_jsx(GroupedSection, {
1458
- items: [{
1459
- id: 'preview-profile',
1460
- icon: 'eye',
1461
- iconColor: '#007AFF',
1462
- title: t('editProfile.items.previewProfile.title') || 'Preview Profile',
1463
- subtitle: t('editProfile.items.previewProfile.subtitle') || 'See how your profile looks to others',
1464
- onPress: () => navigate?.('Profile', {
1465
- userId: user?.id
1466
- })
1467
- }, {
1468
- id: 'privacy-settings',
1469
- icon: 'shield-checkmark',
1470
- iconColor: '#8E8E93',
1471
- title: t('editProfile.items.privacySettings.title') || 'Privacy Settings',
1472
- subtitle: t('editProfile.items.privacySettings.subtitle') || 'Control who can see your profile',
1473
- onPress: () => toast.info(t('editProfile.items.privacySettings.coming') || 'Privacy settings coming soon!')
1474
- }, {
1475
- id: 'verify-account',
1476
- icon: 'checkmark-circle',
1477
- iconColor: '#30D158',
1478
- title: t('editProfile.items.verifyAccount.title') || 'Verify Account',
1479
- subtitle: t('editProfile.items.verifyAccount.subtitle') || 'Get a verified badge',
1480
- onPress: () => toast.info(t('editProfile.items.verifyAccount.coming') || 'Account verification coming soon!')
1593
+ style: [styles.sectionTitle, {
1594
+ color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93'
1481
1595
  }],
1482
- theme: theme
1596
+ children: t('editProfile.sections.quickActions') || 'QUICK ACTIONS'
1597
+ }), /*#__PURE__*/_jsx(View, {
1598
+ style: styles.groupedSectionWrapper,
1599
+ children: /*#__PURE__*/_jsx(GroupedSection, {
1600
+ items: [{
1601
+ id: 'preview-profile',
1602
+ icon: 'eye',
1603
+ iconColor: '#007AFF',
1604
+ title: t('editProfile.items.previewProfile.title') || 'Preview Profile',
1605
+ subtitle: t('editProfile.items.previewProfile.subtitle') || 'See how your profile looks to others',
1606
+ onPress: () => navigate?.('Profile', {
1607
+ userId: user?.id
1608
+ })
1609
+ }, {
1610
+ id: 'privacy-settings',
1611
+ icon: 'shield-checkmark',
1612
+ iconColor: '#8E8E93',
1613
+ title: t('editProfile.items.privacySettings.title') || 'Privacy Settings',
1614
+ subtitle: t('editProfile.items.privacySettings.subtitle') || 'Control who can see your profile',
1615
+ onPress: () => navigate?.('PrivacySettings')
1616
+ }, {
1617
+ id: 'verify-account',
1618
+ icon: 'checkmark-circle',
1619
+ iconColor: '#30D158',
1620
+ title: t('editProfile.items.verifyAccount.title') || 'Verify Account',
1621
+ subtitle: t('editProfile.items.verifyAccount.subtitle') || 'Get a verified badge',
1622
+ onPress: () => navigate?.('AccountVerification')
1623
+ }],
1624
+ theme: theme
1625
+ })
1483
1626
  })]
1484
1627
  }), /*#__PURE__*/_jsxs(View, {
1485
1628
  style: styles.section,
1486
1629
  children: [/*#__PURE__*/_jsx(Text, {
1487
- style: styles.sectionTitle,
1488
- children: t('editProfile.sections.security') || 'Security'
1489
- }), /*#__PURE__*/_jsx(GroupedSection, {
1490
- items: [{
1491
- id: 'two-factor',
1492
- icon: 'shield-checkmark',
1493
- iconColor: '#007AFF',
1494
- title: t('editProfile.items.twoFactor.title') || 'Two‑Factor Authentication',
1495
- subtitle: user?.privacySettings?.twoFactorEnabled ? t('editProfile.items.twoFactor.enabled') || 'Enabled' : t('editProfile.items.twoFactor.disabled') || 'Disabled (recommended)',
1496
- onPress: () => startEditing('twoFactor', '')
1630
+ style: [styles.sectionTitle, {
1631
+ color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93'
1497
1632
  }],
1498
- theme: theme
1633
+ children: t('editProfile.sections.security') || 'SECURITY'
1634
+ }), /*#__PURE__*/_jsx(View, {
1635
+ style: styles.groupedSectionWrapper,
1636
+ children: /*#__PURE__*/_jsx(GroupedSection, {
1637
+ items: [{
1638
+ id: 'two-factor',
1639
+ icon: 'shield-checkmark',
1640
+ iconColor: '#007AFF',
1641
+ title: t('editProfile.items.twoFactor.title') || 'Two‑Factor Authentication',
1642
+ subtitle: user?.privacySettings?.twoFactorEnabled ? t('editProfile.items.twoFactor.enabled') || 'Enabled' : t('editProfile.items.twoFactor.disabled') || 'Disabled (recommended)',
1643
+ onPress: () => startEditing('twoFactor', '')
1644
+ }],
1645
+ theme: theme
1646
+ })
1499
1647
  })]
1500
1648
  })]
1501
1649
  })
@@ -1504,27 +1652,35 @@ const AccountSettingsScreen = ({
1504
1652
  };
1505
1653
  const styles = StyleSheet.create({
1506
1654
  container: {
1507
- flex: 1,
1508
- backgroundColor: '#f2f2f2'
1655
+ flex: 1
1509
1656
  },
1510
1657
  content: {
1511
1658
  flex: 1,
1512
- padding: 16
1659
+ paddingTop: 8,
1660
+ paddingBottom: 24
1513
1661
  },
1514
1662
  contentEditing: {
1515
1663
  flex: 1,
1516
1664
  padding: 0
1517
1665
  },
1518
1666
  section: {
1519
- marginBottom: 24
1667
+ marginBottom: 32
1520
1668
  },
1521
1669
  sectionTitle: {
1522
- fontSize: 16,
1670
+ fontSize: 13,
1523
1671
  fontWeight: '600',
1524
- color: '#333',
1525
- marginBottom: 12,
1672
+ color: '#8E8E93',
1673
+ marginBottom: 8,
1674
+ marginTop: 4,
1675
+ marginHorizontal: 16,
1676
+ textTransform: 'uppercase',
1677
+ letterSpacing: 0.5,
1526
1678
  fontFamily: fontFamilies.phuduSemiBold
1527
1679
  },
1680
+ groupedSectionWrapper: {
1681
+ marginHorizontal: 16,
1682
+ backgroundColor: 'transparent'
1683
+ },
1528
1684
  userIcon: {
1529
1685
  marginRight: 12
1530
1686
  },
@@ -1561,21 +1717,21 @@ const styles = StyleSheet.create({
1561
1717
  flex: 1
1562
1718
  },
1563
1719
  editingFieldLabel: {
1564
- fontSize: 16,
1720
+ fontSize: 13,
1565
1721
  fontWeight: '600',
1566
- color: '#333',
1567
- marginBottom: 12,
1568
- fontFamily: fontFamilies.phuduSemiBold
1722
+ marginBottom: 8,
1723
+ fontFamily: fontFamilies.phuduSemiBold,
1724
+ textTransform: 'uppercase',
1725
+ letterSpacing: 0.5
1569
1726
  },
1570
1727
  editingFieldInput: {
1571
- backgroundColor: '#fff',
1572
- borderWidth: 2,
1573
- borderColor: '#e0e0e0',
1574
- borderRadius: 12,
1728
+ borderWidth: StyleSheet.hairlineWidth,
1729
+ borderRadius: 14,
1575
1730
  padding: 16,
1576
1731
  fontSize: 17,
1577
1732
  minHeight: 52,
1578
- fontWeight: '400'
1733
+ fontWeight: '400',
1734
+ letterSpacing: -0.2
1579
1735
  },
1580
1736
  editingFieldDescription: {
1581
1737
  fontSize: 14,
@@ -1598,22 +1754,20 @@ const styles = StyleSheet.create({
1598
1754
  fontWeight: '600'
1599
1755
  },
1600
1756
  editingFieldTextArea: {
1601
- backgroundColor: '#fff',
1602
- borderWidth: 2,
1603
- borderColor: '#e0e0e0',
1604
- borderRadius: 12,
1757
+ borderWidth: StyleSheet.hairlineWidth,
1758
+ borderRadius: 14,
1605
1759
  padding: 16,
1606
1760
  fontSize: 17,
1607
1761
  minHeight: 120,
1608
1762
  textAlignVertical: 'top',
1609
- fontWeight: '400'
1763
+ fontWeight: '400',
1764
+ letterSpacing: -0.2
1610
1765
  },
1611
1766
  // Custom editing header styles
1612
1767
  editingHeader: {
1613
1768
  paddingTop: Platform.OS === 'ios' ? 50 : 16,
1614
1769
  paddingBottom: 0,
1615
- borderBottomWidth: 1,
1616
- backgroundColor: '#fff'
1770
+ borderBottomWidth: StyleSheet.hairlineWidth
1617
1771
  },
1618
1772
  editingHeaderContent: {
1619
1773
  flexDirection: 'row',
@@ -1622,10 +1776,9 @@ const styles = StyleSheet.create({
1622
1776
  minHeight: 44
1623
1777
  },
1624
1778
  editingBackButton: {
1625
- width: 32,
1626
- height: 32,
1627
- borderRadius: 16,
1628
- backgroundColor: '#F8F9FA',
1779
+ width: 36,
1780
+ height: 36,
1781
+ borderRadius: 20,
1629
1782
  alignItems: 'center',
1630
1783
  justifyContent: 'center',
1631
1784
  marginRight: 12
@@ -1653,8 +1806,7 @@ const styles = StyleSheet.create({
1653
1806
  editingSaveButton: {
1654
1807
  paddingHorizontal: 16,
1655
1808
  paddingVertical: 8,
1656
- borderRadius: 18,
1657
- backgroundColor: '#F8F9FA',
1809
+ borderRadius: 20,
1658
1810
  minWidth: 60,
1659
1811
  alignItems: 'center',
1660
1812
  justifyContent: 'center'
@@ -1667,20 +1819,24 @@ const styles = StyleSheet.create({
1667
1819
  editingHeaderBottom: {
1668
1820
  flexDirection: 'column',
1669
1821
  alignItems: 'flex-start',
1670
- paddingHorizontal: 16,
1671
- paddingBottom: 8,
1672
- paddingTop: 8
1673
- },
1674
- editingBottomIcon: {
1675
- marginBottom: 8,
1676
- alignSelf: 'flex-start'
1822
+ paddingHorizontal: 20,
1823
+ paddingBottom: 20,
1824
+ paddingTop: 24
1825
+ },
1826
+ editingIconContainer: {
1827
+ width: 64,
1828
+ height: 64,
1829
+ borderRadius: 20,
1830
+ alignItems: 'center',
1831
+ justifyContent: 'center',
1832
+ marginBottom: 16
1677
1833
  },
1678
1834
  editingBottomTitle: {
1679
- fontSize: 32,
1835
+ fontSize: 28,
1680
1836
  fontWeight: '700',
1681
1837
  fontFamily: fontFamilies.phuduBold,
1682
1838
  letterSpacing: -0.5,
1683
- lineHeight: 36,
1839
+ lineHeight: 34,
1684
1840
  textAlign: 'left',
1685
1841
  alignSelf: 'flex-start'
1686
1842
  },