@oxyhq/services 5.16.24 → 5.16.26
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 +280 -84
- 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 +280 -84
- 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 +15 -2
- 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 +310 -86
- 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
|
@@ -8,6 +8,7 @@ var _react = require("react");
|
|
|
8
8
|
var _socket = _interopRequireDefault(require("socket.io-client"));
|
|
9
9
|
var _sonner = require("../../lib/sonner");
|
|
10
10
|
var _loggerUtils = require("../../utils/loggerUtils");
|
|
11
|
+
var _TokenService = require("../../core/services/TokenService");
|
|
11
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
function useSessionSocket({
|
|
13
14
|
userId,
|
|
@@ -27,6 +28,9 @@ function useSessionSocket({
|
|
|
27
28
|
const joinedRoomRef = (0, _react.useRef)(null);
|
|
28
29
|
const accessTokenRef = (0, _react.useRef)(null);
|
|
29
30
|
const handlersSetupRef = (0, _react.useRef)(false);
|
|
31
|
+
const lastRegisteredSocketIdRef = (0, _react.useRef)(null);
|
|
32
|
+
const getAccessTokenRef = (0, _react.useRef)(getAccessToken);
|
|
33
|
+
const getTransferCodeRef = (0, _react.useRef)(getTransferCode);
|
|
30
34
|
|
|
31
35
|
// Store callbacks in refs to avoid re-joining when they change
|
|
32
36
|
const refreshSessionsRef = (0, _react.useRef)(refreshSessions);
|
|
@@ -48,7 +52,9 @@ function useSessionSocket({
|
|
|
48
52
|
onIdentityTransferCompleteRef.current = onIdentityTransferComplete;
|
|
49
53
|
activeSessionIdRef.current = activeSessionId;
|
|
50
54
|
currentDeviceIdRef.current = currentDeviceId;
|
|
51
|
-
|
|
55
|
+
getAccessTokenRef.current = getAccessToken;
|
|
56
|
+
getTransferCodeRef.current = getTransferCode;
|
|
57
|
+
}, [refreshSessions, logout, clearSessionState, onRemoteSignOut, onSessionRemoved, onIdentityTransferComplete, activeSessionId, currentDeviceId, getAccessToken, getTransferCode]);
|
|
52
58
|
(0, _react.useEffect)(() => {
|
|
53
59
|
if (!userId || !baseURL) {
|
|
54
60
|
// Clean up if userId or baseURL becomes invalid
|
|
@@ -59,416 +65,431 @@ function useSessionSocket({
|
|
|
59
65
|
}
|
|
60
66
|
return;
|
|
61
67
|
}
|
|
62
|
-
const accessToken = getAccessToken();
|
|
63
|
-
// Recreate socket if token changed or socket doesn't exist
|
|
64
|
-
const tokenChanged = accessTokenRef.current !== accessToken;
|
|
65
|
-
if (!socketRef.current || tokenChanged) {
|
|
66
|
-
// Disconnect old socket if exists
|
|
67
|
-
if (socketRef.current) {
|
|
68
|
-
socketRef.current.disconnect();
|
|
69
|
-
socketRef.current = null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Create new socket with authentication
|
|
73
|
-
const socketOptions = {
|
|
74
|
-
transports: ['websocket']
|
|
75
|
-
};
|
|
76
|
-
if (accessToken) {
|
|
77
|
-
socketOptions.auth = {
|
|
78
|
-
token: accessToken
|
|
79
|
-
};
|
|
80
|
-
if (__DEV__) {
|
|
81
|
-
console.log('[useSessionSocket] Creating socket with auth token', {
|
|
82
|
-
userId,
|
|
83
|
-
hasToken: !!accessToken,
|
|
84
|
-
tokenLength: accessToken.length
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
if (__DEV__) {
|
|
89
|
-
console.warn('[useSessionSocket] No access token available for socket authentication');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
socketRef.current = (0, _socket.default)(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
|
-
const socket = socketRef.current;
|
|
98
68
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
69
|
+
// Initialize socket with token refresh
|
|
70
|
+
const initializeSocket = async () => {
|
|
71
|
+
try {
|
|
72
|
+
// Refresh token if expiring soon before creating socket connection
|
|
73
|
+
await _TokenService.tokenService.refreshTokenIfNeeded();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// If refresh fails, log but continue with current token
|
|
76
|
+
_loggerUtils.logger.debug('Token refresh failed before socket connection', {
|
|
77
|
+
component: 'useSessionSocket',
|
|
105
78
|
userId,
|
|
106
|
-
|
|
79
|
+
error
|
|
107
80
|
});
|
|
108
81
|
}
|
|
109
|
-
|
|
82
|
+
const accessToken = getAccessTokenRef.current();
|
|
83
|
+
// Recreate socket if token changed or socket doesn't exist
|
|
84
|
+
const tokenChanged = accessTokenRef.current !== accessToken;
|
|
85
|
+
if (!socketRef.current || tokenChanged) {
|
|
86
|
+
// Disconnect old socket if exists
|
|
87
|
+
if (socketRef.current) {
|
|
88
|
+
socketRef.current.disconnect();
|
|
89
|
+
socketRef.current = null;
|
|
90
|
+
}
|
|
110
91
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
92
|
+
// Create new socket with authentication
|
|
93
|
+
const socketOptions = {
|
|
94
|
+
transports: ['websocket']
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Get fresh token after potential refresh
|
|
98
|
+
const freshToken = getAccessTokenRef.current();
|
|
99
|
+
if (freshToken) {
|
|
100
|
+
socketOptions.auth = {
|
|
101
|
+
token: freshToken
|
|
102
|
+
};
|
|
103
|
+
} else {
|
|
104
|
+
_loggerUtils.logger.debug('No access token available for socket authentication', {
|
|
105
|
+
component: 'useSessionSocket',
|
|
106
|
+
userId
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
socketRef.current = (0, _socket.default)(baseURL, socketOptions);
|
|
110
|
+
accessTokenRef.current = freshToken;
|
|
111
|
+
joinedRoomRef.current = null; // Reset room tracking
|
|
112
|
+
handlersSetupRef.current = false; // Reset handlers flag for new socket
|
|
127
113
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (
|
|
114
|
+
const socket = socketRef.current;
|
|
115
|
+
if (!socket) return;
|
|
116
|
+
if (!joinedRoomRef.current && socket.connected) {
|
|
131
117
|
joinedRoomRef.current = `user:${userId}`;
|
|
132
118
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
|
|
120
|
+
// Set up event handlers (only once per socket instance)
|
|
121
|
+
// Define handlers - they reference socket from closure
|
|
122
|
+
const handleConnect = () => {
|
|
123
|
+
const currentToken = getAccessTokenRef.current();
|
|
124
|
+
if (__DEV__) {
|
|
125
|
+
console.log('[useSessionSocket] Socket connected', {
|
|
126
|
+
socketId: socket.id,
|
|
127
|
+
userId,
|
|
128
|
+
room: `user:${userId}`,
|
|
129
|
+
hasAuth: !!currentToken
|
|
130
|
+
});
|
|
131
|
+
_loggerUtils.logger.debug('Socket connected', {
|
|
132
|
+
component: 'useSessionSocket',
|
|
133
|
+
socketId: socket.id,
|
|
134
|
+
userId
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Server auto-joins room on connection when authenticated
|
|
138
|
+
// Just track that we're connected
|
|
139
|
+
if (userId) {
|
|
140
|
+
joinedRoomRef.current = `user:${userId}`;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const handleDisconnect = async reason => {
|
|
137
144
|
_loggerUtils.logger.debug('Socket disconnected', {
|
|
138
145
|
component: 'useSessionSocket',
|
|
139
146
|
reason,
|
|
140
147
|
userId
|
|
141
148
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
joinedRoomRef.current = null;
|
|
150
|
+
|
|
151
|
+
// If disconnected due to auth error, try to refresh token and reconnect
|
|
152
|
+
if (reason === 'io server disconnect' || reason.includes('auth') || reason.includes('Authentication')) {
|
|
153
|
+
try {
|
|
154
|
+
// Refresh token and reconnect
|
|
155
|
+
await _TokenService.tokenService.refreshTokenIfNeeded();
|
|
156
|
+
const freshToken = getAccessTokenRef.current();
|
|
157
|
+
if (freshToken && socketRef.current) {
|
|
158
|
+
// Update auth and reconnect
|
|
159
|
+
socketRef.current.auth = {
|
|
160
|
+
token: freshToken
|
|
161
|
+
};
|
|
162
|
+
socketRef.current.connect();
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
_loggerUtils.logger.debug('Failed to refresh token after disconnect', {
|
|
166
|
+
component: 'useSessionSocket',
|
|
167
|
+
userId,
|
|
168
|
+
error
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const handleError = error => {
|
|
148
174
|
_loggerUtils.logger.error('Socket error', error, {
|
|
149
175
|
component: 'useSessionSocket',
|
|
150
176
|
userId
|
|
151
177
|
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
178
|
+
};
|
|
179
|
+
const handleConnectError = async error => {
|
|
180
|
+
_loggerUtils.logger.debug('Socket connection error', {
|
|
181
|
+
component: 'useSessionSocket',
|
|
182
|
+
userId,
|
|
183
|
+
error: error.message
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// If error is due to expired/invalid token, try to refresh and reconnect
|
|
187
|
+
if (error.message.includes('Authentication') || error.message.includes('expired') || error.message.includes('token')) {
|
|
188
|
+
try {
|
|
189
|
+
await _TokenService.tokenService.refreshTokenIfNeeded();
|
|
190
|
+
const freshToken = getAccessTokenRef.current();
|
|
191
|
+
if (freshToken && socketRef.current) {
|
|
192
|
+
// Update auth and reconnect
|
|
193
|
+
socketRef.current.auth = {
|
|
194
|
+
token: freshToken
|
|
195
|
+
};
|
|
196
|
+
socketRef.current.connect();
|
|
197
|
+
}
|
|
198
|
+
} catch (refreshError) {
|
|
199
|
+
_loggerUtils.logger.debug('Failed to refresh token after connection error', {
|
|
200
|
+
component: 'useSessionSocket',
|
|
201
|
+
userId,
|
|
202
|
+
error: refreshError
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const handleSessionUpdate = async data => {
|
|
208
|
+
_loggerUtils.logger.debug('Received session_update event', {
|
|
209
|
+
component: 'useSessionSocket',
|
|
157
210
|
type: data.type,
|
|
158
211
|
socketId: socket.id,
|
|
159
212
|
socketConnected: socket.connected,
|
|
160
213
|
roomId: joinedRoomRef.current
|
|
161
214
|
});
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const currentDeviceId = currentDeviceIdRef.current;
|
|
215
|
+
const currentActiveSessionId = activeSessionIdRef.current;
|
|
216
|
+
const currentDeviceId = currentDeviceIdRef.current;
|
|
165
217
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// If the removed sessionId matches the current activeSessionId, immediately clear state
|
|
174
|
-
if (data.sessionId === currentActiveSessionId) {
|
|
175
|
-
if (onRemoteSignOutRef.current) {
|
|
176
|
-
onRemoteSignOutRef.current();
|
|
177
|
-
} else {
|
|
178
|
-
_sonner.toast.info('You have been signed out remotely.');
|
|
218
|
+
// Handle different event types
|
|
219
|
+
if (data.type === 'session_removed') {
|
|
220
|
+
// Track removed session
|
|
221
|
+
if (data.sessionId && onSessionRemovedRef.current) {
|
|
222
|
+
onSessionRemovedRef.current(data.sessionId);
|
|
179
223
|
}
|
|
180
|
-
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
component: 'useSessionSocket'
|
|
188
|
-
});
|
|
224
|
+
|
|
225
|
+
// If the removed sessionId matches the current activeSessionId, immediately clear state
|
|
226
|
+
if (data.sessionId === currentActiveSessionId) {
|
|
227
|
+
if (onRemoteSignOutRef.current) {
|
|
228
|
+
onRemoteSignOutRef.current();
|
|
229
|
+
} else {
|
|
230
|
+
_sonner.toast.info('You have been signed out remotely.');
|
|
189
231
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
232
|
+
// Use clearSessionState since session was already removed server-side
|
|
233
|
+
// Await to ensure storage cleanup completes before continuing
|
|
234
|
+
try {
|
|
235
|
+
await clearSessionStateRef.current();
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (__DEV__) {
|
|
238
|
+
_loggerUtils.logger.error('Failed to clear session state after session_removed', error instanceof Error ? error : new Error(String(error)), {
|
|
239
|
+
component: 'useSessionSocket'
|
|
240
|
+
});
|
|
241
|
+
}
|
|
199
242
|
}
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
} else if (data.type === 'device_removed') {
|
|
203
|
-
// Track all removed sessions from this device
|
|
204
|
-
if (data.sessionIds && onSessionRemovedRef.current) {
|
|
205
|
-
for (const sessionId of data.sessionIds) {
|
|
206
|
-
onSessionRemovedRef.current(sessionId);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// If the removed deviceId matches the current device, immediately clear state
|
|
211
|
-
if (data.deviceId && data.deviceId === currentDeviceId) {
|
|
212
|
-
if (onRemoteSignOutRef.current) {
|
|
213
|
-
onRemoteSignOutRef.current();
|
|
214
243
|
} else {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
component: 'useSessionSocket'
|
|
225
|
-
});
|
|
226
|
-
}
|
|
244
|
+
// Otherwise, just refresh the sessions list (with error handling)
|
|
245
|
+
refreshSessionsRef.current().catch(error => {
|
|
246
|
+
// Silently handle errors from refresh - they're expected if sessions were removed
|
|
247
|
+
if (__DEV__) {
|
|
248
|
+
_loggerUtils.logger.debug('Failed to refresh sessions after session_removed', {
|
|
249
|
+
component: 'useSessionSocket'
|
|
250
|
+
}, error);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
227
253
|
}
|
|
228
|
-
} else {
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
_loggerUtils.logger.debug('Failed to refresh sessions after device_removed', {
|
|
234
|
-
component: 'useSessionSocket'
|
|
235
|
-
}, error);
|
|
254
|
+
} else if (data.type === 'device_removed') {
|
|
255
|
+
// Track all removed sessions from this device
|
|
256
|
+
if (data.sessionIds && onSessionRemovedRef.current) {
|
|
257
|
+
for (const sessionId of data.sessionIds) {
|
|
258
|
+
onSessionRemovedRef.current(sessionId);
|
|
236
259
|
}
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
} else if (data.type === 'sessions_removed') {
|
|
240
|
-
// Track all removed sessions
|
|
241
|
-
if (data.sessionIds && onSessionRemovedRef.current) {
|
|
242
|
-
for (const sessionId of data.sessionIds) {
|
|
243
|
-
onSessionRemovedRef.current(sessionId);
|
|
244
260
|
}
|
|
245
|
-
}
|
|
246
261
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
262
|
+
// If the removed deviceId matches the current device, immediately clear state
|
|
263
|
+
if (data.deviceId && data.deviceId === currentDeviceId) {
|
|
264
|
+
if (onRemoteSignOutRef.current) {
|
|
265
|
+
onRemoteSignOutRef.current();
|
|
266
|
+
} else {
|
|
267
|
+
_sonner.toast.info('This device has been removed. You have been signed out.');
|
|
268
|
+
}
|
|
269
|
+
// Use clearSessionState since sessions were already removed server-side
|
|
270
|
+
// Await to ensure storage cleanup completes before continuing
|
|
271
|
+
try {
|
|
272
|
+
await clearSessionStateRef.current();
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (__DEV__) {
|
|
275
|
+
_loggerUtils.logger.error('Failed to clear session state after device_removed', error instanceof Error ? error : new Error(String(error)), {
|
|
276
|
+
component: 'useSessionSocket'
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
251
280
|
} else {
|
|
252
|
-
|
|
281
|
+
// Otherwise, refresh sessions and device list (with error handling)
|
|
282
|
+
refreshSessionsRef.current().catch(error => {
|
|
283
|
+
// Silently handle errors from refresh - they're expected if sessions were removed
|
|
284
|
+
if (__DEV__) {
|
|
285
|
+
_loggerUtils.logger.debug('Failed to refresh sessions after device_removed', {
|
|
286
|
+
component: 'useSessionSocket'
|
|
287
|
+
}, error);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
253
290
|
}
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (__DEV__) {
|
|
260
|
-
_loggerUtils.logger.error('Failed to clear session state after sessions_removed', error instanceof Error ? error : new Error(String(error)), {
|
|
261
|
-
component: 'useSessionSocket'
|
|
262
|
-
});
|
|
291
|
+
} else if (data.type === 'sessions_removed') {
|
|
292
|
+
// Track all removed sessions
|
|
293
|
+
if (data.sessionIds && onSessionRemovedRef.current) {
|
|
294
|
+
for (const sessionId of data.sessionIds) {
|
|
295
|
+
onSessionRemovedRef.current(sessionId);
|
|
263
296
|
}
|
|
264
297
|
}
|
|
265
|
-
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}, error);
|
|
298
|
+
|
|
299
|
+
// If the current activeSessionId is in the removed sessionIds list, immediately clear state
|
|
300
|
+
if (data.sessionIds && currentActiveSessionId && data.sessionIds.includes(currentActiveSessionId)) {
|
|
301
|
+
if (onRemoteSignOutRef.current) {
|
|
302
|
+
onRemoteSignOutRef.current();
|
|
303
|
+
} else {
|
|
304
|
+
_sonner.toast.info('You have been signed out remotely.');
|
|
273
305
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
306
|
+
// Use clearSessionState since sessions were already removed server-side
|
|
307
|
+
// Await to ensure storage cleanup completes before continuing
|
|
308
|
+
try {
|
|
309
|
+
await clearSessionStateRef.current();
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (__DEV__) {
|
|
312
|
+
_loggerUtils.logger.error('Failed to clear session state after sessions_removed', error instanceof Error ? error : new Error(String(error)), {
|
|
313
|
+
component: 'useSessionSocket'
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
// Otherwise, refresh sessions list (with error handling)
|
|
319
|
+
refreshSessionsRef.current().catch(error => {
|
|
320
|
+
// Silently handle errors from refresh - they're expected if sessions were removed
|
|
321
|
+
if (__DEV__) {
|
|
322
|
+
_loggerUtils.logger.debug('Failed to refresh sessions after sessions_removed', {
|
|
323
|
+
component: 'useSessionSocket'
|
|
324
|
+
}, error);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
} else if (data.type === 'identity_transfer_complete') {
|
|
329
|
+
// Handle identity transfer completion notification
|
|
330
|
+
const transferData = data;
|
|
331
|
+
_loggerUtils.logger.debug('Received identity_transfer_complete event', {
|
|
332
|
+
component: 'useSessionSocket',
|
|
281
333
|
transferId: transferData.transferId,
|
|
282
334
|
sourceDeviceId: transferData.sourceDeviceId,
|
|
283
335
|
currentDeviceId,
|
|
284
|
-
|
|
285
|
-
socketConnected: socket.connected
|
|
336
|
+
activeSessionId: activeSessionIdRef.current,
|
|
337
|
+
socketConnected: socket.connected,
|
|
338
|
+
userId,
|
|
339
|
+
room: joinedRoomRef.current,
|
|
340
|
+
publicKey: transferData.publicKey.substring(0, 16) + '...'
|
|
286
341
|
});
|
|
287
|
-
}
|
|
288
|
-
_loggerUtils.logger.debug('Received identity_transfer_complete event', {
|
|
289
|
-
component: 'useSessionSocket',
|
|
290
|
-
transferId: transferData.transferId,
|
|
291
|
-
sourceDeviceId: transferData.sourceDeviceId,
|
|
292
|
-
currentDeviceId,
|
|
293
|
-
activeSessionId: activeSessionIdRef.current,
|
|
294
|
-
socketConnected: socket.connected,
|
|
295
|
-
userId,
|
|
296
|
-
room: joinedRoomRef.current,
|
|
297
|
-
publicKey: transferData.publicKey.substring(0, 16) + '...'
|
|
298
|
-
});
|
|
299
342
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// 1. Matching deviceId with sourceDeviceId, OR
|
|
303
|
-
// 2. Having a stored transfer code for this transferId (most reliable check)
|
|
304
|
-
const deviceIdMatches = transferData.sourceDeviceId && transferData.sourceDeviceId === currentDeviceId;
|
|
343
|
+
// CRITICAL: Only call handler on the SOURCE device (the one that initiated the transfer)
|
|
344
|
+
// The new device (target) should NEVER process this event - it would delete its own identity!
|
|
305
345
|
|
|
306
|
-
|
|
307
|
-
|
|
346
|
+
// Check if this device has a stored transfer code (most reliable check - only source device has this)
|
|
347
|
+
const hasStoredTransferCode = getTransferCodeRef.current && !!getTransferCodeRef.current(transferData.transferId);
|
|
308
348
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (
|
|
316
|
-
|
|
349
|
+
// Also check deviceId match (exact match required)
|
|
350
|
+
const deviceIdMatches = transferData.sourceDeviceId && currentDeviceId && transferData.sourceDeviceId === currentDeviceId;
|
|
351
|
+
|
|
352
|
+
// ONLY call handler if BOTH conditions are met:
|
|
353
|
+
// 1. Has stored transfer code (definitive proof this is the source device)
|
|
354
|
+
// 2. DeviceId matches (additional verification)
|
|
355
|
+
// If deviceId is null/undefined, we still allow if stored code exists (logged out source device)
|
|
356
|
+
// But we NEVER process if no stored code exists (definitely not the source device)
|
|
357
|
+
const shouldCallHandler = !!transferData.transferId && hasStoredTransferCode && (deviceIdMatches || !currentDeviceId); // Allow if deviceId matches OR device is logged out (but has stored code)
|
|
358
|
+
|
|
359
|
+
if (shouldCallHandler) {
|
|
360
|
+
const matchReason = deviceIdMatches ? 'deviceId-exact-with-stored-code' : currentDeviceId ? 'deviceId-mismatch-but-has-stored-code' : 'logged-out-source-device-with-stored-code';
|
|
361
|
+
_loggerUtils.logger.debug('Matched source device, calling transfer complete handler', {
|
|
362
|
+
component: 'useSessionSocket',
|
|
317
363
|
transferId: transferData.transferId,
|
|
318
|
-
matchReason,
|
|
319
364
|
sourceDeviceId: transferData.sourceDeviceId,
|
|
320
|
-
currentDeviceId
|
|
365
|
+
currentDeviceId,
|
|
366
|
+
matchReason,
|
|
367
|
+
hasHandler: !!onIdentityTransferCompleteRef.current,
|
|
368
|
+
socketConnected: socket.connected,
|
|
369
|
+
socketId: socket.id
|
|
321
370
|
});
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
sourceDeviceId: transferData.sourceDeviceId,
|
|
327
|
-
currentDeviceId,
|
|
328
|
-
matchReason,
|
|
329
|
-
hasHandler: !!onIdentityTransferCompleteRef.current,
|
|
330
|
-
socketConnected: socket.connected,
|
|
331
|
-
socketId: socket.id
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Call the handler - it will verify using stored transfer codes
|
|
335
|
-
if (onIdentityTransferCompleteRef.current) {
|
|
336
|
-
try {
|
|
337
|
-
if (__DEV__) {
|
|
338
|
-
console.log('[useSessionSocket] Calling onIdentityTransferComplete handler', {
|
|
371
|
+
if (onIdentityTransferCompleteRef.current) {
|
|
372
|
+
try {
|
|
373
|
+
_loggerUtils.logger.debug('Calling onIdentityTransferComplete handler', {
|
|
374
|
+
component: 'useSessionSocket',
|
|
339
375
|
transferId: transferData.transferId
|
|
340
376
|
});
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
|
|
377
|
+
onIdentityTransferCompleteRef.current({
|
|
378
|
+
transferId: transferData.transferId,
|
|
379
|
+
sourceDeviceId: transferData.sourceDeviceId,
|
|
380
|
+
publicKey: transferData.publicKey,
|
|
381
|
+
transferCode: transferData.transferCode,
|
|
382
|
+
completedAt: transferData.completedAt
|
|
383
|
+
});
|
|
384
|
+
_loggerUtils.logger.debug('onIdentityTransferComplete handler called successfully', {
|
|
385
|
+
component: 'useSessionSocket',
|
|
386
|
+
transferId: transferData.transferId
|
|
387
|
+
});
|
|
388
|
+
} catch (error) {
|
|
389
|
+
_loggerUtils.logger.error('Error calling onIdentityTransferComplete handler', error instanceof Error ? error : new Error(String(error)), {
|
|
390
|
+
component: 'useSessionSocket',
|
|
355
391
|
transferId: transferData.transferId
|
|
356
392
|
});
|
|
357
393
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
transferId: transferData.transferId
|
|
361
|
-
});
|
|
362
|
-
} catch (error) {
|
|
363
|
-
if (__DEV__) {
|
|
364
|
-
console.error('[useSessionSocket] Error calling handler', error);
|
|
365
|
-
}
|
|
366
|
-
_loggerUtils.logger.error('Error calling onIdentityTransferComplete handler', error instanceof Error ? error : new Error(String(error)), {
|
|
394
|
+
} else {
|
|
395
|
+
_loggerUtils.logger.debug('No onIdentityTransferComplete handler registered', {
|
|
367
396
|
component: 'useSessionSocket',
|
|
368
397
|
transferId: transferData.transferId
|
|
369
398
|
});
|
|
370
399
|
}
|
|
371
400
|
} else {
|
|
372
|
-
|
|
373
|
-
console.warn('[useSessionSocket] No handler registered');
|
|
374
|
-
}
|
|
375
|
-
_loggerUtils.logger.debug('No onIdentityTransferComplete handler registered', {
|
|
401
|
+
_loggerUtils.logger.debug('Not the source device, ignoring transfer completion', {
|
|
376
402
|
component: 'useSessionSocket',
|
|
377
|
-
transferId: transferData.transferId
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
} else {
|
|
381
|
-
if (__DEV__) {
|
|
382
|
-
console.log('[useSessionSocket] Not matched, ignoring', {
|
|
383
403
|
sourceDeviceId: transferData.sourceDeviceId,
|
|
384
404
|
currentDeviceId,
|
|
385
405
|
hasActiveSession: activeSessionIdRef.current !== null
|
|
386
406
|
});
|
|
387
407
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
408
|
+
} else {
|
|
409
|
+
// For other event types (e.g., session_created), refresh sessions (with error handling)
|
|
410
|
+
refreshSessionsRef.current().catch(error => {
|
|
411
|
+
// Log but don't throw - refresh errors shouldn't break the socket handler
|
|
412
|
+
if (__DEV__) {
|
|
413
|
+
_loggerUtils.logger.debug('Failed to refresh sessions after session_update', {
|
|
414
|
+
component: 'useSessionSocket'
|
|
415
|
+
}, error);
|
|
416
|
+
}
|
|
393
417
|
});
|
|
394
|
-
}
|
|
395
|
-
} else {
|
|
396
|
-
// For other event types (e.g., session_created), refresh sessions (with error handling)
|
|
397
|
-
refreshSessionsRef.current().catch(error => {
|
|
398
|
-
// Log but don't throw - refresh errors shouldn't break the socket handler
|
|
399
|
-
if (__DEV__) {
|
|
400
|
-
_loggerUtils.logger.debug('Failed to refresh sessions after session_update', {
|
|
401
|
-
component: 'useSessionSocket'
|
|
402
|
-
}, error);
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
418
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
419
|
+
// If the current session was logged out (legacy behavior), handle it specially
|
|
420
|
+
if (data.sessionId === currentActiveSessionId) {
|
|
421
|
+
if (onRemoteSignOutRef.current) {
|
|
422
|
+
onRemoteSignOutRef.current();
|
|
423
|
+
} else {
|
|
424
|
+
_sonner.toast.info('You have been signed out remotely.');
|
|
425
|
+
}
|
|
426
|
+
// Use clearSessionState since session was already removed server-side
|
|
427
|
+
// Await to ensure storage cleanup completes before continuing
|
|
428
|
+
try {
|
|
429
|
+
await clearSessionStateRef.current();
|
|
430
|
+
} catch (error) {
|
|
431
|
+
_loggerUtils.logger.error('Failed to clear session state after session_update', error instanceof Error ? error : new Error(String(error)), {
|
|
432
|
+
component: 'useSessionSocket'
|
|
433
|
+
});
|
|
434
|
+
}
|
|
412
435
|
}
|
|
413
|
-
|
|
414
|
-
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// Register event handlers (only once per socket instance)
|
|
440
|
+
// Track by socket.id to prevent duplicate registrations when socket reconnects
|
|
441
|
+
const currentSocketId = socket.id || 'pending';
|
|
442
|
+
if (!handlersSetupRef.current || lastRegisteredSocketIdRef.current !== currentSocketId) {
|
|
443
|
+
// Remove old handlers if socket changed (reconnection)
|
|
444
|
+
if (socketRef.current && handlersSetupRef.current && lastRegisteredSocketIdRef.current) {
|
|
415
445
|
try {
|
|
416
|
-
|
|
446
|
+
socketRef.current.off('connect', handleConnect);
|
|
447
|
+
socketRef.current.off('disconnect', handleDisconnect);
|
|
448
|
+
socketRef.current.off('error', handleError);
|
|
449
|
+
socketRef.current.off('session_update', handleSessionUpdate);
|
|
417
450
|
} catch (error) {
|
|
418
|
-
|
|
419
|
-
console.error('Failed to clear session state after session_update:', error);
|
|
420
|
-
}
|
|
451
|
+
// Ignore errors when removing handlers
|
|
421
452
|
}
|
|
422
453
|
}
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
454
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
455
|
+
// Register handlers on current socket
|
|
456
|
+
socket.on('connect', handleConnect);
|
|
457
|
+
socket.on('disconnect', handleDisconnect);
|
|
458
|
+
socket.on('error', handleError);
|
|
459
|
+
socket.on('connect_error', handleConnectError);
|
|
460
|
+
socket.on('session_update', handleSessionUpdate);
|
|
461
|
+
handlersSetupRef.current = true;
|
|
462
|
+
lastRegisteredSocketIdRef.current = currentSocketId;
|
|
463
|
+
_loggerUtils.logger.debug('Event handlers set up', {
|
|
464
|
+
component: 'useSessionSocket',
|
|
435
465
|
socketId: socket.id,
|
|
436
466
|
userId
|
|
437
467
|
});
|
|
438
468
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
// Ensure socket is connected before proceeding
|
|
442
|
-
if (!socket.connected) {
|
|
443
|
-
if (__DEV__) {
|
|
444
|
-
console.log('[useSessionSocket] Socket not connected, connecting...', {
|
|
445
|
-
userId
|
|
446
|
-
});
|
|
447
|
-
_loggerUtils.logger.debug('Socket not connected, waiting for connection', {
|
|
469
|
+
if (!socket.connected) {
|
|
470
|
+
_loggerUtils.logger.debug('Socket not connected, connecting...', {
|
|
448
471
|
component: 'useSessionSocket',
|
|
449
472
|
userId
|
|
450
473
|
});
|
|
474
|
+
socket.connect();
|
|
451
475
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (__DEV__) {
|
|
455
|
-
console.log('[useSessionSocket] Socket already connected', {
|
|
456
|
-
socketId: socket.id,
|
|
457
|
-
userId,
|
|
458
|
-
connected: socket.connected
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
}
|
|
476
|
+
};
|
|
477
|
+
initializeSocket();
|
|
462
478
|
return () => {
|
|
463
479
|
// Only clean up handlers if socket still exists and handlers were set up
|
|
464
480
|
if (socketRef.current && handlersSetupRef.current) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
481
|
+
try {
|
|
482
|
+
socketRef.current.off('connect');
|
|
483
|
+
socketRef.current.off('disconnect');
|
|
484
|
+
socketRef.current.off('error');
|
|
485
|
+
socketRef.current.off('connect_error');
|
|
486
|
+
socketRef.current.off('session_update');
|
|
487
|
+
} catch (error) {
|
|
488
|
+
// Ignore errors when removing handlers
|
|
489
|
+
}
|
|
469
490
|
handlersSetupRef.current = false;
|
|
470
491
|
}
|
|
471
492
|
};
|
|
472
|
-
}, [userId, baseURL
|
|
493
|
+
}, [userId, baseURL]); // Only depend on userId and baseURL - functions are in refs
|
|
473
494
|
}
|
|
474
495
|
//# sourceMappingURL=useSessionSocket.js.map
|