@oxyhq/services 5.13.3 → 5.13.10

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