@oxyhq/services 5.2.2 → 5.2.4

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 (64) hide show
  1. package/lib/commonjs/core/index.js +117 -0
  2. package/lib/commonjs/core/index.js.map +1 -1
  3. package/lib/commonjs/models/secureSession.js +2 -0
  4. package/lib/commonjs/models/secureSession.js.map +1 -0
  5. package/lib/commonjs/ui/context/LegacyOxyContext.js +643 -0
  6. package/lib/commonjs/ui/context/LegacyOxyContext.js.map +1 -0
  7. package/lib/commonjs/ui/context/OxyContext.js +215 -450
  8. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  9. package/lib/commonjs/ui/context/SecureOxyContext.js +408 -0
  10. package/lib/commonjs/ui/context/SecureOxyContext.js.map +1 -0
  11. package/lib/commonjs/ui/screens/AccountCenterScreen.js +3 -3
  12. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  13. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +31 -30
  14. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  15. package/lib/commonjs/ui/screens/AppInfoScreen.js +4 -4
  16. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  17. package/lib/commonjs/ui/screens/SessionManagementScreen.js +34 -34
  18. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  19. package/lib/commonjs/ui/screens/SignInScreen.js +2 -2
  20. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  21. package/lib/module/core/index.js +117 -0
  22. package/lib/module/core/index.js.map +1 -1
  23. package/lib/module/models/secureSession.js +2 -0
  24. package/lib/module/models/secureSession.js.map +1 -0
  25. package/lib/module/ui/context/LegacyOxyContext.js +639 -0
  26. package/lib/module/ui/context/LegacyOxyContext.js.map +1 -0
  27. package/lib/module/ui/context/OxyContext.js +214 -450
  28. package/lib/module/ui/context/OxyContext.js.map +1 -1
  29. package/lib/module/ui/context/SecureOxyContext.js +403 -0
  30. package/lib/module/ui/context/SecureOxyContext.js.map +1 -0
  31. package/lib/module/ui/screens/AccountCenterScreen.js +3 -3
  32. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  33. package/lib/module/ui/screens/AccountSwitcherScreen.js +31 -30
  34. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  35. package/lib/module/ui/screens/AppInfoScreen.js +4 -4
  36. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  37. package/lib/module/ui/screens/SessionManagementScreen.js +34 -34
  38. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  39. package/lib/module/ui/screens/SignInScreen.js +2 -2
  40. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  41. package/lib/typescript/core/index.d.ts +51 -0
  42. package/lib/typescript/core/index.d.ts.map +1 -1
  43. package/lib/typescript/models/secureSession.d.ts +25 -0
  44. package/lib/typescript/models/secureSession.d.ts.map +1 -0
  45. package/lib/typescript/ui/context/LegacyOxyContext.d.ts +40 -0
  46. package/lib/typescript/ui/context/LegacyOxyContext.d.ts.map +1 -0
  47. package/lib/typescript/ui/context/OxyContext.d.ts +11 -12
  48. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  49. package/lib/typescript/ui/context/SecureOxyContext.d.ts +39 -0
  50. package/lib/typescript/ui/context/SecureOxyContext.d.ts.map +1 -0
  51. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  52. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/core/index.ts +117 -0
  55. package/src/index.ts +2 -2
  56. package/src/models/secureSession.ts +27 -0
  57. package/src/ui/context/LegacyOxyContext.tsx +735 -0
  58. package/src/ui/context/OxyContext.tsx +412 -674
  59. package/src/ui/context/SecureOxyContext.tsx +473 -0
  60. package/src/ui/screens/AccountCenterScreen.tsx +4 -4
  61. package/src/ui/screens/AccountSwitcherScreen.tsx +36 -34
  62. package/src/ui/screens/AppInfoScreen.tsx +3 -3
  63. package/src/ui/screens/SessionManagementScreen.tsx +31 -35
  64. package/src/ui/screens/SignInScreen.tsx +2 -2
