@oxyhq/services 5.21.4 → 5.21.6

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 (63) hide show
  1. package/lib/commonjs/ui/components/BottomSheetRouter.js +100 -286
  2. package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -1
  3. package/lib/commonjs/ui/components/GroupedItem.js +0 -3
  4. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  5. package/lib/commonjs/ui/components/OxyProvider.js +14 -19
  6. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  7. package/lib/commonjs/ui/context/OxyContext.js +50 -30
  8. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  9. package/lib/commonjs/ui/hooks/useWebSSO.js +0 -49
  10. package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -1
  11. package/lib/commonjs/ui/navigation/bottomSheetManager.js +43 -145
  12. package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
  13. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +0 -2
  14. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  15. package/lib/module/ui/components/BottomSheetRouter.js +102 -284
  16. package/lib/module/ui/components/BottomSheetRouter.js.map +1 -1
  17. package/lib/module/ui/components/GroupedItem.js +0 -3
  18. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  19. package/lib/module/ui/components/OxyProvider.js +14 -19
  20. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  21. package/lib/module/ui/context/OxyContext.js +50 -30
  22. package/lib/module/ui/context/OxyContext.js.map +1 -1
  23. package/lib/module/ui/hooks/useWebSSO.js +0 -49
  24. package/lib/module/ui/hooks/useWebSSO.js.map +1 -1
  25. package/lib/module/ui/navigation/bottomSheetManager.js +37 -135
  26. package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
  27. package/lib/module/ui/screens/AccountSettingsScreen.js +0 -2
  28. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  29. package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts +2 -7
  30. package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/ui/components/GroupedItem.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts +11 -60
  36. package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  38. package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts +2 -7
  39. package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts.map +1 -1
  40. package/lib/typescript/module/ui/components/GroupedItem.d.ts.map +1 -1
  41. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  42. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  43. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -1
  44. package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts +11 -60
  45. package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts.map +1 -1
  46. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  47. package/package.json +2 -2
  48. package/src/ui/components/BottomSheetRouter.tsx +97 -319
  49. package/src/ui/components/GroupedItem.tsx +0 -4
  50. package/src/ui/components/OxyProvider.tsx +13 -18
  51. package/src/ui/context/OxyContext.tsx +56 -33
  52. package/src/ui/hooks/useWebSSO.ts +0 -53
  53. package/src/ui/navigation/bottomSheetManager.ts +43 -150
  54. package/src/ui/screens/AccountSettingsScreen.tsx +0 -2
  55. package/lib/commonjs/ui/hooks/use-haptic-press.js +0 -21
  56. package/lib/commonjs/ui/hooks/use-haptic-press.js.map +0 -1
  57. package/lib/module/ui/hooks/use-haptic-press.js +0 -17
  58. package/lib/module/ui/hooks/use-haptic-press.js.map +0 -1
  59. package/lib/typescript/commonjs/ui/hooks/use-haptic-press.d.ts +0 -8
  60. package/lib/typescript/commonjs/ui/hooks/use-haptic-press.d.ts.map +0 -1
  61. package/lib/typescript/module/ui/hooks/use-haptic-press.d.ts +0 -8
  62. package/lib/typescript/module/ui/hooks/use-haptic-press.d.ts.map +0 -1
  63. package/src/ui/hooks/use-haptic-press.ts +0 -15
@@ -16,20 +16,26 @@ setupFonts();
16
16
  // Detect if running on web
17
17
  const isWeb = Platform.OS === 'web';
18
18
 
19
- // Conditionally import native-only components
19
+ // Conditionally import components
20
20
  let KeyboardProvider: any = ({ children }: any) => children;
21
- let BottomSheetRouter: any = () => null;
21
+ let BottomSheetRouter: any = null;
22
22
 
