@oxyhq/services 5.16.29 → 5.16.31

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 (145) hide show
  1. package/lib/commonjs/core/services/SessionService.js +2 -1
  2. package/lib/commonjs/core/services/SessionService.js.map +1 -1
  3. package/lib/commonjs/core/services/TokenService.js +17 -9
  4. package/lib/commonjs/core/services/TokenService.js.map +1 -1
  5. package/lib/commonjs/index.js +64 -0
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/models/interfaces.js +9 -8
  8. package/lib/commonjs/models/interfaces.js.map +1 -1
  9. package/lib/commonjs/ui/context/OxyContext.js +40 -4
  10. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  11. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +25 -14
  12. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  13. package/lib/commonjs/ui/hooks/auth/index.js +37 -0
  14. package/lib/commonjs/ui/hooks/auth/index.js.map +1 -0
  15. package/lib/commonjs/ui/hooks/auth/useUsernameValidation.js +171 -0
  16. package/lib/commonjs/ui/hooks/auth/useUsernameValidation.js.map +1 -0
  17. package/lib/commonjs/ui/hooks/index.js +20 -0
  18. package/lib/commonjs/ui/hooks/index.js.map +1 -1
  19. package/lib/commonjs/ui/hooks/mutations/index.js +12 -0
  20. package/lib/commonjs/ui/hooks/mutations/index.js.map +1 -1
  21. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +45 -1
  22. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  23. package/lib/commonjs/ui/hooks/queries/index.js +12 -0
  24. package/lib/commonjs/ui/hooks/queries/index.js.map +1 -1
  25. package/lib/commonjs/ui/hooks/queries/queryKeys.js +3 -1
  26. package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
  27. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +43 -1
  28. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  29. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +12 -4
  30. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  31. package/lib/commonjs/ui/hooks/useSessionManagement.js +8 -0
  32. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  33. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +76 -97
  34. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  35. package/lib/commonjs/ui/utils/sessionHelpers.js +26 -11
  36. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  37. package/lib/commonjs/utils/sessionUtils.js +8 -1
  38. package/lib/commonjs/utils/sessionUtils.js.map +1 -1
  39. package/lib/module/core/services/SessionService.js +2 -1
  40. package/lib/module/core/services/SessionService.js.map +1 -1
  41. package/lib/module/core/services/TokenService.js +17 -9
  42. package/lib/module/core/services/TokenService.js.map +1 -1
  43. package/lib/module/index.js +3 -3
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/module/models/interfaces.js +9 -8
  46. package/lib/module/models/interfaces.js.map +1 -1
  47. package/lib/module/ui/context/OxyContext.js +40 -4
  48. package/lib/module/ui/context/OxyContext.js.map +1 -1
  49. package/lib/module/ui/context/hooks/useAuthOperations.js +25 -14
  50. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  51. package/lib/module/ui/hooks/auth/index.js +7 -0
  52. package/lib/module/ui/hooks/auth/index.js.map +1 -0
  53. package/lib/module/ui/hooks/auth/useUsernameValidation.js +167 -0
  54. package/lib/module/ui/hooks/auth/useUsernameValidation.js.map +1 -0
  55. package/lib/module/ui/hooks/index.js +1 -0
  56. package/lib/module/ui/hooks/index.js.map +1 -1
  57. package/lib/module/ui/hooks/mutations/index.js +1 -1
  58. package/lib/module/ui/hooks/mutations/index.js.map +1 -1
  59. package/lib/module/ui/hooks/mutations/useAccountMutations.js +42 -0
  60. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  61. package/lib/module/ui/hooks/queries/index.js +1 -1
  62. package/lib/module/ui/hooks/queries/index.js.map +1 -1
  63. package/lib/module/ui/hooks/queries/queryKeys.js +3 -1
  64. package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
  65. package/lib/module/ui/hooks/queries/useAccountQueries.js +40 -0
  66. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  67. package/lib/module/ui/hooks/queries/useServicesQueries.js +13 -5
  68. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  69. package/lib/module/ui/hooks/useSessionManagement.js +8 -0
  70. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  71. package/lib/module/ui/screens/PrivacySettingsScreen.js +77 -98
  72. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  73. package/lib/module/ui/utils/sessionHelpers.js +26 -11
  74. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  75. package/lib/module/utils/sessionUtils.js +8 -1
  76. package/lib/module/utils/sessionUtils.js.map +1 -1
  77. package/lib/typescript/core/services/SessionService.d.ts +4 -2
  78. package/lib/typescript/core/services/SessionService.d.ts.map +1 -1
  79. package/lib/typescript/core/services/TokenService.d.ts +8 -3
  80. package/lib/typescript/core/services/TokenService.d.ts.map +1 -1
  81. package/lib/typescript/index.d.ts +4 -2
  82. package/lib/typescript/index.d.ts.map +1 -1
  83. package/lib/typescript/models/interfaces.d.ts +9 -8
  84. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  85. package/lib/typescript/models/session.d.ts +4 -2
  86. package/lib/typescript/models/session.d.ts.map +1 -1
  87. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  88. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  89. package/lib/typescript/ui/hooks/auth/index.d.ts +6 -0
  90. package/lib/typescript/ui/hooks/auth/index.d.ts.map +1 -0
  91. package/lib/typescript/ui/hooks/auth/useUsernameValidation.d.ts +32 -0
  92. package/lib/typescript/ui/hooks/auth/useUsernameValidation.d.ts.map +1 -0
  93. package/lib/typescript/ui/hooks/index.d.ts +1 -0
  94. package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
  95. package/lib/typescript/ui/hooks/mutations/index.d.ts +1 -1
  96. package/lib/typescript/ui/hooks/mutations/index.d.ts.map +1 -1
  97. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +12 -0
  98. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  99. package/lib/typescript/ui/hooks/queries/index.d.ts +1 -1
  100. package/lib/typescript/ui/hooks/queries/index.d.ts.map +1 -1
  101. package/lib/typescript/ui/hooks/queries/queryKeys.d.ts +2 -0
  102. package/lib/typescript/ui/hooks/queries/queryKeys.d.ts.map +1 -1
  103. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +12 -0
  104. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  105. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  106. package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
  107. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  108. package/lib/typescript/ui/utils/sessionHelpers.d.ts +6 -2
  109. package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -1
  110. package/lib/typescript/utils/sessionUtils.d.ts.map +1 -1
  111. package/package.json +1 -1
  112. package/src/core/services/SessionService.ts +4 -2
  113. package/src/core/services/TokenService.ts +18 -10
  114. package/src/index.ts +6 -0
  115. package/src/models/interfaces.ts +11 -10
  116. package/src/models/session.ts +5 -3
  117. package/src/ui/context/OxyContext.tsx +56 -20
  118. package/src/ui/context/hooks/useAuthOperations.ts +23 -15
  119. package/src/ui/hooks/auth/index.ts +7 -0
  120. package/src/ui/hooks/auth/useUsernameValidation.ts +177 -0
  121. package/src/ui/hooks/index.ts +2 -1
  122. package/src/ui/hooks/mutations/index.ts +2 -0
  123. package/src/ui/hooks/mutations/useAccountMutations.ts +36 -0
  124. package/src/ui/hooks/queries/index.ts +2 -0
  125. package/src/ui/hooks/queries/queryKeys.ts +2 -0
  126. package/src/ui/hooks/queries/useAccountQueries.ts +34 -0
  127. package/src/ui/hooks/queries/useServicesQueries.ts +8 -3
  128. package/src/ui/hooks/useSessionManagement.ts +8 -1
  129. package/src/ui/screens/PrivacySettingsScreen.tsx +67 -101
  130. package/src/ui/utils/sessionHelpers.ts +32 -15
  131. package/src/utils/sessionUtils.ts +8 -1
  132. package/lib/commonjs/ui/context/hooks/useSessionManagement.js +0 -281
  133. package/lib/commonjs/ui/context/hooks/useSessionManagement.js.map +0 -1
  134. package/lib/commonjs/ui/context/hooks/useStorage.js +0 -79
  135. package/lib/commonjs/ui/context/hooks/useStorage.js.map +0 -1
  136. package/lib/module/ui/context/hooks/useSessionManagement.js +0 -276
  137. package/lib/module/ui/context/hooks/useSessionManagement.js.map +0 -1
  138. package/lib/module/ui/context/hooks/useStorage.js +0 -74
  139. package/lib/module/ui/context/hooks/useStorage.js.map +0 -1
  140. package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts +0 -41
  141. package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts.map +0 -1
  142. package/lib/typescript/ui/context/hooks/useStorage.d.ts +0 -22
  143. package/lib/typescript/ui/context/hooks/useStorage.d.ts.map +0 -1
  144. package/src/ui/context/hooks/useSessionManagement.ts +0 -401
  145. package/src/ui/context/hooks/useStorage.ts +0 -104
