@oxyhq/services 5.21.5 → 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.
- package/lib/commonjs/ui/components/BottomSheetRouter.js +100 -286
- package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedItem.js +0 -3
- package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +14 -19
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/navigation/bottomSheetManager.js +43 -145
- package/lib/commonjs/ui/navigation/bottomSheetManager.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +0 -2
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/components/BottomSheetRouter.js +102 -284
- package/lib/module/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/module/ui/components/GroupedItem.js +0 -3
- package/lib/module/ui/components/GroupedItem.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +14 -19
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/navigation/bottomSheetManager.js +37 -135
- package/lib/module/ui/navigation/bottomSheetManager.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +0 -2
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts +2 -7
- package/lib/typescript/commonjs/ui/components/BottomSheetRouter.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/GroupedItem.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts +11 -60
- package/lib/typescript/commonjs/ui/navigation/bottomSheetManager.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts +2 -7
- package/lib/typescript/module/ui/components/BottomSheetRouter.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/GroupedItem.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts +11 -60
- package/lib/typescript/module/ui/navigation/bottomSheetManager.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/ui/components/BottomSheetRouter.tsx +97 -319
- package/src/ui/components/GroupedItem.tsx +0 -4
- package/src/ui/components/OxyProvider.tsx +13 -18
- package/src/ui/navigation/bottomSheetManager.ts +43 -150
- package/src/ui/screens/AccountSettingsScreen.tsx +0 -2
- package/lib/commonjs/ui/hooks/use-haptic-press.js +0 -21
- package/lib/commonjs/ui/hooks/use-haptic-press.js.map +0 -1
- package/lib/module/ui/hooks/use-haptic-press.js +0 -17
- package/lib/module/ui/hooks/use-haptic-press.js.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/use-haptic-press.d.ts +0 -8
- package/lib/typescript/commonjs/ui/hooks/use-haptic-press.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/use-haptic-press.d.ts +0 -8
- package/lib/typescript/module/ui/hooks/use-haptic-press.d.ts.map +0 -1
- package/src/ui/hooks/use-haptic-press.ts +0 -15
|
@@ -1,74 +1,25 @@
|
|
|
1
1
|
import type { RouteName } from './routes';
|
|
2
2
|
/**
|
|
3
|
-
* Bottom Sheet Manager
|
|
4
|
-
*
|
|
5
|
-
* Uses Zustand (vanilla) for robust state management without React dependencies.
|
|
6
|
-
* This ensures the manager can be imported safely anywhere.
|
|
3
|
+
* Bottom Sheet State Manager
|
|
7
4
|
*/
|
|
8
|
-
export interface
|
|
9
|
-
screen: RouteName;
|
|
10
|
-
props: Record<string, unknown>;
|
|
11
|
-
step?: number;
|
|
12
|
-
}
|
|
13
|
-
export interface BottomSheetRouterState {
|
|
5
|
+
export interface BottomSheetState {
|
|
14
6
|
currentScreen: RouteName | null;
|
|
15
7
|
screenProps: Record<string, unknown>;
|
|
16
8
|
currentStep?: number;
|
|
17
|
-
|
|
9
|
+
history: Array<{
|
|
10
|
+
screen: RouteName;
|
|
11
|
+
props: Record<string, unknown>;
|
|
12
|
+
step?: number;
|
|
13
|
+
}>;
|
|
18
14
|
isOpen: boolean;
|
|
19
15
|
}
|
|
20
|
-
export declare const bottomSheetStore: import("zustand/vanilla").StoreApi<
|
|
21
|
-
|
|
22
|
-
current: {
|
|
23
|
-
present: () => void;
|
|
24
|
-
dismiss: () => void;
|
|
25
|
-
} | null;
|
|
26
|
-
} | null;
|
|
27
|
-
/**
|
|
28
|
-
* Set the bottom sheet ref so showBottomSheet can control it
|
|
29
|
-
*/
|
|
30
|
-
export declare const setBottomSheetRef: (ref: BottomSheetRefObject) => void;
|
|
31
|
-
/**
|
|
32
|
-
* Update the bottom sheet state
|
|
33
|
-
* (Kept for backward compatibility, but prefer using store directly if possible)
|
|
34
|
-
*/
|
|
35
|
-
export declare const updateBottomSheetState: (updates: Partial<BottomSheetRouterState>) => void;
|
|
36
|
-
/**
|
|
37
|
-
* Subscribe to bottom sheet state changes
|
|
38
|
-
* (Wrapper around store.subscribe for backward compatibility)
|
|
39
|
-
*/
|
|
40
|
-
export declare const subscribeToBottomSheetState: (listener: (state: BottomSheetRouterState) => void) => () => void;
|
|
41
|
-
/**
|
|
42
|
-
* Get the current bottom sheet state
|
|
43
|
-
* (Wrapper around store.getState for backward compatibility)
|
|
44
|
-
*/
|
|
45
|
-
export declare const getBottomSheetState: () => BottomSheetRouterState;
|
|
46
|
-
/**
|
|
47
|
-
* Show the bottom sheet with a specific screen (internal - no route validation)
|
|
48
|
-
*/
|
|
49
|
-
export declare const managerShowBottomSheet: (screen: RouteName, props?: Record<string, unknown>, options?: {
|
|
50
|
-
addToHistory?: boolean;
|
|
51
|
-
step?: number;
|
|
52
|
-
}) => void;
|
|
53
|
-
/**
|
|
54
|
-
* Close the bottom sheet (internal)
|
|
55
|
-
*/
|
|
56
|
-
export declare const managerCloseBottomSheet: () => void;
|
|
57
|
-
/**
|
|
58
|
-
* Go back in navigation history
|
|
59
|
-
* Returns true if back navigation was successful, false if history is empty
|
|
60
|
-
*/
|
|
61
|
-
export declare const managerGoBack: () => boolean;
|
|
62
|
-
/**
|
|
63
|
-
* Public API for showing bottom sheets
|
|
64
|
-
*/
|
|
16
|
+
export declare const bottomSheetStore: import("zustand/vanilla").StoreApi<BottomSheetState>;
|
|
17
|
+
export declare const getState: () => BottomSheetState;
|
|
65
18
|
export declare const showBottomSheet: (screenOrConfig: RouteName | {
|
|
66
19
|
screen: RouteName;
|
|
67
20
|
props?: Record<string, unknown>;
|
|
68
21
|
}) => void;
|
|
69
|
-
/**
|
|
70
|
-
* Public API for closing bottom sheets
|
|
71
|
-
*/
|
|
72
22
|
export declare const closeBottomSheet: () => void;
|
|
73
|
-
export
|
|
23
|
+
export declare const goBack: () => boolean;
|
|
24
|
+
export declare const updateState: (updates: Partial<BottomSheetState>) => void;
|
|
74
25
|
//# sourceMappingURL=bottomSheetManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottomSheetManager.d.ts","sourceRoot":"","sources":["../../../../../src/ui/navigation/bottomSheetManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAI1C
|
|
1
|
+
{"version":3,"file":"bottomSheetManager.d.ts","sourceRoot":"","sources":["../../../../../src/ui/navigation/bottomSheetManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAI1C;;GAEG;AAEH,MAAM,WAAW,gBAAgB;IAC7B,aAAa,EAAE,SAAS,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF,MAAM,EAAE,OAAO,CAAC;CACnB;AAUD,eAAO,MAAM,gBAAgB,sDAAoD,CAAC;AAElF,eAAO,MAAM,QAAQ,wBAAoC,CAAC;AAE1D,eAAO,MAAM,eAAe,GACxB,gBAAgB,SAAS,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,KACnF,IA4BF,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,IAEnC,CAAC;AAEF,eAAO,MAAM,MAAM,QAAO,OAezB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,SAAS,OAAO,CAAC,gBAAgB,CAAC,SAE7D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountSettingsScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/AccountSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAcjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;
|
|
1
|
+
{"version":3,"file":"AccountSettingsScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/AccountSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAcjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;mBA6Cc,MAAM;qBAAmB,MAAM;;AAqgExG,wBAAiD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "5.21.
|
|
3
|
+
"version": "5.21.6",
|
|
4
4
|
"description": "Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -252,4 +252,4 @@
|
|
|
252
252
|
"react-native-gesture-handler": "~2.28.0",
|
|
253
253
|
"react-native-reanimated": "~4.1.1"
|
|
254
254
|
}
|
|
255
|
-
}
|
|
255
|
+
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import React, { useRef, useEffect, useCallback } from 'react';
|
|
1
|
+
import React, { useRef, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
import { BackHandler, View, StyleSheet, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
-
import Animated, {
|
|
4
|
-
useSharedValue,
|
|
5
|
-
useAnimatedStyle,
|
|
6
|
-
withTiming,
|
|
7
|
-
runOnJS,
|
|
8
|
-
type SharedValue,
|
|
9
|
-
} from 'react-native-reanimated';
|
|
10
3
|
import { useStore } from 'zustand';
|
|
11
4
|
import type { RouteName } from '../navigation/routes';
|
|
12
5
|
import { getScreenComponent, isValidRoute } from '../navigation/routes';
|
|
@@ -15,15 +8,12 @@ import { useColorScheme } from '../hooks/use-color-scheme';
|
|
|
15
8
|
import { Colors } from '../constants/theme';
|
|
16
9
|
import BottomSheet, { type BottomSheetRef } from './BottomSheet';
|
|
17
10
|
import {
|
|
18
|
-
setBottomSheetRef as setManagerRef,
|
|
19
|
-
updateBottomSheetState,
|
|
20
|
-
subscribeToBottomSheetState,
|
|
21
|
-
managerShowBottomSheet,
|
|
22
|
-
managerCloseBottomSheet,
|
|
23
|
-
managerGoBack,
|
|
24
|
-
type BottomSheetRouterState,
|
|
25
|
-
getBottomSheetState,
|
|
26
11
|
bottomSheetStore,
|
|
12
|
+
getState,
|
|
13
|
+
showBottomSheet,
|
|
14
|
+
closeBottomSheet,
|
|
15
|
+
goBack,
|
|
16
|
+
updateState,
|
|
27
17
|
} from '../navigation/bottomSheetManager';
|
|
28
18
|
|
|
29
19
|
export interface BottomSheetRouterProps {
|
|
@@ -31,365 +21,153 @@ export interface BottomSheetRouterProps {
|
|
|
31
21
|
onDismiss?: () => void;
|
|
32
22
|
}
|
|
33
23
|
|
|
34
|
-
// Re-export types for backward compatibility
|
|
35
|
-
export type { BottomSheetRouterState };
|
|
36
|
-
export { subscribeToBottomSheetState };
|
|
37
|
-
|
|
38
|
-
// Re-export BottomSheetRef for backward compatibility
|
|
39
|
-
export type { BottomSheetRef };
|
|
40
|
-
|
|
41
24
|
/**
|
|
42
|
-
* BottomSheetRouter -
|
|
43
|
-
*
|
|
44
|
-
* Uses custom BottomSheet component built with react-native-reanimated v4.
|
|
45
|
-
* State is managed by bottomSheetManager (single source of truth).
|
|
46
|
-
*
|
|
47
|
-
* Features:
|
|
48
|
-
* - Screen navigation with history stack
|
|
49
|
-
* - Step-based navigation for multi-step screens
|
|
50
|
-
* - Android back button handling
|
|
51
|
-
* - Minimal props passing (screens use useOxy() for context)
|
|
25
|
+
* BottomSheetRouter - Navigation container for bottom sheet screens
|
|
52
26
|
*/
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
children: React.ReactNode;
|
|
56
|
-
fadeAnim: SharedValue<number>;
|
|
57
|
-
scaleAnim: SharedValue<number>;
|
|
58
|
-
screenKey: string;
|
|
59
|
-
}> = ({ children, fadeAnim, scaleAnim, screenKey }) => {
|
|
60
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
61
|
-
opacity: fadeAnim.value,
|
|
62
|
-
transform: [
|
|
63
|
-
{ scale: scaleAnim.value }
|
|
64
|
-
]
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<Animated.View
|
|
69
|
-
key={screenKey}
|
|
70
|
-
style={[{ flexShrink: 1 }, animatedStyle]}
|
|
71
|
-
>
|
|
72
|
-
{children}
|
|
73
|
-
</Animated.View>
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const BottomSheetRouterComponent: React.FC<BottomSheetRouterProps> = ({ onScreenChange, onDismiss }) => {
|
|
78
|
-
const bottomSheetRef = useRef<BottomSheetRef>(null);
|
|
27
|
+
const BottomSheetRouter: React.FC<BottomSheetRouterProps> = ({ onScreenChange, onDismiss }) => {
|
|
28
|
+
const sheetRef = useRef<BottomSheetRef>(null);
|
|
79
29
|
const colorScheme = useColorScheme();
|
|
80
30
|
const colors = Colors[colorScheme ?? 'light'];
|
|
31
|
+
const prevScreenRef = useRef<RouteName | null>(null);
|
|
81
32
|
|
|
82
|
-
|
|
83
|
-
const fadeAnim = useSharedValue(1);
|
|
84
|
-
const scaleAnim = useSharedValue(1);
|
|
85
|
-
const previousScreenRef = useRef<RouteName | null>(null);
|
|
86
|
-
const isTransitioningRef = useRef(false);
|
|
87
|
-
|
|
88
|
-
// Create stable ref object for manager compatibility
|
|
89
|
-
// Manager expects: { current: { present: () => void; dismiss: () => void } | null } | null
|
|
90
|
-
const managerRefObject = useRef({
|
|
91
|
-
current: {
|
|
92
|
-
present: () => bottomSheetRef.current?.present(),
|
|
93
|
-
dismiss: () => bottomSheetRef.current?.dismiss(),
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Single source of truth - subscribe to manager state using Zustand
|
|
98
|
-
const state = useStore(bottomSheetStore);
|
|
33
|
+
const { currentScreen, screenProps, currentStep, isOpen } = useStore(bottomSheetStore);
|
|
99
34
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// However, the animation logic relied on queueing state updates.
|
|
105
|
-
// Let's adapt it.
|
|
106
|
-
onScreenChange?.(newScreen);
|
|
107
|
-
|
|
108
|
-
// Note: The original logic tried to defer the state update (setState) until animation completed.
|
|
109
|
-
// With Zustand, the global state is already updated.
|
|
110
|
-
// The animation logic needs to react to the state change.
|
|
111
|
-
|
|
112
|
-
// Since useStore forces a re-render with the NEW state, we are already "in" the new state.
|
|
113
|
-
// To animate "out" the old screen, we would need to have captured the previous screen
|
|
114
|
-
// before the re-render, OR we accept that the transition might be slightly different.
|
|
115
|
-
|
|
116
|
-
// Actually, the original logic had `setState` to control when the UI updates.
|
|
117
|
-
// With global store, the UI updates immediately.
|
|
118
|
-
// For now, let's trust the re-render. If animation is jerky, we can revisit.
|
|
119
|
-
// But wait, the original logic:
|
|
120
|
-
// 1. Check if transitioning. If so, wait.
|
|
121
|
-
// 2. If valid change, start exit animation -> then update local state -> then enter animation.
|
|
122
|
-
|
|
123
|
-
// With Zustand, we can't "delay" the state update because it's global.
|
|
124
|
-
// So `state` (from useStore) is ALREADY the new state.
|
|
125
|
-
// We need to detect that `state.currentScreen` changed from our `previousScreenRef`.
|
|
126
|
-
|
|
127
|
-
}, [onScreenChange]);
|
|
35
|
+
const ScreenComponent = useMemo(
|
|
36
|
+
() => (currentScreen ? getScreenComponent(currentScreen) : null),
|
|
37
|
+
[currentScreen]
|
|
38
|
+
);
|
|
128
39
|
|
|
129
|
-
//
|
|
40
|
+
// Notify screen changes
|
|
130
41
|
useEffect(() => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Logic to handle transition...
|
|
137
|
-
// Since we are already re-rendered with new state, we might see a flash of new content.
|
|
138
|
-
// Ideally we shouldn't have updated the global store until animation started...
|
|
139
|
-
// BUT `bottomSheetManager` updates the store immediately.
|
|
42
|
+
if (prevScreenRef.current !== currentScreen) {
|
|
43
|
+
onScreenChange?.(currentScreen);
|
|
44
|
+
prevScreenRef.current = currentScreen;
|
|
45
|
+
}
|
|
46
|
+
}, [currentScreen, onScreenChange]);
|
|
140
47
|
|
|
141
|
-
|
|
142
|
-
|
|
48
|
+
// Control visibility
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!sheetRef.current) return;
|
|
143
51
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// Opening first time
|
|
147
|
-
fadeAnim.value = 1;
|
|
148
|
-
scaleAnim.value = 1;
|
|
149
|
-
previousScreenRef.current = newScreen;
|
|
150
|
-
onScreenChange?.(newScreen);
|
|
52
|
+
if (isOpen) {
|
|
53
|
+
sheetRef.current.present();
|
|
151
54
|
} else {
|
|
152
|
-
|
|
55
|
+
sheetRef.current.dismiss();
|
|
153
56
|
}
|
|
57
|
+
}, [isOpen]);
|
|
154
58
|
|
|
155
|
-
|
|
156
|
-
}, [state.currentScreen, onScreenChange, fadeAnim, scaleAnim]);
|
|
157
|
-
|
|
158
|
-
// Clean up on unmount
|
|
59
|
+
// Android back button
|
|
159
60
|
useEffect(() => {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// Handle explicit dismiss - only update state, don't call dismiss again
|
|
166
|
-
const handleDismiss = useCallback(() => {
|
|
167
|
-
// Only update state, don't call dismiss() as it's already being dismissed
|
|
168
|
-
updateBottomSheetState({
|
|
169
|
-
currentScreen: null,
|
|
170
|
-
screenProps: {},
|
|
171
|
-
currentStep: undefined,
|
|
172
|
-
navigationHistory: [],
|
|
173
|
-
isOpen: false,
|
|
61
|
+
if (!isOpen) return;
|
|
62
|
+
const handler = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
63
|
+
handleGoBack();
|
|
64
|
+
return true;
|
|
174
65
|
});
|
|
175
|
-
|
|
176
|
-
}, [
|
|
177
|
-
|
|
178
|
-
// Get current screen component (lazy-loaded)
|
|
179
|
-
const ScreenComponent = state.currentScreen ? getScreenComponent(state.currentScreen) : null;
|
|
66
|
+
return () => handler.remove();
|
|
67
|
+
}, [isOpen]);
|
|
180
68
|
|
|
181
|
-
// Navigation handler - validates route and updates manager state
|
|
182
|
-
// For step-based screens, step changes are treated as navigation events (added to history)
|
|
183
69
|
const navigate = useCallback((screen: RouteName, props?: Record<string, unknown>) => {
|
|
184
70
|
if (!isValidRoute(screen)) {
|
|
185
|
-
if (__DEV__) {
|
|
186
|
-
console.warn(`[BottomSheetRouter] Invalid route: ${screen}`);
|
|
187
|
-
}
|
|
71
|
+
if (__DEV__) console.warn(`[BottomSheetRouter] Invalid route: ${screen}`);
|
|
188
72
|
return;
|
|
189
73
|
}
|
|
190
|
-
|
|
191
|
-
const isSameScreen = screen === state.currentScreen;
|
|
192
|
-
const newStep = typeof props?.initialStep === 'number' ? props.initialStep : undefined;
|
|
193
|
-
const currentStep = state.currentStep ?? (typeof state.screenProps?.initialStep === 'number' ? state.screenProps.initialStep : undefined);
|
|
194
|
-
|
|
195
|
-
// For step-based screens: if same screen but different step, treat as navigation (add to history)
|
|
196
|
-
// This allows step navigation to be handled by router with animations
|
|
197
|
-
const isStepChange = isSameScreen && newStep !== undefined && newStep !== currentStep;
|
|
198
|
-
|
|
199
|
-
managerShowBottomSheet(screen, props, {
|
|
200
|
-
addToHistory: !isSameScreen || isStepChange, // Add to history if different screen OR step change
|
|
201
|
-
});
|
|
202
|
-
}, [state.currentScreen, state.currentStep, state.screenProps]);
|
|
203
|
-
|
|
204
|
-
// Step change handler for step-based screens
|
|
205
|
-
// This is called by StepBasedScreen to notify router of step changes
|
|
206
|
-
// The router now handles step navigation through navigate/goBack, so this is mainly for compatibility
|
|
207
|
-
const handleStepChange = useCallback((step: number, _totalSteps?: number) => {
|
|
208
|
-
// Step changes are now handled through navigate/goBack, but we still update currentStep
|
|
209
|
-
// for screens that might query it directly
|
|
210
|
-
if (state.currentScreen) {
|
|
211
|
-
updateBottomSheetState({
|
|
212
|
-
currentStep: step,
|
|
213
|
-
screenProps: {
|
|
214
|
-
...state.screenProps,
|
|
215
|
-
initialStep: step,
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}, [state.currentScreen, state.screenProps]);
|
|
220
|
-
|
|
221
|
-
// Check if the bottom sheet can be dismissed (no navigation history or steps to go back to)
|
|
222
|
-
const canDismiss = useCallback((): boolean => {
|
|
223
|
-
// Use global state to avoid stale closures during transitions
|
|
224
|
-
const currentState = getBottomSheetState();
|
|
225
|
-
|
|
226
|
-
// Check if there's navigation history
|
|
227
|
-
if (currentState.navigationHistory.length > 0) {
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Check if there are steps to go back to
|
|
232
|
-
const initialStep = typeof currentState.screenProps?.initialStep === 'number'
|
|
233
|
-
? currentState.screenProps.initialStep
|
|
234
|
-
: undefined;
|
|
235
|
-
const currentStep = currentState.currentStep ?? initialStep ?? 0;
|
|
236
|
-
const isStepBased = initialStep !== undefined || currentState.currentStep !== undefined;
|
|
237
|
-
|
|
238
|
-
if (isStepBased && typeof currentStep === 'number' && currentStep > 0) {
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// No history and on step 0 (or not step-based) - can dismiss
|
|
243
|
-
return true;
|
|
74
|
+
showBottomSheet({ screen, props });
|
|
244
75
|
}, []);
|
|
245
76
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// 2. Step navigation (navigate to previous step)
|
|
249
|
-
// 3. Close sheet (no history/step available)
|
|
250
|
-
const goBack = useCallback(() => {
|
|
251
|
-
// Use global state to avoid stale closures during transitions
|
|
252
|
-
const currentState = getBottomSheetState();
|
|
77
|
+
const handleGoBack = useCallback(() => {
|
|
78
|
+
const state = getState();
|
|
253
79
|
|
|
254
|
-
//
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
80
|
+
// Try history first
|
|
81
|
+
if (state.history.length > 0) {
|
|
82
|
+
goBack();
|
|
83
|
+
return true;
|
|
260
84
|
}
|
|
261
85
|
|
|
262
|
-
//
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (isStepBased && typeof currentStep === 'number' && currentStep > 0) {
|
|
270
|
-
const previousStep = currentStep - 1;
|
|
271
|
-
updateBottomSheetState({
|
|
272
|
-
screenProps: {
|
|
273
|
-
...currentState.screenProps,
|
|
274
|
-
initialStep: previousStep,
|
|
275
|
-
},
|
|
276
|
-
currentStep: previousStep,
|
|
86
|
+
// Try step back
|
|
87
|
+
const step = state.currentStep ?? 0;
|
|
88
|
+
if (step > 0) {
|
|
89
|
+
updateState({
|
|
90
|
+
currentStep: step - 1,
|
|
91
|
+
screenProps: { ...state.screenProps, initialStep: step - 1 },
|
|
277
92
|
});
|
|
278
93
|
return true;
|
|
279
94
|
}
|
|
280
95
|
|
|
281
|
-
//
|
|
282
|
-
|
|
96
|
+
// Close
|
|
97
|
+
closeBottomSheet();
|
|
283
98
|
return true;
|
|
284
99
|
}, []);
|
|
285
100
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
293
|
-
return goBack();
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
return () => {
|
|
297
|
-
backHandler.remove();
|
|
298
|
-
};
|
|
299
|
-
}, [state.isOpen, goBack]);
|
|
101
|
+
const canDismiss = useCallback((): boolean => {
|
|
102
|
+
const state = getState();
|
|
103
|
+
if (state.history.length > 0) return false;
|
|
104
|
+
const step = state.currentStep ?? 0;
|
|
105
|
+
return step <= 0;
|
|
106
|
+
}, []);
|
|
300
107
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
bottomSheetRef.current.present();
|
|
108
|
+
const handleDismissAttempt = useCallback((): boolean => {
|
|
109
|
+
if (!canDismiss()) {
|
|
110
|
+
handleGoBack();
|
|
111
|
+
return false;
|
|
306
112
|
}
|
|
307
|
-
|
|
308
|
-
|
|
113
|
+
return true;
|
|
114
|
+
}, [canDismiss, handleGoBack]);
|
|
309
115
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
? state.currentStep
|
|
315
|
-
: (typeof state.screenProps?.initialStep === 'number'
|
|
316
|
-
? state.screenProps.initialStep
|
|
317
|
-
: undefined);
|
|
116
|
+
const handleDismiss = useCallback(() => {
|
|
117
|
+
closeBottomSheet();
|
|
118
|
+
onDismiss?.();
|
|
119
|
+
}, [onDismiss]);
|
|
318
120
|
|
|
319
|
-
|
|
320
|
-
|
|
121
|
+
const handleStepChange = useCallback((step: number) => {
|
|
122
|
+
const state = getState();
|
|
123
|
+
updateState({
|
|
124
|
+
currentStep: step,
|
|
125
|
+
screenProps: { ...state.screenProps, initialStep: step },
|
|
126
|
+
});
|
|
127
|
+
}, []);
|
|
321
128
|
|
|
322
129
|
const scrollTo = useCallback((y: number, animated?: boolean) => {
|
|
323
|
-
|
|
130
|
+
sheetRef.current?.scrollTo(y, animated);
|
|
324
131
|
}, []);
|
|
325
132
|
|
|
326
|
-
const screenProps: BaseScreenProps & { scrollTo: (y: number, animated?: boolean) => void } = {
|
|
327
|
-
navigate,
|
|
328
|
-
goBack,
|
|
329
|
-
onClose: () => managerCloseBottomSheet(),
|
|
330
|
-
onAuthenticated: () => managerCloseBottomSheet(),
|
|
331
|
-
theme: colorScheme ?? 'light',
|
|
332
|
-
currentScreen: state.currentScreen ?? undefined,
|
|
333
|
-
initialStep: calculatedInitialStep,
|
|
334
|
-
onStepChange: handleStepChange,
|
|
335
|
-
scrollTo, // Pass scrollTo method
|
|
336
|
-
...otherScreenProps,
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
// renderBackground must be called before any conditional returns (React hooks rule)
|
|
340
133
|
const renderBackground = useCallback(
|
|
341
134
|
(props: { style?: StyleProp<ViewStyle> }) => (
|
|
342
|
-
<View
|
|
343
|
-
style={[
|
|
344
|
-
styles.background,
|
|
345
|
-
{ backgroundColor: colors.background },
|
|
346
|
-
props.style,
|
|
347
|
-
]}
|
|
348
|
-
/>
|
|
135
|
+
<View style={[styles.background, { backgroundColor: colors.background }, props.style]} />
|
|
349
136
|
),
|
|
350
|
-
[colors.background]
|
|
137
|
+
[colors.background]
|
|
351
138
|
);
|
|
352
139
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
goBack
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
140
|
+
const screenPropsValue = useMemo((): BaseScreenProps & { scrollTo: typeof scrollTo } => {
|
|
141
|
+
const { initialStep: _, ...rest } = screenProps;
|
|
142
|
+
return {
|
|
143
|
+
navigate,
|
|
144
|
+
goBack: handleGoBack,
|
|
145
|
+
onClose: closeBottomSheet,
|
|
146
|
+
onAuthenticated: closeBottomSheet,
|
|
147
|
+
theme: colorScheme ?? 'light',
|
|
148
|
+
currentScreen: currentScreen ?? undefined,
|
|
149
|
+
initialStep: currentStep ?? (screenProps?.initialStep as number | undefined),
|
|
150
|
+
onStepChange: handleStepChange,
|
|
151
|
+
scrollTo,
|
|
152
|
+
...rest,
|
|
153
|
+
};
|
|
154
|
+
}, [navigate, handleGoBack, colorScheme, currentScreen, currentStep, screenProps, handleStepChange, scrollTo]);
|
|
368
155
|
|
|
369
156
|
return (
|
|
370
157
|
<BottomSheet
|
|
371
|
-
ref={
|
|
372
|
-
enablePanDownToClose
|
|
158
|
+
ref={sheetRef}
|
|
159
|
+
enablePanDownToClose
|
|
160
|
+
enableHandlePanningGesture
|
|
373
161
|
backgroundComponent={renderBackground}
|
|
374
|
-
enableHandlePanningGesture={true}
|
|
375
162
|
style={styles.container}
|
|
376
163
|
onDismiss={handleDismiss}
|
|
377
164
|
onDismissAttempt={handleDismissAttempt}
|
|
378
165
|
>
|
|
379
|
-
<
|
|
380
|
-
fadeAnim={fadeAnim}
|
|
381
|
-
scaleAnim={scaleAnim}
|
|
382
|
-
screenKey={state.currentScreen}
|
|
383
|
-
>
|
|
384
|
-
<ScreenComponent {...screenProps} />
|
|
385
|
-
</AnimatedScreenContainer>
|
|
166
|
+
{ScreenComponent && currentScreen && <ScreenComponent {...screenPropsValue} />}
|
|
386
167
|
</BottomSheet>
|
|
387
168
|
);
|
|
388
169
|
};
|
|
389
170
|
|
|
390
|
-
const BottomSheetRouter = React.memo(BottomSheetRouterComponent);
|
|
391
|
-
BottomSheetRouter.displayName = 'BottomSheetRouter';
|
|
392
|
-
|
|
393
171
|
const styles = StyleSheet.create({
|
|
394
172
|
container: {
|
|
395
173
|
maxWidth: 800,
|
|
@@ -404,4 +182,4 @@ const styles = StyleSheet.create({
|
|
|
404
182
|
},
|
|
405
183
|
});
|
|
406
184
|
|
|
407
|
-
export default BottomSheetRouter;
|
|
185
|
+
export default React.memo(BottomSheetRouter);
|
|
@@ -4,7 +4,6 @@ import { Ionicons } from '@expo/vector-icons';
|
|
|
4
4
|
// @ts-ignore - MaterialCommunityIcons is available at runtime
|
|
5
5
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
6
6
|
import { useColorScheme } from '../hooks/use-color-scheme';
|
|
7
|
-
import { useHapticPress } from '../hooks/use-haptic-press';
|
|
8
7
|
import { darkenColor } from '../utils/colorUtils';
|
|
9
8
|
import { normalizeColorScheme } from '../utils/themeUtils';
|
|
10
9
|
import { Colors } from '../constants/theme';
|
|
@@ -87,13 +86,10 @@ const GroupedItemComponent = ({
|
|
|
87
86
|
</View>
|
|
88
87
|
);
|
|
89
88
|
|
|
90
|
-
const handlePressIn = useHapticPress();
|
|
91
|
-
|
|
92
89
|
if (onPress && !disabled) {
|
|
93
90
|
return (
|
|
94
91
|
<TouchableOpacity
|
|
95
92
|
style={itemStyles}
|
|
96
|
-
onPressIn={disabled ? undefined : handlePressIn}
|
|
97
93
|
onPress={onPress}
|
|
98
94
|
activeOpacity={0.7}
|
|
99
95
|
accessibilityRole="button"
|