@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.
Files changed (119) hide show
  1. package/lib/commonjs/crypto/index.js +0 -23
  2. package/lib/commonjs/crypto/index.js.map +1 -1
  3. package/lib/commonjs/index.js +0 -15
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/components/Icon.js.map +1 -1
  6. package/lib/commonjs/ui/components/IconButton/utils.js.map +1 -1
  7. package/lib/commonjs/ui/components/TextField/Adornment/utils.js.map +1 -1
  8. package/lib/commonjs/ui/components/TextField/helpers.js.map +1 -1
  9. package/lib/commonjs/ui/components/TouchableRipple/utils.js.map +1 -1
  10. package/lib/commonjs/ui/components/Typography/AnimatedText.js.map +1 -1
  11. package/lib/commonjs/ui/context/OxyContext.js +37 -589
  12. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  13. package/lib/commonjs/ui/context/OxyContextBase.js.map +1 -1
  14. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +60 -425
  15. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  16. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +8 -112
  17. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  18. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +2 -27
  19. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +2 -27
  21. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  22. package/lib/commonjs/ui/hooks/useSessionSocket.js +2 -88
  23. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  24. package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -1
  25. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  26. package/lib/commonjs/ui/stores/authStore.js +52 -15
  27. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  28. package/lib/commonjs/ui/utils/avatarUtils.js +2 -32
  29. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  30. package/lib/module/crypto/index.js +4 -6
  31. package/lib/module/crypto/index.js.map +1 -1
  32. package/lib/module/index.js +6 -3
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/ui/components/Icon.js.map +1 -1
  35. package/lib/module/ui/components/IconButton/utils.js.map +1 -1
  36. package/lib/module/ui/components/TextField/Adornment/utils.js.map +1 -1
  37. package/lib/module/ui/components/TextField/helpers.js.map +1 -1
  38. package/lib/module/ui/components/TouchableRipple/utils.js.map +1 -1
  39. package/lib/module/ui/components/Typography/AnimatedText.js.map +1 -1
  40. package/lib/module/ui/context/OxyContext.js +35 -588
  41. package/lib/module/ui/context/OxyContext.js.map +1 -1
  42. package/lib/module/ui/context/OxyContextBase.js.map +1 -1
  43. package/lib/module/ui/context/hooks/useAuthOperations.js +60 -424
  44. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  45. package/lib/module/ui/hooks/mutations/useAccountMutations.js +8 -112
  46. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  47. package/lib/module/ui/hooks/queries/useAccountQueries.js +2 -27
  48. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  49. package/lib/module/ui/hooks/queries/useServicesQueries.js +2 -27
  50. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  51. package/lib/module/ui/hooks/useSessionSocket.js +2 -88
  52. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  53. package/lib/module/ui/screens/OxyAuthScreen.js +0 -1
  54. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  55. package/lib/module/ui/stores/authStore.js +52 -15
  56. package/lib/module/ui/stores/authStore.js.map +1 -1
  57. package/lib/module/ui/utils/avatarUtils.js +2 -32
  58. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  59. package/lib/typescript/crypto/index.d.ts +2 -5
  60. package/lib/typescript/crypto/index.d.ts.map +1 -1
  61. package/lib/typescript/crypto/types.d.ts +6 -2
  62. package/lib/typescript/crypto/types.d.ts.map +1 -1
  63. package/lib/typescript/index.d.ts +4 -2
  64. package/lib/typescript/index.d.ts.map +1 -1
  65. package/lib/typescript/ui/components/IconButton/utils.d.ts +1 -1
  66. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts +1 -1
  67. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts.map +1 -1
  68. package/lib/typescript/ui/components/TextField/helpers.d.ts +6 -6
  69. package/lib/typescript/ui/components/types.d.ts +0 -4
  70. package/lib/typescript/ui/components/types.d.ts.map +1 -1
  71. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  72. package/lib/typescript/ui/context/OxyContextBase.d.ts +2 -39
  73. package/lib/typescript/ui/context/OxyContextBase.d.ts.map +1 -1
  74. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +10 -25
  75. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  76. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  77. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  78. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  79. package/lib/typescript/ui/hooks/useSessionSocket.d.ts +1 -14
  80. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  81. package/lib/typescript/ui/stores/authStore.d.ts +27 -4
  82. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  83. package/lib/typescript/ui/utils/avatarUtils.d.ts +0 -2
  84. package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
  85. package/package.json +2 -2
  86. package/src/crypto/index.ts +3 -11
  87. package/src/crypto/types.ts +6 -2
  88. package/src/index.ts +6 -11
  89. package/src/ui/components/Icon.tsx +1 -1
  90. package/src/ui/components/IconButton/utils.ts +1 -1
  91. package/src/ui/components/TextField/Adornment/utils.ts +2 -2
  92. package/src/ui/components/TextField/helpers.tsx +8 -8
  93. package/src/ui/components/TouchableRipple/utils.ts +2 -2
  94. package/src/ui/components/Typography/AnimatedText.tsx +2 -2
  95. package/src/ui/components/types.tsx +0 -6
  96. package/src/ui/context/OxyContext.tsx +33 -637
  97. package/src/ui/context/OxyContextBase.tsx +5 -23
  98. package/src/ui/context/hooks/useAuthOperations.ts +84 -460
  99. package/src/ui/hooks/mutations/useAccountMutations.ts +12 -110
  100. package/src/ui/hooks/queries/useAccountQueries.ts +3 -27
  101. package/src/ui/hooks/queries/useServicesQueries.ts +3 -27
  102. package/src/ui/hooks/useSessionSocket.ts +2 -106
  103. package/src/ui/screens/OxyAuthScreen.tsx +1 -1
  104. package/src/ui/stores/authStore.ts +57 -18
  105. package/src/ui/utils/avatarUtils.ts +4 -36
  106. package/lib/commonjs/crypto/keyManager.js +0 -511
  107. package/lib/commonjs/crypto/keyManager.js.map +0 -1
  108. package/lib/commonjs/crypto/signatureService.js +0 -269
  109. package/lib/commonjs/crypto/signatureService.js.map +0 -1
  110. package/lib/module/crypto/keyManager.js +0 -508
  111. package/lib/module/crypto/keyManager.js.map +0 -1
  112. package/lib/module/crypto/signatureService.js +0 -266
  113. package/lib/module/crypto/signatureService.js.map +0 -1
  114. package/lib/typescript/crypto/keyManager.d.ts +0 -97
  115. package/lib/typescript/crypto/keyManager.d.ts.map +0 -1
  116. package/lib/typescript/crypto/signatureService.d.ts +0 -77
  117. package/lib/typescript/crypto/signatureService.d.ts.map +0 -1
  118. package/src/crypto/keyManager.ts +0 -545
  119. package/src/crypto/signatureService.ts +0 -301
