@oxyhq/services 5.5.6 → 5.5.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.
Files changed (34) hide show
  1. package/lib/commonjs/core/index.js +13 -13
  2. package/lib/commonjs/core/index.js.map +1 -1
  3. package/lib/commonjs/ui/context/OxyContext.js +467 -87
  4. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  5. package/lib/commonjs/ui/hooks/useAuthFetch.js +79 -29
  6. package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -1
  7. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  8. package/lib/module/core/index.js +13 -6
  9. package/lib/module/core/index.js.map +1 -1
  10. package/lib/module/ui/context/OxyContext.js +467 -87
  11. package/lib/module/ui/context/OxyContext.js.map +1 -1
  12. package/lib/module/ui/hooks/useAuthFetch.js +79 -29
  13. package/lib/module/ui/hooks/useAuthFetch.js.map +1 -1
  14. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  15. package/lib/typescript/core/index.d.ts +7 -6
  16. package/lib/typescript/core/index.d.ts.map +1 -1
  17. package/lib/typescript/ui/context/OxyContext.d.ts +11 -7
  18. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  19. package/lib/typescript/ui/hooks/useAuthFetch.d.ts +10 -4
  20. package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -1
  21. package/package.json +1 -1
  22. package/src/__tests__/backend-middleware.test.ts +209 -0
  23. package/src/__tests__/ui/hooks/useAuthFetch.test.ts +70 -0
  24. package/src/core/index.ts +13 -7
  25. package/src/ui/context/OxyContext.tsx +522 -99
  26. package/src/ui/hooks/useAuthFetch.ts +83 -29
  27. package/src/ui/screens/SessionManagementScreen.tsx +9 -9
  28. package/lib/commonjs/core/AuthManager.js +0 -352
  29. package/lib/commonjs/core/AuthManager.js.map +0 -1
  30. package/lib/module/core/AuthManager.js +0 -347
  31. package/lib/module/core/AuthManager.js.map +0 -1
  32. package/lib/typescript/core/AuthManager.d.ts +0 -100
  33. package/lib/typescript/core/AuthManager.d.ts.map +0 -1
  34. package/src/core/AuthManager.ts +0 -366
@@ -5,13 +5,17 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useOxy = exports.default = exports.OxyContextProvider = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
- var _AuthManager = require("../../core/AuthManager");
8
+ var _deviceManager = require("../../utils/deviceManager");
9
9
  var _jsxRuntime = require("react/jsx-runtime");
10
- 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); } // Context interface with session management support
10
+ 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); } // Define the context shape
11
+ // Create the context with default values
11
12
  const OxyContext = /*#__PURE__*/(0, _react.createContext)(null);
12
13
 
13
- // Storage implementation
14
+ // Props for the OxyContextProvider
14
15
 