@@ -499,3 +499,39 @@ export const useUploadFile = () => {
499
499
  });
500
500
  };
501
501
 
502
+ /**
503
+ * Unblock a user with query invalidation
504
+ */
505
+ export const useUnblockUser = () => {
506
+ const { oxyServices } = useOxy();
507
+ const queryClient = useQueryClient();
508
+
509
+ return useMutation({
510
+ mutationFn: async (userId: string) => {
511
+ return await oxyServices.unblockUser(userId);
512
+ },
513
+ onSuccess: () => {
514
+ // Invalidate blocked users query to refetch the list
515
+ queryClient.invalidateQueries({ queryKey: queryKeys.privacy.blocked() });
516
+ },
517
+ });
518
+ };
519
+
520
+ /**
521
+ * Unrestrict a user with query invalidation
522
+ */
523
+ export const useUnrestrictUser = () => {
524
+ const { oxyServices } = useOxy();
525
+ const queryClient = useQueryClient();
526
+
527
+ return useMutation({
528
+ mutationFn: async (userId: string) => {
529
+ return await oxyServices.unrestrictUser(userId);
530
+ },
531
+ onSuccess: () => {
532
+ // Invalidate restricted users query to refetch the list
533
+ queryClient.invalidateQueries({ queryKey: queryKeys.privacy.restricted() });
534
+ },
535
+ });
536
+ };
537
+
@@ -14,6 +14,8 @@ export {
14
14
  useUserByUsername,
15
15
  useUsersBySessions,
16
16
  usePrivacySettings,
17
+ useBlockedUsers,
18
+ useRestrictedUsers,
17
19
  } from './useAccountQueries';