@@ -17,7 +17,6 @@ Object.defineProperty(exports, "useOxy", {
17
17
  }
18
18
  });
19
19
  var _react = require("react");
20
- var _reactNative = require("react-native");
21
20
  var _core = require("../../core");
22
21
  var _sonner = require("../../lib/sonner");
23
22
  var _authStore = require("../stores/authStore");
@@ -34,14 +33,14 @@ var _bottomSheetManager = require("../navigation/bottomSheetManager");
34
33
  var _reactQuery = require("@tanstack/react-query");
35
34
  var _queryClient = require("../hooks/queryClient");
36
35
  var _queryKeys = require("../hooks/queries/queryKeys");
37
- var _crypto = require("../../crypto");
38
36
  var _i18n = require("../../i18n");
39
37
  var _avatarUtils = require("../utils/avatarUtils");
40
38
  var _accountStore = require("../stores/accountStore");
41
39
  var _loggerUtils = require("../../utils/loggerUtils");
42
40
  var _OxyContextBase = require("./OxyContextBase");
43
41
  var _jsxRuntime = require("react/jsx-runtime");
44
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } // Import context, types and useOxy from base file to avoid circular dependencies
42
+ // Import context, types and useOxy from base file to avoid circular dependencies
43
+
45
44
  // Re-export for backwards compatibility
46
45
  let cachedUseFollowHook = null;
47
46
  const loadUseFollowHook = () => {
@@ -69,30 +68,6 @@ const loadUseFollowHook = () => {
69
68
  return cachedUseFollowHook;
70
69
  }
71
70
  };