23
+ // KeyboardProvider only on native
23
24
  if (!isWeb) {
24
25
  try {
25
- // Only import on native platforms
26
26
  KeyboardProvider = require('react-native-keyboard-controller').KeyboardProvider;
27
- BottomSheetRouter = require('./BottomSheetRouter').default;
28
27
  } catch {
29
- // Fallback if imports fail
28
+ // KeyboardProvider not available
30
29
  }
31
30
  }
32
31
 
32
+ // BottomSheetRouter works on all platforms
33
+ try {
34
+ BottomSheetRouter = require('./BottomSheetRouter').default;
35
+ } catch {
36
+ // BottomSheetRouter not available
37
+ }
38
+
33
39
  /**
34
40
  * OxyProvider - Universal provider for Expo apps (native + web)
35
41
  *
@@ -199,24 +205,13 @@ const OxyProvider: FC<OxyProviderProps> = ({
199
205
  >
200
206
  {children}
201
207
  {/* Only render bottom sheet router on native */}
202
- {!isWeb && <BottomSheetRouter />}
208
+ {BottomSheetRouter && <BottomSheetRouter />}
203
209
  <Toaster />
204
210
  </OxyContextProvider>
205
211
  </QueryClientProvider>
206
212
  );
207
213
 
208
- // On web, minimal wrappers (GestureHandler and SafeArea work via react-native-web)
209
- if (isWeb) {
210
- return (
211
- <SafeAreaProvider>
212
- <GestureHandlerRootView style={{ flex: 1 }}>
213
- {coreContent}
214
- </GestureHandlerRootView>
215
- </SafeAreaProvider>
216
- );
217
- }
218
-
219
- // On native, full wrappers including KeyboardProvider
214
+ // All platforms use same wrapper (KeyboardProvider is passthrough on web)
220
215
  return (
221
216
  <SafeAreaProvider>
222
217
  <GestureHandlerRootView style={{ flex: 1 }}>
@@ -428,21 +428,13 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
428
428
  // Web SSO: Automatically check for cross-domain session on web platforms
429
429
  // Also used for popup auth - updates all state and persists session
430
430
  const handleWebSSOSession = useCallback(async (session: any) => {
431
- console.log('[OxyContext] handleWebSSOSession called:', {
432
- hasSession: !!session,
433
- hasUser: !!session?.user,
434
- hasSessionId: !!session?.sessionId,
435
- sessionIdPrefix: session?.sessionId?.substring(0, 8),
436
- userId: session?.user?.id,
437
- });
438
-
439
431
  if (!session?.user || !session?.sessionId) {
440
- console.warn('[OxyContext] handleWebSSOSession: Invalid session - missing user or sessionId');
432
+ if (__DEV__) {
433
+ loggerUtil.warn('handleWebSSOSession: Invalid session', { component: 'OxyContext' });
434
+ }
441
435
  return;
442
436
  }
443
437
 
444
- console.log('[OxyContext] handleWebSSOSession: Processing valid session...');
445
-
446
438
  // Update sessions state
447
439
  const clientSession = {
448
440
  sessionId: session.sessionId,
@@ -467,34 +459,12 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
467
459
  sessionIds.push(session.sessionId);
468
460
  await storage.setItem(storageKeys.sessionIds, JSON.stringify(sessionIds));
469
461
  }
470
- console.log('[OxyContext] handleWebSSOSession: Session persisted to storage', {
471
- sessionId: session.sessionId?.substring(0, 8),
472
- totalSessions: sessionIds.length,
473
- });
474
- } else {
475
- console.warn('[OxyContext] handleWebSSOSession: No storage available, session not persisted!');
476
462
  }
477
-
478
- console.log('[OxyContext] handleWebSSOSession: Complete! User should now be authenticated.');
479
463
  }, [updateSessions, setActiveSessionId, loginSuccess, onAuthStateChange, storage, storageKeys]);
480
464
 
481
465
  // Enable web SSO only after local storage check completes and no user found
