@buoy-gg/route-events 1.7.2

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 (79) hide show
  1. package/README.md +654 -0
  2. package/lib/commonjs/RouteObserver.js +54 -0
  3. package/lib/commonjs/RouteParser.js +310 -0
  4. package/lib/commonjs/RouteTracker.js +39 -0
  5. package/lib/commonjs/components/NavigationStack.js +584 -0
  6. package/lib/commonjs/components/RouteEventDetailContent.js +492 -0
  7. package/lib/commonjs/components/RouteEventExpandedContent.js +187 -0
  8. package/lib/commonjs/components/RouteEventItemCompact.js +175 -0
  9. package/lib/commonjs/components/RouteEventsModalWithTabs.js +560 -0
  10. package/lib/commonjs/components/RouteEventsTimeline.js +82 -0
  11. package/lib/commonjs/components/RouteFilterViewV2.js +42 -0
  12. package/lib/commonjs/components/RoutesSitemap.js +948 -0
  13. package/lib/commonjs/expoRouterStore.js +104 -0
  14. package/lib/commonjs/index.js +99 -0
  15. package/lib/commonjs/package.json +1 -0
  16. package/lib/commonjs/preset.js +83 -0
  17. package/lib/commonjs/useNavigationStack.js +241 -0
  18. package/lib/commonjs/useRouteObserver.js +73 -0
  19. package/lib/commonjs/useRouteSitemap.js +234 -0
  20. package/lib/commonjs/utils/safeExpoRouter.js +129 -0
  21. package/lib/commonjs/utils/safeReactNavigation.js +104 -0
  22. package/lib/module/RouteObserver.js +49 -0
  23. package/lib/module/RouteParser.js +305 -0
  24. package/lib/module/RouteTracker.js +35 -0
  25. package/lib/module/components/NavigationStack.js +580 -0
  26. package/lib/module/components/RouteEventDetailContent.js +487 -0
  27. package/lib/module/components/RouteEventExpandedContent.js +183 -0
  28. package/lib/module/components/RouteEventItemCompact.js +171 -0
  29. package/lib/module/components/RouteEventsModalWithTabs.js +557 -0
  30. package/lib/module/components/RouteEventsTimeline.js +78 -0
  31. package/lib/module/components/RouteFilterViewV2.js +38 -0
  32. package/lib/module/components/RoutesSitemap.js +944 -0
  33. package/lib/module/expoRouterStore.js +98 -0
  34. package/lib/module/index.js +23 -0
  35. package/lib/module/preset.js +79 -0
  36. package/lib/module/useNavigationStack.js +238 -0
  37. package/lib/module/useRouteObserver.js +70 -0
  38. package/lib/module/useRouteSitemap.js +229 -0
  39. package/lib/module/utils/safeExpoRouter.js +120 -0
  40. package/lib/module/utils/safeReactNavigation.js +98 -0
  41. package/lib/typescript/RouteObserver.d.ts +37 -0
  42. package/lib/typescript/RouteObserver.d.ts.map +1 -0
  43. package/lib/typescript/RouteParser.d.ts +129 -0
  44. package/lib/typescript/RouteParser.d.ts.map +1 -0
  45. package/lib/typescript/RouteTracker.d.ts +29 -0
  46. package/lib/typescript/RouteTracker.d.ts.map +1 -0
  47. package/lib/typescript/components/NavigationStack.d.ts +11 -0
  48. package/lib/typescript/components/NavigationStack.d.ts.map +1 -0
  49. package/lib/typescript/components/RouteEventDetailContent.d.ts +21 -0
  50. package/lib/typescript/components/RouteEventDetailContent.d.ts.map +1 -0
  51. package/lib/typescript/components/RouteEventExpandedContent.d.ts +16 -0
  52. package/lib/typescript/components/RouteEventExpandedContent.d.ts.map +1 -0
  53. package/lib/typescript/components/RouteEventItemCompact.d.ts +15 -0
  54. package/lib/typescript/components/RouteEventItemCompact.d.ts.map +1 -0
  55. package/lib/typescript/components/RouteEventsModalWithTabs.d.ts +15 -0
  56. package/lib/typescript/components/RouteEventsModalWithTabs.d.ts.map +1 -0
  57. package/lib/typescript/components/RouteEventsTimeline.d.ts +17 -0
  58. package/lib/typescript/components/RouteEventsTimeline.d.ts.map +1 -0
  59. package/lib/typescript/components/RouteFilterViewV2.d.ts +9 -0
  60. package/lib/typescript/components/RouteFilterViewV2.d.ts.map +1 -0
  61. package/lib/typescript/components/RoutesSitemap.d.ts +15 -0
  62. package/lib/typescript/components/RoutesSitemap.d.ts.map +1 -0
  63. package/lib/typescript/expoRouterStore.d.ts +28 -0
  64. package/lib/typescript/expoRouterStore.d.ts.map +1 -0
  65. package/lib/typescript/index.d.ts +18 -0
  66. package/lib/typescript/index.d.ts.map +1 -0
  67. package/lib/typescript/preset.d.ts +76 -0
  68. package/lib/typescript/preset.d.ts.map +1 -0
  69. package/lib/typescript/useNavigationStack.d.ts +48 -0
  70. package/lib/typescript/useNavigationStack.d.ts.map +1 -0
  71. package/lib/typescript/useRouteObserver.d.ts +27 -0
  72. package/lib/typescript/useRouteObserver.d.ts.map +1 -0
  73. package/lib/typescript/useRouteSitemap.d.ts +102 -0
  74. package/lib/typescript/useRouteSitemap.d.ts.map +1 -0
  75. package/lib/typescript/utils/safeExpoRouter.d.ts +13 -0
  76. package/lib/typescript/utils/safeExpoRouter.d.ts.map +1 -0
  77. package/lib/typescript/utils/safeReactNavigation.d.ts +10 -0
  78. package/lib/typescript/utils/safeReactNavigation.d.ts.map +1 -0
  79. package/package.json +72 -0
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getExpoRouterStore = getExpoRouterStore;
7
+ exports.getRouteNodeMetadata = getRouteNodeMetadata;
8
+ exports.loadRouteNode = loadRouteNode;
9
+ let cachedStore = null;
10
+ let cachedStoreSource = null;
11
+ let importError = null;
12
+ let hasLoggedMissingStore = false;
13
+ let hasLoggedMissingRouteNode = false;
14
+ let lastRouteNodeTimestamp = null;
15
+ function logOnce(message, error) {
16
+ if (__DEV__) {
17
+ if (error) {
18
+ console.error(`[RouteEvents] ${message}`, error);
19
+ } else {
20
+ console.warn(`[RouteEvents] ${message}`);
21
+ }
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Attempt to require the expo-router store from known locations.
27
+ * Returns null (with a logged error in dev) when expo-router is not available.
28
+ *
29
+ * Note: The store uses getters that read from an internal storeRef.
30
+ * The storeRef gets populated when Expo Router's useStore() hook runs.
31
+ * So we cache the store reference but its property values update over time.
32
+ */
33
+ function getExpoRouterStore() {
34
+ if (cachedStore) {
35
+ return cachedStore;
36
+ }
37
+ const loadFromBuild = () => {
38
+ try {
39
+ const module = require("expo-router/build/global-state/router-store");
40
+ if (module?.store) {
41
+ cachedStore = module.store;
42
+ cachedStoreSource = "build";
43
+ importError = null;
44
+ hasLoggedMissingStore = false;
45
+ return true;
46
+ }
47
+ } catch (error) {
48
+ importError = error;
49
+ }
50
+ return false;
51
+ };
52
+ const loadFromSrc = () => {
53
+ try {
54
+ const module = require("expo-router/src/global-state/router-store");
55
+ if (module?.store) {
56
+ cachedStore = module.store;
57
+ cachedStoreSource = "src";
58
+ importError = null;
59
+ hasLoggedMissingStore = false;
60
+ return true;
61
+ }
62
+ } catch (error) {
63
+ importError = error;
64
+ }
65
+ return false;
66
+ };
67
+ if (loadFromBuild() || loadFromSrc()) {
68
+ return cachedStore;
69
+ }
70
+ if (!hasLoggedMissingStore) {
71
+ logOnce("Unable to load expo-router internals. @buoy-gg/route-events requires expo-router >= 2.0.0. Install expo-router and ensure it is configured before using the Routes tab.", importError ?? undefined);
72
+ hasLoggedMissingStore = true;
73
+ }
74
+ return null;
75
+ }
76
+
77
+ /**
78
+ * Returns the current RouteNode tree (if available).
79
+ * Logs a helpful warning in development when the tree is missing even though
80
+ * the navigation ref is ready.
81
+ */
82
+ function loadRouteNode() {
83
+ const store = getExpoRouterStore();
84
+ if (!store) {
85
+ return null;
86
+ }
87
+ if (store.routeNode) {
88
+ hasLoggedMissingRouteNode = false;
89
+ lastRouteNodeTimestamp = Date.now();
90
+ return store.routeNode;
91
+ }
92
+ const isReady = store.navigationRef?.isReady?.();
93
+ if (__DEV__ && !hasLoggedMissingRouteNode && isReady) {
94
+ logOnce("Expo Router route tree is unavailable. Ensure your app directory is configured and that expo-router is initialized before opening the Route Events devtool.");
95
+ hasLoggedMissingRouteNode = true;
96
+ }
97
+ return null;
98
+ }
99
+ function getRouteNodeMetadata() {
100
+ return {
101
+ source: cachedStoreSource,
102
+ lastLoadedAt: lastRouteNodeTimestamp
103
+ };
104
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "NavigationStack", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _NavigationStack.NavigationStack;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "RouteEventsModalWithTabs", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _RouteEventsModalWithTabs.RouteEventsModalWithTabs;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "RouteObserver", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _RouteObserver.RouteObserver;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "RouteParser", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _RouteParser.RouteParser;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "RouteTracker", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _RouteTracker.RouteTracker;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "RoutesSitemap", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _RoutesSitemap.RoutesSitemap;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "createRouteEventsTool", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _preset.createRouteEventsTool;
46
+ }
47
+ });
48
+ Object.defineProperty(exports, "routeEventsToolPreset", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _preset.routeEventsToolPreset;
52
+ }
53
+ });
54
+ Object.defineProperty(exports, "routeObserver", {
55
+ enumerable: true,
56
+ get: function () {
57
+ return _RouteObserver.routeObserver;
58
+ }
59
+ });
60
+ Object.defineProperty(exports, "useNavigationStack", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _useNavigationStack.useNavigationStack;
64
+ }
65
+ });
66
+ Object.defineProperty(exports, "useParentRoutes", {
67
+ enumerable: true,
68
+ get: function () {
69
+ return _useRouteSitemap.useParentRoutes;
70
+ }
71
+ });
72
+ Object.defineProperty(exports, "useRoute", {
73
+ enumerable: true,
74
+ get: function () {
75
+ return _useRouteSitemap.useRoute;
76
+ }
77
+ });
78
+ Object.defineProperty(exports, "useRouteObserver", {
79
+ enumerable: true,
80
+ get: function () {
81
+ return _useRouteObserver.useRouteObserver;
82
+ }
83
+ });
84
+ Object.defineProperty(exports, "useRouteSitemap", {
85
+ enumerable: true,
86
+ get: function () {
87
+ return _useRouteSitemap.useRouteSitemap;
88
+ }
89
+ });
90
+ var _RouteEventsModalWithTabs = require("./components/RouteEventsModalWithTabs");
91
+ var _preset = require("./preset");
92
+ var _RouteTracker = require("./RouteTracker");
93
+ var _RoutesSitemap = require("./components/RoutesSitemap");
94
+ var _NavigationStack = require("./components/NavigationStack");
95
+ var _RouteObserver = require("./RouteObserver");
96
+ var _useRouteObserver = require("./useRouteObserver");
97
+ var _RouteParser = require("./RouteParser");
98
+ var _useRouteSitemap = require("./useRouteSitemap");
99
+ var _useNavigationStack = require("./useNavigationStack");
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createRouteEventsTool = createRouteEventsTool;
7
+ exports.routeEventsToolPreset = void 0;
8
+ var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
9
+ var _RouteEventsModalWithTabs = require("./components/RouteEventsModalWithTabs");
10
+ var _jsxRuntime = require("react/jsx-runtime");
11
+ /**
12
+ * Pre-configured route events tool for FloatingDevTools
13
+ *
14
+ * This preset provides a zero-config way to add route tracking to your dev tools.
15
+ * Just import and spread it into your apps array!
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * import { routeEventsToolPreset } from '@buoy-gg/route-events';
20
+ *
21
+ * const installedApps = [
22
+ * routeEventsToolPreset, // That's it!
23
+ * // ...other tools
24
+ * ];
25
+ * ```
26
+ */
27
+
28
+ /**
29
+ * Pre-configured route events tool for FloatingDevTools.
30
+ * Includes:
31
+ * - Route sitemap browser
32
+ * - Event timeline with filtering
33
+ * - Navigation stack visualization
34
+ * - Automatic route tracking (no setup needed)
35
+ */
36
+ const routeEventsToolPreset = exports.routeEventsToolPreset = {
37
+ id: "route-events",
38
+ name: "ROUTES",
39
+ description: "Route tracking & navigation inspector",
40
+ slot: "both",
41
+ icon: ({
42
+ size
43
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.RoutesIcon, {
44
+ size: size
45
+ }),
46
+ component: _RouteEventsModalWithTabs.RouteEventsModalWithTabs,
47
+ props: {
48
+ enableSharedModalDimensions: false
49
+ }
50
+ };
51
+
52
+ /**
53
+ * Create a custom route events tool configuration.
54
+ * Use this if you want to override default settings.
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * import { createRouteEventsTool } from '@buoy-gg/route-events';
59
+ *
60
+ * const myRouteTool = createRouteEventsTool({
61
+ * name: "MY ROUTES",
62
+ * color: "#a78bfa",
63
+ * enableSharedModalDimensions: true,
64
+ * });
65
+ * ```
66
+ */
67
+ function createRouteEventsTool(options) {
68
+ return {
69
+ id: options?.id || "route-events",
70
+ name: options?.name || "ROUTES",
71
+ description: options?.description || "Route tracking & navigation inspector",
72
+ slot: "both",
73
+ icon: ({
74
+ size
75
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.RoutesIcon, {
76
+ size: size
77
+ }),
78
+ component: _RouteEventsModalWithTabs.RouteEventsModalWithTabs,
79
+ props: {
80
+ enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
81
+ }
82
+ };
83
+ }
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useNavigationStack = useNavigationStack;
7
+ var _react = require("react");
8
+ var _native = require("@react-navigation/native");
9
+ var _expoRouter = require("expo-router");
10
+ /**
11
+ * useNavigationStack - Hook to access current navigation stack state
12
+ *
13
+ * Provides real-time access to the navigation stack from Expo Router,
14
+ * showing what screens are currently mounted in memory and which is visible.
15
+ *
16
+ * Data source: @react-navigation/native
17
+ */
18
+
19
+ // ============================================================================
20
+ // Type Definitions
21
+ // ============================================================================
22
+
23
+ // ============================================================================
24
+ // Main Hook
25
+ // ============================================================================
26
+
27
+ /**
28
+ * Recursively collect all routes from the navigation state tree
29
+ */
30
+ function collectRoutesFromState(state, depth = 0, parentPath = "") {
31
+ if (!state || !state.routes) return [];
32
+ const routes = [];
33
+ for (let i = 0; i < state.routes.length; i++) {
34
+ const route = state.routes[i];
35
+ const isFocused = i === state.index;
36
+
37
+ // Build the path
38
+ let pathname = route.name;
39
+ if (route.name === "index") {
40
+ pathname = "/";
41
+ } else if (!pathname.startsWith("/")) {
42
+ pathname = `/${pathname}`;
43
+ }
44
+
45
+ // Add parent path
46
+ if (parentPath && parentPath !== "/") {
47
+ pathname = `${parentPath}${pathname}`;
48
+ }
49
+
50
+ // Add params to pathname for dynamic routes
51
+ if (route.params) {
52
+ const paramEntries = Object.entries(route.params);
53
+ if (paramEntries.length > 0) {
54
+ pathname = pathname.replace(/\[([^\]]+)\]/g, (match, param) => {
55
+ return route.params[param] || match;
56
+ });
57
+ }
58
+ }
59
+ routes.push({
60
+ key: route.key,
61
+ name: route.name,
62
+ path: pathname,
63
+ params: route.params || {},
64
+ state: route.state
65
+ });
66
+
67
+ // If this route has nested state and is focused, recurse
68
+ if (isFocused && route.state) {
69
+ const nestedRoutes = collectRoutesFromState(route.state, depth + 1, pathname);
70
+ routes.push(...nestedRoutes);
71
+ }
72
+ }
73
+ return routes;
74
+ }
75
+
76
+ /**
77
+ * Access the current navigation stack from Expo Router
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * const { stack, focusedRoute, goBack, popToTop } = useNavigationStack();
82
+ *
83
+ * // Display stack
84
+ * stack.map(item => (
85
+ * <View key={item.key}>
86
+ * <Text>{item.pathname}</Text>
87
+ * {item.isFocused && <Text>VISIBLE</Text>}
88
+ * </View>
89
+ * ));
90
+ * ```
91
+ */
92
+ function useNavigationStack() {
93
+ const [isLoaded, setIsLoaded] = (0, _react.useState)(false);
94
+ const [error, setError] = (0, _react.useState)(null);
95
+ const navigation = (0, _native.useNavigation)();
96
+
97
+ // Subscribe to navigation state changes - this will cause re-renders when state updates
98
+ const navigationState = (0, _native.useNavigationState)(state => state);
99
+
100
+ // Mark as loaded once we have navigation
101
+ (0, _react.useEffect)(() => {
102
+ setIsLoaded(true);
103
+ }, []);
104
+
105
+ // Transform navigation state into display items
106
+ const stack = (0, _react.useMemo)(() => {
107
+ if (!navigationState) return [];
108
+ try {
109
+ const routes = collectRoutesFromState(navigationState);
110
+
111
+ // Filter out internal Expo Router routes (layouts, __root, etc.)
112
+ const filteredRoutes = routes.filter(route => {
113
+ // Remove __root and other internal routes
114
+ if (route.name.startsWith("__")) return false;
115
+ if (route.name.includes("_layout")) return false;
116
+ if (route.name.startsWith("+not-found")) return false;
117
+ return true;
118
+ });
119
+
120
+ // If we filtered everything out, just keep the last route
121
+ const finalRoutes = filteredRoutes.length > 0 ? filteredRoutes : routes.slice(-1);
122
+
123
+ // The last route in the collected list is the focused one
124
+ return finalRoutes.map((route, index) => {
125
+ // Clean up pathname by removing /__root prefix
126
+ let cleanPath = route.path || `/${route.name}`;
127
+ cleanPath = cleanPath.replace(/^\/__root/, "");
128
+ // Ensure we always have at least a /
129
+ if (!cleanPath || cleanPath === "") {
130
+ cleanPath = "/";
131
+ }
132
+ return {
133
+ key: route.key,
134
+ name: route.name,
135
+ pathname: cleanPath,
136
+ params: route.params || {},
137
+ isFocused: index === finalRoutes.length - 1,
138
+ index,
139
+ canPop: index > 0
140
+ };
141
+ });
142
+ } catch (err) {
143
+ console.error("Error transforming navigation state:", err);
144
+ return [];
145
+ }
146
+ }, [navigationState]);
147
+
148
+ // Get focused route
149
+ const focusedRoute = (0, _react.useMemo)(() => {
150
+ return stack.find(item => item.isFocused) || null;
151
+ }, [stack]);
152
+
153
+ // Helper properties
154
+ const stackDepth = stack.length;
155
+ const isAtRoot = stackDepth <= 1;
156
+
157
+ // Manual refresh (no-op since we're using React Navigation's state)
158
+ const refresh = () => {
159
+ // Navigation state updates automatically via useNavigationState
160
+ };
161
+
162
+ // Navigation actions
163
+ const navigateToIndex = index => {
164
+ if (index >= stack.length || index < 0) {
165
+ return;
166
+ }
167
+ const targetRoute = stack[index];
168
+ if (!targetRoute) {
169
+ return;
170
+ }
171
+
172
+ // Use router directly from expo-router
173
+ if (!_expoRouter.router) {
174
+ return;
175
+ }
176
+ try {
177
+ _expoRouter.router.navigate(targetRoute.pathname);
178
+ } catch (err) {
179
+ console.error("Failed to navigate:", err);
180
+ }
181
+ };
182
+ const popToIndex = index => {
183
+ if (index >= stack.length || index < 0) {
184
+ return;
185
+ }
186
+ const currentIndex = stack.length - 1;
187
+ const popCount = currentIndex - index;
188
+ if (popCount <= 0) {
189
+ return;
190
+ }
191
+
192
+ // Use router directly from expo-router
193
+ if (!_expoRouter.router) {
194
+ return;
195
+ }
196
+ try {
197
+ // Pop multiple times
198
+ for (let i = 0; i < popCount; i++) {
199
+ _expoRouter.router.back();
200
+ }
201
+ } catch (err) {
202
+ console.error("Failed to pop:", err);
203
+ }
204
+ };
205
+ const goBack = () => {
206
+ if (isAtRoot) {
207
+ return;
208
+ }
209
+
210
+ // Use router directly from expo-router
211
+ if (!_expoRouter.router) {
212
+ return;
213
+ }
214
+ try {
215
+ _expoRouter.router.back();
216
+ } catch (err) {
217
+ console.error("Failed to go back:", err);
218
+ }
219
+ };
220
+ const popToTop = () => {
221
+ if (isAtRoot) {
222
+ return;
223
+ }
224
+
225
+ // Pop to index 0 (root)
226
+ popToIndex(0);
227
+ };
228
+ return {
229
+ stack,
230
+ focusedRoute,
231
+ stackDepth,
232
+ isAtRoot,
233
+ isLoaded,
234
+ error,
235
+ refresh,
236
+ navigateToIndex,
237
+ popToIndex,
238
+ goBack,
239
+ popToTop
240
+ };
241
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useRouteObserver = useRouteObserver;
7
+ var _react = require("react");
8
+ var _expoRouter = require("expo-router");
9
+ var _RouteObserver = require("./RouteObserver");
10
+ /**
11
+ * useRouteObserver - React hook for observing route changes
12
+ *
13
+ * Uses public Expo Router hooks to track route changes and emits them
14
+ * to the global RouteObserver singleton.
15
+ */
16
+
17
+ /**
18
+ * Hook to observe route changes in Expo Router
19
+ * Automatically emits events to the global RouteObserver
20
+ *
21
+ * @param callback - Optional function to call on route changes (in addition to the observer)
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * // Just track routes (no custom callback)
26
+ * useRouteObserver();
27
+ *
28
+ * // Track routes with custom callback
29
+ * useRouteObserver((event) => {
30
+ * // Handle route change
31
+ * analytics.trackPageView(event.pathname);
32
+ * });
33
+ * ```
34
+ */
35
+ function useRouteObserver(callback) {
36
+ const pathname = (0, _expoRouter.usePathname)();
37
+ const segments = (0, _expoRouter.useSegments)();
38
+ const params = (0, _expoRouter.useGlobalSearchParams)();
39
+ const callbackRef = (0, _react.useRef)(callback);
40
+ const previousPathnameRef = (0, _react.useRef)(undefined);
41
+ const previousTimestampRef = (0, _react.useRef)(undefined);
42
+
43
+ // Update ref when callback changes
44
+ (0, _react.useEffect)(() => {
45
+ callbackRef.current = callback;
46
+ }, [callback]);
47
+
48
+ // Trigger observer and callback whenever route changes
49
+ (0, _react.useEffect)(() => {
50
+ const now = Date.now();
51
+ const timeSincePrevious = previousTimestampRef.current ? now - previousTimestampRef.current : undefined;
52
+ const event = {
53
+ pathname,
54
+ params: params,
55
+ segments: segments,
56
+ timestamp: now,
57
+ previousPathname: previousPathnameRef.current,
58
+ timeSincePrevious
59
+ };
60
+
61
+ // Update refs for next navigation
62
+ previousPathnameRef.current = pathname;
63
+ previousTimestampRef.current = now;
64
+
65
+ // Emit to the global observer (this notifies the modal)
66
+ _RouteObserver.routeObserver.emit(event);
67
+
68
+ // Also call the custom callback if provided
69
+ if (callbackRef.current) {
70
+ callbackRef.current(event);
71
+ }
72
+ }, [pathname, segments, params]);
73
+ }