@oxyhq/services 5.3.1 → 5.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ui/components/OxyProvider.js +18 -6
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +24 -11
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/navigation/OxyRouter.js +41 -3
- package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +70 -26
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ModernAccountSwitcherScreen.js +532 -0
- package/lib/commonjs/ui/screens/ModernAccountSwitcherScreen.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +18 -6
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +24 -11
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/navigation/OxyRouter.js +41 -3
- package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +72 -28
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/ModernAccountSwitcherScreen.js +527 -0
- package/lib/module/ui/screens/ModernAccountSwitcherScreen.js.map +1 -0
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/types.d.ts +1 -0
- package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/ModernAccountSwitcherScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/ModernAccountSwitcherScreen.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/ui/components/OxyProvider.tsx +17 -5
- package/src/ui/context/OxyContext.tsx +25 -11
- package/src/ui/navigation/OxyRouter.tsx +42 -2
- package/src/ui/navigation/types.ts +1 -0
- package/src/ui/screens/AccountSwitcherScreen.tsx +85 -25
- package/src/ui/screens/ModernAccountSwitcherScreen.tsx +552 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/ui/navigation/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAChD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/ui/navigation/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAChD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACjD,aAAa,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;CACxG;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;IAEzB;;;OAGG;IACH,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,eAAe,CAAC;IAEtD;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAEvC;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAEzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAEtC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE;QACb;;WAEG;QACH,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB;;WAEG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;WAEG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IAEF;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAEhD;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountSwitcherScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/AccountSwitcherScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AccountSwitcherScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/AccountSwitcherScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAanD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAWtD,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAoRpD,CAAC;AA6IF,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModernAccountSwitcherScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/ModernAccountSwitcherScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAanD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAWtD,QAAA,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAmV1D,CAAC;AA4LF,eAAe,2BAA2B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.3",
|
|
4
4
|
"description": "Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀",
|
|
5
5
|
"main": "lib/commonjs/node/index.js",
|
|
6
6
|
"module": "lib/module/node/index.js",
|
|
@@ -95,6 +95,9 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
|
|
|
95
95
|
}) => {
|
|
96
96
|
// Use the internal ref (which is passed as a prop from OxyProvider)
|
|
97
97
|
const modalRef = useRef<BottomSheetModal>(null);
|
|
98
|
+
|
|
99
|
+
// Create a ref to store the navigation function from OxyRouter
|
|
100
|
+
const navigationRef = useRef<((screen: string, props?: Record<string, any>) => void) | null>(null);
|
|
98
101
|
|
|
99
102
|
// Track content height for dynamic sizing
|
|
100
103
|
const [contentHeight, setContentHeight] = useState<number>(0);
|
|
@@ -122,16 +125,24 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
|
|
|
122
125
|
// Add a method to navigate between screens
|
|
123
126
|
// @ts-ignore - Adding custom method
|
|
124
127
|
bottomSheetRef.current._navigateToScreen = (screenName: string, props?: Record<string, any>) => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
+
console.log(`Navigation requested: ${screenName}`, props);
|
|
129
|
+
|
|
130
|
+
// Try direct navigation function first (most reliable)
|
|
131
|
+
if (navigationRef.current) {
|
|
132
|
+
console.log('Using direct navigation function');
|
|
133
|
+
navigationRef.current(screenName, props);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Fallback to event-based navigation
|
|
128
138
|
if (typeof document !== 'undefined') {
|
|
129
139
|
// For web - use a custom event
|
|
140
|
+
console.log('Using web event navigation');
|
|
130
141
|
const event = new CustomEvent('oxy:navigate', { detail: { screen: screenName, props } });
|
|
131
142
|
document.dispatchEvent(event);
|
|
132
143
|
} else {
|
|
133
|
-
// For React Native - use the
|
|
134
|
-
|
|
144
|
+
// For React Native - use the global variable approach
|
|
145
|
+
console.log('Using React Native global navigation');
|
|
135
146
|
(global as any).oxyNavigateEvent = { screen: screenName, props };
|
|
136
147
|
}
|
|
137
148
|
};
|
|
@@ -481,6 +492,7 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
|
|
|
481
492
|
onAuthenticated={handleAuthenticated}
|
|
482
493
|
theme={theme}
|
|
483
494
|
adjustSnapPoints={adjustSnapPoints}
|
|
495
|
+
navigationRef={navigationRef}
|
|
484
496
|
/>
|
|
485
497
|
</Animated.View>
|
|
486
498
|
</BottomSheetScrollView>
|
|
@@ -502,25 +502,39 @@ export const OxyContextProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
502
502
|
|
|
503
503
|
// Bottom sheet control methods
|
|
504
504
|
const showBottomSheet = useCallback((screenOrConfig?: string | { screen: string; props?: Record<string, any> }) => {
|
|
505
|
+
console.log('showBottomSheet called with:', screenOrConfig);
|
|
506
|
+
|
|
505
507
|
if (bottomSheetRef?.current) {
|
|
506
|
-
|
|
508
|
+
console.log('bottomSheetRef is available');
|
|
509
|
+
|
|
510
|
+
// First, show the bottom sheet
|
|
507
511
|
if (bottomSheetRef.current.expand) {
|
|
512
|
+
console.log('Expanding bottom sheet');
|
|
508
513
|
bottomSheetRef.current.expand();
|
|
514
|
+
} else if (bottomSheetRef.current.present) {
|
|
515
|
+
console.log('Presenting bottom sheet');
|
|
516
|
+
bottomSheetRef.current.present();
|
|
509
517
|
} else {
|
|
510
|
-
|
|
511
|
-
bottomSheetRef.current.present?.();
|
|
518
|
+
console.warn('No expand or present method available on bottomSheetRef');
|
|
512
519
|
}
|
|
513
520
|
|
|
514
|
-
//
|
|
521
|
+
// Then navigate to the specified screen if provided
|
|
515
522
|
if (screenOrConfig) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
+
// Add a small delay to ensure the bottom sheet is opened first
|
|
524
|
+
setTimeout(() => {
|
|
525
|
+
if (typeof screenOrConfig === 'string') {
|
|
526
|
+
// Simple screen name
|
|
527
|
+
console.log('Navigating to screen:', screenOrConfig);
|
|
528
|
+
bottomSheetRef.current?._navigateToScreen?.(screenOrConfig);
|
|
529
|
+
} else {
|
|
530
|
+
// Screen with props
|
|
531
|
+
console.log('Navigating to screen with props:', screenOrConfig.screen, screenOrConfig.props);
|
|
532
|
+
bottomSheetRef.current?._navigateToScreen?.(screenOrConfig.screen, screenOrConfig.props);
|
|
533
|
+
}
|
|
534
|
+
}, 100);
|
|
523
535
|
}
|
|
536
|
+
} else {
|
|
537
|
+
console.warn('bottomSheetRef is not available');
|
|
524
538
|
}
|
|
525
539
|
}, [bottomSheetRef]);
|
|
526
540
|
|
|
@@ -7,6 +7,7 @@ import SignInScreen from '../screens/SignInScreen';
|
|
|
7
7
|
import SignUpScreen from '../screens/SignUpScreen';
|
|
8
8
|
import AccountCenterScreen from '../screens/AccountCenterScreen';
|
|
9
9
|
import AccountSwitcherScreen from '../screens/AccountSwitcherScreen';
|
|
10
|
+
import ModernAccountSwitcherScreen from '../screens/ModernAccountSwitcherScreen';
|
|
10
11
|
import SessionManagementScreen from '../screens/SessionManagementScreen';
|
|
11
12
|
import AccountOverviewScreen from '../screens/AccountOverviewScreen';
|
|
12
13
|
import AccountSettingsScreen from '../screens/AccountSettingsScreen';
|
|
@@ -40,6 +41,10 @@ const routes: Record<string, RouteConfig> = {
|
|
|
40
41
|
component: AccountSwitcherScreen,
|
|
41
42
|
snapPoints: ['70%', '100%'],
|
|
42
43
|
},
|
|
44
|
+
ModernAccountSwitcher: {
|
|
45
|
+
component: ModernAccountSwitcherScreen,
|
|
46
|
+
snapPoints: ['70%', '100%'],
|
|
47
|
+
},
|
|
43
48
|
SessionManagement: {
|
|
44
49
|
component: SessionManagementScreen,
|
|
45
50
|
snapPoints: ['70%', '100%'],
|
|
@@ -93,6 +98,7 @@ const OxyRouter: React.FC<OxyRouterProps> = ({
|
|
|
93
98
|
onAuthenticated,
|
|
94
99
|
theme,
|
|
95
100
|
adjustSnapPoints,
|
|
101
|
+
navigationRef,
|
|
96
102
|
}) => {
|
|
97
103
|
const [currentScreen, setCurrentScreen] = useState<string>(initialScreen);
|
|
98
104
|
const [screenHistory, setScreenHistory] = useState<string[]>([initialScreen]);
|
|
@@ -114,7 +120,22 @@ const OxyRouter: React.FC<OxyRouterProps> = ({
|
|
|
114
120
|
} else {
|
|
115
121
|
console.error(`Screen "${screen}" not found`);
|
|
116
122
|
}
|
|
117
|
-
};
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Expose the navigate function to the parent component
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (navigationRef) {
|
|
128
|
+
navigationRef.current = navigate;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return () => {
|
|
132
|
+
if (navigationRef) {
|
|
133
|
+
navigationRef.current = null;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}, [navigate, navigationRef]);
|
|
137
|
+
|
|
138
|
+
// Expose the navigate method to the parent component (OxyProvider)
|
|
118
139
|
useEffect(() => {
|
|
119
140
|
// Set up event listener for navigation events
|
|
120
141
|
const handleNavigationEvent = (event: any) => {
|
|
@@ -132,9 +153,25 @@ const OxyRouter: React.FC<OxyRouterProps> = ({
|
|
|
132
153
|
}
|
|
133
154
|
};
|
|
134
155
|
|
|
135
|
-
//
|
|
156
|
+
// For React Native - check for global navigation events
|
|
157
|
+
let intervalId: NodeJS.Timeout | null = null;
|
|
158
|
+
|
|
136
159
|
if (typeof document !== 'undefined') {
|
|
160
|
+
// Web - use custom event listener
|
|
137
161
|
document.addEventListener('oxy:navigate', handleNavigationEvent);
|
|
162
|
+
} else {
|
|
163
|
+
// React Native - poll for global navigation events
|
|
164
|
+
intervalId = setInterval(() => {
|
|
165
|
+
const globalNav = (global as any).oxyNavigateEvent;
|
|
166
|
+
if (globalNav) {
|
|
167
|
+
console.log(`RN Navigation event received:`, globalNav);
|
|
168
|
+
if (globalNav.screen) {
|
|
169
|
+
navigate(globalNav.screen, globalNav.props || {});
|
|
170
|
+
}
|
|
171
|
+
// Clear the event after processing
|
|
172
|
+
(global as any).oxyNavigateEvent = null;
|
|
173
|
+
}
|
|
174
|
+
}, 100); // Check every 100ms
|
|
138
175
|
}
|
|
139
176
|
|
|
140
177
|
// Cleanup
|
|
@@ -142,6 +179,9 @@ const OxyRouter: React.FC<OxyRouterProps> = ({
|
|
|
142
179
|
if (typeof document !== 'undefined') {
|
|
143
180
|
document.removeEventListener('oxy:navigate', handleNavigationEvent);
|
|
144
181
|
}
|
|
182
|
+
if (intervalId) {
|
|
183
|
+
clearInterval(intervalId);
|
|
184
|
+
}
|
|
145
185
|
};
|
|
146
186
|
}, []);
|
|
147
187
|
|
|
@@ -33,6 +33,7 @@ export interface OxyRouterProps {
|
|
|
33
33
|
onAuthenticated?: (user: User) => void;
|
|
34
34
|
theme: 'light' | 'dark';
|
|
35
35
|
adjustSnapPoints: (snapPoints: string[]) => void;
|
|
36
|
+
navigationRef?: React.MutableRefObject<((screen: string, props?: Record<string, any>) => void) | null>;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -8,17 +8,26 @@ import {
|
|
|
8
8
|
ScrollView,
|
|
9
9
|
Alert,
|
|
10
10
|
Platform,
|
|
11
|
+
Image,
|
|
12
|
+
Dimensions,
|
|
11
13
|
} from 'react-native';
|
|
12
14
|
import { BaseScreenProps } from '../navigation/types';
|
|
13
15
|
import { useOxy } from '../context/OxyContext';
|
|
14
16
|
import { SecureClientSession } from '../../models/secureSession';
|
|
15
17
|
import { fontFamilies } from '../styles/fonts';
|
|
18
|
+
import { User } from '../../models/interfaces';
|
|
19
|
+
|
|
20
|
+
interface SessionWithUser extends SecureClientSession {
|
|
21
|
+
userProfile?: User;
|
|
22
|
+
isLoadingProfile?: boolean;
|
|
23
|
+
}
|
|
16
24
|
|
|
17
25
|
const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
18
26
|
onClose,
|
|
19
27
|
theme,
|
|
20
28
|
navigate,
|
|
21
29
|
goBack,
|
|
30
|
+
oxyServices,
|
|
22
31
|
}) => {
|
|
23
32
|
const {
|
|
24
33
|
user,
|
|
@@ -30,17 +39,68 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
30
39
|
isLoading
|
|
31
40
|
} = useOxy();
|
|
32
41
|
|
|
42
|
+
const [sessionsWithUsers, setSessionsWithUsers] = useState<SessionWithUser[]>([]);
|
|
33
43
|
const [switchingToUserId, setSwitchingToUserId] = useState<string | null>(null);
|
|
34
44
|
const [removingUserId, setRemovingUserId] = useState<string | null>(null);
|
|
35
45
|
|
|
46
|
+
const screenWidth = Dimensions.get('window').width;
|
|
36
47
|
const isDarkTheme = theme === 'dark';
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
|
|
49
|
+
// Modern color scheme
|
|
50
|
+
const colors = {
|
|
51
|
+
background: isDarkTheme ? '#000000' : '#FFFFFF',
|
|
52
|
+
surface: isDarkTheme ? '#1C1C1E' : '#F2F2F7',
|
|
53
|
+
card: isDarkTheme ? '#2C2C2E' : '#FFFFFF',
|
|
54
|
+
text: isDarkTheme ? '#FFFFFF' : '#000000',
|
|
55
|
+
secondaryText: isDarkTheme ? '#8E8E93' : '#6D6D70',
|
|
56
|
+
accent: '#007AFF',
|
|
57
|
+
destructive: '#FF3B30',
|
|
58
|
+
success: '#34C759',
|
|
59
|
+
border: isDarkTheme ? '#38383A' : '#C6C6C8',
|
|
60
|
+
activeCard: isDarkTheme ? '#0A84FF20' : '#007AFF15',
|
|
61
|
+
shadow: isDarkTheme ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.1)',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Load user profiles for sessions
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const loadUserProfiles = async () => {
|
|
67
|
+
if (!sessions.length || !oxyServices) return;
|
|
68
|
+
|
|
69
|
+
const updatedSessions: SessionWithUser[] = sessions.map(session => ({
|
|
70
|
+
...session,
|
|
71
|
+
isLoadingProfile: true,
|
|
72
|
+
}));
|
|
73
|
+
setSessionsWithUsers(updatedSessions);
|
|
74
|
+
|
|
75
|
+
// Load profiles for each session
|
|
76
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
77
|
+
const session = sessions[i];
|
|
78
|
+
try {
|
|
79
|
+
// Try to get user profile using the session
|
|
80
|
+
const userProfile = await oxyServices.getUserBySession(session.sessionId);
|
|
81
|
+
|
|
82
|
+
setSessionsWithUsers(prev =>
|
|
83
|
+
prev.map(s =>
|
|
84
|
+
s.sessionId === session.sessionId
|
|
85
|
+
? { ...s, userProfile, isLoadingProfile: false }
|
|
86
|
+
: s
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(`Failed to load profile for session ${session.sessionId}:`, error);
|
|
91
|
+
setSessionsWithUsers(prev =>
|
|
92
|
+
prev.map(s =>
|
|
93
|
+
s.sessionId === session.sessionId
|
|
94
|
+
? { ...s, isLoadingProfile: false }
|
|
95
|
+
: s
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
loadUserProfiles();
|
|
103
|
+
}, [sessions, oxyServices]);
|
|
44
104
|
|
|
45
105
|
const handleSwitchSession = async (sessionId: string) => {
|
|
46
106
|
if (sessionId === user?.sessionId) return; // Already active session
|
|
@@ -134,20 +194,20 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
134
194
|
style={[
|
|
135
195
|
styles.userItem,
|
|
136
196
|
{
|
|
137
|
-
backgroundColor: isActive ?
|
|
138
|
-
borderColor: isActive ?
|
|
197
|
+
backgroundColor: isActive ? colors.activeCard : colors.surface,
|
|
198
|
+
borderColor: isActive ? colors.accent : colors.border,
|
|
139
199
|
},
|
|
140
200
|
]}
|
|
141
201
|
>
|
|
142
202
|
<View style={styles.userInfo}>
|
|
143
|
-
<Text style={[styles.username, { color:
|
|
203
|
+
<Text style={[styles.username, { color: colors.text }]}>
|
|
144
204
|
{isActive ? user?.username || 'Current Account' : 'Account Session'}
|
|
145
205
|
</Text>
|
|
146
206
|
<Text style={[styles.email, { color: isDarkTheme ? '#BBBBBB' : '#666666' }]}>
|
|
147
207
|
Last active: {new Date(session.lastActive).toLocaleDateString()}
|
|
148
208
|
</Text>
|
|
149
209
|
{isActive && (
|
|
150
|
-
<View style={[styles.activeBadge, { backgroundColor:
|
|
210
|
+
<View style={[styles.activeBadge, { backgroundColor: colors.success }]}>
|
|
151
211
|
<Text style={styles.activeBadgeText}>Active</Text>
|
|
152
212
|
</View>
|
|
153
213
|
)}
|
|
@@ -156,27 +216,27 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
156
216
|
<View style={styles.userActions}>
|
|
157
217
|
{!isActive && (
|
|
158
218
|
<TouchableOpacity
|
|
159
|
-
style={[styles.switchButton, { borderColor:
|
|
219
|
+
style={[styles.switchButton, { borderColor: colors.accent }]}
|
|
160
220
|
onPress={() => handleSwitchSession(session.sessionId)}
|
|
161
221
|
disabled={isSwitching || isRemoving}
|
|
162
222
|
>
|
|
163
223
|
{isSwitching ? (
|
|
164
|
-
<ActivityIndicator color={
|
|
224
|
+
<ActivityIndicator color={colors.accent} size="small" />
|
|
165
225
|
) : (
|
|
166
|
-
<Text style={[styles.switchButtonText, { color:
|
|
226
|
+
<Text style={[styles.switchButtonText, { color: colors.accent }]}>Switch</Text>
|
|
167
227
|
)}
|
|
168
228
|
</TouchableOpacity>
|
|
169
229
|
)}
|
|
170
230
|
|
|
171
231
|
<TouchableOpacity
|
|
172
|
-
style={[styles.removeButton, { borderColor:
|
|
232
|
+
style={[styles.removeButton, { borderColor: colors.destructive }]}
|
|
173
233
|
onPress={() => handleRemoveSession(session.sessionId)}
|
|
174
234
|
disabled={isSwitching || isRemoving || sessions.length === 1}
|
|
175
235
|
>
|
|
176
236
|
{isRemoving ? (
|
|
177
|
-
<ActivityIndicator color={
|
|
237
|
+
<ActivityIndicator color={colors.destructive} size="small" />
|
|
178
238
|
) : (
|
|
179
|
-
<Text style={[styles.removeButtonText, { color:
|
|
239
|
+
<Text style={[styles.removeButtonText, { color: colors.destructive }]}>Remove</Text>
|
|
180
240
|
)}
|
|
181
241
|
</TouchableOpacity>
|
|
182
242
|
</View>
|
|
@@ -185,12 +245,12 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
185
245
|
};
|
|
186
246
|
|
|
187
247
|
return (
|
|
188
|
-
<View style={[styles.container, { backgroundColor }]}>
|
|
248
|
+
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
|
189
249
|
<View style={styles.header}>
|
|
190
250
|
<TouchableOpacity style={styles.backButton} onPress={goBack}>
|
|
191
|
-
<Text style={[styles.backButtonText, { color:
|
|
251
|
+
<Text style={[styles.backButtonText, { color: colors.accent }]}>‹ Back</Text>
|
|
192
252
|
</TouchableOpacity>
|
|
193
|
-
<Text style={[styles.title, { color:
|
|
253
|
+
<Text style={[styles.title, { color: colors.text }]}>Account Switcher</Text>
|
|
194
254
|
<View style={styles.placeholder} />
|
|
195
255
|
</View>
|
|
196
256
|
|
|
@@ -202,7 +262,7 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
202
262
|
</View>
|
|
203
263
|
|
|
204
264
|
<View style={styles.usersContainer}>
|
|
205
|
-
<Text style={[styles.sectionTitle, { color:
|
|
265
|
+
<Text style={[styles.sectionTitle, { color: colors.text }]}>
|
|
206
266
|
Sessions ({sessions.length})
|
|
207
267
|
</Text>
|
|
208
268
|
|
|
@@ -211,10 +271,10 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
211
271
|
|
|
212
272
|
<View style={styles.actionsContainer}>
|
|
213
273
|
<TouchableOpacity
|
|
214
|
-
style={[styles.actionButton, { borderColor }]}
|
|
274
|
+
style={[styles.actionButton, { borderColor: colors.border }]}
|
|
215
275
|
onPress={() => navigate('SignIn')}
|
|
216
276
|
>
|
|
217
|
-
<Text style={[styles.actionButtonText, { color:
|
|
277
|
+
<Text style={[styles.actionButtonText, { color: colors.text }]}>
|
|
218
278
|
Add Another Account
|
|
219
279
|
</Text>
|
|
220
280
|
</TouchableOpacity>
|
|
@@ -226,9 +286,9 @@ const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({
|
|
|
226
286
|
disabled={isLoading}
|
|
227
287
|
>
|
|
228
288
|
{isLoading ? (
|
|
229
|
-
<ActivityIndicator color={
|
|
289
|
+
<ActivityIndicator color={colors.destructive} size="small" />
|
|
230
290
|
) : (
|
|
231
|
-
<Text style={[styles.dangerButtonText, { color:
|
|
291
|
+
<Text style={[styles.dangerButtonText, { color: colors.destructive }]}>
|
|
232
292
|
Sign Out All Sessions
|
|
233
293
|
</Text>
|
|
234
294
|
)}
|