@oxyhq/services 5.17.7 → 5.17.9
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/index.js +0 -23
- package/lib/commonjs/crypto/index.js.map +1 -1
- package/lib/commonjs/index.js +0 -15
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/Icon.js.map +1 -1
- package/lib/commonjs/ui/components/IconButton/utils.js.map +1 -1
- package/lib/commonjs/ui/components/TextField/Adornment/utils.js.map +1 -1
- package/lib/commonjs/ui/components/TextField/helpers.js.map +1 -1
- package/lib/commonjs/ui/components/TouchableRipple/utils.js.map +1 -1
- package/lib/commonjs/ui/components/Typography/AnimatedText.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +37 -589
- 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 +60 -425
- 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/commonjs/ui/screens/OxyAuthScreen.js +0 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +52 -15
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +2 -32
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/crypto/index.js +4 -6
- package/lib/module/crypto/index.js.map +1 -1
- package/lib/module/index.js +6 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/Icon.js.map +1 -1
- package/lib/module/ui/components/IconButton/utils.js.map +1 -1
- package/lib/module/ui/components/TextField/Adornment/utils.js.map +1 -1
- package/lib/module/ui/components/TextField/helpers.js.map +1 -1
- package/lib/module/ui/components/TouchableRipple/utils.js.map +1 -1
- package/lib/module/ui/components/Typography/AnimatedText.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +35 -588
- 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 +60 -424
- 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/module/ui/screens/OxyAuthScreen.js +0 -1
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +52 -15
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +2 -32
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/typescript/crypto/index.d.ts +2 -5
- package/lib/typescript/crypto/index.d.ts.map +1 -1
- package/lib/typescript/crypto/types.d.ts +6 -2
- package/lib/typescript/crypto/types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +4 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/IconButton/utils.d.ts +1 -1
- package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts +1 -1
- package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts.map +1 -1
- package/lib/typescript/ui/components/TextField/helpers.d.ts +6 -6
- package/lib/typescript/ui/components/types.d.ts +0 -4
- package/lib/typescript/ui/components/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 +2 -39
- package/lib/typescript/ui/context/OxyContextBase.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +10 -25
- 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/lib/typescript/ui/stores/authStore.d.ts +27 -4
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts +0 -2
- package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/crypto/index.ts +3 -11
- package/src/crypto/types.ts +6 -2
- package/src/index.ts +6 -11
- package/src/ui/components/Icon.tsx +1 -1
- package/src/ui/components/IconButton/utils.ts +1 -1
- package/src/ui/components/TextField/Adornment/utils.ts +2 -2
- package/src/ui/components/TextField/helpers.tsx +8 -8
- package/src/ui/components/TouchableRipple/utils.ts +2 -2
- package/src/ui/components/Typography/AnimatedText.tsx +2 -2
- package/src/ui/components/types.tsx +0 -6
- package/src/ui/context/OxyContext.tsx +33 -637
- package/src/ui/context/OxyContextBase.tsx +5 -23
- package/src/ui/context/hooks/useAuthOperations.ts +84 -460
- 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
- package/src/ui/screens/OxyAuthScreen.tsx +1 -1
- package/src/ui/stores/authStore.ts +57 -18
- package/src/ui/utils/avatarUtils.ts +4 -36
- package/lib/commonjs/crypto/keyManager.js +0 -511
- package/lib/commonjs/crypto/keyManager.js.map +0 -1
- package/lib/commonjs/crypto/signatureService.js +0 -269
- package/lib/commonjs/crypto/signatureService.js.map +0 -1
- package/lib/module/crypto/keyManager.js +0 -508
- package/lib/module/crypto/keyManager.js.map +0 -1
- package/lib/module/crypto/signatureService.js +0 -266
- package/lib/module/crypto/signatureService.js.map +0 -1
- package/lib/typescript/crypto/keyManager.d.ts +0 -97
- package/lib/typescript/crypto/keyManager.d.ts.map +0 -1
- package/lib/typescript/crypto/signatureService.d.ts +0 -77
- package/lib/typescript/crypto/signatureService.d.ts.map +0 -1
- package/src/crypto/keyManager.ts +0 -545
- package/src/crypto/signatureService.ts +0 -301
|
@@ -27,7 +27,7 @@ import { showBottomSheet as globalShowBottomSheet } from '../navigation/bottomSh
|
|
|
27
27
|
import { useQueryClient, useQuery } from '@tanstack/react-query';
|
|
28
28
|
import { clearQueryCache } from '../hooks/queryClient';
|
|
29
29
|
import { queryKeys } from '../hooks/queries/queryKeys';
|
|
30
|
-
import {
|
|
30
|
+
import type { BackupData } from '../../crypto';
|
|
31
31
|
import { translate } from '../../i18n';
|
|
32
32
|
import { updateAvatarVisibility, updateProfileWithAvatar } from '../utils/avatarUtils';
|
|
33
33
|
import { useAccountStore } from '../stores/accountStore';
|
|
@@ -74,31 +74,6 @@ const loadUseFollowHook = (): UseFollowHook => {
|
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
// Shared storage key for identity cache - used by accounts app for instant routing
|
|
78
|
-
// Uses expo-secure-store for consistency with identity storage (KeyManager)
|
|
79
|
-
const IDENTITY_CACHE_KEY = 'oxy_identity_exists_cache';
|
|
80
|
-
|
|
81
|
-
// Helper to update identity cache in SecureStore
|
|
82
|
-
const updateIdentityCache = async (exists: boolean): Promise<void> => {
|
|
83
|
-
if (Platform.OS === 'web') return;
|
|
84
|
-
try {
|
|
85
|
-
const SecureStore = await import('expo-secure-store');
|
|
86
|
-
await SecureStore.setItemAsync(IDENTITY_CACHE_KEY, exists ? 'true' : 'false');
|
|
87
|
-
} catch {
|
|
88
|
-
// Silently fail - cache is just an optimization
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const clearIdentityCache = async (): Promise<void> => {
|
|
93
|
-
if (Platform.OS === 'web') return;
|
|
94
|
-
try {
|
|
95
|
-
const SecureStore = await import('expo-secure-store');
|
|
96
|
-
await SecureStore.deleteItemAsync(IDENTITY_CACHE_KEY);
|
|
97
|
-
} catch {
|
|
98
|
-
// Silently fail
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
77
|
export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
103
78
|
children,
|
|
104
79
|
oxyServices: providedOxyServices,
|
|
@@ -123,29 +98,23 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
123
98
|
|
|
124
99
|
const {
|
|
125
100
|
isAuthenticated,
|
|
101
|
+
isOnline,
|
|
126
102
|
isLoading,
|
|
127
103
|
error,
|
|
104
|
+
setOnline,
|
|
128
105
|
loginSuccess,
|
|
129
106
|
loginFailure,
|
|
130
107
|
logoutStore,
|
|
131
|
-
// Identity sync state and actions
|
|
132
|
-
isIdentitySyncedStore,
|
|
133
|
-
isSyncing,
|
|
134
|
-
setIdentitySynced,
|
|
135
|
-
setSyncing,
|
|
136
108
|
} = useAuthStore(
|
|
137
109
|
useShallow((state: AuthState) => ({
|
|
138
110
|
isAuthenticated: state.isAuthenticated,
|
|
111
|
+
isOnline: state.isOnline,
|
|
139
112
|
isLoading: state.isLoading,
|
|
140
113
|
error: state.error,
|
|
114
|
+
setOnline: state.setOnline,
|
|
141
115
|
loginSuccess: state.loginSuccess,
|
|
142
116
|
loginFailure: state.loginFailure,
|
|
143
117
|
logoutStore: state.logout,
|
|
144
|
-
// Identity sync state and actions
|
|
145
|
-
isIdentitySyncedStore: state.isIdentitySynced,
|
|
146
|
-
isSyncing: state.isSyncing,
|
|
147
|
-
setIdentitySynced: state.setIdentitySynced,
|
|
148
|
-
setSyncing: state.setSyncing,
|
|
149
118
|
})),
|
|
150
119
|
);
|
|
151
120
|
|
|
@@ -167,52 +136,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
167
136
|
|
|
168
137
|
const { storage, isReady: isStorageReady } = useStorage({ onError, logger });
|
|
169
138
|
|
|
170
|
-
// Identity integrity check and auto-restore on startup
|
|
171
|
-
// Skip on web platform - identity storage is only available on native platforms
|
|
172
|
-
useEffect(() => {
|
|
173
|
-
if (!storage || !isStorageReady) return;
|
|
174
|
-
if (Platform.OS === 'web') return; // Identity operations are native-only
|
|
175
|
-
|
|
176
|
-
const checkAndRestoreIdentity = async () => {
|
|
177
|
-
try {
|
|
178
|
-
// Check if identity exists and verify integrity
|
|
179
|
-
const hasIdentity = await KeyManager.hasIdentity();
|
|
180
|
-
if (hasIdentity) {
|
|
181
|
-
const isValid = await KeyManager.verifyIdentityIntegrity();
|
|
182
|
-
if (!isValid) {
|
|
183
|
-
// Try to restore from backup
|
|
184
|
-
const restored = await KeyManager.restoreIdentityFromBackup();
|
|
185
|
-
if (__DEV__) {
|
|
186
|
-
logger(restored
|
|
187
|
-
? 'Identity restored from backup successfully'
|
|
188
|
-
: 'Identity integrity check failed - user may need to restore from backup file'
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
} else {
|
|
192
|
-
// Identity is valid - ensure backup is up to date
|
|
193
|
-
await KeyManager.backupIdentity();
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
// No identity - try to restore from backup
|
|
197
|
-
const restored = await KeyManager.restoreIdentityFromBackup();
|
|
198
|
-
if (restored && __DEV__) {
|
|
199
|
-
logger('Identity restored from backup on startup');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
} catch (error) {
|
|
203
|
-
if (__DEV__) {
|
|
204
|
-
logger('Error during identity integrity check', error);
|
|
205
|
-
}
|
|
206
|
-
// Don't block app startup - user can recover with backup file
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
checkAndRestoreIdentity();
|
|
211
|
-
}, [storage, isStorageReady, logger]);
|
|
212
|
-
|
|
213
|
-
// Offline queuing is now handled by TanStack Query mutations
|
|
214
|
-
// No need for custom offline queue
|
|
215
|
-
|
|
216
139
|
const {
|
|
217
140
|
currentLanguage,
|
|
218
141
|
metadata: currentLanguageMetadata,
|
|
@@ -274,15 +197,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
274
197
|
const user = userData ?? null;
|
|
275
198
|
|
|
276
199
|
const {
|
|
277
|
-
|
|
278
|
-
importIdentity: importIdentityBase,
|
|
279
|
-
signIn,
|
|
200
|
+
completeSignIn,
|
|
280
201
|
logout,
|
|
281
202
|
logoutAll,
|
|
282
|
-
hasIdentity,
|
|
283
|
-
getPublicKey,
|
|
284
|
-
isIdentitySynced,
|
|
285
|
-
syncIdentity: syncIdentityBase,
|
|
286
203
|
} = useAuthOperations({
|
|
287
204
|
oxyServices,
|
|
288
205
|
storage,
|
|
@@ -300,48 +217,10 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
300
217
|
loginFailure,
|
|
301
218
|
logoutStore,
|
|
302
219
|
setAuthState,
|
|
303
|
-
setIdentitySynced,
|
|
304
|
-
setSyncing,
|
|
305
220
|
logger,
|
|
306
221
|
});
|
|
307
222
|
|
|
308
|
-
//
|
|
309
|
-
const syncIdentity = useCallback(() => syncIdentityBase(), [syncIdentityBase]);
|
|
310
|
-
|
|
311
|
-
// Wrapper for createIdentity to update identity cache
|
|
312
|
-
const wrappedCreateIdentity = useCallback(
|
|
313
|
-
async (): Promise<{ synced: boolean }> => {
|
|
314
|
-
const result = await createIdentity();
|
|
315
|
-
// Update cache for instant routing on next app launch
|
|
316
|
-
updateIdentityCache(true);
|
|
317
|
-
return result;
|
|
318
|
-
},
|
|
319
|
-
[createIdentity]
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Wrapper for importIdentity to handle legacy calls and update cache
|
|
323
|
-
const importIdentity = useCallback(
|
|
324
|
-
async (backupData: BackupData | string, password?: string): Promise<{ synced: boolean }> => {
|
|
325
|
-
// Handle legacy calls with single string argument (old recovery phrase signature)
|
|
326
|
-
if (typeof backupData === 'string') {
|
|
327
|
-
throw new Error('Recovery phrase import is no longer supported. Please use backup file import or QR code transfer instead.');
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Validate that password is provided
|
|
331
|
-
if (!password || typeof password !== 'string') {
|
|
332
|
-
throw new Error('Password is required for backup file import.');
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const result = await importIdentityBase(backupData, password);
|
|
336
|
-
// Update cache for instant routing on next app launch
|
|
337
|
-
updateIdentityCache(true);
|
|
338
|
-
return result;
|
|
339
|
-
},
|
|
340
|
-
[importIdentityBase]
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
// Clear all account data when identity is lost (for accounts app)
|
|
344
|
-
// In accounts app, identity = account, so losing identity means losing everything
|
|
223
|
+
// Clear all cached data and storage on logout
|
|
345
224
|
const clearAllAccountData = useCallback(async (): Promise<void> => {
|
|
346
225
|
// Clear TanStack Query cache (in-memory)
|
|
347
226
|
queryClient.clear();
|
|
@@ -358,19 +237,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
358
237
|
// Clear session state (sessions, activeSessionId, storage)
|
|
359
238
|
await clearSessionState();
|
|
360
239
|
|
|
361
|
-
// Clear identity sync state from storage
|
|
362
|
-
if (storage) {
|
|
363
|
-
try {
|
|
364
|
-
await storage.removeItem('oxy_identity_synced');
|
|
365
|
-
} catch (error) {
|
|
366
|
-
logger('Failed to clear identity sync state', error);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Reset auth store identity sync state
|
|
371
|
-
useAuthStore.getState().setIdentitySynced(false);
|
|
372
|
-
useAuthStore.getState().setSyncing(false);
|
|
373
|
-
|
|
374
240
|
// Reset account store
|
|
375
241
|
useAccountStore.getState().reset();
|
|
376
242
|
|
|
@@ -378,174 +244,63 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
378
244
|
oxyServices.clearCache();
|
|
379
245
|
}, [queryClient, storage, clearSessionState, logger, oxyServices]);
|
|
380
246
|
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
transferCodesRef.current.forEach((data, transferId) => {
|
|
385
|
-
if (data.state === 'pending') {
|
|
386
|
-
pending.push({ transferId, data });
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
return pending;
|
|
390
|
-
}, []);
|
|
391
|
-
|
|
392
|
-
const getActiveTransferId = useCallback(() => {
|
|
393
|
-
return activeTransferIdRef.current;
|
|
394
|
-
}, []);
|
|
395
|
-
|
|
396
|
-
// Delete identity and clear all account data
|
|
397
|
-
// In accounts app, deleting identity means losing the account completely
|
|
398
|
-
const deleteIdentityAndClearAccount = useCallback(async (
|
|
399
|
-
skipBackup: boolean = false,
|
|
400
|
-
force: boolean = false,
|
|
401
|
-
userConfirmed: boolean = false
|
|
402
|
-
): Promise<void> => {
|
|
403
|
-
// CRITICAL: Check for active transfers before deletion (unless force is true)
|
|
404
|
-
// This prevents accidental identity loss during transfer
|
|
405
|
-
if (!force) {
|
|
406
|
-
const pendingTransfers = getAllPendingTransfers();
|
|
407
|
-
if (pendingTransfers.length > 0) {
|
|
408
|
-
const activeTransferId = getActiveTransferId();
|
|
409
|
-
const hasActiveTransfer = activeTransferId && pendingTransfers.some(t => t.transferId === activeTransferId);
|
|
410
|
-
|
|
411
|
-
if (hasActiveTransfer) {
|
|
412
|
-
throw new Error(
|
|
413
|
-
'Cannot delete identity: An active identity transfer is in progress. ' +
|
|
414
|
-
'Please wait for the transfer to complete or cancel it first. ' +
|
|
415
|
-
'If you proceed, you may lose access to your identity permanently.'
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// First, clear all account data
|
|
422
|
-
await clearAllAccountData();
|
|
423
|
-
|
|
424
|
-
// Then delete the identity keys
|
|
425
|
-
await KeyManager.deleteIdentity(skipBackup, force, userConfirmed);
|
|
426
|
-
|
|
427
|
-
// Clear identity cache for instant routing on next app launch
|
|
428
|
-
clearIdentityCache();
|
|
429
|
-
}, [clearAllAccountData, getAllPendingTransfers, getActiveTransferId]);
|
|
430
|
-
|
|
431
|
-
// Network reconnect sync - TanStack Query automatically retries mutations on reconnect
|
|
432
|
-
// We only need to sync identity if it's not synced
|
|
247
|
+
// Network state monitoring - updates authStore.isOnline
|
|
248
|
+
// When offline: isAuthenticated becomes false automatically
|
|
249
|
+
// When reconnect: isOnline set to true, allowing re-authentication
|
|
433
250
|
useEffect(() => {
|
|
434
251
|
if (!storage) return;
|
|
435
252
|
|
|
436
253
|
let wasOffline = false;
|
|
437
254
|
let checkTimeout: NodeJS.Timeout | null = null;
|
|
438
|
-
let lastReconnectionLog = 0;
|
|
439
|
-
const RECONNECTION_LOG_DEBOUNCE_MS = 5000; // 5 seconds
|
|
440
|
-
|
|
441
|
-
// Circuit breaker and exponential backoff state
|
|
442
|
-
const stateRef = {
|
|
443
|
-
consecutiveFailures: 0,
|
|
444
|
-
currentInterval: 10000, // Start with 10 seconds
|
|
445
|
-
baseInterval: 10000, // Base interval in milliseconds
|
|
446
|
-
maxInterval: 60000, // Maximum interval (60 seconds)
|
|
447
|
-
maxFailures: 5, // Circuit breaker threshold
|
|
448
|
-
};
|
|
449
255
|
|
|
450
256
|
const scheduleNextCheck = () => {
|
|
451
257
|
if (checkTimeout) {
|
|
452
258
|
clearTimeout(checkTimeout);
|
|
453
259
|
}
|
|
454
260
|
checkTimeout = setTimeout(() => {
|
|
455
|
-
|
|
456
|
-
},
|
|
261
|
+
checkNetworkStatus();
|
|
262
|
+
}, 30000); // Check every 30 seconds
|
|
457
263
|
};
|
|
458
264
|
|
|
459
|
-
const
|
|
265
|
+
const checkNetworkStatus = async () => {
|
|
460
266
|
try {
|
|
461
267
|
// Try a lightweight health check to see if we're online
|
|
462
|
-
await oxyServices.healthCheck()
|
|
463
|
-
wasOffline = true;
|
|
464
|
-
throw new Error('Health check failed');
|
|
465
|
-
});
|
|
268
|
+
await oxyServices.healthCheck();
|
|
466
269
|
|
|
467
|
-
//
|
|
468
|
-
if (stateRef.consecutiveFailures > 0) {
|
|
469
|
-
stateRef.consecutiveFailures = 0;
|
|
470
|
-
stateRef.currentInterval = stateRef.baseInterval;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// If we were offline and now we're online, sync identity if needed
|
|
270
|
+
// If we were offline and now we're online
|
|
474
271
|
if (wasOffline) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (timeSinceLastLog >= RECONNECTION_LOG_DEBOUNCE_MS) {
|
|
479
|
-
logger('Network reconnected, checking identity sync...');
|
|
480
|
-
lastReconnectionLog = now;
|
|
481
|
-
|
|
482
|
-
// Sync identity first (if not synced)
|
|
483
|
-
try {
|
|
484
|
-
const hasIdentityValue = await hasIdentity();
|
|
485
|
-
if (hasIdentityValue) {
|
|
486
|
-
// Check sync status directly - sync if not explicitly 'true'
|
|
487
|
-
// undefined = not synced yet, 'false' = explicitly not synced, 'true' = synced
|
|
488
|
-
const syncStatus = await storage.getItem('oxy_identity_synced');
|
|
489
|
-
if (syncStatus !== 'true') {
|
|
490
|
-
await syncIdentity();
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
} catch (syncError: any) {
|
|
494
|
-
// Skip sync silently if username is required (expected when offline onboarding)
|
|
495
|
-
if (syncError?.code === 'USERNAME_REQUIRED' || syncError?.message === 'USERNAME_REQUIRED') {
|
|
496
|
-
if (__DEV__) {
|
|
497
|
-
loggerUtil.debug('Sync skipped - username required', { component: 'OxyContext', method: 'checkNetworkAndSync' }, syncError as unknown);
|
|
498
|
-
}
|
|
499
|
-
// Don't log or show error - username will be set later
|
|
500
|
-
} else if (!isTimeoutOrNetworkError(syncError)) {
|
|
501
|
-
// Only log unexpected errors - timeouts/network issues are expected when offline
|
|
502
|
-
logger('Error syncing identity on reconnect', syncError);
|
|
503
|
-
} else if (__DEV__) {
|
|
504
|
-
loggerUtil.debug('Identity sync timeout (expected when offline)', { component: 'OxyContext', method: 'checkNetworkAndSync' }, syncError as unknown);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
272
|
+
logger('Network reconnected - setting online state');
|
|
273
|
+
setOnline(true);
|
|
509
274
|
// TanStack Query will automatically retry pending mutations
|
|
510
|
-
//
|
|
275
|
+
// Session management will handle token refresh if needed
|
|
511
276
|
wasOffline = false;
|
|
277
|
+
} else {
|
|
278
|
+
// We're online and were already online
|
|
279
|
+
setOnline(true);
|
|
512
280
|
}
|
|
513
281
|
} catch (error) {
|
|
514
282
|
// Network check failed - we're offline
|
|
515
|
-
wasOffline
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
// Calculate new interval with exponential backoff, capped at maxInterval
|
|
521
|
-
const backoffMultiplier = Math.min(
|
|
522
|
-
Math.pow(2, stateRef.consecutiveFailures - 1),
|
|
523
|
-
stateRef.maxInterval / stateRef.baseInterval
|
|
524
|
-
);
|
|
525
|
-
stateRef.currentInterval = Math.min(
|
|
526
|
-
stateRef.baseInterval * backoffMultiplier,
|
|
527
|
-
stateRef.maxInterval
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
// If we hit the circuit breaker threshold, use max interval
|
|
531
|
-
if (stateRef.consecutiveFailures >= stateRef.maxFailures) {
|
|
532
|
-
stateRef.currentInterval = stateRef.maxInterval;
|
|
283
|
+
if (!wasOffline) {
|
|
284
|
+
if (__DEV__) {
|
|
285
|
+
logger('Network appears offline - suspending authentication');
|
|
286
|
+
}
|
|
287
|
+
setOnline(false); // This will set isAuthenticated to false
|
|
533
288
|
}
|
|
289
|
+
wasOffline = true;
|
|
534
290
|
} finally {
|
|
535
|
-
// Always schedule next check (will use updated interval)
|
|
536
291
|
scheduleNextCheck();
|
|
537
292
|
}
|
|
538
293
|
};
|
|
539
294
|
|
|
540
295
|
// Check immediately
|
|
541
|
-
|
|
296
|
+
checkNetworkStatus();
|
|
542
297
|
|
|
543
298
|
return () => {
|
|
544
299
|
if (checkTimeout) {
|
|
545
300
|
clearTimeout(checkTimeout);
|
|
546
301
|
}
|
|
547
302
|
};
|
|
548
|
-
}, [oxyServices, storage,
|
|
303
|
+
}, [oxyServices, storage, logger, setOnline]);
|
|
549
304
|
|
|
550
305
|
const { getDeviceSessions, logoutAllDeviceSessions, updateDeviceName } = useDeviceManagement({
|
|
551
306
|
oxyServices,
|
|
@@ -662,183 +417,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
662
417
|
// Get userId from JWT token (MongoDB ObjectId) for socket room matching
|
|
663
418
|
const userId = oxyServices.getCurrentUserId();
|
|
664
419
|
|
|
665
|
-
// Transfer code storage interface
|
|
666
|
-
interface TransferCodeData {
|
|
667
|
-
code: string;
|
|
668
|
-
sourceDeviceId: string | null;
|
|
669
|
-
publicKey: string;
|
|
670
|
-
timestamp: number;
|
|
671
|
-
state: 'pending' | 'completed' | 'failed';
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// Store transfer codes in memory for verification (also persisted to storage)
|
|
675
|
-
// Map: transferId -> TransferCodeData
|
|
676
|
-
const transferCodesRef = useRef<Map<string, TransferCodeData>>(new Map());
|
|
677
|
-
const activeTransferIdRef = useRef<string | null>(null);
|
|
678
|
-
const TRANSFER_CODES_STORAGE_KEY = `${storageKeyPrefix}_transfer_codes`;
|
|
679
|
-
const ACTIVE_TRANSFER_STORAGE_KEY = `${storageKeyPrefix}_active_transfer_id`;
|
|
680
|
-
|
|
681
|
-
// Clear stale transfer codes on startup
|
|
682
|
-
// Transfers are ephemeral and should not persist across app restarts
|
|
683
|
-
useEffect(() => {
|
|
684
|
-
if (!storage || !isStorageReady) return;
|
|
685
|
-
|
|
686
|
-
const clearStaleTransfers = async () => {
|
|
687
|
-
try {
|
|
688
|
-
// Clear any stored transfer codes - they're only valid during active transfer sessions
|
|
689
|
-
await storage.removeItem(TRANSFER_CODES_STORAGE_KEY);
|
|
690
|
-
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
691
|
-
|
|
692
|
-
if (__DEV__) {
|
|
693
|
-
logger('Cleared stale transfer codes on startup');
|
|
694
|
-
}
|
|
695
|
-
} catch (error) {
|
|
696
|
-
if (__DEV__) {
|
|
697
|
-
logger('Failed to clear stale transfer codes', error);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
clearStaleTransfers();
|
|
703
|
-
}, [storage, isStorageReady, logger, storageKeyPrefix]);
|
|
704
|
-
|
|
705
|
-
// Persist transfer codes to storage whenever they change
|
|
706
|
-
const persistTransferCodes = useCallback(async () => {
|
|
707
|
-
if (!storage) return;
|
|
708
|
-
|
|
709
|
-
try {
|
|
710
|
-
const codesToStore: Record<string, TransferCodeData> = {};
|
|
711
|
-
transferCodesRef.current.forEach((value, key) => {
|
|
712
|
-
codesToStore[key] = value;
|
|
713
|
-
});
|
|
714
|
-
await storage.setItem(TRANSFER_CODES_STORAGE_KEY, JSON.stringify(codesToStore));
|
|
715
|
-
} catch (error) {
|
|
716
|
-
if (__DEV__) {
|
|
717
|
-
logger('Failed to persist transfer codes', error);
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
}, [storage, logger]);
|
|
721
|
-
|
|
722
|
-
// Cleanup old transfer codes (older than 15 minutes)
|
|
723
|
-
useEffect(() => {
|
|
724
|
-
const cleanup = setInterval(async () => {
|
|
725
|
-
const now = Date.now();
|
|
726
|
-
const fifteenMinutes = 15 * 60 * 1000;
|
|
727
|
-
let needsPersist = false;
|
|
728
|
-
|
|
729
|
-
transferCodesRef.current.forEach((value, key) => {
|
|
730
|
-
if (now - value.timestamp > fifteenMinutes) {
|
|
731
|
-
transferCodesRef.current.delete(key);
|
|
732
|
-
needsPersist = true;
|
|
733
|
-
if (__DEV__) {
|
|
734
|
-
logger('Cleaned up expired transfer code', { transferId: key });
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
// Clear active transfer if it was deleted
|
|
740
|
-
if (activeTransferIdRef.current && !transferCodesRef.current.has(activeTransferIdRef.current)) {
|
|
741
|
-
activeTransferIdRef.current = null;
|
|
742
|
-
if (storage) {
|
|
743
|
-
try {
|
|
744
|
-
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
745
|
-
} catch (error) {
|
|
746
|
-
// Ignore storage errors
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
if (needsPersist) {
|
|
752
|
-
await persistTransferCodes();
|
|
753
|
-
}
|
|
754
|
-
}, 60000); // Check every minute
|
|
755
|
-
|
|
756
|
-
return () => clearInterval(cleanup);
|
|
757
|
-
}, [logger, persistTransferCodes, storage]);
|
|
758
|
-
|
|
759
|
-
// Transfer code management functions
|
|
760
|
-
const storeTransferCode = useCallback(async (transferId: string, code: string, sourceDeviceId: string | null, publicKey: string) => {
|
|
761
|
-
const transferData: TransferCodeData = {
|
|
762
|
-
code,
|
|
763
|
-
sourceDeviceId,
|
|
764
|
-
publicKey,
|
|
765
|
-
timestamp: Date.now(),
|
|
766
|
-
state: 'pending',
|
|
767
|
-
};
|
|
768
|
-
|
|
769
|
-
transferCodesRef.current.set(transferId, transferData);
|
|
770
|
-
activeTransferIdRef.current = transferId;
|
|
771
|
-
|
|
772
|
-
// Persist to storage
|
|
773
|
-
await persistTransferCodes();
|
|
774
|
-
if (storage) {
|
|
775
|
-
try {
|
|
776
|
-
await storage.setItem(ACTIVE_TRANSFER_STORAGE_KEY, transferId);
|
|
777
|
-
} catch (error) {
|
|
778
|
-
if (__DEV__) {
|
|
779
|
-
logger('Failed to persist active transfer ID', error);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (__DEV__) {
|
|
785
|
-
logger('Stored transfer code', { transferId, sourceDeviceId, publicKey: publicKey.substring(0, 16) + '...' });
|
|
786
|
-
}
|
|
787
|
-
}, [logger, persistTransferCodes, storage]);
|
|
788
|
-
|
|
789
|
-
const getTransferCode = useCallback((transferId: string) => {
|
|
790
|
-
return transferCodesRef.current.get(transferId) || null;
|
|
791
|
-
}, []);
|
|
792
|
-
|
|
793
|
-
const updateTransferState = useCallback(async (transferId: string, state: 'pending' | 'completed' | 'failed') => {
|
|
794
|
-
const transferData = transferCodesRef.current.get(transferId);
|
|
795
|
-
if (transferData) {
|
|
796
|
-
transferData.state = state;
|
|
797
|
-
transferCodesRef.current.set(transferId, transferData);
|
|
798
|
-
|
|
799
|
-
// Clear active transfer if completed or failed
|
|
800
|
-
if (state === 'completed' || state === 'failed') {
|
|
801
|
-
if (activeTransferIdRef.current === transferId) {
|
|
802
|
-
activeTransferIdRef.current = null;
|
|
803
|
-
if (storage) {
|
|
804
|
-
try {
|
|
805
|
-
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
806
|
-
} catch (error) {
|
|
807
|
-
// Ignore storage errors
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
await persistTransferCodes();
|
|
814
|
-
|
|
815
|
-
if (__DEV__) {
|
|
816
|
-
logger('Updated transfer state', { transferId, state });
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}, [logger, persistTransferCodes, storage]);
|
|
820
|
-
|
|
821
|
-
const clearTransferCode = useCallback(async (transferId: string) => {
|
|
822
|
-
transferCodesRef.current.delete(transferId);
|
|
823
|
-
|
|
824
|
-
if (activeTransferIdRef.current === transferId) {
|
|
825
|
-
activeTransferIdRef.current = null;
|
|
826
|
-
if (storage) {
|
|
827
|
-
try {
|
|
828
|
-
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
829
|
-
} catch (error) {
|
|
830
|
-
// Ignore storage errors
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
await persistTransferCodes();
|
|
836
|
-
|
|
837
|
-
if (__DEV__) {
|
|
838
|
-
logger('Cleared transfer code', { transferId });
|
|
839
|
-
}
|
|
840
|
-
}, [logger, persistTransferCodes, storage]);
|
|
841
|
-
|
|
842
420
|
const refreshSessionsWithUser = useCallback(
|
|
843
421
|
() => refreshSessions(userId || undefined),
|
|
844
422
|
[refreshSessions, userId],
|
|
@@ -856,154 +434,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
856
434
|
logout().catch((remoteError) => logger('Failed to process remote sign out', remoteError));
|
|
857
435
|
}, [logger, logout]);
|
|
858
436
|
|
|
859
|
-
const handleIdentityTransferComplete = useCallback(
|
|
860
|
-
async (data: { transferId: string; sourceDeviceId: string; publicKey: string; transferCode?: string; completedAt: string }) => {
|
|
861
|
-
try {
|
|
862
|
-
logger('Received identity transfer complete notification', {
|
|
863
|
-
transferId: data.transferId,
|
|
864
|
-
sourceDeviceId: data.sourceDeviceId,
|
|
865
|
-
currentDeviceId,
|
|
866
|
-
hasActiveSession: activeSessionId !== null,
|
|
867
|
-
publicKey: data.publicKey.substring(0, 16) + '...',
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
const storedTransfer = getTransferCode(data.transferId);
|
|
871
|
-
|
|
872
|
-
if (!storedTransfer) {
|
|
873
|
-
logger('Transfer code not found for transferId', {
|
|
874
|
-
transferId: data.transferId,
|
|
875
|
-
});
|
|
876
|
-
toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Verify publicKey matches first (most important check)
|
|
881
|
-
const publicKeyMatches = data.publicKey === storedTransfer.publicKey;
|
|
882
|
-
if (!publicKeyMatches) {
|
|
883
|
-
logger('Public key mismatch for transfer', {
|
|
884
|
-
transferId: data.transferId,
|
|
885
|
-
receivedPublicKey: data.publicKey.substring(0, 16) + '...',
|
|
886
|
-
storedPublicKey: storedTransfer.publicKey.substring(0, 16) + '...',
|
|
887
|
-
});
|
|
888
|
-
toast.error('Transfer verification failed: Public key mismatch. Identity will not be deleted.');
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Verify deviceId matches - very lenient since publicKey is the critical check
|
|
893
|
-
// If publicKey matches, we allow deletion even if deviceId doesn't match exactly
|
|
894
|
-
// This handles cases where deviceId might not be available or slightly different
|
|
895
|
-
const deviceIdMatches =
|
|
896
|
-
// Exact match
|
|
897
|
-
(data.sourceDeviceId && data.sourceDeviceId === currentDeviceId) ||
|
|
898
|
-
// Stored sourceDeviceId matches current deviceId
|
|
899
|
-
(storedTransfer.sourceDeviceId && storedTransfer.sourceDeviceId === currentDeviceId);
|
|
900
|
-
|
|
901
|
-
// If publicKey matches, we're very lenient with deviceId - only warn but don't block
|
|
902
|
-
if (!deviceIdMatches && publicKeyMatches) {
|
|
903
|
-
logger('Device ID mismatch for transfer, but publicKey matches - proceeding with deletion', {
|
|
904
|
-
transferId: data.transferId,
|
|
905
|
-
receivedDeviceId: data.sourceDeviceId,
|
|
906
|
-
storedDeviceId: storedTransfer.sourceDeviceId,
|
|
907
|
-
currentDeviceId,
|
|
908
|
-
hasActiveSession: activeSessionId !== null,
|
|
909
|
-
});
|
|
910
|
-
// Proceed with deletion - publicKey match is the critical verification
|
|
911
|
-
} else if (!deviceIdMatches && !publicKeyMatches) {
|
|
912
|
-
// Both don't match - this is suspicious, block deletion
|
|
913
|
-
logger('Device ID and publicKey mismatch for transfer', {
|
|
914
|
-
transferId: data.transferId,
|
|
915
|
-
receivedDeviceId: data.sourceDeviceId,
|
|
916
|
-
currentDeviceId,
|
|
917
|
-
});
|
|
918
|
-
toast.error('Transfer verification failed: Device and key mismatch. Identity will not be deleted.');
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// Verify transfer code matches (if provided)
|
|
923
|
-
// Transfer code is optional - if not provided, we still proceed if publicKey matches
|
|
924
|
-
if (data.transferCode) {
|
|
925
|
-
const codeMatches = data.transferCode.toUpperCase() === storedTransfer.code.toUpperCase();
|
|
926
|
-
if (!codeMatches) {
|
|
927
|
-
logger('Transfer code mismatch, but publicKey matches - proceeding with deletion', {
|
|
928
|
-
transferId: data.transferId,
|
|
929
|
-
receivedCode: data.transferCode,
|
|
930
|
-
storedCode: storedTransfer.code.substring(0, 2) + '****',
|
|
931
|
-
});
|
|
932
|
-
// Don't block - publicKey match is sufficient, code mismatch might be due to user error
|
|
933
|
-
// Log warning but proceed
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// Check if transfer is too old (safety timeout - 10 minutes)
|
|
938
|
-
const transferAge = Date.now() - storedTransfer.timestamp;
|
|
939
|
-
const tenMinutes = 10 * 60 * 1000;
|
|
940
|
-
if (transferAge > tenMinutes) {
|
|
941
|
-
logger('Transfer confirmation received too late', {
|
|
942
|
-
transferId: data.transferId,
|
|
943
|
-
age: transferAge,
|
|
944
|
-
ageMinutes: Math.round(transferAge / 60000),
|
|
945
|
-
});
|
|
946
|
-
toast.error('Transfer verification failed: Confirmation received too late. Identity will not be deleted.');
|
|
947
|
-
clearTransferCode(data.transferId);
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
// NOTE: Target device verification already happened server-side when notifyTransferComplete was called
|
|
952
|
-
// The server verified that the target device is authenticated and has the matching public key
|
|
953
|
-
// Additional client-side verification is not necessary and would require source device authentication
|
|
954
|
-
// which may not be available. The existing checks (public key match, transfer code, device ID) are sufficient.
|
|
955
|
-
|
|
956
|
-
logger('All transfer verifications passed, deleting identity from source device', {
|
|
957
|
-
transferId: data.transferId,
|
|
958
|
-
sourceDeviceId: data.sourceDeviceId,
|
|
959
|
-
publicKey: data.publicKey.substring(0, 16) + '...',
|
|
960
|
-
});
|
|
961
|
-
|
|
962
|
-
try {
|
|
963
|
-
// Verify identity still exists before deletion (safety check)
|
|
964
|
-
const identityStillExists = await KeyManager.hasIdentity();
|
|
965
|
-
if (!identityStillExists) {
|
|
966
|
-
logger('Identity already deleted - skipping deletion', {
|
|
967
|
-
transferId: data.transferId,
|
|
968
|
-
});
|
|
969
|
-
await updateTransferState(data.transferId, 'completed');
|
|
970
|
-
await clearTransferCode(data.transferId);
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
await deleteIdentityAndClearAccount(false, false, true);
|
|
975
|
-
|
|
976
|
-
// Verify identity was actually deleted
|
|
977
|
-
const identityDeleted = !(await KeyManager.hasIdentity());
|
|
978
|
-
if (!identityDeleted) {
|
|
979
|
-
logger('Identity deletion failed - identity still exists', {
|
|
980
|
-
transferId: data.transferId,
|
|
981
|
-
});
|
|
982
|
-
await updateTransferState(data.transferId, 'failed');
|
|
983
|
-
throw new Error('Identity deletion failed - identity still exists');
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
await updateTransferState(data.transferId, 'completed');
|
|
987
|
-
await clearTransferCode(data.transferId);
|
|
988
|
-
|
|
989
|
-
logger('Identity successfully deleted and transfer code cleared', {
|
|
990
|
-
transferId: data.transferId,
|
|
991
|
-
});
|
|
992
|
-
|
|
993
|
-
toast.success('Identity successfully transferred and removed from this device');
|
|
994
|
-
} catch (deleteError: any) {
|
|
995
|
-
logger('Error during identity deletion', deleteError);
|
|
996
|
-
await updateTransferState(data.transferId, 'failed');
|
|
997
|
-
throw deleteError;
|
|
998
|
-
}
|
|
999
|
-
} catch (error: any) {
|
|
1000
|
-
logger('Failed to delete identity after transfer', error);
|
|
1001
|
-
toast.error(error?.message || 'Failed to remove identity from this device. Please try again manually from Security Settings.');
|
|
1002
|
-
}
|
|
1003
|
-
},
|
|
1004
|
-
[deleteIdentityAndClearAccount, logger, getTransferCode, clearTransferCode, updateTransferState, currentDeviceId, activeSessionId, oxyServices],
|
|
1005
|
-
);
|
|
1006
|
-
|
|
1007
437
|
useSessionSocket({
|
|
1008
438
|
userId,
|
|
1009
439
|
activeSessionId,
|
|
@@ -1013,10 +443,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1013
443
|
clearSessionState,
|
|
1014
444
|
baseURL: oxyServices.getBaseURL(),
|
|
1015
445
|
getAccessToken: () => oxyServices.getAccessToken(),
|
|
1016
|
-
getTransferCode: getTransferCode,
|
|
1017
446
|
onRemoteSignOut: handleRemoteSignOut,
|
|
1018
447
|
onSessionRemoved: handleSessionRemoved,
|
|
1019
|
-
onIdentityTransferComplete: handleIdentityTransferComplete,
|
|
1020
448
|
});
|
|
1021
449
|
|
|
1022
450
|
const switchSessionForContext = useCallback(
|
|
@@ -1058,7 +486,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1058
486
|
oxyServices,
|
|
1059
487
|
activeSessionId,
|
|
1060
488
|
queryClient,
|
|
1061
|
-
{
|
|
489
|
+
{ deviceId: currentDeviceId || undefined }
|
|
1062
490
|
);
|
|
1063
491
|
|
|
1064
492
|
toast.success(translate(currentLanguage, 'editProfile.toasts.avatarUpdated') || 'Avatar updated');
|
|
@@ -1068,7 +496,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1068
496
|
},
|
|
1069
497
|
},
|
|
1070
498
|
});
|
|
1071
|
-
}, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient
|
|
499
|
+
}, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient]);
|
|
1072
500
|
|
|
1073
501
|
const contextValue: OxyContextState = useMemo(() => ({
|
|
1074
502
|
user,
|
|
@@ -1084,24 +512,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1084
512
|
currentLanguageMetadata,
|
|
1085
513
|
currentLanguageName,
|
|
1086
514
|
currentNativeLanguageName,
|
|
1087
|
-
|
|
1088
|
-
importIdentity,
|
|
1089
|
-
signIn,
|
|
1090
|
-
hasIdentity,
|
|
1091
|
-
getPublicKey,
|
|
1092
|
-
isIdentitySynced,
|
|
1093
|
-
syncIdentity,
|
|
1094
|
-
deleteIdentityAndClearAccount,
|
|
1095
|
-
storeTransferCode,
|
|
1096
|
-
getTransferCode,
|
|
1097
|
-
clearTransferCode,
|
|
1098
|
-
getAllPendingTransfers,
|
|
1099
|
-
getActiveTransferId,
|
|
1100
|
-
updateTransferState,
|
|
1101
|
-
identitySyncState: {
|
|
1102
|
-
isSynced: isIdentitySyncedStore ?? true,
|
|
1103
|
-
isSyncing: isSyncing ?? false,
|
|
1104
|
-
},
|
|
515
|
+
completeSignIn,
|
|
1105
516
|
logout,
|
|
1106
517
|
logoutAll,
|
|
1107
518
|
switchSession: switchSessionForContext,
|
|
@@ -1120,22 +531,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1120
531
|
}), [
|
|
1121
532
|
activeSessionId,
|
|
1122
533
|
currentDeviceId,
|
|
1123
|
-
|
|
1124
|
-
importIdentity,
|
|
1125
|
-
signIn,
|
|
1126
|
-
hasIdentity,
|
|
1127
|
-
getPublicKey,
|
|
1128
|
-
isIdentitySynced,
|
|
1129
|
-
syncIdentity,
|
|
1130
|
-
deleteIdentityAndClearAccount,
|
|
1131
|
-
storeTransferCode,
|
|
1132
|
-
getTransferCode,
|
|
1133
|
-
clearTransferCode,
|
|
1134
|
-
getAllPendingTransfers,
|
|
1135
|
-
getActiveTransferId,
|
|
1136
|
-
updateTransferState,
|
|
1137
|
-
isIdentitySyncedStore,
|
|
1138
|
-
isSyncing,
|
|
534
|
+
completeSignIn,
|
|
1139
535
|
currentLanguage,
|
|
1140
536
|
currentLanguageMetadata,
|
|
1141
537
|
currentLanguageName,
|