16
+ // Platform storage implementation
17
+
18
+ // Web localStorage implementation
15
19
  class WebStorage {
16
20
  async getItem(key) {
17
21
  return localStorage.getItem(key);
@@ -26,10 +30,16 @@ class WebStorage {
26
30
  localStorage.clear();
27
31
  }
28
32
  }
33
+
34
+ // React Native AsyncStorage implementation
29
35
  let AsyncStorage;
36
+
37
+ // Determine the platform and set up storage
30
38
  const isReactNative = () => {
31
39
  return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
32
40
  };
41
+
42
+ // Get appropriate storage for the platform
33
43
  const getStorage = async () => {
34
44
  if (isReactNative()) {
35
45
  if (!AsyncStorage) {
@@ -45,6 +55,13 @@ const getStorage = async () => {
45
55
  }
46
56
  return new WebStorage();
47
57
  };
58
+
59
+ // Storage keys for secure sessions
60
+ const getSecureStorageKeys = (prefix = 'oxy_secure') => ({
61
+ sessions: `${prefix}_sessions`,
62
+ // Array of SecureClientSession objects
63
+ activeSessionId: `${prefix}_active_session_id` // ID of currently active session
64
+ });
48
65
  const OxyContextProvider = ({
49
66
  children,
50
67
  oxyServices,
@@ -52,29 +69,17 @@ const OxyContextProvider = ({
52
69
  onAuthStateChange,
53
70
  bottomSheetRef
54
71
  }) => {
72
+ // Authentication state
73
+ const [user, setUser] = (0, _react.useState)(null);
74
+ const [minimalUser, setMinimalUser] = (0, _react.useState)(null);
75
+ const [sessions, setSessions] = (0, _react.useState)([]);
76
+ const [activeSessionId, setActiveSessionId] = (0, _react.useState)(null);
77
+ const [isLoading, setIsLoading] = (0, _react.useState)(true);
78
+ const [error, setError] = (0, _react.useState)(null);
55
79
  const [storage, setStorage] = (0, _react.useState)(null);
56
- const [authState, setAuthState] = (0, _react.useState)({
57
- isAuthenticated: false,
58
- accessToken: null,
59
- user: null,
60
- activeSessionId: null,
61
- sessions: [],
62
- isLoading: true,
63
- error: null
64
- });
65
80
 
66
- // Create AuthManager instance
67
- const authManager = (0, _react.useMemo)(() => {
68
- return new _AuthManager.AuthManager({
69
- oxyServices,
70
- onStateChange: newState => {
71
- setAuthState(newState);
72
- if (onAuthStateChange) {
73
- onAuthStateChange(newState.user);
74
- }
75
- }
76
- });
77
- }, [oxyServices, onAuthStateChange]);
81
+ // Storage keys (memoized to prevent infinite loops)
82
+ const keys = (0, _react.useMemo)(() => getSecureStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
78
83
 
79
84
  // Initialize storage
80
85
  (0, _react.useEffect)(() => {
@@ -84,102 +89,449 @@ const OxyContextProvider = ({
84
89
  setStorage(platformStorage);
85
90
  } catch (error) {
86
91
  console.error('Failed to initialize storage:', error);
87
- setAuthState(prev => ({
88
- ...prev,
89
- error: 'Failed to initialize storage',
90
- isLoading: false
91
- }));
92
+ setError('Failed to initialize storage');
92
93
  }
93
94
  };
94
95
  initStorage();
95
96
  }, []);
96
97
 
97
- // Initialize authentication
98
+ // Effect to initialize authentication state
98
99
  (0, _react.useEffect)(() => {
99
100
  const initAuth = async () => {
100
101
  if (!storage) return;
102
+ setIsLoading(true);
101
103
  try {
102
- const activeSessionId = await storage.getItem(`${storageKeyPrefix}_active_session_id`);
103
- await authManager.initialize(activeSessionId);
104
- } catch (error) {
105
- console.error('Auth initialization failed:', error);
106
- await storage.removeItem(`${storageKeyPrefix}_active_session_id`);
104
+ // Load stored sessions
105
+ const sessionsData = await storage.getItem(keys.sessions);
106
+ const storedActiveSessionId = await storage.getItem(keys.activeSessionId);
107
+ console.log('SecureAuth - sessionsData:', sessionsData);
108
+ console.log('SecureAuth - activeSessionId:', storedActiveSessionId);
109
+ if (sessionsData) {
110
+ const parsedSessions = JSON.parse(sessionsData);
111
+
112
+ // Migrate old session format to include user info
113
+ const migratedSessions = [];
114
+ let shouldUpdateStorage = false;
115
+ for (const session of parsedSessions) {
116
+ if (!session.userId || !session.username) {
117
+ // Session is missing user info, try to fetch it
118
+ try {
119
+ const sessionUser = await oxyServices.getUserBySession(session.sessionId);
120
+ migratedSessions.push({
121
+ ...session,
122
+ userId: sessionUser.id,
123
+ username: sessionUser.username
124
+ });
125
+ shouldUpdateStorage = true;
126
+ console.log(`Migrated session ${session.sessionId} for user ${sessionUser.username}`);
127
+ } catch (error) {
128
+ // Session might be invalid, skip it
129
+ console.log(`Removing invalid session ${session.sessionId}:`, error);
130
+ shouldUpdateStorage = true;
131
+ }
132
+ } else {
133
+ // Session already has user info
134
+ migratedSessions.push(session);
135
+ }
136
+ }
137
+
138
+ // Update storage if we made changes
139
+ if (shouldUpdateStorage) {
140
+ await saveSessionsToStorage(migratedSessions);
141
+ }
142
+ setSessions(migratedSessions);
143
+ if (storedActiveSessionId && migratedSessions.length > 0) {
144
+ const activeSession = migratedSessions.find(s => s.sessionId === storedActiveSessionId);
145
+ if (activeSession) {
146
+ console.log('SecureAuth - activeSession found:', activeSession);
147
+
148
+ // Validate session
149
+ try {
150
+ const validation = await oxyServices.validateSession(activeSession.sessionId);
151
+ if (validation.valid) {
152
+ console.log('SecureAuth - session validated successfully');
153
+ setActiveSessionId(activeSession.sessionId);
154
+
155
+ // Get access token for API calls
156
+ await oxyServices.getTokenBySession(activeSession.sessionId);
157
+
158
+ // Load full user data
159
+ const fullUser = await oxyServices.getUserBySession(activeSession.sessionId);
160
+ setUser(fullUser);
161
+ setMinimalUser({
162
+ id: fullUser.id,
163
+ username: fullUser.username,
164
+ avatar: fullUser.avatar
165
+ });
166
+ if (onAuthStateChange) {
167
+ onAuthStateChange(fullUser);
168
+ }
169
+ } else {
170
+ console.log('SecureAuth - session invalid, removing');
171
+ await removeInvalidSession(activeSession.sessionId);
172
+ }
173
+ } catch (error) {
174
+ console.error('SecureAuth - session validation error:', error);
175
+ await removeInvalidSession(activeSession.sessionId);
176
+ }
177
+ }
178
+ }
179
+ }
180
+ } catch (err) {
181
+ console.error('Secure auth initialization error:', err);
182
+ await clearAllStorage();
183
+ } finally {
184
+ setIsLoading(false);
107
185
  }
108
186
  };
109
187
  if (storage) {
110
188
  initAuth();
111
189
  }
112
- }, [storage, authManager, storageKeyPrefix]);
190
+ }, [storage, oxyServices, keys, onAuthStateChange]);
191
+
192
+ // Remove invalid session
193
+ const removeInvalidSession = (0, _react.useCallback)(async sessionId => {
194
+ const filteredSessions = sessions.filter(s => s.sessionId !== sessionId);
195
+ setSessions(filteredSessions);
196
+ await saveSessionsToStorage(filteredSessions);
197
+
198
+ // If there are other sessions, switch to the first one
199
+ if (filteredSessions.length > 0) {
200
+ await switchToSession(filteredSessions[0].sessionId);
201
+ } else {
202
+ // No valid sessions left
203
+ setActiveSessionId(null);
204
+ setUser(null);
205
+ setMinimalUser(null);
206
+ await storage?.removeItem(keys.activeSessionId);
207
+ if (onAuthStateChange) {
208
+ onAuthStateChange(null);
209
+ }
210
+ }
211
+ }, [sessions, storage, keys, onAuthStateChange]);
212
+
213
+ // Save sessions to storage
214
+ const saveSessionsToStorage = (0, _react.useCallback)(async sessionsList => {
215
+ if (!storage) return;
216
+ await storage.setItem(keys.sessions, JSON.stringify(sessionsList));
217
+ }, [storage, keys.sessions]);
113
218
 
114
- // Auth methods that integrate with storage
115
- const login = (0, _react.useCallback)(async (username, password, deviceName) => {
116
- await authManager.login(username, password, deviceName);
219
+ // Save active session ID to storage
220
+ const saveActiveSessionId = (0, _react.useCallback)(async sessionId => {
221
+ if (!storage) return;
222
+ await storage.setItem(keys.activeSessionId, sessionId);
223
+ }, [storage, keys.activeSessionId]);
117
224
 
118
- // Save session ID to storage
119
- const sessionId = authManager.getActiveSessionId();
120
- if (sessionId && storage) {
121
- await storage.setItem(`${storageKeyPrefix}_active_session_id`, sessionId);
225
+ // Clear all storage
226
+ const clearAllStorage = (0, _react.useCallback)(async () => {
227
+ if (!storage) return;
228
+ try {
229
+ await storage.removeItem(keys.sessions);
230
+ await storage.removeItem(keys.activeSessionId);
231
+ } catch (err) {
232
+ console.error('Clear secure storage error:', err);
122
233
  }
123
- return authManager.getCurrentUser();
124
- }, [authManager, storage, storageKeyPrefix]);
125
- const logout = (0, _react.useCallback)(async targetSessionId => {
126
- await authManager.logout(targetSessionId);
234
+ }, [storage, keys]);
127
235
 
128
- // Clear from storage if logging out current session
129
- if (!targetSessionId && storage) {
130
- await storage.removeItem(`${storageKeyPrefix}_active_session_id`);
236
+ // Switch to a different session
237
+ const switchToSession = (0, _react.useCallback)(async sessionId => {
238
+ try {
239
+ setIsLoading(true);
240
+
241
+ // Get access token for this session
242
+ await oxyServices.getTokenBySession(sessionId);
243
+
244
+ // Load full user data
245
+ const fullUser = await oxyServices.getUserBySession(sessionId);
246
+ setActiveSessionId(sessionId);
247
+ setUser(fullUser);
248
+ setMinimalUser({
249
+ id: fullUser.id,
250
+ username: fullUser.username,
251
+ avatar: fullUser.avatar
252
+ });
253
+ await saveActiveSessionId(sessionId);
254
+ if (onAuthStateChange) {
255
+ onAuthStateChange(fullUser);
256
+ }
257
+ } catch (error) {
258
+ console.error('Switch session error:', error);
259
+ setError('Failed to switch session');
260
+ } finally {
261
+ setIsLoading(false);
131
262
  }
132
- }, [authManager, storage, storageKeyPrefix]);
133
- const refreshSessions = (0, _react.useCallback)(async () => {
134
- await authManager.refreshSessions();
135
- }, [authManager]);
136
- const switchSession = (0, _react.useCallback)(async sessionId => {
137
- await authManager.switchSession(sessionId);
263
+ }, [oxyServices, onAuthStateChange, saveActiveSessionId]);
138
264
 
139
- // Update storage with new active session
140
- if (storage) {
141
- await storage.setItem(`${storageKeyPrefix}_active_session_id`, sessionId);
265
+ // Secure login method
266
+ const login = async (username, password, deviceName) => {
267
+ if (!storage) throw new Error('Storage not initialized');
268
+ setIsLoading(true);
269
+ setError(null);
270
+ try {
271
+ // Get device fingerprint for enhanced device identification
272
+ const deviceFingerprint = _deviceManager.DeviceManager.getDeviceFingerprint();
273
+
274
+ // Get or generate persistent device info
275
+ const deviceInfo = await _deviceManager.DeviceManager.getDeviceInfo();
276
+ console.log('SecureAuth - Using device fingerprint:', deviceFingerprint);
277
+ console.log('SecureAuth - Using device ID:', deviceInfo.deviceId);
278
+ const response = await oxyServices.secureLogin(username, password, deviceName || deviceInfo.deviceName || _deviceManager.DeviceManager.getDefaultDeviceName(), deviceFingerprint);
279
+
280
+ // Create client session object with user info for duplicate detection
281
+ const clientSession = {
282
+ sessionId: response.sessionId,
283
+ deviceId: response.deviceId,
284
+ expiresAt: response.expiresAt,
285
+ lastActive: new Date().toISOString(),
286
+ userId: response.user.id,
287
+ username: response.user.username
288
+ };
289
+
290
+ // Check if this user already has a session (prevent duplicate accounts)
291
+ const existingUserSessionIndex = sessions.findIndex(s => s.userId === response.user.id || s.username === response.user.username);
292
+ let updatedSessions;
293
+ if (existingUserSessionIndex !== -1) {
294
+ // User already has a session - replace it with the new one (reused session scenario)
295
+ const existingSession = sessions[existingUserSessionIndex];
296
+ updatedSessions = [...sessions];
297
+ updatedSessions[existingUserSessionIndex] = clientSession;
298
+ console.log(`Reusing/updating existing session for user ${response.user.username}. Previous session: ${existingSession.sessionId}, New session: ${response.sessionId}`);
299
+
300
+ // If the replaced session was the active one, update active session
301
+ if (activeSessionId === existingSession.sessionId) {
302
+ setActiveSessionId(response.sessionId);
303
+ await saveActiveSessionId(response.sessionId);
304
+ }
305
+ } else {
306
+ // Add new session for new user
307
+ updatedSessions = [...sessions, clientSession];
308
+ console.log(`Added new session for user ${response.user.username} on device ${response.deviceId}`);
309
+ }
310
+ setSessions(updatedSessions);
311
+ await saveSessionsToStorage(updatedSessions);
312
+
313
+ // Set as active session
314
+ setActiveSessionId(response.sessionId);
315
+ await saveActiveSessionId(response.sessionId);
316
+
317
+ // Get access token for API calls
318
+ await oxyServices.getTokenBySession(response.sessionId);
319
+
320
+ // Load full user data
321
+ const fullUser = await oxyServices.getUserBySession(response.sessionId);
322
+ setUser(fullUser);
323
+ setMinimalUser(response.user);
324
+ if (onAuthStateChange) {
325
+ onAuthStateChange(fullUser);
326
+ }
327
+ return fullUser;
328
+ } catch (error) {
329
+ setError(error.message || 'Login failed');
330
+ throw error;
331
+ } finally {
332
+ setIsLoading(false);
142
333
  }
143
- }, [authManager, storage, storageKeyPrefix]);
144
- const removeSession = (0, _react.useCallback)(async sessionId => {
145
- await authManager.removeSession(sessionId);
146
- }, [authManager]);
147
- const logoutAll = (0, _react.useCallback)(async () => {
148
- await authManager.logoutAll();
334
+ };
149
335
 
150
- // Clear from storage
151
- if (storage) {
152
- await storage.removeItem(`${storageKeyPrefix}_active_session_id`);
336
+ // Logout method
337
+ const logout = async targetSessionId => {
338
+ if (!activeSessionId) return;
339
+ try {
340
+ const sessionToLogout = targetSessionId || activeSessionId;
341
+ await oxyServices.logoutSecureSession(activeSessionId, sessionToLogout);
342
+
343
+ // Remove session from local storage
344
+ const filteredSessions = sessions.filter(s => s.sessionId !== sessionToLogout);
345
+ setSessions(filteredSessions);
346
+ await saveSessionsToStorage(filteredSessions);
347
+
348
+ // If logging out active session
349
+ if (sessionToLogout === activeSessionId) {
350
+ if (filteredSessions.length > 0) {
351
+ // Switch to another session
352
+ await switchToSession(filteredSessions[0].sessionId);
353
+ } else {
354
+ // No sessions left
355
+ setActiveSessionId(null);
356
+ setUser(null);
357
+ setMinimalUser(null);
358
+ await storage?.removeItem(keys.activeSessionId);
359
+ if (onAuthStateChange) {
360
+ onAuthStateChange(null);
361
+ }
362
+ }
363
+ }
364
+ } catch (error) {
365
+ console.error('Logout error:', error);
366
+ setError('Logout failed');
367
+ }
368
+ };
369
+
370
+ // Logout all sessions
371
+ const logoutAll = async () => {
372
+ console.log('logoutAll called with activeSessionId:', activeSessionId);
373
+ if (!activeSessionId) {
374
+ console.error('No active session ID found, cannot logout all');
375
+ setError('No active session found');
376
+ throw new Error('No active session found');
377
+ }
378
+ if (!oxyServices) {
379
+ console.error('OxyServices not initialized');
380
+ setError('Service not available');
381
+ throw new Error('Service not available');
382
+ }
383
+ try {
384
+ console.log('Calling oxyServices.logoutAllSecureSessions with sessionId:', activeSessionId);
385
+ await oxyServices.logoutAllSecureSessions(activeSessionId);
386
+ console.log('logoutAllSecureSessions completed successfully');
387
+
388
+ // Clear all local data
389
+ setSessions([]);
390
+ setActiveSessionId(null);
391
+ setUser(null);
392
+ setMinimalUser(null);
393
+ await clearAllStorage();
394
+ console.log('Local storage cleared');
395
+ if (onAuthStateChange) {
396
+ onAuthStateChange(null);
397
+ console.log('Auth state change callback called');
398
+ }
399
+ } catch (error) {
400
+ console.error('Logout all error:', error);
401
+ setError(`Logout all failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
402
+ throw error;
403
+ }
404
+ };
405
+
406
+ // Sign up method
407
+ const signUp = async (username, email, password) => {
408
+ if (!storage) throw new Error('Storage not initialized');
409
+ setIsLoading(true);
410
+ setError(null);
411
+ try {
412
+ // Create new account using the OxyServices signUp method
413
+ const response = await oxyServices.signUp(username, email, password);
414
+ console.log('SignUp successful:', response);
415
+
416
+ // Now log the user in securely to create a session
417
+ // This will handle the session creation and device registration
418
+ const user = await login(username, password);
419
+ return user;
420
+ } catch (error) {
421
+ setError(error.message || 'Sign up failed');
422
+ throw error;
423
+ } finally {
424
+ setIsLoading(false);
425
+ }
426
+ };
427
+
428
+ // Switch session method
429
+ const switchSession = async sessionId => {
430
+ await switchToSession(sessionId);
431
+ };
432
+
433
+ // Remove session method
434
+ const removeSession = async sessionId => {
435
+ await logout(sessionId);
436
+ };
437
+
438
+ // Refresh sessions method
439
+ const refreshSessions = async () => {
440
+ if (!activeSessionId) return;
441
+ try {
442
+ const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
443
+
444
+ // Update local sessions with server data
445
+ const updatedSessions = serverSessions.map(serverSession => ({
446
+ sessionId: serverSession.sessionId,
447
+ deviceId: serverSession.deviceId,
448
+ expiresAt: new Date().toISOString(),
449
+ // You might want to get this from server
450
+ lastActive: new Date().toISOString()
451
+ }));
452
+ setSessions(updatedSessions);
453
+ await saveSessionsToStorage(updatedSessions);
454
+ } catch (error) {
455
+ console.error('Refresh sessions error:', error);
153
456
  }
154
- }, [authManager, storage, storageKeyPrefix]);
155
- const signUp = (0, _react.useCallback)(async (username, email, password) => {
156
- await authManager.signUp(username, email, password);
457
+ };
157
458
 
158
- // Save session ID to storage after auto-login
159
- const sessionId = authManager.getActiveSessionId();
160
- if (sessionId && storage) {
161
- await storage.setItem(`${storageKeyPrefix}_active_session_id`, sessionId);
459
+ // Device management methods
460
+ const getDeviceSessions = async () => {
461
+ if (!activeSessionId) throw new Error('No active session');
462
+ try {
463
+ return await oxyServices.getDeviceSessions(activeSessionId);
464
+ } catch (error) {
465
+ console.error('Get device sessions error:', error);
466
+ throw error;
162
467
  }
163
- return authManager.getCurrentUser();
164
- }, [authManager, storage, storageKeyPrefix]);
468
+ };
469
+ const logoutAllDeviceSessions = async () => {
470
+ if (!activeSessionId) throw new Error('No active session');
471
+ try {
472
+ await oxyServices.logoutAllDeviceSessions(activeSessionId);
165
473
 
166
- // Bottom sheet controls
474
+ // Clear all local sessions since we logged out from all devices
475
+ setSessions([]);
476
+ setActiveSessionId(null);
477
+ setUser(null);
478
+ setMinimalUser(null);
479
+ await clearAllStorage();
480
+ if (onAuthStateChange) {
481
+ onAuthStateChange(null);
482
+ }
483
+ } catch (error) {
484
+ console.error('Logout all device sessions error:', error);
485
+ throw error;
486
+ }
487
+ };
488
+ const updateDeviceName = async deviceName => {
489
+ if (!activeSessionId) throw new Error('No active session');
490
+ try {
491
+ await oxyServices.updateDeviceName(activeSessionId, deviceName);
492
+
493
+ // Update local device info
494
+ await _deviceManager.DeviceManager.updateDeviceName(deviceName);
495
+ } catch (error) {
496
+ console.error('Update device name error:', error);
497
+ throw error;
498
+ }
499
+ };
500
+
501
+ // Bottom sheet control methods
167
502
  const showBottomSheet = (0, _react.useCallback)(screenOrConfig => {
503
+ console.log('showBottomSheet called with:', screenOrConfig);
168
504
  if (bottomSheetRef?.current) {
505
+ console.log('bottomSheetRef is available');
506
+
507
+ // First, show the bottom sheet
169
508
  if (bottomSheetRef.current.expand) {
509
+ console.log('Expanding bottom sheet');
170
510
  bottomSheetRef.current.expand();
171
511
  } else if (bottomSheetRef.current.present) {
512
+ console.log('Presenting bottom sheet');
172
513
  bottomSheetRef.current.present();
514
+ } else {
515
+ console.warn('No expand or present method available on bottomSheetRef');
173
516
  }
517
+
518
+ // Then navigate to the specified screen if provided
174
519
  if (screenOrConfig) {
520
+ // Add a small delay to ensure the bottom sheet is opened first
175
521
  setTimeout(() => {
176
522
  if (typeof screenOrConfig === 'string') {
523
+ // Simple screen name
524
+ console.log('Navigating to screen:', screenOrConfig);
177
525
  bottomSheetRef.current?._navigateToScreen?.(screenOrConfig);
178
526
  } else {
527
+ // Screen with props
528
+ console.log('Navigating to screen with props:', screenOrConfig.screen, screenOrConfig.props);
179
529
  bottomSheetRef.current?._navigateToScreen?.(screenOrConfig.screen, screenOrConfig.props);
180
530
  }
181
531
  }, 100);
182
532
  }
533
+ } else {
534
+ console.warn('bottomSheetRef is not available');
183
535
  }
184
536
  }, [bottomSheetRef]);
185
537
  const hideBottomSheet = (0, _react.useCallback)(() => {
@@ -187,22 +539,48 @@ const OxyContextProvider = ({
187
539
  bottomSheetRef.current.dismiss?.();
188
540
  }
189
541
  }, [bottomSheetRef]);
542
+
543
+ // API URL configuration
544
+ const setApiUrl = (0, _react.useCallback)(url => {
545
+ try {
546
+ oxyServices.setBaseURL(url);
547
+ } catch (error) {
548
+ console.error('Failed to update API URL:', error);
549
+ setError(`Failed to update API URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
550
+ }
551
+ }, [oxyServices]);
552
+
553
+ // Compute comprehensive authentication status
554
+ // This is the single source of truth for authentication across the entire app
555
+ const isAuthenticated = (0, _react.useMemo)(() => {
556
+ // User is authenticated if:
557
+ // 1. We have a full user object loaded, OR
558
+ // 2. We have an active session (token will be fetched on-demand)
559
+ // This covers both the loaded state and the loading-but-authenticated state
560
+ return !!user || !!activeSessionId;
561
+ }, [user, activeSessionId]);
562
+
563
+ // Context value
190
564
  const contextValue = {
191
- isAuthenticated: authState.isAuthenticated,
192
- user: authState.user,
193
- isLoading: authState.isLoading,
194
- error: authState.error,
195
- sessions: authState.sessions,
196
- activeSessionId: authState.activeSessionId,
565
+ user,
566
+ minimalUser,
567
+ sessions,
568
+ activeSessionId,
569
+ isAuthenticated,
570
+ isLoading,
571
+ error,
197
572
  login,
198
573
  logout,
574
+ logoutAll,
199
575
  signUp,
200
- refreshSessions,
201
576
  switchSession,
202
577
  removeSession,
203
- logoutAll,
578
+ refreshSessions,
579
+ getDeviceSessions,
580
+ logoutAllDeviceSessions,
581
+ updateDeviceName,
204
582
  oxyServices,
205
- authManager,
583
+ setApiUrl,
206
584
  bottomSheetRef,
207
585
  showBottomSheet,
208
586
  hideBottomSheet
@@ -212,6 +590,8 @@ const OxyContextProvider = ({
212
590
  children: children
213
591
  });
214
592
  };
593
+
594
+ // Hook to use the context
215
595
  exports.OxyContextProvider = OxyContextProvider;
216
596
  const useOxy = () => {
217
597
  const context = (0, _react.useContext)(OxyContext);