@@ -0,0 +1,473 @@
1
+ import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode, useMemo } from 'react';
2
+ import { OxyServices } from '../../core';
3
+ import { User } from '../../models/interfaces';
4
+ import { SecureLoginResponse, SecureClientSession, MinimalUserData } from '../../models/secureSession';
5
+
6
+ // Define the secure context shape
7
+ export interface SecureOxyContextState {
8
+ // Authentication state
9
+ user: User | null; // Current active user (loaded from server)
10
+ minimalUser: MinimalUserData | null; // Minimal user data for UI
11
+ sessions: SecureClientSession[]; // All active sessions
12
+ activeSessionId: string | null;
13
+ isAuthenticated: boolean;
14
+ isLoading: boolean;
15
+ error: string | null;
16
+
17
+ // Auth methods
18
+ secureLogin: (username: string, password: string, deviceName?: string) => Promise<User>;
19
+ logout: (targetSessionId?: string) => Promise<void>;
20
+ logoutAll: () => Promise<void>;
21
+ signUp: (username: string, email: string, password: string) => Promise<User>;
22
+
23
+ // Multi-session methods
24
+ switchSession: (sessionId: string) => Promise<void>;
25
+ removeSession: (sessionId: string) => Promise<void>;
26
+ refreshSessions: () => Promise<void>;
27
+
28
+ // Access to services
29
+ oxyServices: OxyServices;
30
+ bottomSheetRef?: React.RefObject<any>;
31
+
32
+ // Methods to directly control the bottom sheet
33
+ showBottomSheet?: (screenOrConfig?: string | { screen: string; props?: Record<string, any> }) => void;
34
+ hideBottomSheet?: () => void;
35
+ }
36
+
37
+ // Create the context with default values
38
+ const SecureOxyContext = createContext<SecureOxyContextState | null>(null);
39
+
40
+ // Props for the SecureOxyContextProvider
41
+ export interface SecureOxyContextProviderProps {
42
+ children: ReactNode;
43
+ oxyServices: OxyServices;
44
+ storageKeyPrefix?: string;
45
+ onAuthStateChange?: (user: User | null) => void;
46
+ bottomSheetRef?: React.RefObject<any>;
47
+ }
48
+
49
+ // Platform storage implementation
50
+ interface StorageInterface {
51
+ getItem: (key: string) => Promise<string | null>;
52
+ setItem: (key: string, value: string) => Promise<void>;
53
+ removeItem: (key: string) => Promise<void>;
54
+ clear: () => Promise<void>;
55
+ }
56
+
57
+ // Web localStorage implementation
58
+ class WebStorage implements StorageInterface {
59
+ async getItem(key: string): Promise<string | null> {
60
+ return localStorage.getItem(key);
61
+ }
62
+
63
+ async setItem(key: string, value: string): Promise<void> {
64
+ localStorage.setItem(key, value);
65
+ }
66
+
67
+ async removeItem(key: string): Promise<void> {
68
+ localStorage.removeItem(key);
69
+ }
70
+
71
+ async clear(): Promise<void> {
72
+ localStorage.clear();
73
+ }
74
+ }
75
+
76
+ // React Native AsyncStorage implementation
77
+ let AsyncStorage: StorageInterface;
78
+
79
+ // Determine the platform and set up storage
80
+ const isReactNative = (): boolean => {
81
+ return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
82
+ };
83
+
84
+ // Get appropriate storage for the platform
85
+ const getStorage = async (): Promise<StorageInterface> => {
86
+ if (isReactNative()) {
87
+ if (!AsyncStorage) {
88
+ try {
89
+ const asyncStorageModule = await import('@react-native-async-storage/async-storage');
90
+ AsyncStorage = asyncStorageModule.default;
91
+ } catch (error) {
92
+ console.error('Failed to import AsyncStorage:', error);
93
+ throw new Error('AsyncStorage is required in React Native environment');
94
+ }
95
+ }
96
+ return AsyncStorage;
97
+ }
98
+
99
+ return new WebStorage();
100
+ };
101
+
102
+ // Storage keys for secure sessions
103
+ const getSecureStorageKeys = (prefix = 'oxy_secure') => ({
104
+ sessions: `${prefix}_sessions`, // Array of SecureClientSession objects
105
+ activeSessionId: `${prefix}_active_session_id`, // ID of currently active session
106
+ });
107
+
108
+ export const SecureOxyContextProvider: React.FC<SecureOxyContextProviderProps> = ({
109
+ children,
110
+ oxyServices,
111
+ storageKeyPrefix = 'oxy_secure',
112
+ onAuthStateChange,
113
+ bottomSheetRef,
114
+ }) => {
115
+ // Authentication state
116
+ const [user, setUser] = useState<User | null>(null);
117
+ const [minimalUser, setMinimalUser] = useState<MinimalUserData | null>(null);
118
+ const [sessions, setSessions] = useState<SecureClientSession[]>([]);
119
+ const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
120
+ const [isLoading, setIsLoading] = useState(true);
121
+ const [error, setError] = useState<string | null>(null);
122
+ const [storage, setStorage] = useState<StorageInterface | null>(null);
123
+
124
+ // Storage keys (memoized to prevent infinite loops)
125
+ const keys = useMemo(() => getSecureStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
126
+
127
+ // Initialize storage
128
+ useEffect(() => {
129
+ const initStorage = async () => {
130
+ try {
131
+ const platformStorage = await getStorage();
132
+ setStorage(platformStorage);
133
+ } catch (error) {
134
+ console.error('Failed to initialize storage:', error);
135
+ setError('Failed to initialize storage');
136
+ }
137
+ };
138
+
139
+ initStorage();
140
+ }, []);
141
+
142
+ // Effect to initialize authentication state
143
+ useEffect(() => {
144
+ const initAuth = async () => {
145
+ if (!storage) return;
146
+
147
+ setIsLoading(true);
148
+ try {
149
+ // Load stored sessions
150
+ const sessionsData = await storage.getItem(keys.sessions);
151
+ const storedActiveSessionId = await storage.getItem(keys.activeSessionId);
152
+
153
+ console.log('SecureAuth - sessionsData:', sessionsData);
154
+ console.log('SecureAuth - activeSessionId:', storedActiveSessionId);
155
+
156
+ if (sessionsData) {
157
+ const parsedSessions: SecureClientSession[] = JSON.parse(sessionsData);
158
+ setSessions(parsedSessions);
159
+
160
+ if (storedActiveSessionId && parsedSessions.length > 0) {
161
+ const activeSession = parsedSessions.find(s => s.sessionId === storedActiveSessionId);
162
+
163
+ if (activeSession) {
164
+ console.log('SecureAuth - activeSession found:', activeSession);
165
+
166
+ // Validate session
167
+ try {
168
+ const validation = await oxyServices.validateSession(activeSession.sessionId);
169
+
170
+ if (validation.valid) {
171
+ console.log('SecureAuth - session validated successfully');
172
+ setActiveSessionId(activeSession.sessionId);
173
+
174
+ // Get access token for API calls
175
+ await oxyServices.getTokenBySession(activeSession.sessionId);
176
+
177
+ // Load full user data
178
+ const fullUser = await oxyServices.getUserBySession(activeSession.sessionId);
179
+ setUser(fullUser);
180
+ setMinimalUser({
181
+ id: fullUser.id,
182
+ username: fullUser.username,
183
+ avatar: fullUser.avatar
184
+ });
185
+
186
+ if (onAuthStateChange) {
187
+ onAuthStateChange(fullUser);
188
+ }
189
+ } else {
190
+ console.log('SecureAuth - session invalid, removing');
191
+ await removeInvalidSession(activeSession.sessionId);
192
+ }
193
+ } catch (error) {
194
+ console.error('SecureAuth - session validation error:', error);
195
+ await removeInvalidSession(activeSession.sessionId);
196
+ }
197
+ }
198
+ }
199
+ }
200
+ } catch (err) {
201
+ console.error('Secure auth initialization error:', err);
202
+ await clearAllStorage();
203
+ } finally {
204
+ setIsLoading(false);
205
+ }
206
+ };
207
+
208
+ if (storage) {
209
+ initAuth();
210
+ }
211
+ }, [storage, oxyServices, keys, onAuthStateChange]);
212
+
213
+ // Remove invalid session
214
+ const removeInvalidSession = useCallback(async (sessionId: string): Promise<void> => {
215
+ const filteredSessions = sessions.filter(s => s.sessionId !== sessionId);
216
+ setSessions(filteredSessions);
217
+ await saveSessionsToStorage(filteredSessions);
218
+
219
+ // If there are other sessions, switch to the first one
220
+ if (filteredSessions.length > 0) {
221
+ await switchToSession(filteredSessions[0].sessionId);
222
+ } else {
223
+ // No valid sessions left
224
+ setActiveSessionId(null);
225
+ setUser(null);
226
+ setMinimalUser(null);
227
+ await storage?.removeItem(keys.activeSessionId);
228
+
229
+ if (onAuthStateChange) {
230
+ onAuthStateChange(null);
231
+ }
232
+ }
233
+ }, [sessions, storage, keys, onAuthStateChange]);
234
+
235
+ // Save sessions to storage
236
+ const saveSessionsToStorage = useCallback(async (sessionsList: SecureClientSession[]): Promise<void> => {
237
+ if (!storage) return;
238
+ await storage.setItem(keys.sessions, JSON.stringify(sessionsList));
239
+ }, [storage, keys.sessions]);
240
+
241
+ // Save active session ID to storage
242
+ const saveActiveSessionId = useCallback(async (sessionId: string): Promise<void> => {
243
+ if (!storage) return;
244
+ await storage.setItem(keys.activeSessionId, sessionId);
245
+ }, [storage, keys.activeSessionId]);
246
+
247
+ // Clear all storage
248
+ const clearAllStorage = useCallback(async (): Promise<void> => {
249
+ if (!storage) return;
250
+ try {
251
+ await storage.removeItem(keys.sessions);
252
+ await storage.removeItem(keys.activeSessionId);
253
+ } catch (err) {
254
+ console.error('Clear secure storage error:', err);
255
+ }
256
+ }, [storage, keys]);
257
+
258
+ // Switch to a different session
259
+ const switchToSession = useCallback(async (sessionId: string): Promise<void> => {
260
+ try {
261
+ setIsLoading(true);
262
+
263
+ // Get access token for this session
264
+ await oxyServices.getTokenBySession(sessionId);
265
+
266
+ // Load full user data
267
+ const fullUser = await oxyServices.getUserBySession(sessionId);
268
+
269
+ setActiveSessionId(sessionId);
270
+ setUser(fullUser);
271
+ setMinimalUser({
272
+ id: fullUser.id,
273
+ username: fullUser.username,
274
+ avatar: fullUser.avatar
275
+ });
276
+
277
+ await saveActiveSessionId(sessionId);
278
+
279
+ if (onAuthStateChange) {
280
+ onAuthStateChange(fullUser);
281
+ }
282
+ } catch (error) {
283
+ console.error('Switch session error:', error);
284
+ setError('Failed to switch session');
285
+ } finally {
286
+ setIsLoading(false);
287
+ }
288
+ }, [oxyServices, onAuthStateChange, saveActiveSessionId]);
289
+
290
+ // Secure login method
291
+ const secureLogin = async (username: string, password: string, deviceName?: string): Promise<User> => {
292
+ if (!storage) throw new Error('Storage not initialized');
293
+
294
+ setIsLoading(true);
295
+ setError(null);
296
+
297
+ try {
298
+ const response: SecureLoginResponse = await oxyServices.secureLogin(username, password, deviceName);
299
+
300
+ // Create client session object
301
+ const clientSession: SecureClientSession = {
302
+ sessionId: response.sessionId,
303
+ deviceId: response.deviceId,
304
+ expiresAt: response.expiresAt,
305
+ lastActive: new Date().toISOString()
306
+ };
307
+
308
+ // Add to sessions list
309
+ const updatedSessions = [...sessions, clientSession];
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
+
325
+ if (onAuthStateChange) {
326
+ onAuthStateChange(fullUser);
327
+ }
328
+
329
+ return fullUser;
330
+ } catch (error: any) {
331
+ setError(error.message || 'Login failed');
332
+ throw error;
333
+ } finally {
334
+ setIsLoading(false);
335
+ }
336
+ };
337
+
338
+ // Logout method
339
+ const logout = async (targetSessionId?: string): Promise<void> => {
340
+ if (!activeSessionId) return;
341
+
342
+ try {
343
+ const sessionToLogout = targetSessionId || activeSessionId;
344
+ await oxyServices.logoutSecureSession(activeSessionId, sessionToLogout);
345
+
346
+ // Remove session from local storage
347
+ const filteredSessions = sessions.filter(s => s.sessionId !== sessionToLogout);
348
+ setSessions(filteredSessions);
349
+ await saveSessionsToStorage(filteredSessions);
350
+
351
+ // If logging out active session
352
+ if (sessionToLogout === activeSessionId) {
353
+ if (filteredSessions.length > 0) {
354
+ // Switch to another session
355
+ await switchToSession(filteredSessions[0].sessionId);
356
+ } else {
357
+ // No sessions left
358
+ setActiveSessionId(null);
359
+ setUser(null);
360
+ setMinimalUser(null);
361
+ await storage?.removeItem(keys.activeSessionId);
362
+
363
+ if (onAuthStateChange) {
364
+ onAuthStateChange(null);
365
+ }
366
+ }
367
+ }
368
+ } catch (error) {
369
+ console.error('Logout error:', error);
370
+ setError('Logout failed');
371
+ }
372
+ };
373
+
374
+ // Logout all sessions
375
+ const logoutAll = async (): Promise<void> => {
376
+ if (!activeSessionId) return;
377
+
378
+ try {
379
+ await oxyServices.logoutAllSecureSessions(activeSessionId);
380
+
381
+ // Clear all local data
382
+ setSessions([]);
383
+ setActiveSessionId(null);
384
+ setUser(null);
385
+ setMinimalUser(null);
386
+ await clearAllStorage();
387
+
388
+ if (onAuthStateChange) {
389
+ onAuthStateChange(null);
390
+ }
391
+ } catch (error) {
392
+ console.error('Logout all error:', error);
393
+ setError('Logout all failed');
394
+ }
395
+ };
396
+
397
+ // Sign up method (placeholder - you can implement based on your needs)
398
+ const signUp = async (username: string, email: string, password: string): Promise<User> => {
399
+ // Implement sign up logic similar to secureLogin
400
+ throw new Error('Sign up not implemented yet');
401
+ };
402
+
403
+ // Switch session method
404
+ const switchSession = async (sessionId: string): Promise<void> => {
405
+ await switchToSession(sessionId);
406
+ };
407
+
408
+ // Remove session method
409
+ const removeSession = async (sessionId: string): Promise<void> => {
410
+ await logout(sessionId);
411
+ };
412
+
413
+ // Refresh sessions method
414
+ const refreshSessions = async (): Promise<void> => {
415
+ if (!activeSessionId) return;
416
+
417
+ try {
418
+ const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
419
+
420
+ // Update local sessions with server data
421
+ const updatedSessions: SecureClientSession[] = serverSessions.map(serverSession => ({
422
+ sessionId: serverSession.sessionId,
423
+ deviceId: serverSession.deviceId,
424
+ expiresAt: new Date().toISOString(), // You might want to get this from server
425
+ lastActive: new Date().toISOString()
426
+ }));
427
+
428
+ setSessions(updatedSessions);
429
+ await saveSessionsToStorage(updatedSessions);
430
+ } catch (error) {
431
+ console.error('Refresh sessions error:', error);
432
+ }
433
+ };
434
+
435
+ // Context value
436
+ const contextValue: SecureOxyContextState = {
437
+ user,
438
+ minimalUser,
439
+ sessions,
440
+ activeSessionId,
441
+ isAuthenticated: !!user,
442
+ isLoading,
443
+ error,
444
+ secureLogin,
445
+ logout,
446
+ logoutAll,
447
+ signUp,
448
+ switchSession,
449
+ removeSession,
450
+ refreshSessions,
451
+ oxyServices,
452
+ bottomSheetRef,
453
+ showBottomSheet: undefined, // Implement as needed
454
+ hideBottomSheet: undefined, // Implement as needed
455
+ };
456
+
457
+ return (
458
+ <SecureOxyContext.Provider value={contextValue}>
459
+ {children}
460
+ </SecureOxyContext.Provider>
461
+ );
462
+ };
463
+
464
+ // Hook to use the secure context
465
+ export const useSecureOxyContext = (): SecureOxyContextState => {
466
+ const context = useContext(SecureOxyContext);
467
+ if (!context) {
468
+ throw new Error('useSecureOxyContext must be used within a SecureOxyContextProvider');
469
+ }
470
+ return context;
471
+ };
472
+
473
+ export default SecureOxyContext;
@@ -20,7 +20,7 @@ const AccountCenterScreen: React.FC<BaseScreenProps> = ({
20
20
  theme,
21
21
  navigate,
22
22
  }) => {
23
- const { user, logout, isLoading, users } = useOxy();
23
+ const { user, logout, isLoading, sessions } = useOxy();
24
24
 
25
25
  const isDarkTheme = theme === 'dark';
26
26
  const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
@@ -94,14 +94,14 @@ const AccountCenterScreen: React.FC<BaseScreenProps> = ({
94
94
  </View>
95
95
 
96
96
  <View style={styles.actionsContainer}>
97
- {/* Show Account Switcher if multiple users exist */}
98
- {users && users.length > 1 && (
97
+ {/* Show Account Switcher if multiple sessions exist */}
98
+ {sessions && sessions.length > 1 && (
99
99
  <TouchableOpacity
100
100
  style={[styles.actionButton, { borderColor }]}
101
101
  onPress={() => navigate('AccountSwitcher')}
102
102
  >
103
103
  <Text style={[styles.actionButtonText, { color: textColor }]}>
104
- Switch Account ({users.length} accounts)
104
+ Switch Account ({sessions.length} accounts)
105
105
  </Text>
106
106
  </TouchableOpacity>
107
107
  )}
@@ -10,7 +10,8 @@ import {
10
10
  Platform,
11
11
  } from 'react-native';
12
12
  import { BaseScreenProps } from '../navigation/types';
13
- import { useOxy, AuthenticatedUser } from '../context/OxyContext';
13
+ import { useOxy } from '../context/OxyContext';
14
+ import { SecureClientSession } from '../../models/secureSession';
14
15
  import { fontFamilies } from '../styles/fonts';
15
16
 
16
17
  const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
@@ -21,9 +22,10 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
21
22
  }) => {
22
23
  const {
23
24
  user,
24
- users,
25
- switchUser,
26
- removeUser,
25
+ sessions,
26
+ activeSessionId,
27
+ switchSession,
28
+ removeSession,
27
29
  logoutAll,
28
30
  isLoading
29
31
  } = useOxy();
@@ -40,31 +42,31 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
40
42
  const dangerColor = '#D32F2F';
41
43
  const successColor = '#2E7D32';
42
44
 
43
- const handleSwitchUser = async (userId: string) => {
44
- if (userId === user?._id) return; // Already active user
45
+ const handleSwitchSession = async (sessionId: string) => {
46
+ if (sessionId === user?.sessionId) return; // Already active session
45
47
 
46
- setSwitchingToUserId(userId);
48
+ setSwitchingToUserId(sessionId);
47
49
  try {
48
- await switchUser(userId);
50
+ await switchSession(sessionId);
49
51
  Alert.alert('Success', 'Account switched successfully!');
50
52
  if (onClose) {
51
53
  onClose();
52
54
  }
53
55
  } catch (error) {
54
- console.error('Switch user failed:', error);
56
+ console.error('Switch session failed:', error);
55
57
  Alert.alert('Switch Failed', 'There was a problem switching accounts. Please try again.');
56
58
  } finally {
57
59
  setSwitchingToUserId(null);
58
60
  }
59
61
  };
60
62
 
61
- const handleRemoveUser = async (userId: string) => {
62
- const userToRemove = users.find(u => u._id === userId);
63
- if (!userToRemove) return;
63
+ const handleRemoveSession = async (sessionId: string) => {
64
+ const sessionToRemove = sessions.find(s => s.sessionId === sessionId);
65
+ if (!sessionToRemove) return;
64
66
 
65
67
  Alert.alert(
66
68
  'Remove Account',
67
- `Are you sure you want to remove ${userToRemove.username} from this device? You'll need to sign in again to access this account.`,
69
+ `Are you sure you want to remove this session from this device? You'll need to sign in again to access this account.`,
68
70
  [
69
71
  {
70
72
  text: 'Cancel',
@@ -74,12 +76,12 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
74
76
  text: 'Remove',
75
77
  style: 'destructive',
76
78
  onPress: async () => {
77
- setRemovingUserId(userId);
79
+ setRemovingUserId(sessionId);
78
80
  try {
79
- await removeUser(userId);
81
+ await removeSession(sessionId);
80
82
  Alert.alert('Success', 'Account removed successfully!');
81
83
  } catch (error) {
82
- console.error('Remove user failed:', error);
84
+ console.error('Remove session failed:', error);
83
85
  Alert.alert('Remove Failed', 'There was a problem removing the account. Please try again.');
84
86
  } finally {
85
87
  setRemovingUserId(null);
@@ -121,14 +123,14 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
121
123
  );
122
124
  };
123
125
 
124
- const renderUserItem = (userItem: AuthenticatedUser) => {
125
- const isActive = userItem._id === user?._id;
126
- const isSwitching = switchingToUserId === userItem._id;
127
- const isRemoving = removingUserId === userItem._id;
126
+ const renderSessionItem = (session: SecureClientSession) => {
127
+ const isActive = session.sessionId === activeSessionId;
128
+ const isSwitching = switchingToUserId === session.sessionId;
129
+ const isRemoving = removingUserId === session.sessionId;
128
130
 
129
131
  return (
130
132
  <View
131
- key={userItem._id}
133
+ key={session.sessionId}
132
134
  style={[
133
135
  styles.userItem,
134
136
  {
@@ -138,12 +140,12 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
138
140
  ]}
139
141
  >
140
142
  <View style={styles.userInfo}>
141
- <Text style={[styles.username, { color: textColor }]}>{userItem.username}</Text>
142
- {userItem.email && (
143
- <Text style={[styles.email, { color: isDarkTheme ? '#BBBBBB' : '#666666' }]}>
144
- {userItem.email}
145
- </Text>
146
- )}
143
+ <Text style={[styles.username, { color: textColor }]}>
144
+ {isActive ? user?.username || 'Current Account' : 'Account Session'}
145
+ </Text>
146
+ <Text style={[styles.email, { color: isDarkTheme ? '#BBBBBB' : '#666666' }]}>
147
+ Last active: {new Date(session.lastActive).toLocaleDateString()}
148
+ </Text>
147
149
  {isActive && (
148
150
  <View style={[styles.activeBadge, { backgroundColor: successColor }]}>
149
151
  <Text style={styles.activeBadgeText}>Active</Text>
@@ -155,7 +157,7 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
155
157
  {!isActive && (
156
158
  <TouchableOpacity
157
159
  style={[styles.switchButton, { borderColor: primaryColor }]}
158
- onPress={() => handleSwitchUser(userItem._id)}
160
+ onPress={() => handleSwitchSession(session.sessionId)}
159
161
  disabled={isSwitching || isRemoving}
160
162
  >
161
163
  {isSwitching ? (
@@ -168,8 +170,8 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
168
170
 
169
171
  <TouchableOpacity
170
172
  style={[styles.removeButton, { borderColor: dangerColor }]}
171
- onPress={() => handleRemoveUser(userItem._id)}
172
- disabled={isSwitching || isRemoving || users.length === 1}
173
+ onPress={() => handleRemoveSession(session.sessionId)}
174
+ disabled={isSwitching || isRemoving || sessions.length === 1}
173
175
  >
174
176
  {isRemoving ? (
175
177
  <ActivityIndicator color={dangerColor} size="small" />
@@ -201,10 +203,10 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
201
203
 
202
204
  <View style={styles.usersContainer}>
203
205
  <Text style={[styles.sectionTitle, { color: textColor }]}>
204
- Accounts ({users.length})
206
+ Sessions ({sessions.length})
205
207
  </Text>
206
208
 
207
- {users.map(renderUserItem)}
209
+ {sessions.map(renderSessionItem)}
208
210
  </View>
209
211
 
210
212
  <View style={styles.actionsContainer}>
@@ -217,7 +219,7 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
217
219
  </Text>
218
220
  </TouchableOpacity>
219
221
 
220
- {users.length > 1 && (
222
+ {sessions.length > 1 && (
221
223
  <TouchableOpacity
222
224
  style={[styles.dangerButton, { backgroundColor: isDarkTheme ? '#300000' : '#FFEBEE' }]}
223
225
  onPress={handleLogoutAll}
@@ -227,7 +229,7 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
227
229
  <ActivityIndicator color={dangerColor} size="small" />
228
230
  ) : (
229
231
  <Text style={[styles.dangerButtonText, { color: dangerColor }]}>
230
- Sign Out All Accounts
232
+ Sign Out All Sessions
231
233
  </Text>
232
234
  )}
233
235
  </TouchableOpacity>