@oxyhq/services 5.16.24 → 5.16.25

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 (38) hide show
  1. package/lib/commonjs/core/mixins/OxyServices.user.js +14 -4
  2. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
  3. package/lib/commonjs/ui/context/OxyContext.js +40 -75
  4. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  5. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +7 -6
  6. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +4 -3
  8. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  9. package/lib/commonjs/ui/hooks/useSessionSocket.js +349 -328
  10. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  11. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +13 -6
  12. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  13. package/lib/module/core/mixins/OxyServices.user.js +14 -4
  14. package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
  15. package/lib/module/ui/context/OxyContext.js +40 -75
  16. package/lib/module/ui/context/OxyContext.js.map +1 -1
  17. package/lib/module/ui/hooks/mutations/useAccountMutations.js +7 -6
  18. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  19. package/lib/module/ui/hooks/queries/useAccountQueries.js +4 -3
  20. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  21. package/lib/module/ui/hooks/useSessionSocket.js +349 -328
  22. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  23. package/lib/module/ui/screens/PrivacySettingsScreen.js +13 -6
  24. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  25. package/lib/typescript/core/mixins/OxyServices.user.d.ts +2 -2
  26. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
  27. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  28. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  29. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  30. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  31. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/core/mixins/OxyServices.user.ts +14 -4
  34. package/src/ui/context/OxyContext.tsx +39 -75
  35. package/src/ui/hooks/mutations/useAccountMutations.ts +8 -6
  36. package/src/ui/hooks/queries/useAccountQueries.ts +4 -2
  37. package/src/ui/hooks/useSessionSocket.ts +153 -155
  38. package/src/ui/screens/PrivacySettingsScreen.tsx +12 -6
@@ -2,6 +2,7 @@ import { useEffect, useRef } from 'react';
2
2
  import io from 'socket.io-client';
3
3
  import { toast } from '../../lib/sonner';
4
4
  import { logger } from '../../utils/loggerUtils';
5
+ import { tokenService } from '../../core/services/TokenService';
5
6
 
6
7
  interface UseSessionSocketProps {
7
8
  userId: string | null | undefined;
@@ -23,6 +24,9 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
23
24
  const joinedRoomRef = useRef<string | null>(null);
24
25
  const accessTokenRef = useRef<string | null>(null);
25
26
  const handlersSetupRef = useRef<boolean>(false);
27
+ const lastRegisteredSocketIdRef = useRef<string | null>(null);
28
+ const getAccessTokenRef = useRef(getAccessToken);
29
+ const getTransferCodeRef = useRef(getTransferCode);
26
30
 
27
31
  // Store callbacks in refs to avoid re-joining when they change
28
32
  const refreshSessionsRef = useRef(refreshSessions);
@@ -44,7 +48,9 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
44
48
  onIdentityTransferCompleteRef.current = onIdentityTransferComplete;
45
49
  activeSessionIdRef.current = activeSessionId;
46
50
  currentDeviceIdRef.current = currentDeviceId;
47
- }, [refreshSessions, logout, clearSessionState, onRemoteSignOut, onSessionRemoved, onIdentityTransferComplete, activeSessionId, currentDeviceId]);
51
+ getAccessTokenRef.current = getAccessToken;
52
+ getTransferCodeRef.current = getTransferCode;
53
+ }, [refreshSessions, logout, clearSessionState, onRemoteSignOut, onSessionRemoved, onIdentityTransferComplete, activeSessionId, currentDeviceId, getAccessToken, getTransferCode]);
48
54
 
