@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.
- package/lib/commonjs/core/mixins/OxyServices.user.js +14 -4
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +40 -75
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +7 -6
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +4 -3
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +349 -328
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +13 -6
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js +14 -4
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +40 -75
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +7 -6
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js +4 -3
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +349 -328
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +13 -6
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +2 -2
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/mixins/OxyServices.user.ts +14 -4
- package/src/ui/context/OxyContext.tsx +39 -75
- package/src/ui/hooks/mutations/useAccountMutations.ts +8 -6
- package/src/ui/hooks/queries/useAccountQueries.ts +4 -2
- package/src/ui/hooks/useSessionSocket.ts +153 -155
- 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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const currentToken =
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
298
|
-
|
|
299
|
-
//
|
|
300
|
-
const
|
|
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
|
-
//
|
|
303
|
-
const
|
|
322
|
+
// Also check deviceId match (exact match required)
|
|
323
|
+
const deviceIdMatches = transferData.sourceDeviceId &&
|
|
324
|
+
currentDeviceId &&
|
|
325
|
+
transferData.sourceDeviceId === currentDeviceId;
|
|
304
326
|
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
|
|
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
|
-
: (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
127
|
-
|
|
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,
|
|
144
|
+
}, [settings, oxyServices, t]);
|
|
139
145
|
|
|
140
146
|
const handleUnblock = useCallback(async (userId: string) => {
|
|
141
147
|
if (!oxyServices) return;
|