72
-
73
- // Shared storage key for identity cache - used by accounts app for instant routing
74
- // Uses expo-secure-store for consistency with identity storage (KeyManager)
75
- const IDENTITY_CACHE_KEY = 'oxy_identity_exists_cache';
76
-
77
- // Helper to update identity cache in SecureStore
78
- const updateIdentityCache = async exists => {
79
- if (_reactNative.Platform.OS === 'web') return;
80
- try {
81
- const SecureStore = await Promise.resolve().then(() => _interopRequireWildcard(require('expo-secure-store')));
82
- await SecureStore.setItemAsync(IDENTITY_CACHE_KEY, exists ? 'true' : 'false');
83
- } catch {
84
- // Silently fail - cache is just an optimization
85
- }
86
- };
87
- const clearIdentityCache = async () => {
88
- if (_reactNative.Platform.OS === 'web') return;
89
- try {
90
- const SecureStore = await Promise.resolve().then(() => _interopRequireWildcard(require('expo-secure-store')));
91
- await SecureStore.deleteItemAsync(IDENTITY_CACHE_KEY);
92
- } catch {
93
- // Silently fail
94
- }
95
- };
96
71
  const OxyProvider = ({
97
72
  children,
98
73
  oxyServices: providedOxyServices,
@@ -116,28 +91,22 @@ const OxyProvider = ({
116
91
  const oxyServices = oxyServicesRef.current;
117
92
  const {
118
93
  isAuthenticated,
94
+ isOnline,
119
95
  isLoading,
120
96
  error,
97
+ setOnline,
121
98
  loginSuccess,
122
99
  loginFailure,
123
- logoutStore,
124
- // Identity sync state and actions
125
- isIdentitySyncedStore,
126
- isSyncing,
127
- setIdentitySynced,
128
- setSyncing
100
+ logoutStore
129
101
  } = (0, _authStore.useAuthStore)((0, _shallow.useShallow)(state => ({
130
102
  isAuthenticated: state.isAuthenticated,
103
+ isOnline: state.isOnline,
131
104
  isLoading: state.isLoading,
132
105
  error: state.error,
106
+ setOnline: state.setOnline,
133
107
  loginSuccess: state.loginSuccess,
134
108
  loginFailure: state.loginFailure,
135
- logoutStore: state.logout,
136
- // Identity sync state and actions
137
- isIdentitySyncedStore: state.isIdentitySynced,
138
- isSyncing: state.isSyncing,
139
- setIdentitySynced: state.setIdentitySynced,
140
- setSyncing: state.setSyncing
109
+ logoutStore: state.logout
141
110
  })));
142
111
  const [tokenReady, setTokenReady] = (0, _react.useState)(true);
143
112
  const initializedRef = (0, _react.useRef)(false);
@@ -159,49 +128,6 @@ const OxyProvider = ({
159
128
  onError,
160
129
  logger
161
130
  });
162
-
163
- // Identity integrity check and auto-restore on startup
164
- // Skip on web platform - identity storage is only available on native platforms
165
- (0, _react.useEffect)(() => {
166
- if (!storage || !isStorageReady) return;
167
- if (_reactNative.Platform.OS === 'web') return; // Identity operations are native-only
168
-
169
- const checkAndRestoreIdentity = async () => {
170
- try {
171
- // Check if identity exists and verify integrity
172
- const hasIdentity = await _crypto.KeyManager.hasIdentity();
173
- if (hasIdentity) {
174
- const isValid = await _crypto.KeyManager.verifyIdentityIntegrity();
175
- if (!isValid) {
176
- // Try to restore from backup
177
- const restored = await _crypto.KeyManager.restoreIdentityFromBackup();
178
- if (__DEV__) {
179
- logger(restored ? 'Identity restored from backup successfully' : 'Identity integrity check failed - user may need to restore from backup file');
180
- }
181
- } else {
182
- // Identity is valid - ensure backup is up to date
183
- await _crypto.KeyManager.backupIdentity();
184
- }
185
- } else {
186
- // No identity - try to restore from backup
187
- const restored = await _crypto.KeyManager.restoreIdentityFromBackup();
188
- if (restored && __DEV__) {
189
- logger('Identity restored from backup on startup');
190
- }
191
- }
192
- } catch (error) {
193
- if (__DEV__) {
194
- logger('Error during identity integrity check', error);
195
- }
196
- // Don't block app startup - user can recover with backup file
197
- }
198
- };
199
- checkAndRestoreIdentity();
200
- }, [storage, isStorageReady, logger]);
201
-
202
- // Offline queuing is now handled by TanStack Query mutations
203
- // No need for custom offline queue
204
-
205
131
  const {
206
132
  currentLanguage,
207
133
  metadata: currentLanguageMetadata,
@@ -266,15 +192,9 @@ const OxyProvider = ({
266
192
  });
267
193
  const user = userData ?? null;
268
194
  const {
269
- createIdentity,
270
- importIdentity: importIdentityBase,
271
- signIn,
195
+ completeSignIn,
272
196
  logout,
273
- logoutAll,
274
- hasIdentity,
275
- getPublicKey,
276
- isIdentitySynced,
277
- syncIdentity: syncIdentityBase
197
+ logoutAll
278
198
  } = (0, _useAuthOperations.useAuthOperations)({
279
199
  oxyServices,
280
200
  storage,
@@ -292,41 +212,10 @@ const OxyProvider = ({
292
212
  loginFailure,
293
213
  logoutStore,
294
214
  setAuthState,
295
- setIdentitySynced,
296
- setSyncing,
297
215
  logger
298
216
  });
299
217
 
300
- // syncIdentity - TanStack Query handles offline mutations automatically
301
- const syncIdentity = (0, _react.useCallback)(() => syncIdentityBase(), [syncIdentityBase]);
302
-
303
- // Wrapper for createIdentity to update identity cache
304
- const wrappedCreateIdentity = (0, _react.useCallback)(async () => {
305
- const result = await createIdentity();
306
- // Update cache for instant routing on next app launch
307
- updateIdentityCache(true);
308
- return result;
309
- }, [createIdentity]);
310
-
311
- // Wrapper for importIdentity to handle legacy calls and update cache
312
- const importIdentity = (0, _react.useCallback)(async (backupData, password) => {
313
- // Handle legacy calls with single string argument (old recovery phrase signature)
314
- if (typeof backupData === 'string') {
315
- throw new Error('Recovery phrase import is no longer supported. Please use backup file import or QR code transfer instead.');
316
- }
317
-
318
- // Validate that password is provided
319
- if (!password || typeof password !== 'string') {
320
- throw new Error('Password is required for backup file import.');
321
- }
322
- const result = await importIdentityBase(backupData, password);
323
- // Update cache for instant routing on next app launch
324
- updateIdentityCache(true);
325
- return result;
326
- }, [importIdentityBase]);
327
-
328
- // Clear all account data when identity is lost (for accounts app)
329
- // In accounts app, identity = account, so losing identity means losing everything
218
+ // Clear all cached data and storage on logout
330
219
  const clearAllAccountData = (0, _react.useCallback)(async () => {
331
220
  // Clear TanStack Query cache (in-memory)
332
221
  queryClient.clear();
@@ -343,19 +232,6 @@ const OxyProvider = ({
343
232
  // Clear session state (sessions, activeSessionId, storage)
344
233
  await clearSessionState();
345
234
 
346
- // Clear identity sync state from storage
347
- if (storage) {
348
- try {
349
- await storage.removeItem('oxy_identity_synced');
350
- } catch (error) {
351
- logger('Failed to clear identity sync state', error);
352
- }
353
- }
354
-
355
- // Reset auth store identity sync state
356
- _authStore.useAuthStore.getState().setIdentitySynced(false);
357
- _authStore.useAuthStore.getState().setSyncing(false);
358
-
359
235
  // Reset account store
360
236
  _accountStore.useAccountStore.getState().reset();
361
237
 
@@ -363,165 +239,59 @@ const OxyProvider = ({
363
239
  oxyServices.clearCache();
364
240
  }, [queryClient, storage, clearSessionState, logger, oxyServices]);
365
241
 
366
- // Transfer code management functions (must be defined before deleteIdentityAndClearAccount)
367
- const getAllPendingTransfers = (0, _react.useCallback)(() => {
368
- const pending = [];
369
- transferCodesRef.current.forEach((data, transferId) => {
370
- if (data.state === 'pending') {
371
- pending.push({
372
- transferId,
373
- data
374
- });
375
- }
376
- });
377
- return pending;
378
- }, []);
379
- const getActiveTransferId = (0, _react.useCallback)(() => {
380
- return activeTransferIdRef.current;
381
- }, []);
382
-
383
- // Delete identity and clear all account data
384
- // In accounts app, deleting identity means losing the account completely
385
- const deleteIdentityAndClearAccount = (0, _react.useCallback)(async (skipBackup = false, force = false, userConfirmed = false) => {
386
- // CRITICAL: Check for active transfers before deletion (unless force is true)
387
- // This prevents accidental identity loss during transfer
388
- if (!force) {
389
- const pendingTransfers = getAllPendingTransfers();
390
- if (pendingTransfers.length > 0) {
391
- const activeTransferId = getActiveTransferId();
392
- const hasActiveTransfer = activeTransferId && pendingTransfers.some(t => t.transferId === activeTransferId);
393
- if (hasActiveTransfer) {
394
- throw new Error('Cannot delete identity: An active identity transfer is in progress. ' + 'Please wait for the transfer to complete or cancel it first. ' + 'If you proceed, you may lose access to your identity permanently.');
395
- }
396
- }
397
- }
398
-
399
- // First, clear all account data
400
- await clearAllAccountData();
401
-
402
- // Then delete the identity keys
403
- await _crypto.KeyManager.deleteIdentity(skipBackup, force, userConfirmed);
404
-
405
- // Clear identity cache for instant routing on next app launch
406
- clearIdentityCache();
407
- }, [clearAllAccountData, getAllPendingTransfers, getActiveTransferId]);
408
-
409
- // Network reconnect sync - TanStack Query automatically retries mutations on reconnect
410
- // We only need to sync identity if it's not synced
242
+ // Network state monitoring - updates authStore.isOnline
243
+ // When offline: isAuthenticated becomes false automatically
244
+ // When reconnect: isOnline set to true, allowing re-authentication
411
245
  (0, _react.useEffect)(() => {
412
246
  if (!storage) return;
413
247
  let wasOffline = false;
414
248
  let checkTimeout = null;
415
- let lastReconnectionLog = 0;
416
- const RECONNECTION_LOG_DEBOUNCE_MS = 5000; // 5 seconds
417
-
418
- // Circuit breaker and exponential backoff state
419
- const stateRef = {
420
- consecutiveFailures: 0,
421
- currentInterval: 10000,
422
- // Start with 10 seconds
423
- baseInterval: 10000,
424
- // Base interval in milliseconds
425
- maxInterval: 60000,
426
- // Maximum interval (60 seconds)
427
- maxFailures: 5 // Circuit breaker threshold
428
- };
429
249
  const scheduleNextCheck = () => {
430
250
  if (checkTimeout) {
431
251
  clearTimeout(checkTimeout);
432
252
  }
433
253
  checkTimeout = setTimeout(() => {
434
- checkNetworkAndSync();
435
- }, stateRef.currentInterval);
254
+ checkNetworkStatus();
255
+ }, 30000); // Check every 30 seconds
436
256
  };
437
- const checkNetworkAndSync = async () => {
257
+ const checkNetworkStatus = async () => {
438
258
  try {
439
259
  // Try a lightweight health check to see if we're online
440
- await oxyServices.healthCheck().catch(() => {
441
- wasOffline = true;
442
- throw new Error('Health check failed');
443
- });
444
-
445
- // Health check succeeded - reset circuit breaker and backoff
446
- if (stateRef.consecutiveFailures > 0) {
447
- stateRef.consecutiveFailures = 0;
448
- stateRef.currentInterval = stateRef.baseInterval;
449
- }
260
+ await oxyServices.healthCheck();
450
261
 
451
- // If we were offline and now we're online, sync identity if needed
262
+ // If we were offline and now we're online
452
263
  if (wasOffline) {
453
- const now = Date.now();
454
- const timeSinceLastLog = now - lastReconnectionLog;
455
- if (timeSinceLastLog >= RECONNECTION_LOG_DEBOUNCE_MS) {
456
- logger('Network reconnected, checking identity sync...');
457
- lastReconnectionLog = now;
458
-
459
- // Sync identity first (if not synced)
460
- try {
461
- const hasIdentityValue = await hasIdentity();
462
- if (hasIdentityValue) {
463
- // Check sync status directly - sync if not explicitly 'true'
464
- // undefined = not synced yet, 'false' = explicitly not synced, 'true' = synced
465
- const syncStatus = await storage.getItem('oxy_identity_synced');
466
- if (syncStatus !== 'true') {
467
- await syncIdentity();
468
- }
469
- }
470
- } catch (syncError) {
471
- // Skip sync silently if username is required (expected when offline onboarding)
472
- if (syncError?.code === 'USERNAME_REQUIRED' || syncError?.message === 'USERNAME_REQUIRED') {
473
- if (__DEV__) {
474
- _loggerUtils.logger.debug('Sync skipped - username required', {
475
- component: 'OxyContext',
476
- method: 'checkNetworkAndSync'
477
- }, syncError);
478
- }
479
- // Don't log or show error - username will be set later
480
- } else if (!(0, _errorHandlers.isTimeoutOrNetworkError)(syncError)) {
481
- // Only log unexpected errors - timeouts/network issues are expected when offline
482
- logger('Error syncing identity on reconnect', syncError);
483
- } else if (__DEV__) {
484
- _loggerUtils.logger.debug('Identity sync timeout (expected when offline)', {
485
- component: 'OxyContext',
486
- method: 'checkNetworkAndSync'
487
- }, syncError);
488
- }
489
- }
490
- }
491
-
264
+ logger('Network reconnected - setting online state');
265
+ setOnline(true);
492
266
  // TanStack Query will automatically retry pending mutations
493
- // Reset flag immediately after processing (whether logged or not)
267
+ // Session management will handle token refresh if needed
494
268
  wasOffline = false;
269
+ } else {
270
+ // We're online and were already online
271
+ setOnline(true);
495
272
  }
496
273
  } catch (error) {
497
274
  // Network check failed - we're offline
498
- wasOffline = true;
499
-
500
- // Increment failure count and apply exponential backoff
501
- stateRef.consecutiveFailures++;
502
-
503
- // Calculate new interval with exponential backoff, capped at maxInterval
504
- const backoffMultiplier = Math.min(Math.pow(2, stateRef.consecutiveFailures - 1), stateRef.maxInterval / stateRef.baseInterval);
505
- stateRef.currentInterval = Math.min(stateRef.baseInterval * backoffMultiplier, stateRef.maxInterval);
506
-
507
- // If we hit the circuit breaker threshold, use max interval
508
- if (stateRef.consecutiveFailures >= stateRef.maxFailures) {
509
- stateRef.currentInterval = stateRef.maxInterval;
275
+ if (!wasOffline) {
276
+ if (__DEV__) {
277
+ logger('Network appears offline - suspending authentication');
278
+ }
279
+ setOnline(false); // This will set isAuthenticated to false
510
280
  }
281
+ wasOffline = true;
511
282
  } finally {
512
- // Always schedule next check (will use updated interval)
513
283
  scheduleNextCheck();
514
284
  }
515
285
  };
516
286
 
517
287
  // Check immediately
518
- checkNetworkAndSync();
288
+ checkNetworkStatus();
519
289
  return () => {
520
290
  if (checkTimeout) {
521
291
  clearTimeout(checkTimeout);
522
292
  }
523
293
  };
524
- }, [oxyServices, storage, syncIdentity, logger]);
294
+ }, [oxyServices, storage, logger, setOnline]);
525
295
  const {
526
296
  getDeviceSessions,
527
297
  logoutAllDeviceSessions,
@@ -630,171 +400,6 @@ const OxyProvider = ({
630
400
 
631
401
  // Get userId from JWT token (MongoDB ObjectId) for socket room matching
632
402
  const userId = oxyServices.getCurrentUserId();
633
-
634
- // Transfer code storage interface
635
-
636
- // Store transfer codes in memory for verification (also persisted to storage)
637
- // Map: transferId -> TransferCodeData
638
- const transferCodesRef = (0, _react.useRef)(new Map());
639
- const activeTransferIdRef = (0, _react.useRef)(null);
640
- const TRANSFER_CODES_STORAGE_KEY = `${storageKeyPrefix}_transfer_codes`;
641
- const ACTIVE_TRANSFER_STORAGE_KEY = `${storageKeyPrefix}_active_transfer_id`;
642
-
643
- // Clear stale transfer codes on startup
644
- // Transfers are ephemeral and should not persist across app restarts
645
- (0, _react.useEffect)(() => {
646
- if (!storage || !isStorageReady) return;
647
- const clearStaleTransfers = async () => {
648
- try {
649
- // Clear any stored transfer codes - they're only valid during active transfer sessions
650
- await storage.removeItem(TRANSFER_CODES_STORAGE_KEY);
651
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
652
- if (__DEV__) {
653
- logger('Cleared stale transfer codes on startup');
654
- }
655
- } catch (error) {
656
- if (__DEV__) {
657
- logger('Failed to clear stale transfer codes', error);
658
- }
659
- }
660
- };
661
- clearStaleTransfers();
662
- }, [storage, isStorageReady, logger, storageKeyPrefix]);
663
-
664
- // Persist transfer codes to storage whenever they change
665
- const persistTransferCodes = (0, _react.useCallback)(async () => {
666
- if (!storage) return;
667
- try {
668
- const codesToStore = {};
669
- transferCodesRef.current.forEach((value, key) => {
670
- codesToStore[key] = value;
671
- });
672
- await storage.setItem(TRANSFER_CODES_STORAGE_KEY, JSON.stringify(codesToStore));
673
- } catch (error) {
674
- if (__DEV__) {
675
- logger('Failed to persist transfer codes', error);
676
- }
677
- }
678
- }, [storage, logger]);
679
-
680
- // Cleanup old transfer codes (older than 15 minutes)
681
- (0, _react.useEffect)(() => {
682
- const cleanup = setInterval(async () => {
683
- const now = Date.now();
684
- const fifteenMinutes = 15 * 60 * 1000;
685
- let needsPersist = false;
686
- transferCodesRef.current.forEach((value, key) => {
687
- if (now - value.timestamp > fifteenMinutes) {
688
- transferCodesRef.current.delete(key);
689
- needsPersist = true;
690
- if (__DEV__) {
691
- logger('Cleaned up expired transfer code', {
692
- transferId: key
693
- });
694
- }
695
- }
696
- });
697
-
698
- // Clear active transfer if it was deleted
699
- if (activeTransferIdRef.current && !transferCodesRef.current.has(activeTransferIdRef.current)) {
700
- activeTransferIdRef.current = null;
701
- if (storage) {
702
- try {
703
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
704
- } catch (error) {
705
- // Ignore storage errors
706
- }
707
- }
708
- }
709
- if (needsPersist) {
710
- await persistTransferCodes();
711
- }
712
- }, 60000); // Check every minute
713
-
714
- return () => clearInterval(cleanup);
715
- }, [logger, persistTransferCodes, storage]);
716
-
717
- // Transfer code management functions
718
- const storeTransferCode = (0, _react.useCallback)(async (transferId, code, sourceDeviceId, publicKey) => {
719
- const transferData = {
720
- code,
721
- sourceDeviceId,
722
- publicKey,
723
- timestamp: Date.now(),
724
- state: 'pending'
725
- };
726
- transferCodesRef.current.set(transferId, transferData);
727
- activeTransferIdRef.current = transferId;
728
-
729
- // Persist to storage
730
- await persistTransferCodes();
731
- if (storage) {
732
- try {
733
- await storage.setItem(ACTIVE_TRANSFER_STORAGE_KEY, transferId);
734
- } catch (error) {
735
- if (__DEV__) {
736
- logger('Failed to persist active transfer ID', error);
737
- }
738
- }
739
- }
740
- if (__DEV__) {
741
- logger('Stored transfer code', {
742
- transferId,
743
- sourceDeviceId,
744
- publicKey: publicKey.substring(0, 16) + '...'
745
- });
746
- }
747
- }, [logger, persistTransferCodes, storage]);
748
- const getTransferCode = (0, _react.useCallback)(transferId => {
749
- return transferCodesRef.current.get(transferId) || null;
750
- }, []);
751
- const updateTransferState = (0, _react.useCallback)(async (transferId, state) => {
752
- const transferData = transferCodesRef.current.get(transferId);
753
- if (transferData) {
754
- transferData.state = state;
755
- transferCodesRef.current.set(transferId, transferData);
756
-
757
- // Clear active transfer if completed or failed
758
- if (state === 'completed' || state === 'failed') {
759
- if (activeTransferIdRef.current === transferId) {
760
- activeTransferIdRef.current = null;
761
- if (storage) {
762
- try {
763
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
764
- } catch (error) {
765
- // Ignore storage errors
766
- }
767
- }
768
- }
769
- }
770
- await persistTransferCodes();
771
- if (__DEV__) {
772
- logger('Updated transfer state', {
773
- transferId,
774
- state
775
- });
776
- }
777
- }
778
- }, [logger, persistTransferCodes, storage]);
779
- const clearTransferCode = (0, _react.useCallback)(async transferId => {
780
- transferCodesRef.current.delete(transferId);
781
- if (activeTransferIdRef.current === transferId) {
782
- activeTransferIdRef.current = null;
783
- if (storage) {
784
- try {
785
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
786
- } catch (error) {
787
- // Ignore storage errors
788
- }
789
- }
790
- }
791
- await persistTransferCodes();
792
- if (__DEV__) {
793
- logger('Cleared transfer code', {
794
- transferId
795
- });
796
- }
797
- }, [logger, persistTransferCodes, storage]);
798
403
  const refreshSessionsWithUser = (0, _react.useCallback)(() => refreshSessions(userId || undefined), [refreshSessions, userId]);
799
404
  const handleSessionRemoved = (0, _react.useCallback)(sessionId => {
800
405
  trackRemovedSession(sessionId);
@@ -803,143 +408,6 @@ const OxyProvider = ({
803
408
  _sonner.toast.info('You have been signed out remotely.');
804
409
  logout().catch(remoteError => logger('Failed to process remote sign out', remoteError));
805
410
  }, [logger, logout]);
806
- const handleIdentityTransferComplete = (0, _react.useCallback)(async data => {
807
- try {
808
- logger('Received identity transfer complete notification', {
809
- transferId: data.transferId,
810
- sourceDeviceId: data.sourceDeviceId,
811
- currentDeviceId,
812
- hasActiveSession: activeSessionId !== null,
813
- publicKey: data.publicKey.substring(0, 16) + '...'
814
- });
815
- const storedTransfer = getTransferCode(data.transferId);
816
- if (!storedTransfer) {
817
- logger('Transfer code not found for transferId', {
818
- transferId: data.transferId
819
- });
820
- _sonner.toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
821
- return;
822
- }
823
-
824
- // Verify publicKey matches first (most important check)
825
- const publicKeyMatches = data.publicKey === storedTransfer.publicKey;
826
- if (!publicKeyMatches) {
827
- logger('Public key mismatch for transfer', {
828
- transferId: data.transferId,
829
- receivedPublicKey: data.publicKey.substring(0, 16) + '...',
830
- storedPublicKey: storedTransfer.publicKey.substring(0, 16) + '...'
831
- });
832
- _sonner.toast.error('Transfer verification failed: Public key mismatch. Identity will not be deleted.');
833
- return;
834
- }
835
-
836
- // Verify deviceId matches - very lenient since publicKey is the critical check
837
- // If publicKey matches, we allow deletion even if deviceId doesn't match exactly
838
- // This handles cases where deviceId might not be available or slightly different
839
- const deviceIdMatches =
840
- // Exact match
841
- data.sourceDeviceId && data.sourceDeviceId === currentDeviceId ||
842
- // Stored sourceDeviceId matches current deviceId
843
- storedTransfer.sourceDeviceId && storedTransfer.sourceDeviceId === currentDeviceId;
844
-
845
- // If publicKey matches, we're very lenient with deviceId - only warn but don't block
846
- if (!deviceIdMatches && publicKeyMatches) {
847
- logger('Device ID mismatch for transfer, but publicKey matches - proceeding with deletion', {
848
- transferId: data.transferId,
849
- receivedDeviceId: data.sourceDeviceId,
850
- storedDeviceId: storedTransfer.sourceDeviceId,
851
- currentDeviceId,
852
- hasActiveSession: activeSessionId !== null
853
- });
854
- // Proceed with deletion - publicKey match is the critical verification
855
- } else if (!deviceIdMatches && !publicKeyMatches) {
856
- // Both don't match - this is suspicious, block deletion
857
- logger('Device ID and publicKey mismatch for transfer', {
858
- transferId: data.transferId,
859
- receivedDeviceId: data.sourceDeviceId,
860
- currentDeviceId
861
- });
862
- _sonner.toast.error('Transfer verification failed: Device and key mismatch. Identity will not be deleted.');
863
- return;
864
- }
865
-
866
- // Verify transfer code matches (if provided)
867
- // Transfer code is optional - if not provided, we still proceed if publicKey matches
868
- if (data.transferCode) {
869
- const codeMatches = data.transferCode.toUpperCase() === storedTransfer.code.toUpperCase();
870
- if (!codeMatches) {
871
- logger('Transfer code mismatch, but publicKey matches - proceeding with deletion', {
872
- transferId: data.transferId,
873
- receivedCode: data.transferCode,
874
- storedCode: storedTransfer.code.substring(0, 2) + '****'
875
- });
876
- // Don't block - publicKey match is sufficient, code mismatch might be due to user error
877
- // Log warning but proceed
878
- }
879
- }
880
-
881
- // Check if transfer is too old (safety timeout - 10 minutes)
882
- const transferAge = Date.now() - storedTransfer.timestamp;
883
- const tenMinutes = 10 * 60 * 1000;
884
- if (transferAge > tenMinutes) {
885
- logger('Transfer confirmation received too late', {
886
- transferId: data.transferId,
887
- age: transferAge,
888
- ageMinutes: Math.round(transferAge / 60000)
889
- });
890
- _sonner.toast.error('Transfer verification failed: Confirmation received too late. Identity will not be deleted.');
891
- clearTransferCode(data.transferId);
892
- return;
893
- }
894
-
895
- // NOTE: Target device verification already happened server-side when notifyTransferComplete was called
896
- // The server verified that the target device is authenticated and has the matching public key
897
- // Additional client-side verification is not necessary and would require source device authentication
898
- // which may not be available. The existing checks (public key match, transfer code, device ID) are sufficient.
899
-
900
- logger('All transfer verifications passed, deleting identity from source device', {
901
- transferId: data.transferId,
902
- sourceDeviceId: data.sourceDeviceId,
903
- publicKey: data.publicKey.substring(0, 16) + '...'
904
- });
905
- try {
906
- // Verify identity still exists before deletion (safety check)
907
- const identityStillExists = await _crypto.KeyManager.hasIdentity();
908
- if (!identityStillExists) {
909
- logger('Identity already deleted - skipping deletion', {
910
- transferId: data.transferId
911
- });
912
- await updateTransferState(data.transferId, 'completed');
913
- await clearTransferCode(data.transferId);
914
- return;
915
- }
916
- await deleteIdentityAndClearAccount(false, false, true);
917
-
918
- // Verify identity was actually deleted
919
- const identityDeleted = !(await _crypto.KeyManager.hasIdentity());
920
- if (!identityDeleted) {
921
- logger('Identity deletion failed - identity still exists', {
922
- transferId: data.transferId
923
- });
924
- await updateTransferState(data.transferId, 'failed');
925
- throw new Error('Identity deletion failed - identity still exists');
926
- }
927
- await updateTransferState(data.transferId, 'completed');
928
- await clearTransferCode(data.transferId);
929
- logger('Identity successfully deleted and transfer code cleared', {
930
- transferId: data.transferId
931
- });
932
- _sonner.toast.success('Identity successfully transferred and removed from this device');
933
- } catch (deleteError) {
934
- logger('Error during identity deletion', deleteError);
935
- await updateTransferState(data.transferId, 'failed');
936
- throw deleteError;
937
- }
938
- } catch (error) {
939
- logger('Failed to delete identity after transfer', error);
940
- _sonner.toast.error(error?.message || 'Failed to remove identity from this device. Please try again manually from Security Settings.');
941
- }
942
- }, [deleteIdentityAndClearAccount, logger, getTransferCode, clearTransferCode, updateTransferState, currentDeviceId, activeSessionId, oxyServices]);
943
411
  (0, _useSessionSocket.useSessionSocket)({
944
412
  userId,
945
413
  activeSessionId,
@@ -949,10 +417,8 @@ const OxyProvider = ({
949
417
  clearSessionState,
950
418
  baseURL: oxyServices.getBaseURL(),
951
419
  getAccessToken: () => oxyServices.getAccessToken(),
952
- getTransferCode: getTransferCode,
953
420
  onRemoteSignOut: handleRemoteSignOut,
954
- onSessionRemoved: handleSessionRemoved,
955
- onIdentityTransferComplete: handleIdentityTransferComplete
421
+ onSessionRemoved: handleSessionRemoved
956
422
  });
957
423
  const switchSessionForContext = (0, _react.useCallback)(async sessionId => {
958
424
  await switchSession(sessionId);
@@ -986,7 +452,6 @@ const OxyProvider = ({
986
452
  await (0, _avatarUtils.updateProfileWithAvatar)({
987
453
  avatar: file.id
988
454
  }, oxyServices, activeSessionId, queryClient, {
989
- syncIdentity,
990
455
  deviceId: currentDeviceId || undefined
991
456
  });
992
457
  _sonner.toast.success((0, _i18n.translate)(currentLanguage, 'editProfile.toasts.avatarUpdated') || 'Avatar updated');
@@ -996,7 +461,7 @@ const OxyProvider = ({
996
461
  }
997
462
  }
998
463
  });
999
- }, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient, syncIdentity]);
464
+ }, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient]);
1000
465
  const contextValue = (0, _react.useMemo)(() => ({
1001
466
  user,
1002
467
  sessions,
@@ -1011,24 +476,7 @@ const OxyProvider = ({
1011
476
  currentLanguageMetadata,
1012
477
  currentLanguageName,
1013
478
  currentNativeLanguageName,
1014
- createIdentity: wrappedCreateIdentity,
1015
- importIdentity,
1016
- signIn,
1017
- hasIdentity,
1018
- getPublicKey,
1019
- isIdentitySynced,
1020
- syncIdentity,
1021
- deleteIdentityAndClearAccount,
1022
- storeTransferCode,
1023
- getTransferCode,
1024
- clearTransferCode,
1025
- getAllPendingTransfers,
1026
- getActiveTransferId,
1027
- updateTransferState,
1028
- identitySyncState: {
1029
- isSynced: isIdentitySyncedStore ?? true,
1030
- isSyncing: isSyncing ?? false
1031
- },
479
+ completeSignIn,
1032
480
  logout,
1033
481
  logoutAll,
1034
482
  switchSession: switchSessionForContext,
@@ -1044,7 +492,7 @@ const OxyProvider = ({
1044
492
  useFollow: useFollowHook,
1045
493
  showBottomSheet: showBottomSheetForContext,
1046
494
  openAvatarPicker
1047
- }), [activeSessionId, currentDeviceId, wrappedCreateIdentity, importIdentity, signIn, hasIdentity, getPublicKey, isIdentitySynced, syncIdentity, deleteIdentityAndClearAccount, storeTransferCode, getTransferCode, clearTransferCode, getAllPendingTransfers, getActiveTransferId, updateTransferState, isIdentitySyncedStore, isSyncing, currentLanguage, currentLanguageMetadata, currentLanguageName, currentNativeLanguageName, error, getDeviceSessions, isAuthenticated, isLoading, logout, logoutAll, logoutAllDeviceSessions, oxyServices, refreshSessionsWithUser, sessions, setLanguage, switchSessionForContext, tokenReady, isStorageReady, updateDeviceName, clearAllAccountData, useFollowHook, user, showBottomSheetForContext, openAvatarPicker]);
495
+ }), [activeSessionId, currentDeviceId, completeSignIn, currentLanguage, currentLanguageMetadata, currentLanguageName, currentNativeLanguageName, error, getDeviceSessions, isAuthenticated, isLoading, logout, logoutAll, logoutAllDeviceSessions, oxyServices, refreshSessionsWithUser, sessions, setLanguage, switchSessionForContext, tokenReady, isStorageReady, updateDeviceName, clearAllAccountData, useFollowHook, user, showBottomSheetForContext, openAvatarPicker]);
1048
496
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyContextBase.OxyContext.Provider, {
1049
497
  value: contextValue,
1050
498
  children: children