@oxyhq/services 5.16.1 → 5.16.3
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.
- package/lib/commonjs/core/mixins/OxyServices.user.js +14 -13
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +164 -3
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +26 -0
- package/lib/commonjs/crypto/signatureService.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedSection.js +1 -1
- package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +71 -24
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js +1 -4
- package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +175 -4
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +148 -49
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useSessionManagement.js +22 -2
- package/lib/commonjs/ui/context/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/index.js +28 -0
- package/lib/commonjs/ui/hooks/mutations/index.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +314 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js +193 -0
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/index.js +39 -0
- package/lib/commonjs/ui/hooks/queries/index.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/queryKeys.js +85 -0
- package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +145 -0
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +138 -0
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -0
- package/lib/commonjs/ui/hooks/queryClient.js +117 -0
- package/lib/commonjs/ui/hooks/queryClient.js.map +1 -0
- package/lib/commonjs/ui/hooks/useIdentityMutations.js +111 -0
- package/lib/commonjs/ui/hooks/useIdentityMutations.js.map +1 -0
- package/lib/commonjs/ui/hooks/useProfileEditing.js +42 -58
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/hooks/useQueryClient.js +20 -0
- package/lib/commonjs/ui/hooks/useQueryClient.js.map +1 -0
- package/lib/commonjs/ui/hooks/useSessionManagement.js +22 -2
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +43 -42
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +63 -58
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +6 -6
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/accountStore.js +57 -42
- package/lib/commonjs/ui/stores/accountStore.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +4 -25
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js +14 -13
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/crypto/keyManager.js +164 -3
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/signatureService.js +26 -0
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/ui/components/GroupedSection.js +1 -1
- package/lib/module/ui/components/GroupedSection.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +72 -25
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/profile/EditDisplayNameModal.js +1 -4
- package/lib/module/ui/components/profile/EditDisplayNameModal.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +174 -4
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +148 -49
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/hooks/useSessionManagement.js +22 -2
- package/lib/module/ui/context/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/mutations/index.js +6 -0
- package/lib/module/ui/hooks/mutations/index.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +308 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useServicesMutations.js +185 -0
- package/lib/module/ui/hooks/mutations/useServicesMutations.js.map +1 -0
- package/lib/module/ui/hooks/queries/index.js +7 -0
- package/lib/module/ui/hooks/queries/index.js.map +1 -0
- package/lib/module/ui/hooks/queries/queryKeys.js +78 -0
- package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -0
- package/lib/module/ui/hooks/queries/useAccountQueries.js +136 -0
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -0
- package/lib/module/ui/hooks/queries/useServicesQueries.js +130 -0
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -0
- package/lib/module/ui/hooks/queryClient.js +110 -0
- package/lib/module/ui/hooks/queryClient.js.map +1 -0
- package/lib/module/ui/hooks/useIdentityMutations.js +105 -0
- package/lib/module/ui/hooks/useIdentityMutations.js.map +1 -0
- package/lib/module/ui/hooks/useProfileEditing.js +43 -59
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/hooks/useQueryClient.js +15 -0
- package/lib/module/ui/hooks/useQueryClient.js.map +1 -0
- package/lib/module/ui/hooks/useSessionManagement.js +22 -2
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +43 -42
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +63 -58
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +6 -6
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/stores/accountStore.js +57 -42
- package/lib/module/ui/stores/accountStore.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +4 -25
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +0 -1
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts +19 -2
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +5 -0
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/profile/EditDisplayNameModal.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +4 -0
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts +3 -1
- package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/index.d.ts +3 -0
- package/lib/typescript/ui/hooks/mutations/index.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +25 -0
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts +23 -0
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/index.d.ts +4 -0
- package/lib/typescript/ui/hooks/queries/index.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/queryKeys.d.ts +56 -0
- package/lib/typescript/ui/hooks/queries/queryKeys.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +41 -0
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts +34 -0
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queryClient.d.ts +19 -0
- package/lib/typescript/ui/hooks/queryClient.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useIdentityMutations.d.ts +29 -0
- package/lib/typescript/ui/hooks/useIdentityMutations.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useQueryClient.d.ts +7 -0
- package/lib/typescript/ui/hooks/useQueryClient.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +3 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +0 -4
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/core/mixins/OxyServices.user.ts +17 -10
- package/src/crypto/keyManager.ts +177 -2
- package/src/crypto/signatureService.ts +30 -0
- package/src/ui/components/GroupedSection.tsx +1 -1
- package/src/ui/components/OxyProvider.tsx +91 -37
- package/src/ui/components/profile/EditDisplayNameModal.tsx +1 -3
- package/src/ui/context/OxyContext.tsx +185 -2
- package/src/ui/context/hooks/useAuthOperations.ts +171 -58
- package/src/ui/context/hooks/useSessionManagement.ts +24 -1
- package/src/ui/hooks/mutations/index.ts +4 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +277 -0
- package/src/ui/hooks/mutations/useServicesMutations.ts +164 -0
- package/src/ui/hooks/queries/index.ts +5 -0
- package/src/ui/hooks/queries/queryKeys.ts +73 -0
- package/src/ui/hooks/queries/useAccountQueries.ts +126 -0
- package/src/ui/hooks/queries/useServicesQueries.ts +121 -0
- package/src/ui/hooks/queryClient.ts +112 -0
- package/src/ui/hooks/useIdentityMutations.ts +115 -0
- package/src/ui/hooks/useProfileEditing.ts +46 -60
- package/src/ui/hooks/useQueryClient.ts +17 -0
- package/src/ui/hooks/useSessionManagement.ts +24 -1
- package/src/ui/screens/AccountOverviewScreen.tsx +38 -46
- package/src/ui/screens/AccountSettingsScreen.tsx +54 -54
- package/src/ui/screens/WelcomeNewUserScreen.tsx +13 -12
- package/src/ui/stores/accountStore.ts +54 -43
- package/src/ui/stores/authStore.ts +3 -17
|
@@ -39,6 +39,8 @@ import { EditLinksModal } from '../components/profile/EditLinksModal';
|
|
|
39
39
|
import { getDisplayName } from '../utils/user-utils';
|
|
40
40
|
import { TTLCache, registerCacheForCleanup } from '../../utils/cache';
|
|
41
41
|
import { useOxy } from '../context/OxyContext';
|
|
42
|
+
import { useCurrentUser } from '../hooks/queries/useAccountQueries';
|
|
43
|
+
import { useUpdateProfile, useUploadAvatar } from '../hooks/mutations/useAccountMutations';
|
|
42
44
|
import {
|
|
43
45
|
SCREEN_PADDING_HORIZONTAL,
|
|
44
46
|
SCREEN_PADDING_VERTICAL,
|
|
@@ -67,20 +69,24 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
67
69
|
}) => {
|
|
68
70
|
// Use useOxy() hook for OxyContext values
|
|
69
71
|
const {
|
|
70
|
-
user: userFromContext,
|
|
71
72
|
oxyServices,
|
|
72
|
-
isLoading: authLoading,
|
|
73
73
|
isAuthenticated,
|
|
74
74
|
activeSessionId,
|
|
75
75
|
} = useOxy();
|
|
76
76
|
const { t } = useI18n();
|
|
77
77
|
const normalizedTheme = normalizeTheme(theme);
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
const user =
|
|
78
|
+
|
|
79
|
+
// Use TanStack Query for user data
|
|
80
|
+
const { data: user, isLoading: userLoading } = useCurrentUser({ enabled: isAuthenticated });
|
|
81
|
+
const updateProfileMutation = useUpdateProfile();
|
|
82
|
+
const uploadAvatarMutation = useUploadAvatar();
|
|
83
|
+
|
|
84
|
+
// Fallback to store for backward compatibility
|
|
85
|
+
const userFromStore = useAuthStore((state) => state.user);
|
|
86
|
+
const finalUser = user || userFromStore;
|
|
81
87
|
const [isLoading, setIsLoading] = useState(false);
|
|
82
|
-
const
|
|
83
|
-
const
|
|
88
|
+
const isSaving = updateProfileMutation.isPending;
|
|
89
|
+
const isUpdatingAvatar = uploadAvatarMutation.isPending;
|
|
84
90
|
const [optimisticAvatarId, setOptimisticAvatarId] = useState<string | null>(null);
|
|
85
91
|
const scrollViewRef = useRef<ScrollView>(null);
|
|
86
92
|
const avatarSectionRef = useRef<View>(null);
|
|
@@ -202,9 +208,9 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
202
208
|
|
|
203
209
|
// Load user data - only reset fields when user actually changes (not just avatar)
|
|
204
210
|
useEffect(() => {
|
|
205
|
-
if (
|
|
206
|
-
const currentUserId =
|
|
207
|
-
const currentAvatar = typeof
|
|
211
|
+
if (finalUser) {
|
|
212
|
+
const currentUserId = finalUser.id;
|
|
213
|
+
const currentAvatar = typeof finalUser.avatar === 'string' ? finalUser.avatar : '';
|
|
208
214
|
const isNewUser = previousUserIdRef.current !== currentUserId;
|
|
209
215
|
const isAvatarOnlyUpdate = !isNewUser && previousUserIdRef.current === currentUserId &&
|
|
210
216
|
previousAvatarRef.current !== currentAvatar &&
|
|
@@ -214,30 +220,30 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
214
220
|
// Only reset all fields if it's a new user or first load
|
|
215
221
|
// Skip reset if it's just an avatar update
|
|
216
222
|
if (shouldInitialize && !isAvatarOnlyUpdate) {
|
|
217
|
-
const userDisplayName = typeof
|
|
218
|
-
?
|
|
219
|
-
:
|
|
220
|
-
const userLastName = typeof
|
|
223
|
+
const userDisplayName = typeof finalUser.name === 'string'
|
|
224
|
+
? finalUser.name
|
|
225
|
+
: finalUser.name?.first || finalUser.name?.full || '';
|
|
226
|
+
const userLastName = typeof finalUser.name === 'object' ? finalUser.name?.last || '' : '';
|
|
221
227
|
setDisplayName(userDisplayName);
|
|
222
228
|
setLastName(userLastName);
|
|
223
|
-
setUsername(
|
|
224
|
-
setEmail(
|
|
225
|
-
setBio(
|
|
226
|
-
setLocation(
|
|
229
|
+
setUsername(finalUser.username || '');
|
|
230
|
+
setEmail(finalUser.email || '');
|
|
231
|
+
setBio(finalUser.bio || '');
|
|
232
|
+
setLocation(finalUser.location || '');
|
|
227
233
|
|
|
228
234
|
// Handle locations - convert single location to array format
|
|
229
|
-
if (
|
|
230
|
-
setLocations(
|
|
235
|
+
if (finalUser.locations && Array.isArray(finalUser.locations)) {
|
|
236
|
+
setLocations(finalUser.locations.map((loc, index) => ({
|
|
231
237
|
id: loc.id || `existing-${index}`,
|
|
232
238
|
name: loc.name,
|
|
233
239
|
label: loc.label,
|
|
234
240
|
coordinates: loc.coordinates
|
|
235
241
|
})));
|
|
236
|
-
} else if (
|
|
242
|
+
} else if (finalUser.location) {
|
|
237
243
|
// Convert single location string to array format
|
|
238
244
|
setLocations([{
|
|
239
245
|
id: 'existing-0',
|
|
240
|
-
name:
|
|
246
|
+
name: finalUser.location,
|
|
241
247
|
label: 'Location'
|
|
242
248
|
}]);
|
|
243
249
|
} else {
|
|
@@ -245,16 +251,16 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
245
251
|
}
|
|
246
252
|
|
|
247
253
|
// Handle links - simple and direct like other fields
|
|
248
|
-
if (
|
|
249
|
-
const urls =
|
|
254
|
+
if (finalUser.linksMetadata && Array.isArray(finalUser.linksMetadata)) {
|
|
255
|
+
const urls = finalUser.linksMetadata.map(l => l.url);
|
|
250
256
|
setLinks(urls);
|
|
251
|
-
const metadataWithIds =
|
|
257
|
+
const metadataWithIds = finalUser.linksMetadata.map((link, index) => ({
|
|
252
258
|
...link,
|
|
253
259
|
id: link.id || `existing-${index}`
|
|
254
260
|
}));
|
|
255
261
|
setLinksMetadata(metadataWithIds);
|
|
256
|
-
} else if (Array.isArray(
|
|
257
|
-
const simpleLinks =
|
|
262
|
+
} else if (Array.isArray(finalUser.links)) {
|
|
263
|
+
const simpleLinks = finalUser.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean);
|
|
258
264
|
setLinks(simpleLinks);
|
|
259
265
|
const linksWithMetadata = simpleLinks.map((url, index) => ({
|
|
260
266
|
url,
|
|
@@ -264,12 +270,12 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
264
270
|
id: `existing-${index}`
|
|
265
271
|
}));
|
|
266
272
|
setLinksMetadata(linksWithMetadata);
|
|
267
|
-
} else if (
|
|
268
|
-
setLinks([
|
|
273
|
+
} else if (finalUser.website) {
|
|
274
|
+
setLinks([finalUser.website]);
|
|
269
275
|
setLinksMetadata([{
|
|
270
|
-
url:
|
|
271
|
-
title:
|
|
272
|
-
description: `Link to ${
|
|
276
|
+
url: finalUser.website,
|
|
277
|
+
title: finalUser.website.replace(/^https?:\/\//, '').replace(/\/$/, ''),
|
|
278
|
+
description: `Link to ${finalUser.website}`,
|
|
273
279
|
image: undefined,
|
|
274
280
|
id: 'existing-0'
|
|
275
281
|
}]);
|
|
@@ -298,7 +304,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
298
304
|
previousUserIdRef.current = currentUserId;
|
|
299
305
|
previousAvatarRef.current = currentAvatar;
|
|
300
306
|
}
|
|
301
|
-
}, [
|
|
307
|
+
}, [finalUser, avatarFileId, isUpdatingAvatar, optimisticAvatarId]);
|
|
302
308
|
|
|
303
309
|
// Set initial editing field if provided via props (e.g., from navigation)
|
|
304
310
|
// Use a ref to track if we've already set the initial field to avoid loops
|
|
@@ -358,32 +364,31 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
358
364
|
const saveField = useCallback(async (field: string | null) => {
|
|
359
365
|
if (!field) return;
|
|
360
366
|
|
|
361
|
-
setIsSaving(true);
|
|
362
367
|
try {
|
|
363
368
|
switch (field) {
|
|
364
369
|
case 'displayName':
|
|
365
|
-
await
|
|
370
|
+
await updateProfileMutation.mutateAsync({ name: { first: tempDisplayName, last: tempLastName } });
|
|
366
371
|
setDisplayName(tempDisplayName);
|
|
367
372
|
setLastName(tempLastName);
|
|
368
373
|
break;
|
|
369
374
|
case 'username':
|
|
370
|
-
await
|
|
375
|
+
await updateProfileMutation.mutateAsync({ username: tempUsername });
|
|
371
376
|
setUsername(tempUsername);
|
|
372
377
|
break;
|
|
373
378
|
case 'email':
|
|
374
|
-
await
|
|
379
|
+
await updateProfileMutation.mutateAsync({ email: tempEmail });
|
|
375
380
|
setEmail(tempEmail);
|
|
376
381
|
break;
|
|
377
382
|
case 'bio':
|
|
378
|
-
await
|
|
383
|
+
await updateProfileMutation.mutateAsync({ bio: tempBio });
|
|
379
384
|
setBio(tempBio);
|
|
380
385
|
break;
|
|
381
386
|
case 'location':
|
|
382
|
-
await
|
|
387
|
+
await updateProfileMutation.mutateAsync({ locations: tempLocations });
|
|
383
388
|
setLocations(tempLocations);
|
|
384
389
|
break;
|
|
385
390
|
case 'links':
|
|
386
|
-
await
|
|
391
|
+
await updateProfileMutation.mutateAsync({ linksMetadata: tempLinksWithMetadata });
|
|
387
392
|
setLinksMetadata(tempLinksWithMetadata);
|
|
388
393
|
setLinks(tempLinksWithMetadata.map(l => l.url));
|
|
389
394
|
break;
|
|
@@ -391,11 +396,9 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
391
396
|
setEditingField(null);
|
|
392
397
|
toast.success(t('editProfile.toasts.saved') || 'Saved');
|
|
393
398
|
} catch (error: any) {
|
|
394
|
-
|
|
395
|
-
} finally {
|
|
396
|
-
setIsSaving(false);
|
|
399
|
+
// Error is already handled by mutation's onError
|
|
397
400
|
}
|
|
398
|
-
}, [tempDisplayName, tempLastName, tempUsername, tempEmail, tempBio, tempLocations, tempLinksWithMetadata,
|
|
401
|
+
}, [tempDisplayName, tempLastName, tempUsername, tempEmail, tempBio, tempLocations, tempLinksWithMetadata, updateProfileMutation, t]);
|
|
399
402
|
|
|
400
403
|
// Helper to get current value for a field
|
|
401
404
|
const getFieldCurrentValue = useCallback((field: string): string => {
|
|
@@ -452,10 +455,9 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
452
455
|
}, [initialSection, sectionYPositions]);
|
|
453
456
|
|
|
454
457
|
const handleSave = async () => {
|
|
455
|
-
if (!
|
|
458
|
+
if (!finalUser) return;
|
|
456
459
|
|
|
457
460
|
try {
|
|
458
|
-
setIsSaving(true);
|
|
459
461
|
animateSaveButton(0.95); // Scale down slightly for animation
|
|
460
462
|
|
|
461
463
|
const updates: Record<string, any> = {
|
|
@@ -474,11 +476,11 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
474
476
|
}
|
|
475
477
|
|
|
476
478
|
// Handle avatar
|
|
477
|
-
if (avatarFileId !== (typeof
|
|
479
|
+
if (avatarFileId !== (typeof finalUser.avatar === 'string' ? finalUser.avatar : '')) {
|
|
478
480
|
updates.avatar = avatarFileId;
|
|
479
481
|
}
|
|
480
482
|
|
|
481
|
-
await
|
|
483
|
+
await updateProfileMutation.mutateAsync(updates);
|
|
482
484
|
toast.success(t('editProfile.toasts.profileUpdated') || 'Profile updated successfully');
|
|
483
485
|
|
|
484
486
|
animateSaveButton(1); // Scale back to normal
|
|
@@ -489,10 +491,8 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
489
491
|
goBack();
|
|
490
492
|
}
|
|
491
493
|
} catch (error: any) {
|
|
492
|
-
|
|
494
|
+
// Error is already handled by mutation's onError
|
|
493
495
|
animateSaveButton(1); // Scale back to normal on error
|
|
494
|
-
} finally {
|
|
495
|
-
setIsSaving(false);
|
|
496
496
|
}
|
|
497
497
|
};
|
|
498
498
|
|
|
@@ -739,7 +739,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
739
739
|
};
|
|
740
740
|
|
|
741
741
|
// Memoize display name for avatar
|
|
742
|
-
const displayNameForAvatar = useMemo(() => getDisplayName(
|
|
742
|
+
const displayNameForAvatar = useMemo(() => getDisplayName(finalUser), [finalUser]);
|
|
743
743
|
|
|
744
744
|
// Legacy renderEditingField function (fallback)
|
|
745
745
|
const renderEditingField = (type: string | null) => {
|
|
@@ -1110,7 +1110,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
1110
1110
|
|
|
1111
1111
|
|
|
1112
1112
|
|
|
1113
|
-
if (
|
|
1113
|
+
if (userLoading || !isAuthenticated) {
|
|
1114
1114
|
return (
|
|
1115
1115
|
<View style={[styles.container, {
|
|
1116
1116
|
backgroundColor: themeStyles.backgroundColor,
|
|
@@ -1418,7 +1418,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
|
|
|
1418
1418
|
iconColor: colors.sidebarIconHome,
|
|
1419
1419
|
title: t('editProfile.items.previewProfile.title') || 'Preview Profile',
|
|
1420
1420
|
subtitle: t('editProfile.items.previewProfile.subtitle') || 'See how your profile looks to others',
|
|
1421
|
-
onPress: () => navigate?.('Profile', { userId:
|
|
1421
|
+
onPress: () => navigate?.('Profile', { userId: finalUser?.id }),
|
|
1422
1422
|
},
|
|
1423
1423
|
{
|
|
1424
1424
|
id: 'privacy-settings',
|
|
@@ -11,6 +11,7 @@ import { normalizeTheme } from '../utils/themeUtils';
|
|
|
11
11
|
import GroupedPillButtons from '../components/internal/GroupedPillButtons';
|
|
12
12
|
import { useI18n } from '../hooks/useI18n';
|
|
13
13
|
import { useOxy } from '../context/OxyContext';
|
|
14
|
+
import { useUpdateProfile } from '../hooks/mutations/useAccountMutations';
|
|
14
15
|
|
|
15
16
|
const GAP = 12;
|
|
16
17
|
const INNER_GAP = 8;
|
|
@@ -62,7 +63,7 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
|
|
|
62
63
|
// Use useOxy() hook for OxyContext values
|
|
63
64
|
const { user, oxyServices } = useOxy();
|
|
64
65
|
const { t } = useI18n();
|
|
65
|
-
const
|
|
66
|
+
const updateProfileMutation = useUpdateProfile();
|
|
66
67
|
const currentUser = user || newUser; // fallback
|
|
67
68
|
const normalizedTheme = normalizeTheme(theme);
|
|
68
69
|
const colors = useThemeColors(normalizedTheme);
|
|
@@ -161,11 +162,11 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
|
|
|
161
162
|
|
|
162
163
|
// Update the avatar immediately in local state
|
|
163
164
|
setSelectedAvatarId(file.id);
|
|
164
|
-
|
|
165
|
-
// Update user
|
|
166
|
-
await
|
|
165
|
+
|
|
166
|
+
// Update user using TanStack Query mutation
|
|
167
|
+
await updateProfileMutation.mutateAsync({ avatar: file.id });
|
|
167
168
|
toast.success(t('editProfile.toasts.avatarUpdated') || 'Avatar updated');
|
|
168
|
-
|
|
169
|
+
|
|
169
170
|
// Ensure we stay on the avatar step
|
|
170
171
|
if (avatarStepIndex >= 0 && currentStep !== avatarStepIndex) {
|
|
171
172
|
animateToStepCallback(avatarStepIndex);
|
|
@@ -175,7 +176,7 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
|
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
});
|
|
178
|
-
}, [navigate,
|
|
179
|
+
}, [navigate, updateProfileMutation, oxyServices, currentStep, avatarStepIndex, animateToStepCallback, t]);
|
|
179
180
|
|
|
180
181
|
const step = steps[currentStep];
|
|
181
182
|
const pillButtons = useMemo(() => {
|
|
@@ -233,13 +234,13 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
|
|
|
233
234
|
)}
|
|
234
235
|
{step.showAvatar && (
|
|
235
236
|
<View style={[styles.avatarSection, styles.sectionSpacing]}>
|
|
236
|
-
<Avatar
|
|
237
|
-
size={120}
|
|
238
|
-
name={currentUser?.name?.full || currentUser?.name?.first || currentUser?.username}
|
|
239
|
-
uri={avatarUri}
|
|
240
|
-
|
|
237
|
+
<Avatar
|
|
238
|
+
size={120}
|
|
239
|
+
name={currentUser?.name?.full || currentUser?.name?.first || currentUser?.username}
|
|
240
|
+
uri={avatarUri}
|
|
241
|
+
|
|
241
242
|
backgroundColor={colors.primary + '20'}
|
|
242
|
-
style={styles.avatar}
|
|
243
|
+
style={styles.avatar}
|
|
243
244
|
/>
|
|
244
245
|
<TouchableOpacity style={[styles.changeAvatarButton, { backgroundColor: colors.primary }]} onPress={openAvatarPicker}>
|
|
245
246
|
<Ionicons name="image-outline" size={18} color="#FFFFFF" />
|
|
@@ -209,64 +209,75 @@ export const useAccountStore = create<AccountState>((set, get) => ({
|
|
|
209
209
|
return;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
for (const account of Object.values(state.accounts)) {
|
|
214
|
-
existingMap.set(account.sessionId, account);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const missingSessionIds = uniqueSessionIds.filter(id => !existingMap.has(id));
|
|
218
|
-
|
|
219
|
-
if (missingSessionIds.length === 0) {
|
|
220
|
-
const ordered = uniqueSessionIds
|
|
221
|
-
.map(id => existingMap.get(id))
|
|
222
|
-
.filter((acc): acc is QuickAccount => acc !== undefined);
|
|
223
|
-
get().setAccounts(ordered);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (state.loading) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
set({ loading: true, error: null });
|
|
232
|
-
|
|
212
|
+
// Try to get data from TanStack Query cache first
|
|
233
213
|
try {
|
|
234
|
-
|
|
214
|
+
// This will be called from a component, so we need to access queryClient differently
|
|
215
|
+
// For now, we'll keep the API call but optimize it
|
|
216
|
+
const existingMap = new Map(existingAccounts.map(a => [a.sessionId, a]));
|
|
217
|
+
for (const account of Object.values(state.accounts)) {
|
|
218
|
+
existingMap.set(account.sessionId, account);
|
|
219
|
+
}
|
|
235
220
|
|
|
236
|
-
const
|
|
221
|
+
const missingSessionIds = uniqueSessionIds.filter(id => !existingMap.has(id));
|
|
237
222
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
223
|
+
if (missingSessionIds.length === 0) {
|
|
224
|
+
const ordered = uniqueSessionIds
|
|
225
|
+
.map(id => existingMap.get(id))
|
|
226
|
+
.filter((acc): acc is QuickAccount => acc !== undefined);
|
|
227
|
+
get().setAccounts(ordered);
|
|
228
|
+
return;
|
|
243
229
|
}
|
|
244
230
|
|
|
245
|
-
|
|
246
|
-
|
|
231
|
+
if (state.loading) {
|
|
232
|
+
return;
|
|
247
233
|
}
|
|
248
234
|
|
|
249
|
-
|
|
250
|
-
const seen = new Set<string>();
|
|
251
|
-
const ordered: QuickAccount[] = [];
|
|
235
|
+
set({ loading: true, error: null });
|
|
252
236
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
237
|
+
try {
|
|
238
|
+
const batchResults = await oxyServices.getUsersBySessions(missingSessionIds);
|
|
239
|
+
|
|
240
|
+
const accountMap = new Map<string, QuickAccount>();
|
|
241
|
+
|
|
242
|
+
for (const { sessionId, user: userData } of batchResults) {
|
|
243
|
+
if (userData && !accountMap.has(sessionId)) {
|
|
244
|
+
const existing = existingMap.get(sessionId);
|
|
245
|
+
accountMap.set(sessionId, createQuickAccount(sessionId, userData, existing, oxyServices));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const [sessionId, account] of accountMap) {
|
|
250
|
+
existingMap.set(sessionId, account);
|
|
251
|
+
}
|
|
256
252
|
|
|
257
|
-
const
|
|
258
|
-
|
|
253
|
+
const orderToUse = preserveOrder ? uniqueSessionIds : [...uniqueSessionIds, ...state.accountOrder];
|
|
254
|
+
const seen = new Set<string>();
|
|
255
|
+
const ordered: QuickAccount[] = [];
|
|
256
|
+
|
|
257
|
+
for (const sessionId of orderToUse) {
|
|
258
|
+
if (seen.has(sessionId)) continue;
|
|
259
|
+
seen.add(sessionId);
|
|
260
|
+
|
|
261
|
+
const account = existingMap.get(sessionId);
|
|
262
|
+
if (account) ordered.push(account);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
get().setAccounts(ordered);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to load accounts';
|
|
268
|
+
if (__DEV__) {
|
|
269
|
+
console.error('AccountStore: Failed to load accounts:', error);
|
|
270
|
+
}
|
|
271
|
+
set({ error: errorMessage });
|
|
272
|
+
} finally {
|
|
273
|
+
set({ loading: false });
|
|
259
274
|
}
|
|
260
|
-
|
|
261
|
-
get().setAccounts(ordered);
|
|
262
275
|
} catch (error) {
|
|
263
276
|
const errorMessage = error instanceof Error ? error.message : 'Failed to load accounts';
|
|
264
277
|
if (__DEV__) {
|
|
265
278
|
console.error('AccountStore: Failed to load accounts:', error);
|
|
266
279
|
}
|
|
267
|
-
set({ error: errorMessage });
|
|
268
|
-
} finally {
|
|
269
|
-
set({ loading: false });
|
|
280
|
+
set({ error: errorMessage, loading: false });
|
|
270
281
|
}
|
|
271
282
|
},
|
|
272
283
|
|
|
@@ -16,7 +16,6 @@ export interface AuthState {
|
|
|
16
16
|
loginFailure: (error: string) => void;
|
|
17
17
|
logout: () => void;
|
|
18
18
|
fetchUser: (oxyServices: { getCurrentUser: () => Promise<User> }, forceRefresh?: boolean) => Promise<void>;
|
|
19
|
-
updateUser: (updates: Partial<User>, oxyServices: { updateProfile: (updates: Partial<User>) => Promise<User>; getCurrentUser: () => Promise<User> }) => Promise<void>;
|
|
20
19
|
setUser: (user: User) => void; // Direct user setter for caching
|
|
21
20
|
|
|
22
21
|
// Identity sync actions
|
|
@@ -47,7 +46,9 @@ export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>)
|
|
|
47
46
|
user: null,
|
|
48
47
|
isAuthenticated: false,
|
|
49
48
|
lastUserFetch: null,
|
|
50
|
-
//
|
|
49
|
+
// Reset identity sync state when logging out (for accounts app, identity = account)
|
|
50
|
+
isIdentitySynced: false,
|
|
51
|
+
isSyncing: false,
|
|
51
52
|
}),
|
|
52
53
|
setUser: (user: User) => set({ user, lastUserFetch: Date.now() }),
|
|
53
54
|
|
|
@@ -80,19 +81,4 @@ export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>)
|
|
|
80
81
|
set({ error: errorMessage, isLoading: false });
|
|
81
82
|
}
|
|
82
83
|
},
|
|
83
|
-
updateUser: async (updates, oxyServices) => {
|
|
84
|
-
set({ isLoading: true, error: null });
|
|
85
|
-
try {
|
|
86
|
-
await oxyServices.updateProfile(updates);
|
|
87
|
-
// Immediately fetch the latest user data after update
|
|
88
|
-
// Use arrow function to preserve 'this' context
|
|
89
|
-
await useAuthStore.getState().fetchUser({ getCurrentUser: () => oxyServices.getCurrentUser() }, true);
|
|
90
|
-
} catch (error) {
|
|
91
|
-
const errorMessage = error instanceof Error ? error.message : 'Failed to update user';
|
|
92
|
-
if (__DEV__) {
|
|
93
|
-
console.error('AuthStore: Error updating user:', error);
|
|
94
|
-
}
|
|
95
|
-
set({ error: errorMessage, isLoading: false });
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
84
|
}));
|