482
466
  const shouldTryWebSSO = isWebBrowser() && tokenReady && !user && initialized;
483
467
 
484
- // Debug logging for SSO conditions
485
- useEffect(() => {
486
- if (isWebBrowser()) {
487
- console.log('[OxyContext] Web SSO conditions:', {
488
- isWebBrowser: true,
489
- tokenReady,
490
- hasUser: !!user,
491
- initialized,
492
- shouldTryWebSSO,
493
- hostname: typeof window !== 'undefined' ? window.location.hostname : 'unknown',
494
- });
495
- }
496
- }, [tokenReady, user, shouldTryWebSSO]);
497
-
498
468
  useWebSSO({
499
469
  oxyServices,
500
470
  onSessionFound: handleWebSSOSession,
@@ -506,6 +476,59 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
506
476
  enabled: shouldTryWebSSO,
507
477
  });
508
478
 
479
+ // IdP session validation via lightweight iframe check
480
+ // When user returns to tab, verify auth.oxy.so still has their session
481
+ // If session is gone (cleared/logged out), clear local session too
482
+ const lastIdPCheckRef = useRef<number>(0);
483
+
484
+ useEffect(() => {
485
+ if (!isWebBrowser() || !user || !initialized) return;
486
+
487
+ const checkIdPSession = () => {
488
+ // Debounce: check at most once per 30 seconds
489
+ const now = Date.now();
490
+ if (now - lastIdPCheckRef.current < 30000) return;
491
+ lastIdPCheckRef.current = now;
492
+
493
+ // Load hidden iframe to check IdP session via postMessage
494
+ const iframe = document.createElement('iframe');
495
+ iframe.style.cssText = 'display:none;width:0;height:0;border:0';
496
+ iframe.src = 'https://auth.oxy.so/api/auth/session-check';
497
+
498
+ let cleaned = false;
499
+ const cleanup = () => {
500
+ if (cleaned) return;
501
+ cleaned = true;
502
+ window.removeEventListener('message', handleMessage);
503
+ iframe.remove();
504
+ };
505
+
506
+ const handleMessage = async (event: MessageEvent) => {
507
+ if (event.origin !== 'https://auth.oxy.so') return;
508
+ if (event.data?.type !== 'oxy-session-check') return;
509
+ cleanup();
510
+
511
+ if (!event.data.hasSession) {
512
+ toast.info('Your session has ended. Please sign in again.');
513
+ await clearSessionState();
514
+ }
515
+ };
516
+
517
+ window.addEventListener('message', handleMessage);
518
+ document.body.appendChild(iframe);
519
+ setTimeout(cleanup, 5000); // Timeout after 5s
520
+ };
521
+
522
+ const handleVisibilityChange = () => {
523
+ if (document.visibilityState === 'visible') {
524
+ checkIdPSession();
525
+ }
526
+ };
527
+
528
+ document.addEventListener('visibilitychange', handleVisibilityChange);
529
+ return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
530
+ }, [user, initialized, clearSessionState]);
531
+
509
532
  const activeSession = activeSessionId
510
533
  ? sessions.find((session) => session.sessionId === activeSessionId)
511
534
  : undefined;
