@buoy-gg/route-events 3.0.1 → 3.0.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.
- package/lib/commonjs/RouteTracker.js +38 -4
- package/lib/commonjs/components/NavigationStack.js +47 -31
- package/lib/commonjs/components/RoutesSitemap.js +176 -133
- package/lib/commonjs/expoRouterStore.js +17 -0
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/stores/navigationStackStore.js +45 -0
- package/lib/commonjs/sync/routeEventsSyncAdapter.js +148 -0
- package/lib/commonjs/useRouteSitemap.js +28 -7
- package/lib/module/RouteTracker.js +39 -5
- package/lib/module/components/NavigationStack.js +47 -31
- package/lib/module/components/RoutesSitemap.js +177 -134
- package/lib/module/expoRouterStore.js +16 -0
- package/lib/module/index.js +4 -0
- package/lib/module/stores/navigationStackStore.js +41 -0
- package/lib/module/sync/routeEventsSyncAdapter.js +145 -0
- package/lib/module/useRouteSitemap.js +29 -8
- package/lib/typescript/RouteTracker.d.ts.map +1 -1
- package/lib/typescript/components/NavigationStack.d.ts +18 -1
- package/lib/typescript/components/NavigationStack.d.ts.map +1 -1
- package/lib/typescript/components/RoutesSitemap.d.ts +19 -1
- package/lib/typescript/components/RoutesSitemap.d.ts.map +1 -1
- package/lib/typescript/expoRouterStore.d.ts +8 -0
- package/lib/typescript/expoRouterStore.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +3 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/stores/navigationStackStore.d.ts +33 -0
- package/lib/typescript/stores/navigationStackStore.d.ts.map +1 -0
- package/lib/typescript/sync/routeEventsSyncAdapter.d.ts +69 -0
- package/lib/typescript/sync/routeEventsSyncAdapter.d.ts.map +1 -0
- package/lib/typescript/useRouteSitemap.d.ts +17 -0
- package/lib/typescript/useRouteSitemap.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.navigationStackStore = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Navigation Stack Store
|
|
9
|
+
*
|
|
10
|
+
* Holds the latest serializable navigation stack plus references to the live
|
|
11
|
+
* navigation action functions. The stack is captured inside the navigation tree
|
|
12
|
+
* by <RouteTracker /> (which runs useNavigationStack), and read by the
|
|
13
|
+
* route-events sync adapter so the dashboard can render the Stack tab and drive
|
|
14
|
+
* navigation on the device remotely.
|
|
15
|
+
*
|
|
16
|
+
* The stack itself (StackDisplayItem[]) is JSON-serializable and synced to the
|
|
17
|
+
* dashboard. The action functions are NOT serialized — they stay on the device
|
|
18
|
+
* and are invoked when the dashboard sends a remote action.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
class NavigationStackStore {
|
|
22
|
+
stack = [];
|
|
23
|
+
actions = null;
|
|
24
|
+
listeners = new Set();
|
|
25
|
+
getStack() {
|
|
26
|
+
return this.stack;
|
|
27
|
+
}
|
|
28
|
+
setStack(stack) {
|
|
29
|
+
this.stack = stack;
|
|
30
|
+
this.listeners.forEach(listener => listener());
|
|
31
|
+
}
|
|
32
|
+
getActions() {
|
|
33
|
+
return this.actions;
|
|
34
|
+
}
|
|
35
|
+
setActions(actions) {
|
|
36
|
+
this.actions = actions;
|
|
37
|
+
}
|
|
38
|
+
subscribe(listener) {
|
|
39
|
+
this.listeners.add(listener);
|
|
40
|
+
return () => {
|
|
41
|
+
this.listeners.delete(listener);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const navigationStackStore = exports.navigationStackStore = new NavigationStackStore();
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.routeEventsSyncAdapter = void 0;
|
|
7
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
8
|
+
var _routeEventStore = require("../stores/routeEventStore");
|
|
9
|
+
var _navigationStackStore = require("../stores/navigationStackStore");
|
|
10
|
+
var _RouteParser = require("../RouteParser");
|
|
11
|
+
var _expoRouterStore = require("../expoRouterStore");
|
|
12
|
+
/**
|
|
13
|
+
* Serializable snapshot of the device's route tree, sent to the dashboard so
|
|
14
|
+
* the Routes tab can render a sitemap (the raw expo-router RouteNode itself is
|
|
15
|
+
* not JSON-serializable — it holds components/functions — so we send the parsed
|
|
16
|
+
* RouteInfo[] instead).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Payload shape for the route-events tool (adapter version 3). Consumers must
|
|
21
|
+
* tolerate older shapes: v1 sends a bare RouteChangeEvent[]; v2 omits `stack`.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
function getSitemapSnapshot() {
|
|
25
|
+
const metadata = (0, _expoRouterStore.getRouteNodeMetadata)();
|
|
26
|
+
return {
|
|
27
|
+
routes: _RouteParser.RouteParser.parseRouteTree((0, _expoRouterStore.loadRouteNode)()),
|
|
28
|
+
source: metadata.source,
|
|
29
|
+
lastUpdatedAt: metadata.lastLoadedAt
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Sync adapter for the route-events tool, consumed by @buoy-gg/external-sync's
|
|
35
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
36
|
+
* this package doesn't need a dependency on it).
|
|
37
|
+
*
|
|
38
|
+
* Subscribing attaches the routeObserver listener, so route changes are only
|
|
39
|
+
* recorded while a dashboard is watching. (Route events still require a
|
|
40
|
+
* <RouteTracker /> inside the navigation tree to be emitted at all.)
|
|
41
|
+
*
|
|
42
|
+
* The snapshot carries both the route-change events AND the parsed route
|
|
43
|
+
* sitemap, so the dashboard — which has no local expo-router store — can render
|
|
44
|
+
* the route tree. The `navigate` action lets the dashboard drive navigation on
|
|
45
|
+
* the device.
|
|
46
|
+
*/
|
|
47
|
+
const routeEventsSyncAdapter = exports.routeEventsSyncAdapter = {
|
|
48
|
+
version: 3,
|
|
49
|
+
getSnapshot: () => ({
|
|
50
|
+
events: _routeEventStore.routeEventStore.getEvents(),
|
|
51
|
+
sitemap: getSitemapSnapshot(),
|
|
52
|
+
stack: _navigationStackStore.navigationStackStore.getStack()
|
|
53
|
+
}),
|
|
54
|
+
subscribe: onChange => {
|
|
55
|
+
const unsubscribeEvents = _routeEventStore.routeEventStore.subscribeToEvents(onChange);
|
|
56
|
+
const unsubscribeStack = _navigationStackStore.navigationStackStore.subscribe(onChange);
|
|
57
|
+
|
|
58
|
+
// The expo-router route tree can still be populating when a dashboard
|
|
59
|
+
// starts watching. Poll briefly until it's available and push a snapshot
|
|
60
|
+
// the moment it loads, so the Routes tab fills in without needing the user
|
|
61
|
+
// to navigate first. Stops as soon as the tree is found.
|
|
62
|
+
let pollTimer = null;
|
|
63
|
+
if (!(0, _expoRouterStore.loadRouteNode)()) {
|
|
64
|
+
let retries = 0;
|
|
65
|
+
const maxRetries = 100; // ~10s
|
|
66
|
+
const poll = () => {
|
|
67
|
+
retries += 1;
|
|
68
|
+
if ((0, _expoRouterStore.loadRouteNode)()) {
|
|
69
|
+
pollTimer = null;
|
|
70
|
+
onChange();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
pollTimer = retries < maxRetries ? setTimeout(poll, 100) : null;
|
|
74
|
+
};
|
|
75
|
+
pollTimer = setTimeout(poll, 100);
|
|
76
|
+
}
|
|
77
|
+
return () => {
|
|
78
|
+
unsubscribeEvents();
|
|
79
|
+
unsubscribeStack();
|
|
80
|
+
if (pollTimer) clearTimeout(pollTimer);
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
actions: {
|
|
84
|
+
clearEvents: () => {
|
|
85
|
+
_routeEventStore.routeEventStore.clearEvents();
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Navigate the device to a concrete path. The dashboard resolves any
|
|
89
|
+
* dynamic params into a concrete path before invoking this.
|
|
90
|
+
*/
|
|
91
|
+
navigate: params => {
|
|
92
|
+
const path = params?.path;
|
|
93
|
+
if (!path) {
|
|
94
|
+
throw new Error("navigate requires a 'path' param");
|
|
95
|
+
}
|
|
96
|
+
const router = (0, _sharedUi.getSafeRouter)();
|
|
97
|
+
if (!router) {
|
|
98
|
+
throw new Error("expo-router is not available on this device");
|
|
99
|
+
}
|
|
100
|
+
router.navigate(path);
|
|
101
|
+
return {
|
|
102
|
+
navigated: path
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
// ── Stack actions: delegate to the live navigation actions captured by
|
|
106
|
+
// <RouteTracker /> (they hold the React Navigation container ref). ──
|
|
107
|
+
stackNavigateToIndex: params => {
|
|
108
|
+
const index = params?.index;
|
|
109
|
+
if (typeof index !== "number") {
|
|
110
|
+
throw new Error("stackNavigateToIndex requires a numeric 'index'");
|
|
111
|
+
}
|
|
112
|
+
const actions = _navigationStackStore.navigationStackStore.getActions();
|
|
113
|
+
if (!actions) throw new Error("navigation stack is not available");
|
|
114
|
+
actions.navigateToIndex(index);
|
|
115
|
+
return {
|
|
116
|
+
navigatedToIndex: index
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
stackPopToIndex: params => {
|
|
120
|
+
const index = params?.index;
|
|
121
|
+
if (typeof index !== "number") {
|
|
122
|
+
throw new Error("stackPopToIndex requires a numeric 'index'");
|
|
123
|
+
}
|
|
124
|
+
const actions = _navigationStackStore.navigationStackStore.getActions();
|
|
125
|
+
if (!actions) throw new Error("navigation stack is not available");
|
|
126
|
+
actions.popToIndex(index);
|
|
127
|
+
return {
|
|
128
|
+
poppedToIndex: index
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
stackGoBack: () => {
|
|
132
|
+
const actions = _navigationStackStore.navigationStackStore.getActions();
|
|
133
|
+
if (!actions) throw new Error("navigation stack is not available");
|
|
134
|
+
actions.goBack();
|
|
135
|
+
return {
|
|
136
|
+
wentBack: true
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
stackPopToTop: () => {
|
|
140
|
+
const actions = _navigationStackStore.navigationStackStore.getActions();
|
|
141
|
+
if (!actions) throw new Error("navigation stack is not available");
|
|
142
|
+
actions.popToTop();
|
|
143
|
+
return {
|
|
144
|
+
poppedToTop: true
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
@@ -96,8 +96,12 @@ function useRouteSitemap(options = {}) {
|
|
|
96
96
|
searchQuery = "",
|
|
97
97
|
sortBy = "path",
|
|
98
98
|
autoRefresh = false,
|
|
99
|
-
refreshInterval = 1000
|
|
99
|
+
refreshInterval = 1000,
|
|
100
|
+
injectedRoutes,
|
|
101
|
+
injectedSource = null,
|
|
102
|
+
injectedLastUpdatedAt = null
|
|
100
103
|
} = options;
|
|
104
|
+
const hasInjectedRoutes = injectedRoutes != null;
|
|
101
105
|
const [routeTreeState, setRouteTreeState] = (0, _react.useState)(() => {
|
|
102
106
|
const node = (0, _expoRouterStore.loadRouteNode)();
|
|
103
107
|
const metadata = (0, _expoRouterStore.getRouteNodeMetadata)();
|
|
@@ -132,6 +136,16 @@ function useRouteSitemap(options = {}) {
|
|
|
132
136
|
if (routeNode) {
|
|
133
137
|
return;
|
|
134
138
|
}
|
|
139
|
+
|
|
140
|
+
// Routes injected from outside (dashboard) don't come from the local store.
|
|
141
|
+
if (hasInjectedRoutes) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// On web (no expo-router) the tree will never load — don't poll forever.
|
|
146
|
+
if (!(0, _expoRouterStore.isExpoRouterStoreSupported)()) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
135
149
|
let retryCount = 0;
|
|
136
150
|
const maxRetries = 100; // 10 seconds max
|
|
137
151
|
|
|
@@ -144,7 +158,7 @@ function useRouteSitemap(options = {}) {
|
|
|
144
158
|
};
|
|
145
159
|
let timeoutRef = setTimeout(poll, 100);
|
|
146
160
|
return () => clearTimeout(timeoutRef);
|
|
147
|
-
}, [routeNode, refresh]);
|
|
161
|
+
}, [routeNode, refresh, hasInjectedRoutes]);
|
|
148
162
|
|
|
149
163
|
// Optional auto-refresh hook for callers that want periodic updates
|
|
150
164
|
(0, _react.useEffect)(() => {
|
|
@@ -152,13 +166,19 @@ function useRouteSitemap(options = {}) {
|
|
|
152
166
|
const interval = setInterval(refresh, refreshInterval);
|
|
153
167
|
return () => clearInterval(interval);
|
|
154
168
|
}, [autoRefresh, refreshInterval, refresh]);
|
|
155
|
-
const isLoaded = !!routeNode;
|
|
156
169
|
|
|
157
|
-
//
|
|
170
|
+
// With injected routes we're "supported" regardless of the local runtime, and
|
|
171
|
+
// "loaded" once the device has actually sent a non-empty tree (an empty array
|
|
172
|
+
// means the device is still booting expo-router → keep showing "Loading...").
|
|
173
|
+
const isLoaded = hasInjectedRoutes ? injectedRoutes.length > 0 : !!routeNode;
|
|
174
|
+
const isSupported = hasInjectedRoutes ? true : (0, _expoRouterStore.isExpoRouterStoreSupported)();
|
|
175
|
+
|
|
176
|
+
// Parse routes (or use the pre-parsed routes injected from the dashboard).
|
|
158
177
|
const routes = (0, _react.useMemo)(() => {
|
|
178
|
+
if (hasInjectedRoutes) return injectedRoutes;
|
|
159
179
|
if (!routeNode) return [];
|
|
160
180
|
return _RouteParser.RouteParser.parseRouteTree(routeNode);
|
|
161
|
-
}, [routeNode, routeNodeVersion]);
|
|
181
|
+
}, [routeNode, routeNodeVersion, hasInjectedRoutes, injectedRoutes]);
|
|
162
182
|
|
|
163
183
|
// Sort routes
|
|
164
184
|
const sortedRoutes = (0, _react.useMemo)(() => {
|
|
@@ -190,11 +210,12 @@ function useRouteSitemap(options = {}) {
|
|
|
190
210
|
stats,
|
|
191
211
|
filteredRoutes,
|
|
192
212
|
isLoaded,
|
|
213
|
+
isSupported,
|
|
193
214
|
refresh,
|
|
194
215
|
findRoute,
|
|
195
216
|
getParents,
|
|
196
|
-
lastUpdatedAt,
|
|
197
|
-
source
|
|
217
|
+
lastUpdatedAt: hasInjectedRoutes ? injectedLastUpdatedAt : lastUpdatedAt,
|
|
218
|
+
source: hasInjectedRoutes ? injectedSource : source
|
|
198
219
|
};
|
|
199
220
|
}
|
|
200
221
|
|
|
@@ -41,10 +41,13 @@
|
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
+
import { useEffect } from "react";
|
|
44
45
|
import { isExpoRouterAvailable } from "@buoy-gg/shared-ui";
|
|
45
46
|
import { useRouteObserver } from "./useRouteObserver";
|
|
46
47
|
import { useRouteObserverReactNavigation } from "./useRouteObserverReactNavigation";
|
|
47
|
-
import {
|
|
48
|
+
import { useNavigationStack } from "./useNavigationStack";
|
|
49
|
+
import { navigationStackStore } from "./stores/navigationStackStore";
|
|
50
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
48
51
|
function ExpoRouteTracker() {
|
|
49
52
|
useRouteObserver();
|
|
50
53
|
return null;
|
|
@@ -53,10 +56,41 @@ function ReactNavigationRouteTracker() {
|
|
|
53
56
|
useRouteObserverReactNavigation();
|
|
54
57
|
return null;
|
|
55
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Mirrors the live navigation stack (and its action functions) into
|
|
62
|
+
* navigationStackStore so the route-events sync adapter can expose the Stack
|
|
63
|
+
* tab to the dashboard. Runs inside the navigation tree where the container ref
|
|
64
|
+
* context is available.
|
|
65
|
+
*/
|
|
66
|
+
function NavigationStackCapture() {
|
|
67
|
+
const {
|
|
68
|
+
stack,
|
|
69
|
+
navigateToIndex,
|
|
70
|
+
popToIndex,
|
|
71
|
+
goBack,
|
|
72
|
+
popToTop
|
|
73
|
+
} = useNavigationStack();
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
navigationStackStore.setStack(stack);
|
|
76
|
+
}, [stack]);
|
|
77
|
+
|
|
78
|
+
// Keep the action references fresh (they change as the stack changes) so a
|
|
79
|
+
// remote action always operates on the current navigation state.
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
navigationStackStore.setActions({
|
|
82
|
+
navigateToIndex,
|
|
83
|
+
popToIndex,
|
|
84
|
+
goBack,
|
|
85
|
+
popToTop
|
|
86
|
+
});
|
|
87
|
+
return () => navigationStackStore.setActions(null);
|
|
88
|
+
});
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
56
91
|
export function RouteTracker() {
|
|
57
92
|
const hasExpo = isExpoRouterAvailable();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
return /*#__PURE__*/_jsx(ReactNavigationRouteTracker, {});
|
|
93
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
94
|
+
children: [hasExpo ? /*#__PURE__*/_jsx(ExpoRouteTracker, {}) : /*#__PURE__*/_jsx(ReactNavigationRouteTracker, {}), /*#__PURE__*/_jsx(NavigationStackCapture, {})]
|
|
95
|
+
});
|
|
62
96
|
}
|
|
@@ -17,25 +17,35 @@ import { useNavigationStack } from "../useNavigationStack";
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
// Types
|
|
19
19
|
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/** Stack manipulation a host can be asked to perform on the device. */
|
|
20
22
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
21
23
|
// ============================================================================
|
|
22
24
|
// Main Component
|
|
23
25
|
// ============================================================================
|
|
24
26
|
|
|
25
27
|
export function NavigationStack({
|
|
26
|
-
style
|
|
28
|
+
style,
|
|
29
|
+
injectedStack,
|
|
30
|
+
onAction
|
|
27
31
|
}) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const hookResult = useNavigationStack();
|
|
33
|
+
const isInjected = injectedStack != null;
|
|
34
|
+
|
|
35
|
+
// Data source: injected (dashboard) or the local navigation container.
|
|
36
|
+
const stack = isInjected ? injectedStack : hookResult.stack;
|
|
37
|
+
const isLoaded = isInjected ? true : hookResult.isLoaded;
|
|
38
|
+
const error = isInjected ? null : hookResult.error;
|
|
39
|
+
const focusedRoute = useMemo(() => stack.find(item => item.isFocused) ?? null, [stack]);
|
|
40
|
+
const stackDepth = stack.length;
|
|
41
|
+
const isAtRoot = stackDepth <= 1;
|
|
42
|
+
|
|
43
|
+
// Action wrappers: delegate to the host when provided, else act locally.
|
|
44
|
+
const navigateToIndex = index => onAction ? onAction("navigateToIndex", {
|
|
45
|
+
index
|
|
46
|
+
}) : hookResult.navigateToIndex(index);
|
|
47
|
+
const goBack = () => onAction ? onAction("goBack") : hookResult.goBack();
|
|
48
|
+
const popToTop = () => onAction ? onAction("popToTop") : hookResult.popToTop();
|
|
39
49
|
const [expandedIndex, setExpandedIndex] = useState(null);
|
|
40
50
|
const [showHelp, setShowHelp] = useState(false);
|
|
41
51
|
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
|
@@ -124,6 +134,26 @@ export function NavigationStack({
|
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
// Handlers
|
|
137
|
+
// Confirm a destructive action. On device we use the native Alert; when an
|
|
138
|
+
// action delegate is set (dashboard, react-native-web) Alert button presses
|
|
139
|
+
// don't fire, so fall back to window.confirm.
|
|
140
|
+
const confirmDestructive = (title, message, confirmLabel, onConfirm) => {
|
|
141
|
+
if (onAction) {
|
|
142
|
+
const confirmFn = globalThis.window?.confirm;
|
|
143
|
+
if (!confirmFn || confirmFn(`${title}\n\n${message}`)) {
|
|
144
|
+
onConfirm();
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
Alert.alert(title, message, [{
|
|
149
|
+
text: "Cancel",
|
|
150
|
+
style: "cancel"
|
|
151
|
+
}, {
|
|
152
|
+
text: confirmLabel,
|
|
153
|
+
style: "destructive",
|
|
154
|
+
onPress: onConfirm
|
|
155
|
+
}]);
|
|
156
|
+
};
|
|
127
157
|
const handleGoBack = () => {
|
|
128
158
|
// Gate behind Pro
|
|
129
159
|
if (!isPro) {
|
|
@@ -146,14 +176,7 @@ export function NavigationStack({
|
|
|
146
176
|
Alert.alert("Already at Top", "Stack only has one screen");
|
|
147
177
|
return;
|
|
148
178
|
}
|
|
149
|
-
|
|
150
|
-
text: "Cancel",
|
|
151
|
-
style: "cancel"
|
|
152
|
-
}, {
|
|
153
|
-
text: "Pop to Top",
|
|
154
|
-
style: "destructive",
|
|
155
|
-
onPress: popToTop
|
|
156
|
-
}]);
|
|
179
|
+
confirmDestructive("Pop to Top", "This will remove all screens except the root screen.", "Pop to Top", popToTop);
|
|
157
180
|
};
|
|
158
181
|
const toggleExpand = index => {
|
|
159
182
|
setExpandedIndex(expandedIndex === index ? null : index);
|
|
@@ -195,17 +218,10 @@ export function NavigationStack({
|
|
|
195
218
|
return;
|
|
196
219
|
}
|
|
197
220
|
const screensToRemove = stackDepth - 1 - selectedRoute.index;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
text: "Pop",
|
|
203
|
-
style: "destructive",
|
|
204
|
-
onPress: () => {
|
|
205
|
-
// Navigate to the selected route, which effectively pops everything above it
|
|
206
|
-
navigateToIndex(selectedRoute.index);
|
|
207
|
-
}
|
|
208
|
-
}]);
|
|
221
|
+
const targetIndex = selectedRoute.index;
|
|
222
|
+
confirmDestructive("Pop to Route", `Remove ${screensToRemove} screen${screensToRemove !== 1 ? "s" : ""} above ${selectedRoute.pathname}?`, "Pop",
|
|
223
|
+
// Navigate to the selected route, which effectively pops everything above it
|
|
224
|
+
() => navigateToIndex(targetIndex));
|
|
209
225
|
};
|
|
210
226
|
return /*#__PURE__*/_jsxs(View, {
|
|
211
227
|
style: [styles.container, style],
|