@oxyhq/services 5.17.5 → 5.17.8
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/crypto/keyManager.js +6 -161
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +20 -543
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContextBase.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +14 -331
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +8 -112
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +2 -27
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +2 -27
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +2 -88
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/crypto/keyManager.js +6 -161
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +20 -543
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/OxyContextBase.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +14 -330
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +8 -112
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js +2 -27
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useServicesQueries.js +2 -27
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +2 -88
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts +3 -20
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/types.d.ts +4 -0
- package/lib/typescript/crypto/types.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContextBase.d.ts +0 -37
- package/lib/typescript/ui/context/OxyContextBase.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +1 -20
- package/lib/typescript/ui/context/hooks/useAuthOperations.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/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts +1 -14
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/crypto/keyManager.ts +4 -170
- package/src/crypto/types.ts +4 -0
- package/src/ui/context/OxyContext.tsx +17 -588
- package/src/ui/context/OxyContextBase.tsx +2 -20
- package/src/ui/context/hooks/useAuthOperations.ts +22 -347
- package/src/ui/hooks/mutations/useAccountMutations.ts +12 -110
- package/src/ui/hooks/queries/useAccountQueries.ts +3 -27
- package/src/ui/hooks/queries/useServicesQueries.ts +3 -27
- package/src/ui/hooks/useSessionSocket.ts +2 -106
|
@@ -13,7 +13,7 @@ const getDeviceIdForSession = (sessions: ClientSession[] = [], sessionId: string
|
|
|
13
13
|
* Update user profile with optimistic updates and offline queue support
|
|
14
14
|
*/
|
|
15
15
|
export const useUpdateProfile = () => {
|
|
16
|
-
const { oxyServices, activeSessionId, user,
|
|
16
|
+
const { oxyServices, activeSessionId, user, sessions } = useOxy();
|
|
17
17
|
const queryClient = useQueryClient();
|
|
18
18
|
|
|
19
19
|
return useMutation({
|
|
@@ -24,19 +24,7 @@ export const useUpdateProfile = () => {
|
|
|
24
24
|
// Try to get token for the session
|
|
25
25
|
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
26
26
|
} catch (tokenError) {
|
|
27
|
-
|
|
28
|
-
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
29
|
-
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
30
|
-
try {
|
|
31
|
-
await syncIdentity();
|
|
32
|
-
// Retry getting token after sync
|
|
33
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
34
|
-
} catch (syncError) {
|
|
35
|
-
throw new Error('Session needs to be synced. Please try again.');
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
throw tokenError;
|
|
39
|
-
}
|
|
27
|
+
throw tokenError;
|
|
40
28
|
}
|
|
41
29
|
}
|
|
42
30
|
|
|
@@ -48,19 +36,7 @@ export const useUpdateProfile = () => {
|
|
|
48
36
|
|
|
49
37
|
// Handle authentication errors
|
|
50
38
|
if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
|
|
51
|
-
|
|
52
|
-
if (activeSessionId) {
|
|
53
|
-
try {
|
|
54
|
-
await syncIdentity();
|
|
55
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
56
|
-
// Retry the update after getting token
|
|
57
|
-
return await oxyServices.updateProfile(updates);
|
|
58
|
-
} catch (retryError) {
|
|
59
|
-
throw new Error('Authentication failed. Please sign in again.');
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
throw new Error('No active session. Please sign in.');
|
|
63
|
-
}
|
|
39
|
+
throw error;
|
|
64
40
|
}
|
|
65
41
|
|
|
66
42
|
// TanStack Query will automatically retry on network errors
|
|
@@ -127,7 +103,7 @@ export const useUpdateProfile = () => {
|
|
|
127
103
|
* Upload avatar with progress tracking and offline queue support
|
|
128
104
|
*/
|
|
129
105
|
export const useUploadAvatar = () => {
|
|
130
|
-
const { oxyServices, activeSessionId,
|
|
106
|
+
const { oxyServices, activeSessionId, sessions } = useOxy();
|
|
131
107
|
const queryClient = useQueryClient();
|
|
132
108
|
|
|
133
109
|
return useMutation({
|
|
@@ -137,17 +113,7 @@ export const useUploadAvatar = () => {
|
|
|
137
113
|
try {
|
|
138
114
|
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
139
115
|
} catch (tokenError) {
|
|
140
|
-
|
|
141
|
-
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
142
|
-
try {
|
|
143
|
-
await syncIdentity();
|
|
144
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
145
|
-
} catch (syncError) {
|
|
146
|
-
throw new Error('Session needs to be synced. Please try again.');
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
throw tokenError;
|
|
150
|
-
}
|
|
116
|
+
throw tokenError;
|
|
151
117
|
}
|
|
152
118
|
}
|
|
153
119
|
|
|
@@ -168,23 +134,7 @@ export const useUploadAvatar = () => {
|
|
|
168
134
|
|
|
169
135
|
// Handle authentication errors
|
|
170
136
|
if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
await syncIdentity();
|
|
174
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
175
|
-
// Retry upload
|
|
176
|
-
const uploadResult = await oxyServices.assetUpload(file as any, 'public');
|
|
177
|
-
const fileId = uploadResult?.file?.id || uploadResult?.id || uploadResult;
|
|
178
|
-
if (!fileId || typeof fileId !== 'string') {
|
|
179
|
-
throw new Error('Failed to get file ID from upload result');
|
|
180
|
-
}
|
|
181
|
-
return await oxyServices.updateProfile({ avatar: fileId });
|
|
182
|
-
} catch (retryError) {
|
|
183
|
-
throw new Error('Authentication failed. Please sign in again.');
|
|
184
|
-
}
|
|
185
|
-
} else {
|
|
186
|
-
throw new Error('No active session. Please sign in.');
|
|
187
|
-
}
|
|
137
|
+
throw error;
|
|
188
138
|
}
|
|
189
139
|
|
|
190
140
|
// TanStack Query will automatically retry on network errors
|
|
@@ -286,7 +236,7 @@ export const useUpdateAccountSettings = () => {
|
|
|
286
236
|
* Update privacy settings with optimistic updates and authentication handling
|
|
287
237
|
*/
|
|
288
238
|
export const useUpdatePrivacySettings = () => {
|
|
289
|
-
const { oxyServices, activeSessionId,
|
|
239
|
+
const { oxyServices, activeSessionId, sessions } = useOxy();
|
|
290
240
|
const queryClient = useQueryClient();
|
|
291
241
|
|
|
292
242
|
return useMutation({
|
|
@@ -304,19 +254,7 @@ export const useUpdatePrivacySettings = () => {
|
|
|
304
254
|
// Try to get token for the session
|
|
305
255
|
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
306
256
|
} catch (tokenError) {
|
|
307
|
-
|
|
308
|
-
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
309
|
-
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
310
|
-
try {
|
|
311
|
-
await syncIdentity();
|
|
312
|
-
// Retry getting token after sync
|
|
313
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
314
|
-
} catch (syncError) {
|
|
315
|
-
throw new Error('Session needs to be synced. Please try again.');
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
throw tokenError;
|
|
319
|
-
}
|
|
257
|
+
throw tokenError;
|
|
320
258
|
}
|
|
321
259
|
}
|
|
322
260
|
|
|
@@ -328,19 +266,7 @@ export const useUpdatePrivacySettings = () => {
|
|
|
328
266
|
|
|
329
267
|
// Handle authentication errors
|
|
330
268
|
if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
|
|
331
|
-
|
|
332
|
-
if (activeSessionId) {
|
|
333
|
-
try {
|
|
334
|
-
await syncIdentity();
|
|
335
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
336
|
-
// Retry the update after getting token
|
|
337
|
-
return await oxyServices.updatePrivacySettings(settings, targetUserId);
|
|
338
|
-
} catch (retryError) {
|
|
339
|
-
throw new Error('Authentication failed. Please sign in again.');
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
throw new Error('No active session. Please sign in.');
|
|
343
|
-
}
|
|
269
|
+
throw error;
|
|
344
270
|
}
|
|
345
271
|
|
|
346
272
|
// TanStack Query will automatically retry on network errors
|
|
@@ -424,7 +350,7 @@ export const useUpdatePrivacySettings = () => {
|
|
|
424
350
|
* Upload file with authentication handling and progress tracking
|
|
425
351
|
*/
|
|
426
352
|
export const useUploadFile = () => {
|
|
427
|
-
const { oxyServices, activeSessionId,
|
|
353
|
+
const { oxyServices, activeSessionId, sessions } = useOxy();
|
|
428
354
|
|
|
429
355
|
return useMutation({
|
|
430
356
|
mutationFn: async ({
|
|
@@ -444,19 +370,7 @@ export const useUploadFile = () => {
|
|
|
444
370
|
// Try to get token for the session
|
|
445
371
|
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
446
372
|
} catch (tokenError) {
|
|
447
|
-
|
|
448
|
-
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
449
|
-
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
450
|
-
try {
|
|
451
|
-
await syncIdentity();
|
|
452
|
-
// Retry getting token after sync
|
|
453
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
454
|
-
} catch (syncError) {
|
|
455
|
-
throw new Error('Session needs to be synced. Please try again.');
|
|
456
|
-
}
|
|
457
|
-
} else {
|
|
458
|
-
throw tokenError;
|
|
459
|
-
}
|
|
373
|
+
throw tokenError;
|
|
460
374
|
}
|
|
461
375
|
}
|
|
462
376
|
|
|
@@ -468,19 +382,7 @@ export const useUploadFile = () => {
|
|
|
468
382
|
|
|
469
383
|
// Handle authentication errors
|
|
470
384
|
if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
|
|
471
|
-
|
|
472
|
-
if (activeSessionId) {
|
|
473
|
-
try {
|
|
474
|
-
await syncIdentity();
|
|
475
|
-
await oxyServices.getTokenBySession(activeSessionId, getDeviceIdForSession(sessions, activeSessionId));
|
|
476
|
-
// Retry the upload after getting token
|
|
477
|
-
return await oxyServices.assetUpload(file as any, visibility, metadata, onProgress);
|
|
478
|
-
} catch (retryError) {
|
|
479
|
-
throw new Error('Authentication failed. Please sign in again.');
|
|
480
|
-
}
|
|
481
|
-
} else {
|
|
482
|
-
throw new Error('No active session. Please sign in.');
|
|
483
|
-
}
|
|
385
|
+
throw error;
|
|
484
386
|
}
|
|
485
387
|
|
|
486
388
|
// TanStack Query will automatically retry on network errors
|
|
@@ -128,7 +128,7 @@ export const useUsersBySessions = (sessionIds: string[], options?: { enabled?: b
|
|
|
128
128
|
* Get privacy settings for a user
|
|
129
129
|
*/
|
|
130
130
|
export const usePrivacySettings = (userId?: string, options?: { enabled?: boolean }) => {
|
|
131
|
-
const { oxyServices, activeSessionId,
|
|
131
|
+
const { oxyServices, activeSessionId, sessions } = useOxy();
|
|
132
132
|
// Use getCurrentUserId() which returns MongoDB ObjectId from JWT token
|
|
133
133
|
// Never use user?.id as it may be set to publicKey
|
|
134
134
|
const targetUserId = userId || oxyServices.getCurrentUserId() || undefined;
|
|
@@ -147,19 +147,7 @@ export const usePrivacySettings = (userId?: string, options?: { enabled?: boolea
|
|
|
147
147
|
// Try to get token for the session
|
|
148
148
|
await oxyServices.getTokenBySession(activeSessionId, deviceId);
|
|
149
149
|
} catch (tokenError) {
|
|
150
|
-
|
|
151
|
-
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
152
|
-
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
153
|
-
try {
|
|
154
|
-
await syncIdentity();
|
|
155
|
-
// Retry getting token after sync
|
|
156
|
-
await oxyServices.getTokenBySession(activeSessionId, deviceId);
|
|
157
|
-
} catch (syncError) {
|
|
158
|
-
throw new Error('Session needs to be synced. Please try again.');
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
throw tokenError;
|
|
162
|
-
}
|
|
150
|
+
throw tokenError;
|
|
163
151
|
}
|
|
164
152
|
}
|
|
165
153
|
|
|
@@ -171,19 +159,7 @@ export const usePrivacySettings = (userId?: string, options?: { enabled?: boolea
|
|
|
171
159
|
|
|
172
160
|
// Handle authentication errors
|
|
173
161
|
if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
|
|
174
|
-
|
|
175
|
-
if (activeSessionId) {
|
|
176
|
-
try {
|
|
177
|
-
await syncIdentity();
|
|
178
|
-
await oxyServices.getTokenBySession(activeSessionId, deviceId);
|
|
179
|
-
// Retry the request after getting token
|
|
180
|
-
return await oxyServices.getPrivacySettings(targetUserId);
|
|
181
|
-
} catch (retryError) {
|
|
182
|
-
throw new Error('Authentication failed. Please sign in again.');
|
|
183
|
-
}
|
|
184
|
-
} else {
|
|
185
|
-
throw new Error('No active session. Please sign in.');
|
|
186
|
-
}
|
|
162
|
+
throw error;
|
|
187
163
|
}
|
|
188
164
|
|
|
189
165
|
// TanStack Query will automatically retry on network errors
|
|
@@ -89,7 +89,7 @@ export const useDeviceSessions = (options?: { enabled?: boolean }) => {
|
|
|
89
89
|
* Get user devices
|
|
90
90
|
*/
|
|
91
91
|
export const useUserDevices = (options?: { enabled?: boolean }) => {
|
|
92
|
-
const { oxyServices, isAuthenticated, activeSessionId,
|
|
92
|
+
const { oxyServices, isAuthenticated, activeSessionId, sessions } = useOxy();
|
|
93
93
|
const deviceId = activeSessionId ? sessions.find((s) => s.sessionId === activeSessionId)?.deviceId : undefined;
|
|
94
94
|
|
|
95
95
|
return useQuery({
|
|
@@ -101,19 +101,7 @@ export const useUserDevices = (options?: { enabled?: boolean }) => {
|
|
|
101
101
|
// Try to get token for the session
|
|
102
102
|
await oxyServices.getTokenBySession(activeSessionId, deviceId);
|
|
103
103
|
} catch (tokenError) {
|
|
104
|
-
|
|
105
|
-
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
106
|
-
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
107
|
-
try {
|
|
108
|
-
await syncIdentity();
|
|
109
|
-
// Retry getting token after sync
|
|
110
|
-
await oxyServices.getTokenBySession(activeSessionId, deviceId);
|
|
111
|
-
} catch (syncError) {
|
|
112
|
-
throw new Error('Session needs to be synced. Please try again.');
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
throw tokenError;
|
|
116
|
-
}
|
|
104
|
+
throw tokenError;
|
|
117
105
|
}
|
|
118
106
|
}
|
|
119
107
|
|
|
@@ -125,19 +113,7 @@ export const useUserDevices = (options?: { enabled?: boolean }) => {
|
|
|
125
113
|
|
|
126
114
|
// Handle authentication errors
|
|
127
115
|
if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
|
|
128
|
-
|
|
129
|
-
if (activeSessionId) {
|
|
130
|
-
try {
|
|
131
|
-
await syncIdentity();
|
|
132
|
-
await oxyServices.getTokenBySession(activeSessionId, deviceId);
|
|
133
|
-
// Retry the request after getting token
|
|
134
|
-
return await oxyServices.getUserDevices();
|
|
135
|
-
} catch (retryError) {
|
|
136
|
-
throw new Error('Authentication failed. Please sign in again.');
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
throw new Error('No active session. Please sign in.');
|
|
140
|
-
}
|
|
116
|
+
throw error;
|
|
141
117
|
}
|
|
142
118
|
|
|
143
119
|
// TanStack Query will automatically retry on network errors
|
|
@@ -13,20 +13,17 @@ interface UseSessionSocketProps {
|
|
|
13
13
|
clearSessionState: () => Promise<void>;
|
|
14
14
|
baseURL: string;
|
|
15
15
|
getAccessToken: () => string | null;
|
|
16
|
-
getTransferCode?: (transferId: string) => { code: string; sourceDeviceId: string | null; publicKey: string; timestamp: number } | null;
|
|
17
16
|
onRemoteSignOut?: () => void;
|
|
18
17
|
onSessionRemoved?: (sessionId: string) => void;
|
|
19
|
-
onIdentityTransferComplete?: (data: { transferId: string; sourceDeviceId: string; publicKey: string; transferCode?: string; completedAt: string }) => void;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
export function useSessionSocket({ userId, activeSessionId, currentDeviceId, refreshSessions, logout, clearSessionState, baseURL, getAccessToken,
|
|
20
|
+
export function useSessionSocket({ userId, activeSessionId, currentDeviceId, refreshSessions, logout, clearSessionState, baseURL, getAccessToken, onRemoteSignOut, onSessionRemoved }: UseSessionSocketProps) {
|
|
23
21
|
const socketRef = useRef<any>(null);
|
|
24
22
|
const joinedRoomRef = useRef<string | null>(null);
|
|
25
23
|
const accessTokenRef = useRef<string | null>(null);
|
|
26
24
|
const handlersSetupRef = useRef<boolean>(false);
|
|
27
25
|
const lastRegisteredSocketIdRef = useRef<string | null>(null);
|
|
28
26
|
const getAccessTokenRef = useRef(getAccessToken);
|
|
29
|
-
const getTransferCodeRef = useRef(getTransferCode);
|
|
30
27
|
|
|
31
28
|
// Store callbacks in refs to avoid re-joining when they change
|
|
32
29
|
const refreshSessionsRef = useRef(refreshSessions);
|
|
@@ -34,7 +31,6 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
|
|
|
34
31
|
const clearSessionStateRef = useRef(clearSessionState);
|
|
35
32
|
const onRemoteSignOutRef = useRef(onRemoteSignOut);
|
|
36
33
|
const onSessionRemovedRef = useRef(onSessionRemoved);
|
|
37
|
-
const onIdentityTransferCompleteRef = useRef(onIdentityTransferComplete);
|
|
38
34
|
const activeSessionIdRef = useRef(activeSessionId);
|
|
39
35
|
const currentDeviceIdRef = useRef(currentDeviceId);
|
|
40
36
|
|
|
@@ -45,12 +41,10 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
|
|
|
45
41
|
clearSessionStateRef.current = clearSessionState;
|
|
46
42
|
onRemoteSignOutRef.current = onRemoteSignOut;
|
|
47
43
|
onSessionRemovedRef.current = onSessionRemoved;
|
|
48
|
-
onIdentityTransferCompleteRef.current = onIdentityTransferComplete;
|
|
49
44
|
activeSessionIdRef.current = activeSessionId;
|
|
50
45
|
currentDeviceIdRef.current = currentDeviceId;
|
|
51
46
|
getAccessTokenRef.current = getAccessToken;
|
|
52
|
-
|
|
53
|
-
}, [refreshSessions, logout, clearSessionState, onRemoteSignOut, onSessionRemoved, onIdentityTransferComplete, activeSessionId, currentDeviceId, getAccessToken, getTransferCode]);
|
|
47
|
+
}, [refreshSessions, logout, clearSessionState, onRemoteSignOut, onSessionRemoved, activeSessionId, currentDeviceId, getAccessToken]);
|
|
54
48
|
|
|
55
49
|
useEffect(() => {
|
|
56
50
|
if (!userId || !baseURL) {
|
|
@@ -307,104 +301,6 @@ export function useSessionSocket({ userId, activeSessionId, currentDeviceId, ref
|
|
|
307
301
|
}
|
|
308
302
|
});
|
|
309
303
|
}
|
|
310
|
-
} else if (data.type === 'identity_transfer_complete') {
|
|
311
|
-
// Handle identity transfer completion notification
|
|
312
|
-
const transferData = data as {
|
|
313
|
-
type: 'identity_transfer_complete';
|
|
314
|
-
transferId: string;
|
|
315
|
-
sourceDeviceId: string;
|
|
316
|
-
publicKey: string;
|
|
317
|
-
transferCode?: string;
|
|
318
|
-
completedAt: string;
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
logger.debug('Received identity_transfer_complete event', {
|
|
322
|
-
component: 'useSessionSocket',
|
|
323
|
-
transferId: transferData.transferId,
|
|
324
|
-
sourceDeviceId: transferData.sourceDeviceId,
|
|
325
|
-
currentDeviceId,
|
|
326
|
-
activeSessionId: activeSessionIdRef.current,
|
|
327
|
-
socketConnected: socket.connected,
|
|
328
|
-
userId,
|
|
329
|
-
room: joinedRoomRef.current,
|
|
330
|
-
publicKey: transferData.publicKey.substring(0, 16) + '...',
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// CRITICAL: Only call handler on the SOURCE device (the one that initiated the transfer)
|
|
334
|
-
// The new device (target) should NEVER process this event - it would delete its own identity!
|
|
335
|
-
|
|
336
|
-
// Check if this device has a stored transfer code (most reliable check - only source device has this)
|
|
337
|
-
const hasStoredTransferCode = getTransferCodeRef.current && !!getTransferCodeRef.current(transferData.transferId);
|
|
338
|
-
|
|
339
|
-
// Also check deviceId match (exact match required)
|
|
340
|
-
const deviceIdMatches = transferData.sourceDeviceId &&
|
|
341
|
-
currentDeviceId &&
|
|
342
|
-
transferData.sourceDeviceId === currentDeviceId;
|
|
343
|
-
|
|
344
|
-
// ONLY call handler if BOTH conditions are met:
|
|
345
|
-
// 1. Has stored transfer code (definitive proof this is the source device)
|
|
346
|
-
// 2. DeviceId matches (additional verification)
|
|
347
|
-
// If deviceId is null/undefined, we still allow if stored code exists (logged out source device)
|
|
348
|
-
// But we NEVER process if no stored code exists (definitely not the source device)
|
|
349
|
-
const shouldCallHandler = !!transferData.transferId &&
|
|
350
|
-
hasStoredTransferCode &&
|
|
351
|
-
(deviceIdMatches || !currentDeviceId); // Allow if deviceId matches OR device is logged out (but has stored code)
|
|
352
|
-
|
|
353
|
-
if (shouldCallHandler) {
|
|
354
|
-
const matchReason = deviceIdMatches
|
|
355
|
-
? 'deviceId-exact-with-stored-code'
|
|
356
|
-
: (currentDeviceId ? 'deviceId-mismatch-but-has-stored-code' : 'logged-out-source-device-with-stored-code');
|
|
357
|
-
|
|
358
|
-
logger.debug('Matched source device, calling transfer complete handler', {
|
|
359
|
-
component: 'useSessionSocket',
|
|
360
|
-
transferId: transferData.transferId,
|
|
361
|
-
sourceDeviceId: transferData.sourceDeviceId,
|
|
362
|
-
currentDeviceId,
|
|
363
|
-
matchReason,
|
|
364
|
-
hasHandler: !!onIdentityTransferCompleteRef.current,
|
|
365
|
-
socketConnected: socket.connected,
|
|
366
|
-
socketId: socket.id,
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
if (onIdentityTransferCompleteRef.current) {
|
|
370
|
-
try {
|
|
371
|
-
logger.debug('Calling onIdentityTransferComplete handler', {
|
|
372
|
-
component: 'useSessionSocket',
|
|
373
|
-
transferId: transferData.transferId,
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
onIdentityTransferCompleteRef.current({
|
|
377
|
-
transferId: transferData.transferId,
|
|
378
|
-
sourceDeviceId: transferData.sourceDeviceId,
|
|
379
|
-
publicKey: transferData.publicKey,
|
|
380
|
-
transferCode: transferData.transferCode,
|
|
381
|
-
completedAt: transferData.completedAt,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
logger.debug('onIdentityTransferComplete handler called successfully', {
|
|
385
|
-
component: 'useSessionSocket',
|
|
386
|
-
transferId: transferData.transferId,
|
|
387
|
-
});
|
|
388
|
-
} catch (error) {
|
|
389
|
-
logger.error('Error calling onIdentityTransferComplete handler', error instanceof Error ? error : new Error(String(error)), {
|
|
390
|
-
component: 'useSessionSocket',
|
|
391
|
-
transferId: transferData.transferId,
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
} else {
|
|
395
|
-
logger.debug('No onIdentityTransferComplete handler registered', {
|
|
396
|
-
component: 'useSessionSocket',
|
|
397
|
-
transferId: transferData.transferId,
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
} else {
|
|
401
|
-
logger.debug('Not the source device, ignoring transfer completion', {
|
|
402
|
-
component: 'useSessionSocket',
|
|
403
|
-
sourceDeviceId: transferData.sourceDeviceId,
|
|
404
|
-
currentDeviceId,
|
|
405
|
-
hasActiveSession: activeSessionIdRef.current !== null,
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
304
|
} else {
|
|
409
305
|
// For other event types (e.g., session_created), refresh sessions (with error handling)
|
|
410
306
|
refreshSessionsRef.current().catch((error) => {
|