18
20
 
19
21
  // Service query hooks (sessions, devices, security)
@@ -53,6 +53,8 @@ export const queryKeys = {
53
53
  privacy: {
54
54
  all: ['privacy'] as const,
55
55
  settings: (userId?: string) => [...queryKeys.privacy.all, 'settings', userId || 'current'] as const,
56
+ blocked: () => [...queryKeys.privacy.all, 'blocked'] as const,
57
+ restricted: () => [...queryKeys.privacy.all, 'restricted'] as const,
56
58
  },
57
59
 
58
60
  // Security activity queries
@@ -195,3 +195,37 @@ export const usePrivacySettings = (userId?: string, options?: { enabled?: boolea
195
195
  });
196
196
  };
197
197
 
198
+ /**
199
+ * Get blocked users
200
+ */
201
+ export const useBlockedUsers = (options?: { enabled?: boolean }) => {
202
+ const { oxyServices, isAuthenticated } = useOxy();
203
+
204
+ return useQuery({
205
+ queryKey: queryKeys.privacy.blocked(),
206
+ queryFn: async () => {
207
+ return await oxyServices.getBlockedUsers();
208
+ },
209
+ enabled: (options?.enabled !== false) && isAuthenticated,
210
+ staleTime: 1 * 60 * 1000, // 1 minute
211
+ gcTime: 5 * 60 * 1000, // 5 minutes
212
+ });
213
+ };
214
+
215
+ /**
216
+ * Get restricted users
217
+ */
218
+ export const useRestrictedUsers = (options?: { enabled?: boolean }) => {
219
+ const { oxyServices, isAuthenticated } = useOxy();
220
+
221
+ return useQuery({
222
+ queryKey: queryKeys.privacy.restricted(),
223
+ queryFn: async () => {
224
+ return await oxyServices.getRestrictedUsers();
225
+ },
226
+ enabled: (options?.enabled !== false) && isAuthenticated,
227
+ staleTime: 1 * 60 * 1000, // 1 minute
228
+ gcTime: 5 * 60 * 1000, // 5 minutes
229
+ });
230
+ };
231
+
@@ -8,7 +8,7 @@ import { fetchSessionsWithFallback, mapSessionsToClient } from '../../utils/sess
8
8
  * Get all active sessions for the current user
9
9
  */