@@ -87,60 +87,35 @@ export function useWebSSO({
87
87
  const fedCMSupported = isWebBrowser() && (oxyServices as any).isFedCMSupported?.();
88
88
 
89
89
  const checkSSO = useCallback(async (): Promise<SessionLoginResponse | null> => {
90
- console.log('[useWebSSO] checkSSO called', {
91
- isWebBrowser: isWebBrowser(),
92
- isChecking: isCheckingRef.current,
93
- isIdP: isIdentityProvider(),
94
- fedCMSupported,
95
- });
96
-
97
90
  if (!isWebBrowser() || isCheckingRef.current) {
98
91
  return null;
99
92
  }
100
93
 
101
94
  // Don't use FedCM on the auth domain itself - it would authenticate against itself
102
95
  if (isIdentityProvider()) {
103
- console.log('[useWebSSO] Skipping - on identity provider domain');
104
96
  onSSOUnavailable?.();
105
97
  return null;
106
98
  }
107
99
 
108
100
  // FedCM is the only reliable cross-domain SSO mechanism
109
- // Third-party cookies are deprecated and unreliable
110
101
  if (!fedCMSupported) {
111
- console.log('[useWebSSO] Skipping - FedCM not supported');
112
102
  onSSOUnavailable?.();
113
103
  return null;
114
104
  }
115
105
 
116
106
  isCheckingRef.current = true;
117
- console.log('[useWebSSO] Starting FedCM silent sign-in...');
118
107
 
119
108
  try {
120
- // Use FedCM for cross-domain SSO
121
- // This works because browser treats IdP requests as first-party
122
109
  const session = await (oxyServices as any).silentSignInWithFedCM?.();
123
110
 
124
- console.log('[useWebSSO] FedCM result:', {
125
- hasSession: !!session,
126
- hasUser: !!session?.user,
127
- hasSessionId: !!session?.sessionId,
128
- });
129
-
130
111
  if (session) {
131
- console.log('[useWebSSO] Session found, calling onSessionFound...');
132
112
  await onSessionFound(session);
133
- console.log('[useWebSSO] onSessionFound completed');
134
113
  return session;
135
114
  }
136
115
 
137
- // No session found - user needs to sign in
138
- console.log('[useWebSSO] No session returned from FedCM');
139
116
  onSSOUnavailable?.();
140
117
  return null;
141
118
  } catch (error) {
142
- // FedCM failed - could be network error, user not signed in, etc.
143
- console.error('[useWebSSO] FedCM error:', error);
144
119
  onSSOUnavailable?.();
145
120
  onError?.(error instanceof Error ? error : new Error(String(error)));
146
121
  return null;
@@ -155,41 +130,27 @@ export function useWebSSO({
155
130
  * Use this when silent mediation fails (user hasn't previously consented).
156
131
  */
157
132
  const signInWithFedCM = useCallback(async (): Promise<SessionLoginResponse | null> => {
158
- console.log('[useWebSSO] signInWithFedCM called');
159
-
160
133
  if (!isWebBrowser() || isCheckingRef.current) {
161
134
  return null;
162
135
  }
163
136
 
164
137
  if (!fedCMSupported) {
165
- console.log('[useWebSSO] FedCM not supported for interactive sign-in');
166
138
  onError?.(new Error('FedCM is not supported in this browser'));
167
139
  return null;
168
140
  }
169
141
 
170
142
  isCheckingRef.current = true;
171
- console.log('[useWebSSO] Starting interactive FedCM sign-in...');
172
143
 
173
144
  try {
174
- // Use interactive sign-in (shows browser UI)
175
145
  const session = await (oxyServices as any).signInWithFedCM?.();
176
146
 
177
- console.log('[useWebSSO] Interactive FedCM result:', {
178
- hasSession: !!session,
179
- hasUser: !!session?.user,
180
- hasSessionId: !!session?.sessionId,
181
- });
182
-
183
147
  if (session) {
184
- console.log('[useWebSSO] Interactive session found, calling onSessionFound...');
185
148
  await onSessionFound(session);
186
- console.log('[useWebSSO] onSessionFound completed');
187
149
  return session;
188
150
  }
189
151
 
190
152
  return null;
191
153
  } catch (error) {
192
- console.error('[useWebSSO] Interactive FedCM error:', error);
193
154
  onError?.(error instanceof Error ? error : new Error(String(error)));
194
155
  return null;
195
156
  } finally {
@@ -199,19 +160,7 @@ export function useWebSSO({
199
160
 
200
161
  // Auto-check SSO on mount (web only, FedCM only, not on auth domain)
201
162
  useEffect(() => {
202
- console.log('[useWebSSO] Effect running:', {
203
- enabled,
204
- isWeb: isWebBrowser(),
205
- hasChecked: hasCheckedRef.current,
206
- isIdP: isIdentityProvider(),
207
- fedCMSupported,
208
- hostname: typeof window !== 'undefined' ? window.location.hostname : 'unknown',
209
- });
210
-
211
163
  if (!enabled || !isWebBrowser() || hasCheckedRef.current || isIdentityProvider()) {
212
- console.log('[useWebSSO] Skipping SSO check:', {
213
- reason: !enabled ? 'not enabled' : !isWebBrowser() ? 'not web' : hasCheckedRef.current ? 'already checked' : 'is IdP',
214
- });
215
164
  if (isIdentityProvider()) {
216
165
  onSSOUnavailable?.();
217
166
  }
@@ -223,8 +172,6 @@ export function useWebSSO({
223
172
  if (fedCMSupported) {
224
173
  checkSSO();
225
174
  } else {
226
- // Browser doesn't support FedCM - notify caller
227
- console.log('[useWebSSO] FedCM not supported');
228
175
  onSSOUnavailable?.();
229
176
  }
230
177
  }, [enabled, checkSSO, fedCMSupported, onSSOUnavailable]);
@@ -3,189 +3,82 @@ import { isValidRoute } from './routes';
3
3
  import { createStore } from 'zustand/vanilla';
4
4
 
5
5
  /**
6
- * Bottom Sheet Manager - Pure state management module
7
- *
8
- * Uses Zustand (vanilla) for robust state management without React dependencies.
9
- * This ensures the manager can be imported safely anywhere.
6
+ * Bottom Sheet State Manager
10
7
  */
11
8
 
12
- export interface NavigationHistoryEntry {
13
- screen: RouteName;
14
- props: Record<string, unknown>;
15
- step?: number; // For step-based screens
16
- }
17
-
18
- export interface BottomSheetRouterState {
9
+ export interface BottomSheetState {
19
10
  currentScreen: RouteName | null;
20
11
  screenProps: Record<string, unknown>;
21
- currentStep?: number; // Current step in step-based screen
22
- navigationHistory: NavigationHistoryEntry[];
12
+ currentStep?: number;
13
+ history: Array<{ screen: RouteName; props: Record<string, unknown>; step?: number }>;
23
14
  isOpen: boolean;
24
15
  }
25
16
 
26
- // Initial state
27
- const initialState: BottomSheetRouterState = {
17
+ const initialState: BottomSheetState = {
28
18
  currentScreen: null,
29
19
  screenProps: {},
30
20
  currentStep: undefined,
31
- navigationHistory: [],
21
+ history: [],
32
22
  isOpen: false,
33
23
  };
34
24
 
35
- // Create vanilla store
36
- export const bottomSheetStore = createStore<BottomSheetRouterState>(() => initialState);
25
+ export const bottomSheetStore = createStore<BottomSheetState>(() => initialState);
37
26
 
38
- // Use a generic ref type to avoid importing React types
39
- type BottomSheetRefObject = { current: { present: () => void; dismiss: () => void } | null } | null;
40
- let bottomSheetRef: BottomSheetRefObject = null;
41
-
42
- /**
43
- * Set the bottom sheet ref so showBottomSheet can control it
44
- */
45
- export const setBottomSheetRef = (ref: BottomSheetRefObject) => {
46
- bottomSheetRef = ref;
47
- };
27
+ export const getState = () => bottomSheetStore.getState();
48
28
 
49
- /**
50
- * Update the bottom sheet state
51
- * (Kept for backward compatibility, but prefer using store directly if possible)
52
- */
53
- export const updateBottomSheetState = (updates: Partial<BottomSheetRouterState>) => {
54
- bottomSheetStore.setState((state) => ({ ...state, ...updates }));
55
- };
29
+ export const showBottomSheet = (
30
+ screenOrConfig: RouteName | { screen: RouteName; props?: Record<string, unknown> },
31
+ ): void => {
32
+ const screen = typeof screenOrConfig === 'string' ? screenOrConfig : screenOrConfig.screen;
33
+ const props = typeof screenOrConfig === 'string' ? {} : (screenOrConfig.props || {});
56
34
 
57
- /**
58
- * Subscribe to bottom sheet state changes
59
- * (Wrapper around store.subscribe for backward compatibility)
60
- */
61
- export const subscribeToBottomSheetState = (listener: (state: BottomSheetRouterState) => void) => {
62
- return bottomSheetStore.subscribe(listener);
63
- };
35
+ if (!isValidRoute(screen)) {
36
+ if (__DEV__) console.warn(`[BottomSheet] Invalid route: ${screen}`);
37
+ return;
38
+ }
64
39
 
65
- /**
66
- * Get the current bottom sheet state
67
- * (Wrapper around store.getState for backward compatibility)
68
- */
69
- export const getBottomSheetState = (): BottomSheetRouterState => {
70
- return bottomSheetStore.getState();
71
- };
40
+ const state = bottomSheetStore.getState();
72
41
 
73
- /**
74
- * Show the bottom sheet with a specific screen (internal - no route validation)
75
- */
76
- export const managerShowBottomSheet = (
77
- screen: RouteName,
78
- props?: Record<string, unknown>,
79
- options?: { addToHistory?: boolean; step?: number },
80
- ): void => {
81
- const currentState = bottomSheetStore.getState();
82
- const addToHistory = options?.addToHistory !== false; // Default to true
83
-
84
- // If adding to history and there's a current screen, push it to history
85
- if (addToHistory && currentState.currentScreen) {
86
- const historyEntry: NavigationHistoryEntry = {
87
- screen: currentState.currentScreen,
88
- props: { ...currentState.screenProps },
89
- step: currentState.currentStep,
90
- };
91
-
92
- // We need to create a new array for immutability
93
- const newHistory = [...currentState.navigationHistory, historyEntry];
94
- bottomSheetStore.setState({ navigationHistory: newHistory });
42
+ // Push current screen to history if navigating to different screen
43
+ if (state.currentScreen && state.currentScreen !== screen) {
44
+ bottomSheetStore.setState({
45
+ history: [...state.history, {
46
+ screen: state.currentScreen,
47
+ props: state.screenProps,
48
+ step: state.currentStep,
49
+ }],
50
+ });
95
51
  }
96
-
97
- // Determine the new step
98
- const newStep = options?.step ??
99
- (props?.initialStep !== undefined ? props.initialStep :
100
- (addToHistory ? undefined : currentState.currentStep));
101
-
52
+
102
53
  bottomSheetStore.setState({
103
54
  currentScreen: screen,
104
- screenProps: props || {},
105
- currentStep: newStep !== null && newStep !== undefined ? (newStep as number) : undefined,
55
+ screenProps: props,
56
+ currentStep: typeof props.initialStep === 'number' ? props.initialStep : undefined,
106
57
  isOpen: true,
107
58
  });
108
-
109
- // Present the sheet after state update
110
- if (bottomSheetRef?.current) {
111
- bottomSheetRef.current.present();
112
- }
113
59
  };
114
60
 
115
- /**
116
- * Close the bottom sheet (internal)
117
- */
118
- export const managerCloseBottomSheet = (): void => {
119
- bottomSheetStore.setState({
120
- currentScreen: null,
121
- screenProps: {},
122
- currentStep: undefined,
123
- navigationHistory: [],
124
- isOpen: false,
125
- });
126
-
127
- if (bottomSheetRef?.current) {
128
- bottomSheetRef.current.dismiss();
129
- }
61
+ export const closeBottomSheet = (): void => {
62
+ bottomSheetStore.setState(initialState);
130
63
  };
131
64
 
132
- /**
133
- * Go back in navigation history
134
- * Returns true if back navigation was successful, false if history is empty
135
- */
136
- export const managerGoBack = (): boolean => {
137
- const currentState = bottomSheetStore.getState();
65
+ export const goBack = (): boolean => {
66
+ const { history } = bottomSheetStore.getState();
138
67
 
139
- // If there's history, pop and navigate to previous screen
140
- if (currentState.navigationHistory.length > 0) {
141
- const previous = currentState.navigationHistory[currentState.navigationHistory.length - 1];
142
- const newHistory = currentState.navigationHistory.slice(0, -1);
143
-
68
+ if (history.length > 0) {
69
+ const prev = history[history.length - 1];
144
70
  bottomSheetStore.setState({
145
- currentScreen: previous.screen,
146
- screenProps: previous.props,
147
- currentStep: previous.step,
148
- navigationHistory: newHistory,
149
- isOpen: true,
71
+ currentScreen: prev.screen,
72
+ screenProps: prev.props,
73
+ currentStep: prev.step,
74
+ history: history.slice(0, -1),
150
75
  });
151
-
152
76
  return true;
153
77
  }
154
-
155
- return false;
156
- };
157
-
158
- /**
159
- * Public API for showing bottom sheets
160
- */
161
- export const showBottomSheet = (
162
- screenOrConfig: RouteName | { screen: RouteName; props?: Record<string, unknown> },
163
- ): void => {
164
- let screen: RouteName;
165
- let props: Record<string, unknown> = {};
166
-
167
- if (typeof screenOrConfig === 'string') {
168
- screen = screenOrConfig;
169
- } else {
170
- screen = screenOrConfig.screen;
171
- props = screenOrConfig.props || {};
172
- }
173
-
174
- if (!isValidRoute(screen)) {
175
- if (__DEV__) {
176
- console.warn(`[BottomSheetAPI] Invalid route: ${screen}`);
177
- }
178
- return;
179
- }
180
78
 
181
- managerShowBottomSheet(screen, props);
79
+ return false;
182
80
  };
183
81
 
184
- /**
185
- * Public API for closing bottom sheets
186
- */
187
- export const closeBottomSheet = (): void => {
188
- managerCloseBottomSheet();
82
+ export const updateState = (updates: Partial<BottomSheetState>) => {
83
+ bottomSheetStore.setState((state) => ({ ...state, ...updates }));
189
84
  };
190
-
191
-
@@ -29,7 +29,6 @@ import { useThemeStyles } from '../hooks/useThemeStyles';
29
29
  import { useColorScheme } from '../hooks/use-color-scheme';
30
30
  import { Colors } from '../constants/theme';
31
31
  import { normalizeColorScheme, normalizeTheme } from '../utils/themeUtils';
32
- import { useHapticPress } from '../hooks/use-haptic-press';
33
32
  import { EditDisplayNameModal } from '../components/profile/EditDisplayNameModal';
34
33
  import { EditUsernameModal } from '../components/profile/EditUsernameModal';
35
34
  import { EditEmailModal } from '../components/profile/EditEmailModal';
@@ -176,7 +175,6 @@ const AccountSettingsScreen: React.FC<BaseScreenProps & { initialField?: string;
176
175
  // Get theme colors using centralized hook
177
176
  const colorScheme = useColorScheme();
178
177
  const themeStyles = useThemeStyles(theme || 'light', colorScheme);
179
- const handlePressIn = useHapticPress();
180
178
 
181
179
  // Extract colors for convenience - ensure it's always defined
182
180
  // useThemeStyles always returns colors, but add safety check for edge cases
@@ -1,21 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.useHapticPress = useHapticPress;
7
- var _react = require("react");
8
- var Haptics = _interopRequireWildcard(require("expo-haptics"));
9
- 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); }
10
- /**
11
- * Hook that returns a memoized callback for haptic feedback on press.
12
- * Provides consistent light haptic feedback across the app.
13
- *
14
- * @returns A stable callback function that triggers haptic feedback
15
- */
16
- function useHapticPress() {
17
- return (0, _react.useCallback)(() => {
18
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
19
- }, []);
20
- }
21
- //# sourceMappingURL=use-haptic-press.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_react","require","Haptics","_interopRequireWildcard","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","useHapticPress","useCallback","impactAsync","ImpactFeedbackStyle","Light"],"sourceRoot":"../../../../src","sources":["ui/hooks/use-haptic-press.ts"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAC,uBAAA,CAAAF,OAAA;AAAwC,SAAAE,wBAAAC,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAH,uBAAA,YAAAA,CAAAC,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAExC;AACA;AACA;AACA;AACA;AACA;AACO,SAASkB,cAAcA,CAAA,EAAG;EAC/B,OAAO,IAAAC,kBAAW,EAAC,MAAM;IACvBtB,OAAO,CAACuB,WAAW,CAACvB,OAAO,CAACwB,mBAAmB,CAACC,KAAK,CAAC;EACxD,CAAC,EAAE,EAAE,CAAC;AACR","ignoreList":[]}
@@ -1,17 +0,0 @@
1
- "use strict";
2
-
3
- import { useCallback } from 'react';
4
- import * as Haptics from 'expo-haptics';
5
-
6
- /**
7
- * Hook that returns a memoized callback for haptic feedback on press.
8
- * Provides consistent light haptic feedback across the app.
9
- *
10
- * @returns A stable callback function that triggers haptic feedback
11
- */
12
- export function useHapticPress() {
13
- return useCallback(() => {
14
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
15
- }, []);
16
- }
17
- //# sourceMappingURL=use-haptic-press.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["useCallback","Haptics","useHapticPress","impactAsync","ImpactFeedbackStyle","Light"],"sourceRoot":"../../../../src","sources":["ui/hooks/use-haptic-press.ts"],"mappings":";;AAAA,SAASA,WAAW,QAAQ,OAAO;AACnC,OAAO,KAAKC,OAAO,MAAM,cAAc;;AAEvC;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAcA,CAAA,EAAG;EAC/B,OAAOF,WAAW,CAAC,MAAM;IACvBC,OAAO,CAACE,WAAW,CAACF,OAAO,CAACG,mBAAmB,CAACC,KAAK,CAAC;EACxD,CAAC,EAAE,EAAE,CAAC;AACR","ignoreList":[]}
@@ -1,8 +0,0 @@
1
- /**
2
- * Hook that returns a memoized callback for haptic feedback on press.
3
- * Provides consistent light haptic feedback across the app.
4
- *
5
- * @returns A stable callback function that triggers haptic feedback
6
- */
7
- export declare function useHapticPress(): () => void;
8
- //# sourceMappingURL=use-haptic-press.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-haptic-press.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/use-haptic-press.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,cAAc,eAI7B"}
@@ -1,8 +0,0 @@
1
- /**
2
- * Hook that returns a memoized callback for haptic feedback on press.
3
- * Provides consistent light haptic feedback across the app.
4
- *
5
- * @returns A stable callback function that triggers haptic feedback
6
- */
7
- export declare function useHapticPress(): () => void;
8
- //# sourceMappingURL=use-haptic-press.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-haptic-press.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/use-haptic-press.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,cAAc,eAI7B"}
@@ -1,15 +0,0 @@
1
- import { useCallback } from 'react';
2
- import * as Haptics from 'expo-haptics';
3
-
4
- /**
5
- * Hook that returns a memoized callback for haptic feedback on press.
6
- * Provides consistent light haptic feedback across the app.
7
- *
8
- * @returns A stable callback function that triggers haptic feedback
9
- */
10
- export function useHapticPress() {
11
- return useCallback(() => {
12
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
13
- }, []);
14
- }
15
-