49
55
  useEffect(() => {
50
56
  if (!userId || !baseURL) {
@@ -57,62 +63,58 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
57
63
  return;
58
64
  }
59
65
 
60
- const accessToken = getAccessToken();
61
- // Recreate socket if token changed or socket doesn't exist
62
- const tokenChanged = accessTokenRef.current !== accessToken;
63
- if (!socketRef.current || tokenChanged) {
64
- // Disconnect old socket if exists
65
- if (socketRef.current) {
66
- socketRef.current.disconnect();
67
- socketRef.current = null;
66
+ // Initialize socket with token refresh
67
+ const initializeSocket = async () => {
68
+ try {
69
+ // Refresh token if expiring soon before creating socket connection
70
+ await tokenService.refreshTokenIfNeeded();
71
+ } catch (error) {
72
+ // If refresh fails, log but continue with current token
73
+ logger.debug('Token refresh failed before socket connection', { component: 'useSessionSocket', userId, error });
68
74
  }
69
-
70
- // Create new socket with authentication
71
- const socketOptions: any = {
72
- transports: ['websocket'],
73
- };
74
-
75
- if (accessToken) {
76
- socketOptions.auth = {
77
- token: accessToken,
78
- };
79
- if (__DEV__) {
80
- console.log('[useSessionSocket] Creating socket with auth token', {
81
- userId,
82
- hasToken: !!accessToken,
83
- tokenLength: accessToken.length,
84
- });
75
+
76
+ const accessToken = getAccessTokenRef.current();
77
+ // Recreate socket if token changed or socket doesn't exist
78
+ const tokenChanged = accessTokenRef.current !== accessToken;
79
+ if (!socketRef.current || tokenChanged) {
80
+ // Disconnect old socket if exists
81
+ if (socketRef.current) {
82
+ socketRef.current.disconnect();
83
+ socketRef.current = null;
85
84
  }
86
- } else {
87
- if (__DEV__) {
88
- console.warn('[useSessionSocket] No access token available for socket authentication');
85
+
86
+ // Create new socket with authentication
87
+ const socketOptions: any = {
88
+ transports: ['websocket'],
89
+ };
90
+
91
+ // Get fresh token after potential refresh
92
+ const freshToken = getAccessTokenRef.current();
93
+ if (freshToken) {
94
+ socketOptions.auth = {
95
+ token: freshToken,
96
+ };
97
+ } else {
98
+ logger.debug('No access token available for socket authentication', { component: 'useSessionSocket', userId });
89
99
  }
100
+
101
+ socketRef.current = io(baseURL, socketOptions);
102
+ accessTokenRef.current = freshToken;
103
+ joinedRoomRef.current = null; // Reset room tracking
104
+ handlersSetupRef.current = false; // Reset handlers flag for new socket
90
105
  }
91
106
 
92
- socketRef.current = io(baseURL, socketOptions);
93
- accessTokenRef.current = accessToken;
94
- joinedRoomRef.current = null; // Reset room tracking
95
- handlersSetupRef.current = false; // Reset handlers flag for new socket
96
- }
97
-
98
- const socket = socketRef.current;
99
-
100
- // Server auto-joins room on connection when authenticated, so we don't need to manually join
101
- // Just track that we're in the room
102
- if (!joinedRoomRef.current && socket.connected) {
103
- joinedRoomRef.current = `user:${userId}`;
104
- if (__DEV__) {
105
- console.log('[useSessionSocket] Socket connected, should be auto-joined to room', {
106
- userId,
107
- room: `user:${userId}`,
108
- });
107
+ const socket = socketRef.current;
108
+ if (!socket) return;
109
+
110
+ if (!joinedRoomRef.current && socket.connected) {
111
+ joinedRoomRef.current = `user:${userId}`;
109
112
  }
110
- }
111
113
 
112
- // Set up event handlers (only once per socket instance)
113
- // Define handlers outside if block so they're always available
114
- const handleConnect = () => {
115
- const currentToken = getAccessToken();
114
+ // Set up event handlers (only once per socket instance)
115
+ // Define handlers - they reference socket from closure
116
+ const handleConnect = () => {
117
+ const currentToken = getAccessTokenRef.current();
116
118
  if (__DEV__) {
117
119
  console.log('[useSessionSocket] Socket connected', {
118
120
  socketId: socket.id,
@@ -129,18 +131,47 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
129
131
  }
130
132
  };
131
133
 
132
- const handleDisconnect = (reason: string) => {
133
- if (__DEV__) {
134
- console.log('[useSessionSocket] Socket disconnected:', reason);
135
- logger.debug('Socket disconnected', { component: 'useSessionSocket', reason, userId });
134
+ const handleDisconnect = async (reason: string) => {
135
+ logger.debug('Socket disconnected', { component: 'useSessionSocket', reason, userId });
136
+ joinedRoomRef.current = null;
137
+
138
+ // If disconnected due to auth error, try to refresh token and reconnect
139
+ if (reason === 'io server disconnect' || reason.includes('auth') || reason.includes('Authentication')) {
140
+ try {
141
+ // Refresh token and reconnect
142
+ await tokenService.refreshTokenIfNeeded();
143
+ const freshToken = getAccessTokenRef.current();
144
+ if (freshToken && socketRef.current) {
145
+ // Update auth and reconnect
146
+ socketRef.current.auth = { token: freshToken };
147
+ socketRef.current.connect();
148
+ }
149
+ } catch (error) {
150
+ logger.debug('Failed to refresh token after disconnect', { component: 'useSessionSocket', userId, error });
151
+ }
136
152
  }
137
- joinedRoomRef.current = null; // Reset room tracking on disconnect
138
153
  };
139
154
 
140
155
  const handleError = (error: Error) => {
141
- if (__DEV__) {
142
- console.error('[useSessionSocket] Socket error', error);
143
- logger.error('Socket error', error, { component: 'useSessionSocket', userId });
156
+ logger.error('Socket error', error, { component: 'useSessionSocket', userId });
157
+ };
158
+
159
+ const handleConnectError = async (error: Error) => {
160
+ logger.debug('Socket connection error', { component: 'useSessionSocket', userId, error: error.message });
161
+
162
+ // If error is due to expired/invalid token, try to refresh and reconnect
163
+ if (error.message.includes('Authentication') || error.message.includes('expired') || error.message.includes('token')) {
164
+ try {
165
+ await tokenService.refreshTokenIfNeeded();
166
+ const freshToken = getAccessTokenRef.current();
167
+ if (freshToken && socketRef.current) {
168
+ // Update auth and reconnect
169
+ socketRef.current.auth = { token: freshToken };
170
+ socketRef.current.connect();
171
+ }
172
+ } catch (refreshError) {
173
+ logger.debug('Failed to refresh token after connection error', { component: 'useSessionSocket', userId, error: refreshError });
174
+ }
144
175
  }
145
176
  };
146
177
 
@@ -150,14 +181,13 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
150
181
  deviceId?: string;
151
182
  sessionIds?: string[]
152
183
  }) => {
153
- if (__DEV__) {
154
- console.log('[useSessionSocket] Received session_update event:', {
155
- type: data.type,
156
- socketId: socket.id,
157
- socketConnected: socket.connected,
158
- roomId: joinedRoomRef.current,
159
- });
160
- }
184
+ logger.debug('Received session_update event', {
185
+ component: 'useSessionSocket',
186
+ type: data.type,
187
+ socketId: socket.id,
188
+ socketConnected: socket.connected,
189
+ roomId: joinedRoomRef.current,
190
+ });
161
191
 
162
192
  const currentActiveSessionId = activeSessionIdRef.current;
163
193
  const currentDeviceId = currentDeviceIdRef.current;
@@ -271,16 +301,6 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
271
301
  completedAt: string;
272
302
  };
273
303
 
274
- if (__DEV__) {
275
- console.log('[useSessionSocket] Received identity_transfer_complete event', {
276
- transferId: transferData.transferId,
277
- sourceDeviceId: transferData.sourceDeviceId,
278
- currentDeviceId,
279
- hasActiveSession: activeSessionIdRef.current !== null,
280
- socketConnected: socket.connected,
281
- });
282
- }
283
-
284
304
  logger.debug('Received identity_transfer_complete event', {
285
305
  component: 'useSessionSocket',
286
306
  transferId: transferData.transferId,
@@ -293,33 +313,30 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
293
313
  publicKey: transferData.publicKey.substring(0, 16) + '...',
294
314
  });
295
315
 
296
- // Only call handler on the SOURCE device (the one that initiated the transfer)
297
- // The source device is identified by:
298
- // 1. Matching deviceId with sourceDeviceId, OR
299
- // 2. Having a stored transfer code for this transferId (most reliable check)
300
- const deviceIdMatches = transferData.sourceDeviceId && transferData.sourceDeviceId === currentDeviceId;
316
+ // CRITICAL: Only call handler on the SOURCE device (the one that initiated the transfer)
317
+ // The new device (target) should NEVER process this event - it would delete its own identity!
318
+
319
+ // Check if this device has a stored transfer code (most reliable check - only source device has this)
320
+ const hasStoredTransferCode = getTransferCodeRef.current && !!getTransferCodeRef.current(transferData.transferId);
301
321
 
302
- // Check if this device has a stored transfer code (meaning it's the source device)
303
- const hasStoredTransferCode = getTransferCode && !!getTransferCode(transferData.transferId);
322
+ // Also check deviceId match (exact match required)
323
+ const deviceIdMatches = transferData.sourceDeviceId &&
324
+ currentDeviceId &&
325
+ transferData.sourceDeviceId === currentDeviceId;
304
326
 
305
- // Only call handler if this is the source device
306
- // We check both deviceId match AND stored transfer code to handle cases where
307
- // deviceId might be null (logged out device) but transfer code still exists
308
- const shouldCallHandler = !!transferData.transferId && (deviceIdMatches || hasStoredTransferCode);
327
+ // ONLY call handler if BOTH conditions are met:
328
+ // 1. Has stored transfer code (definitive proof this is the source device)
329
+ // 2. DeviceId matches (additional verification)
330
+ // If deviceId is null/undefined, we still allow if stored code exists (logged out source device)
331
+ // But we NEVER process if no stored code exists (definitely not the source device)
332
+ const shouldCallHandler = !!transferData.transferId &&
333
+ hasStoredTransferCode &&
334
+ (deviceIdMatches || !currentDeviceId); // Allow if deviceId matches OR device is logged out (but has stored code)
309
335
 
310
336
  if (shouldCallHandler) {
311
337
  const matchReason = deviceIdMatches
312
- ? 'deviceId-exact'
313
- : (activeSessionIdRef.current !== null ? 'active-session' : 'transferId-only');
314
-
315
- if (__DEV__) {
316
- console.log('[useSessionSocket] Matched source device, calling handler', {
317
- transferId: transferData.transferId,
318
- matchReason,
319
- sourceDeviceId: transferData.sourceDeviceId,
320
- currentDeviceId,
321
- });
322
- }
338
+ ? 'deviceId-exact-with-stored-code'
339
+ : (currentDeviceId ? 'deviceId-mismatch-but-has-stored-code' : 'logged-out-source-device-with-stored-code');
323
340
 
324
341
  logger.debug('Matched source device, calling transfer complete handler', {
325
342
  component: 'useSessionSocket',
@@ -332,15 +349,8 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
332
349
  socketId: socket.id,
333
350
  });
334
351
 
335
- // Call the handler - it will verify using stored transfer codes
336
352
  if (onIdentityTransferCompleteRef.current) {
337
353
  try {
338
- if (__DEV__) {
339
- console.log('[useSessionSocket] Calling onIdentityTransferComplete handler', {
340
- transferId: transferData.transferId,
341
- });
342
- }
343
-
344
354
  logger.debug('Calling onIdentityTransferComplete handler', {
345
355
  component: 'useSessionSocket',
346
356
  transferId: transferData.transferId,
@@ -354,42 +364,23 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
354
364
  completedAt: transferData.completedAt,
355
365
  });
356
366
 
357
- if (__DEV__) {
358
- console.log('[useSessionSocket] Handler called successfully', {
359
- transferId: transferData.transferId,
360
- });
361
- }
362
-
363
367
  logger.debug('onIdentityTransferComplete handler called successfully', {
364
368
  component: 'useSessionSocket',
365
369
  transferId: transferData.transferId,
366
370
  });
367
371
  } catch (error) {
368
- if (__DEV__) {
369
- console.error('[useSessionSocket] Error calling handler', error);
370
- }
371
372
  logger.error('Error calling onIdentityTransferComplete handler', error instanceof Error ? error : new Error(String(error)), {
372
373
  component: 'useSessionSocket',
373
374
  transferId: transferData.transferId,
374
375
  });
375
376
  }
376
377
  } else {
377
- if (__DEV__) {
378
- console.warn('[useSessionSocket] No handler registered');
379
- }
380
378
  logger.debug('No onIdentityTransferComplete handler registered', {
381
379
  component: 'useSessionSocket',
382
380
  transferId: transferData.transferId,
383
381
  });
384
382
  }
385
383
  } else {
386
- if (__DEV__) {
387
- console.log('[useSessionSocket] Not matched, ignoring', {
388
- sourceDeviceId: transferData.sourceDeviceId,
389
- currentDeviceId,
390
- hasActiveSession: activeSessionIdRef.current !== null,
391
- });
392
- }
393
384
  logger.debug('Not the source device, ignoring transfer completion', {
394
385
  component: 'useSessionSocket',
395
386
  sourceDeviceId: transferData.sourceDeviceId,
@@ -418,57 +409,64 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
418
409
  try {
419
410
  await clearSessionStateRef.current();
420
411
  } catch (error) {
421
- if (__DEV__) {
422
- console.error('Failed to clear session state after session_update:', error);
423
- }
412
+ logger.error('Failed to clear session state after session_update', error instanceof Error ? error : new Error(String(error)), { component: 'useSessionSocket' });
424
413
  }
425
414
  }
426
415
  }
427
416
  };
428
417
 
429
418
  // Register event handlers (only once per socket instance)
430
- if (!handlersSetupRef.current) {
419
+ // Track by socket.id to prevent duplicate registrations when socket reconnects
420
+ const currentSocketId = socket.id || 'pending';
421
+
422
+ if (!handlersSetupRef.current || lastRegisteredSocketIdRef.current !== currentSocketId) {
423
+ // Remove old handlers if socket changed (reconnection)
424
+ if (socketRef.current && handlersSetupRef.current && lastRegisteredSocketIdRef.current) {
425
+ try {
426
+ socketRef.current.off('connect', handleConnect);
427
+ socketRef.current.off('disconnect', handleDisconnect);
428
+ socketRef.current.off('error', handleError);
429
+ socketRef.current.off('session_update', handleSessionUpdate);
430
+ } catch (error) {
431
+ // Ignore errors when removing handlers
432
+ }
433
+ }
434
+
435
+ // Register handlers on current socket
431
436
  socket.on('connect', handleConnect);
432
437
  socket.on('disconnect', handleDisconnect);
433
438
  socket.on('error', handleError);
439
+ socket.on('connect_error', handleConnectError);
434
440
  socket.on('session_update', handleSessionUpdate);
435
441
 
436
442
  handlersSetupRef.current = true;
443
+ lastRegisteredSocketIdRef.current = currentSocketId;
437
444
 
438
- if (__DEV__) {
439
- console.log('[useSessionSocket] Event handlers set up', {
440
- socketId: socket.id,
441
- userId,
442
- });
443
- }
445
+ logger.debug('Event handlers set up', { component: 'useSessionSocket', socketId: socket.id, userId });
444
446
  }
445
447
 
446
- // Ensure socket is connected before proceeding
447
- if (!socket.connected) {
448
- if (__DEV__) {
449
- console.log('[useSessionSocket] Socket not connected, connecting...', { userId });
450
- logger.debug('Socket not connected, waiting for connection', { component: 'useSessionSocket', userId });
451
- }
452
- socket.connect();
453
- } else {
454
- if (__DEV__) {
455
- console.log('[useSessionSocket] Socket already connected', {
456
- socketId: socket.id,
457
- userId,
458
- connected: socket.connected
459
- });
448
+ if (!socket.connected) {
449
+ logger.debug('Socket not connected, connecting...', { component: 'useSessionSocket', userId });
450
+ socket.connect();
460
451
  }
461
- }
452
+ };
453
+
454
+ initializeSocket();
462
455
 
463
456
  return () => {
464
457
  // Only clean up handlers if socket still exists and handlers were set up
465
458
  if (socketRef.current && handlersSetupRef.current) {
466
- socketRef.current.off('connect', handleConnect);
467
- socketRef.current.off('disconnect', handleDisconnect);
468
- socketRef.current.off('error', handleError);
469
- socketRef.current.off('session_update', handleSessionUpdate);
459
+ try {
460
+ socketRef.current.off('connect');
461
+ socketRef.current.off('disconnect');
462
+ socketRef.current.off('error');
463
+ socketRef.current.off('connect_error');
464
+ socketRef.current.off('session_update');
465
+ } catch (error) {
466
+ // Ignore errors when removing handlers
467
+ }
470
468
  handlersSetupRef.current = false;
471
469
  }
472
470
  };
473
- }, [userId, baseURL, getAccessToken]); // Depend on userId, baseURL, and getAccessToken
474
- }
471
+ }, [userId, baseURL]); // Only depend on userId and baseURL - functions are in refs
472
+ }
@@ -78,8 +78,11 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
78
78
  const loadSettings = async () => {
79
79
  try {
80
80
  setIsLoading(true);
81
- if (user?.id && oxyServices) {
82
- const privacySettings = await oxyServices.getPrivacySettings(user.id);
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);
83
86
  if (privacySettings) {
84
87
  setSettings(privacySettings);
85
88
  }
@@ -93,7 +96,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
93
96
  };
94
97
 
95
98
  loadSettings();
96
- }, [user?.id, oxyServices, t]);
99
+ }, [oxyServices, t]);
97
100
 
98
101
  // Load blocked and restricted users
99
102
  useEffect(() => {
@@ -123,8 +126,11 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
123
126
  const newSettings = { ...settings, [key]: value };
124
127
  setSettings(newSettings);
125
128
 
126
- if (user?.id && oxyServices) {
127
- await oxyServices.updatePrivacySettings({ [key]: value }, user.id);
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);
128
134
  toast.success(t('privacySettings.updated') || 'Privacy settings updated');
129
135
  }
130
136
  } catch (error) {
@@ -135,7 +141,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
135
141
  } finally {
136
142
  setIsSaving(false);
137
143
  }
138
- }, [settings, user?.id, oxyServices, t]);
144
+ }, [settings, oxyServices, t]);
139
145
 
140
146
  const handleUnblock = useCallback(async (userId: string) => {
141
147
  if (!oxyServices) return;