10
10
  export const useSessions = (userId?: string, options?: { enabled?: boolean }) => {
11
- const { oxyServices, activeSessionId } = useOxy();
11
+ const { oxyServices, activeSessionId, user } = useOxy();
12
12
 
13
13
  return useQuery({
14
14
  queryKey: queryKeys.sessions.list(userId),
@@ -17,12 +17,14 @@ export const useSessions = (userId?: string, options?: { enabled?: boolean }) =>
17
17
  throw new Error('No active session');
18
18
  }
19
19
 
20
+ const publicKey = user?.publicKey || user?.id || ''; // user.id is now publicKey
20
21
  const sessions = await fetchSessionsWithFallback(oxyServices, activeSessionId, {
21
22
  fallbackDeviceId: undefined,
22
23
  fallbackUserId: userId,
24
+ fallbackPublicKey: publicKey,
23
25
  });
24
26
 
25
- return mapSessionsToClient(sessions, activeSessionId);
27
+ return sessions; // Already mapped by fetchSessionsWithFallback
26
28
  },
27
29
  enabled: (options?.enabled !== false) && !!activeSessionId,
28
30
  staleTime: 2 * 60 * 1000, // 2 minutes (sessions change frequently)
@@ -49,12 +51,15 @@ export const useSession = (sessionId: string | null, options?: { enabled?: boole
49
51
  }
50
52
 
51
53
  const now = new Date();
54
+ // user.id is now publicKey (canonical identity)
55
+ const publicKey = validation.user.publicKey || validation.user.id || '';
52
56
  return {
53
57
  sessionId,
54
58
  deviceId: '', // Device ID not available from validation response
55
59
  expiresAt: validation.expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
56
60
  lastActive: validation.lastActivity || now.toISOString(),
57
- userId: validation.user.id?.toString() ?? '',
61
+ publicKey, // Canonical user identity
62
+ userId: validation.user.id?.toString(), // Optional MongoDB ObjectId
58
63
  isCurrent: false,
59
64
  } as ClientSession;
60
65
  },
@@ -255,8 +255,10 @@ export const useSessionManagement = ({
255
255
  await activateSession(sessionId, user);
256
256
 
257
257
  try {
258
+ const publicKey = user.publicKey || user.id || ''; // user.id is now publicKey
258
259
  const deviceSessions = await fetchSessionsWithFallback(oxyServices, sessionId, {
259
- fallbackUserId: user.id,
260
+ fallbackUserId: user.id, // Still pass for backward compatibility
261
+ fallbackPublicKey: publicKey,
260
262
  logger,
261
263
  });
262
264
  updateSessions(deviceSessions, { merge: true });
@@ -330,8 +332,13 @@ export const useSessionManagement = ({
330
332
 
331
333
  const refreshPromise = (async () => {
332
334
  try {
335
+ // Get publicKey from active session or current user
336
+ const activeSession = sessions.find(s => s.sessionId === activeSessionId);
337
+ const publicKey = activeSession?.publicKey || activeUserId || ''; // Fallback to userId if publicKey not available
338
+
333
339
  const deviceSessions = await fetchSessionsWithFallback(oxyServices, activeSessionId, {
334
340
  fallbackUserId: activeUserId,
341
+ fallbackPublicKey: publicKey,
335
342
  logger,
336
343
  });
337
344
  updateSessions(deviceSessions, { merge: true });
@@ -1,10 +1,9 @@
1
- import React, { useState, useCallback, useEffect, useMemo } from 'react';
1
+ import React, { useState, useCallback, useMemo } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
5
5
  StyleSheet,
6
6
  ScrollView,
7
- ActivityIndicator,
8
7
  TouchableOpacity,
9
8
  } from 'react-native';
10
9
  import type { BaseScreenProps } from '../types/navigation';
@@ -15,6 +14,8 @@ import { useThemeStyles } from '../hooks/useThemeStyles';
15
14
  import { normalizeTheme } from '../utils/themeUtils';
16
15
  import type { BlockedUser, RestrictedUser } from '../../models/interfaces';
17
16
  import { useOxy } from '../context/OxyContext';
17
+ import { usePrivacySettings, useBlockedUsers, useRestrictedUsers } from '../hooks/queries';
18
+ import { useUpdatePrivacySettings, useUnblockUser, useUnrestrictUser } from '../hooks/mutations';
18
19
 
19
20
  interface PrivacySettings {
20
21
  isPrivateAccount: boolean;
@@ -44,8 +45,20 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
44
45
  goBack,
45
46
  }) => {
46
47
  // Use useOxy() hook for OxyContext values
47
- const { oxyServices, user } = useOxy();
48
+ const { oxyServices } = useOxy();
48
49
  const { t } = useI18n();
50
+
51
+ // TanStack Query hooks for server state
52
+ const { data: privacySettingsData, isLoading: isLoadingSettings, error: settingsError } = usePrivacySettings();
53
+ const { data: blockedUsers = [], isLoading: isLoadingBlocked } = useBlockedUsers();
54
+ const { data: restrictedUsers = [], isLoading: isLoadingRestricted } = useRestrictedUsers();
55
+
56
+ // Mutations
57
+ const updatePrivacySettingsMutation = useUpdatePrivacySettings();
58
+ const unblockUserMutation = useUnblockUser();
59
+ const unrestrictUserMutation = useUnrestrictUser();
60
+
61
+ // Client state for optimistic UI updates
49
62
  const [settings, setSettings] = useState<PrivacySettings>({
50
63
  isPrivateAccount: false,
51
64
  hideOnlineStatus: false,
@@ -67,111 +80,64 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
67
80
  autoFilter: true,
68
81
  muteKeywords: false,
69
82
  });
70
- const [isLoading, setIsLoading] = useState(true);
71
- const [isSaving, setIsSaving] = useState(false);
72
- const [blockedUsers, setBlockedUsers] = useState<BlockedUser[]>([]);
73
- const [restrictedUsers, setRestrictedUsers] = useState<RestrictedUser[]>([]);
74
- const [isLoadingUsers, setIsLoadingUsers] = useState(false);
75
-
76
- // Load settings and users
77
- useEffect(() => {
78
- const loadSettings = async () => {
79
- try {
80
- setIsLoading(true);
81
- // Use getCurrentUserId() which returns MongoDB ObjectId from JWT token
82
- // Never use user?.id as it may be set to publicKey
83
- const userId = oxyServices?.getCurrentUserId();
84
- if (userId && oxyServices) {
85
- const privacySettings = await oxyServices.getPrivacySettings(userId);
86
- if (privacySettings) {
87
- setSettings(privacySettings);
88
- }
89
- }
90
- } catch (error) {
91
- console.error('Failed to load privacy settings:', error);
92
- toast.error(t('privacySettings.loadError') || 'Failed to load privacy settings');
93
- } finally {
94
- setIsLoading(false);
95
- }
96
- };
97
-
98
- loadSettings();
99
- }, [oxyServices, t]);
100
-
101
- // Load blocked and restricted users
102
- useEffect(() => {
103
- const loadUsers = async () => {
104
- if (!oxyServices) return;
105
- try {
106
- setIsLoadingUsers(true);
107
- const [blocked, restricted] = await Promise.all([
108
- oxyServices.getBlockedUsers(),
109
- oxyServices.getRestrictedUsers(),
110
- ]);
111
- setBlockedUsers(blocked);
112
- setRestrictedUsers(restricted);
113
- } catch (error) {
114
- console.error('Failed to load blocked/restricted users:', error);
115
- } finally {
116
- setIsLoadingUsers(false);
117
- }
118
- };
119
-
120
- loadUsers();
121
- }, [oxyServices]);
83
+
84
+ // Update local state when server data changes
85
+ React.useEffect(() => {
86
+ if (privacySettingsData) {
87
+ setSettings(privacySettingsData as PrivacySettings);
88
+ }
89
+ }, [privacySettingsData]);
90
+
91
+ // Show error toast if settings failed to load
92
+ React.useEffect(() => {
93
+ if (settingsError) {
94
+ toast.error(t('privacySettings.loadError') || 'Failed to load privacy settings');
95
+ }
96
+ }, [settingsError, t]);
97
+
98
+ const isLoading = isLoadingSettings;
99
+ const isSaving = updatePrivacySettingsMutation.isPending;
100
+ const isLoadingUsers = isLoadingBlocked || isLoadingRestricted;
122
101
 
123
102
  const updateSetting = useCallback(async (key: keyof PrivacySettings, value: boolean) => {
124
- try {
125
- setIsSaving(true);
126
- const newSettings = { ...settings, [key]: value };
127
- setSettings(newSettings);
128
-
129
- // Use getCurrentUserId() which returns MongoDB ObjectId from JWT token
130
- // Never use user?.id as it may be set to publicKey
131
- const userId = oxyServices?.getCurrentUserId();
132
- if (userId && oxyServices) {
133
- await oxyServices.updatePrivacySettings({ [key]: value }, userId);
134
- toast.success(t('privacySettings.updated') || 'Privacy settings updated');
103
+ // Optimistic update
104
+ const newSettings = { ...settings, [key]: value };
105
+ setSettings(newSettings);
106
+
107
+ // Use mutation hook
108
+ updatePrivacySettingsMutation.mutate(
109
+ { settings: { [key]: value } },
110
+ {
111
+ onError: () => {
112
+ // Revert on error
113
+ setSettings(settings);
114
+ toast.error(t('privacySettings.updateError') || 'Failed to update privacy setting');
115
+ },
135
116
  }
136
- } catch (error) {
137
- console.error(`Failed to update ${key}:`, error);
138
- toast.error(t('privacySettings.updateError') || 'Failed to update privacy setting');
139
- // Revert on error
140
- setSettings(settings);
141
- } finally {
142
- setIsSaving(false);
143
- }
144
- }, [settings, oxyServices, t]);
117
+ );
118
+ }, [settings, updatePrivacySettingsMutation, t]);
145
119
 
146
120
  const handleUnblock = useCallback(async (userId: string) => {
147
- if (!oxyServices) return;
148
- try {
149
- await oxyServices.unblockUser(userId);
150
- setBlockedUsers(prev => prev.filter(u => {
151
- const id = typeof u.blockedId === 'string' ? u.blockedId : u.blockedId._id;
152
- return id !== userId;
153
- }));
154
- toast.success(t('privacySettings.userUnblocked') || 'User unblocked');
155
- } catch (error) {
156
- console.error('Failed to unblock user:', error);
157
- toast.error(t('privacySettings.unblockError') || 'Failed to unblock user');
158
- }
159
- }, [oxyServices, t]);
121
+ unblockUserMutation.mutate(userId, {
122
+ onSuccess: () => {
123
+ toast.success(t('privacySettings.userUnblocked') || 'User unblocked');
124
+ },
125
+ onError: () => {
126
+ toast.error(t('privacySettings.unblockError') || 'Failed to unblock user');
127
+ },
128
+ });
129
+ }, [unblockUserMutation, t]);
160
130
 
161
131
  const handleUnrestrict = useCallback(async (userId: string) => {
162
- if (!oxyServices) return;
163
- try {
164
- await oxyServices.unrestrictUser(userId);
165
- setRestrictedUsers(prev => prev.filter(u => {
166
- const id = typeof u.restrictedId === 'string' ? u.restrictedId : u.restrictedId._id;
167
- return id !== userId;
168
- }));
169
- toast.success(t('privacySettings.userUnrestricted') || 'User unrestricted');
170
- } catch (error) {
171
- console.error('Failed to unrestrict user:', error);
172
- toast.error(t('privacySettings.unrestrictError') || 'Failed to unrestrict user');
173
- }
174
- }, [oxyServices, t]);
132
+ unrestrictUserMutation.mutate(userId, {
133
+ onSuccess: () => {
134
+ toast.success(t('privacySettings.userUnrestricted') || 'User unrestricted');
135
+ },
136
+ onError: () => {
137
+ toast.error(t('privacySettings.unrestrictError') || 'Failed to unrestrict user');
138
+ },
139
+ });
140
+ }, [unrestrictUserMutation, t]);
175
141
 
176
142
  // Helper to extract user info from blocked/restricted objects
177
143
  const extractUserInfo = useCallback((
@@ -7,14 +7,16 @@ interface DeviceSession {
7
7
  deviceName?: string;
8
8
  expiresAt?: string;
9
9
  lastActive?: string;
10
- user?: { id?: string; _id?: { toString(): string } };
10
+ user?: { id?: string; publicKey?: string; _id?: { toString(): string } };
11
11
  userId?: string;
12
+ publicKey?: string;
12
13
  isCurrent?: boolean;
13
14
  }
14
15
 
15
16
  export interface FetchSessionsWithFallbackOptions {
16
17
  fallbackDeviceId?: string;
17
18
  fallbackUserId?: string;
19
+ fallbackPublicKey?: string; // Canonical user identity
18
20
  logger?: (message: string, error?: unknown) => void;
19
21
  }
20
22
 
@@ -37,27 +39,41 @@ export interface SessionValidationResult {
37
39
  * @param sessions - Raw session array returned from the API
38
40
  * @param fallbackDeviceId - Device identifier to use when missing from payload
39
41
  * @param fallbackUserId - User identifier to use when missing from payload
42
+ * @param fallbackPublicKey - Public key to use when missing from payload (canonical identity)
40
43
  */
41
44
  export const mapSessionsToClient = (
42
45
  sessions: DeviceSession[],
43
46
  fallbackDeviceId?: string,
44
47
  fallbackUserId?: string,
48
+ fallbackPublicKey?: string,
45
49
  ): ClientSession[] => {
46
50
  const now = new Date();
47
51
 
48
- return sessions.map((session) => ({
49
- sessionId: session.sessionId,
50
- deviceId: session.deviceId || fallbackDeviceId || '',
51
- expiresAt: session.expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
52
- lastActive: session.lastActive || now.toISOString(),
53
- userId:
54
- session.user?.id ||
52
+ return sessions.map((session) => {
53
+ // publicKey is the canonical user identity (user.id now equals publicKey)
54
+ // Extract from user.id (which is now publicKey), user.publicKey, or session.publicKey
55
+ const publicKey =
56
+ session.user?.id || // user.id is now publicKey (canonical identifier)
57
+ session.user?.publicKey ||
58
+ session.publicKey ||
59
+ fallbackPublicKey ||
60
+ '';
61
+
62
+ // userId is MongoDB ObjectId (internal backend reference, optional)
63
+ const userId =
55
64
  session.userId ||
56
- (session.user?._id ? session.user._id.toString() : undefined) ||
57
- fallbackUserId ||
58
- '',
59
- isCurrent: Boolean(session.isCurrent),
60
- }));
65
+ (session.user?._id ? session.user._id.toString() : undefined);
66
+
67
+ return {
68
+ sessionId: session.sessionId,
69
+ deviceId: session.deviceId || fallbackDeviceId || '',
70
+ expiresAt: session.expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
71
+ lastActive: session.lastActive || now.toISOString(),
72
+ publicKey, // Canonical user identity - REQUIRED
73
+ userId, // MongoDB ObjectId (optional, for backward compatibility)
74
+ isCurrent: Boolean(session.isCurrent),
75
+ };
76
+ });
61
77
  };
62
78
 
63
79
  /**
@@ -73,19 +89,20 @@ export const fetchSessionsWithFallback = async (
73
89
  {
74
90
  fallbackDeviceId,
75
91
  fallbackUserId,
92
+ fallbackPublicKey,
76
93
  logger,
77
94
  }: FetchSessionsWithFallbackOptions = {},
78
95
  ): Promise<ClientSession[]> => {
79
96
  try {
80
97
  const deviceSessions = await oxyServices.getDeviceSessions(sessionId);
81
- return mapSessionsToClient(deviceSessions, fallbackDeviceId, fallbackUserId);
98
+ return mapSessionsToClient(deviceSessions, fallbackDeviceId, fallbackUserId, fallbackPublicKey);
82
99
  } catch (error) {
83
100
  if (__DEV__ && logger) {
84
101
  logger('Failed to get device sessions, falling back to user sessions', error);
85
102
  }
86
103
 
87
104
  const userSessions = await oxyServices.getSessionsBySessionId(sessionId);
88
- return mapSessionsToClient(userSessions, fallbackDeviceId, fallbackUserId);
105
+ return mapSessionsToClient(userSessions, fallbackDeviceId, fallbackUserId, fallbackPublicKey);
89
106
  }
90
107
  };
91
108
 
@@ -12,12 +12,19 @@ import type { ClientSession } from '../models/session';
12
12
  */
13
13
  export function normalizeSession(session: Partial<ClientSession> & { sessionId: string }): ClientSession {
14
14
  const now = new Date().toISOString();
15
+
16
+ // publicKey is required (canonical user identity)
17
+ if (!session.publicKey) {
18
+ throw new Error(`Session ${session.sessionId} is missing required publicKey field`);
19
+ }
20
+
15
21
  return {
16
22
  sessionId: session.sessionId,
17
23
  deviceId: session.deviceId || '',
18
24
  expiresAt: session.expiresAt || now,
19
25
  lastActive: session.lastActive || now,
20
- userId: session.userId || '',
26
+ publicKey: session.publicKey, // Canonical user identity - required
27
+ userId: session.userId, // Optional MongoDB ObjectId for backward compatibility
21
28
  };
22
29
  }
23
30