expo-router 2.0.6 → 2.0.7
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/build/global-state/routing.d.ts +0 -4
- package/build/global-state/routing.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/global-state/routing.ts +83 -120
- package/build/link/stateOperations.d.ts +0 -81
- package/build/link/stateOperations.d.ts.map +0 -1
- package/src/link/stateOperations.ts +0 -163
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { getActionFromState } from "@react-navigation/core";
|
|
2
1
|
import { Href } from "../link/href";
|
|
3
|
-
import { NavigateAction } from "../link/stateOperations";
|
|
4
2
|
import type { RouterStore } from "./router-store";
|
|
5
3
|
export declare function push(this: RouterStore, url: Href): void;
|
|
6
4
|
export declare function replace(this: RouterStore, url: Href): void;
|
|
@@ -8,6 +6,4 @@ export declare function goBack(this: RouterStore): void;
|
|
|
8
6
|
export declare function canGoBack(this: RouterStore): boolean;
|
|
9
7
|
export declare function setParams(this: RouterStore, params?: Record<string, string | number>): any;
|
|
10
8
|
export declare function linkTo(this: RouterStore, href: string, event?: string): void;
|
|
11
|
-
/** @returns `true` if the action is moving to the first screen of all the navigators in the action. */
|
|
12
|
-
export declare function isAbsoluteInitialRoute(action: ReturnType<typeof getActionFromState>): action is NavigateAction;
|
|
13
9
|
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/global-state/routing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/global-state/routing.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAe,MAAM,cAAc,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAUlD,wBAAgB,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,QAEhD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,QAEnD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,QAGvC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAUpD;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAM,OAI7C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,QAsDrE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-router",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"main": "src/index.tsx",
|
|
5
5
|
"types": "build/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -105,12 +105,12 @@
|
|
|
105
105
|
},
|
|
106
106
|
"dependencies": {
|
|
107
107
|
"@bacons/react-views": "^1.1.3",
|
|
108
|
-
"@expo/metro-runtime": "2.2.
|
|
108
|
+
"@expo/metro-runtime": "2.2.9",
|
|
109
109
|
"@radix-ui/react-slot": "1.0.1",
|
|
110
110
|
"@react-navigation/bottom-tabs": "~6.5.7",
|
|
111
111
|
"@react-navigation/native": "~6.1.6",
|
|
112
112
|
"@react-navigation/native-stack": "~6.9.12",
|
|
113
|
-
"expo-head": "0.0.
|
|
113
|
+
"expo-head": "0.0.13",
|
|
114
114
|
"expo-splash-screen": "~0.20.2",
|
|
115
115
|
"query-string": "7.1.3",
|
|
116
116
|
"react-helmet-async": "^1.3.0",
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "@react-navigation/core";
|
|
6
|
-
import { TabActions } from "@react-navigation/native";
|
|
2
|
+
type NavigationAction,
|
|
3
|
+
type NavigationState,
|
|
4
|
+
} from "@react-navigation/native";
|
|
7
5
|
import * as Linking from "expo-linking";
|
|
8
6
|
|
|
7
|
+
import { ResultState } from "../fork/getStateFromPath";
|
|
9
8
|
import { Href, resolveHref } from "../link/href";
|
|
10
9
|
import { resolve } from "../link/path";
|
|
11
|
-
import {
|
|
12
|
-
NavigateAction,
|
|
13
|
-
findTopRouteForTarget,
|
|
14
|
-
getEarliestMismatchedRoute,
|
|
15
|
-
getQualifiedStateForTopOfTargetState,
|
|
16
|
-
isMovingToSiblingRoute,
|
|
17
|
-
} from "../link/stateOperations";
|
|
18
10
|
import { hasUrlProtocolPrefix } from "../utils/url";
|
|
19
11
|
import type { RouterStore } from "./router-store";
|
|
20
12
|
|
|
@@ -40,6 +32,11 @@ export function goBack(this: RouterStore) {
|
|
|
40
32
|
}
|
|
41
33
|
|
|
42
34
|
export function canGoBack(this: RouterStore): boolean {
|
|
35
|
+
// Return a default value here if the navigation hasn't mounted yet.
|
|
36
|
+
// This can happen if the user calls `canGoBack` from the Root Layout route
|
|
37
|
+
// before mounting a navigator. This behavior exists due to React Navigation being dynamically
|
|
38
|
+
// constructed at runtime. We can get rid of this in the future if we use
|
|
39
|
+
// the static configuration internally.
|
|
43
40
|
if (!this.navigationRef.isReady()) {
|
|
44
41
|
return false;
|
|
45
42
|
}
|
|
@@ -93,128 +90,94 @@ export function linkTo(this: RouterStore, href: string, event?: string) {
|
|
|
93
90
|
|
|
94
91
|
const state = this.linking.getStateFromPath!(href, this.linking.config);
|
|
95
92
|
|
|
96
|
-
if (!state) {
|
|
93
|
+
if (!state || state.routes.length === 0) {
|
|
97
94
|
console.error(
|
|
98
95
|
"Could not generate a valid navigation state for the given path: " + href
|
|
99
96
|
);
|
|
100
97
|
return;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const knownOwnerState = getQualifiedStateForTopOfTargetState(
|
|
111
|
-
rootState,
|
|
112
|
-
state
|
|
113
|
-
)!;
|
|
114
|
-
const nextRoute = findTopRouteForTarget(state);
|
|
115
|
-
// NOTE(EvanBacon): There's an issue where moving from "a -> b" is considered siblings:
|
|
116
|
-
// a. index (initialRouteName="index")
|
|
117
|
-
// b. stack/index
|
|
118
|
-
// However, the preservation approach doesn't work because it would be moving to a route with the same name.
|
|
119
|
-
// The next check will see if the current focused route has the same name as the next route, if so, then fallback on
|
|
120
|
-
// the default React Navigation logic.
|
|
121
|
-
if (
|
|
122
|
-
findTopRouteForTarget(
|
|
123
|
-
// @ts-expect-error: stale types don't matter here
|
|
124
|
-
rootState
|
|
125
|
-
)?.name !== nextRoute.name
|
|
126
|
-
) {
|
|
127
|
-
if (event === "REPLACE") {
|
|
128
|
-
if (knownOwnerState.type === "tab") {
|
|
129
|
-
navigationRef.dispatch(
|
|
130
|
-
TabActions.jumpTo(nextRoute.name, nextRoute.params)
|
|
131
|
-
);
|
|
132
|
-
} else {
|
|
133
|
-
navigationRef.dispatch(
|
|
134
|
-
StackActions.replace(nextRoute.name, nextRoute.params)
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
// NOTE: Not sure if we should pop or push here...
|
|
139
|
-
navigationRef.dispatch(
|
|
140
|
-
CommonActions.navigate(nextRoute.name, nextRoute.params)
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
100
|
+
switch (event) {
|
|
101
|
+
case "REPLACE":
|
|
102
|
+
return navigationRef.dispatch(
|
|
103
|
+
getNavigateReplaceAction(state, navigationRef.getRootState())
|
|
104
|
+
);
|
|
105
|
+
default:
|
|
106
|
+
return navigationRef.dispatch(getNavigatePushAction(state));
|
|
145
107
|
}
|
|
108
|
+
}
|
|
146
109
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Here we have a navigation action to a nested screen, where we should ideally replace.
|
|
152
|
-
// This request can only be fulfilled if the target is an initial route.
|
|
153
|
-
// First, check if the action is fully initial routes.
|
|
154
|
-
// Then find the nearest mismatched route in the existing state.
|
|
155
|
-
// Finally, use the correct navigator-based action to replace the nested screens.
|
|
156
|
-
// NOTE(EvanBacon): A future version of this will involve splitting the navigation request so we replace as much as possible, then push the remaining screens to fulfill the request.
|
|
157
|
-
if (event === "REPLACE" && isAbsoluteInitialRoute(action)) {
|
|
158
|
-
const earliest = getEarliestMismatchedRoute(rootState, action.payload);
|
|
159
|
-
if (earliest) {
|
|
160
|
-
if (earliest.type === "stack") {
|
|
161
|
-
navigationRef.dispatch(
|
|
162
|
-
StackActions.replace(earliest.name, earliest.params)
|
|
163
|
-
);
|
|
164
|
-
} else {
|
|
165
|
-
navigationRef.dispatch(
|
|
166
|
-
TabActions.jumpTo(earliest.name, earliest.params)
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
} else {
|
|
171
|
-
// This should never happen because moving to the same route would be handled earlier
|
|
172
|
-
// in the sibling operations.
|
|
173
|
-
}
|
|
174
|
-
}
|
|
110
|
+
type NavigationParams = Partial<{
|
|
111
|
+
screen: string;
|
|
112
|
+
params: NavigationParams;
|
|
113
|
+
}>;
|
|
175
114
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
115
|
+
function rewriteNavigationStateToParams(
|
|
116
|
+
state?: { routes: ResultState["routes"] },
|
|
117
|
+
params: NavigationParams = {}
|
|
118
|
+
) {
|
|
119
|
+
if (!state) return params;
|
|
120
|
+
// We Should always have at least one route in the state
|
|
121
|
+
const lastRoute = state.routes.at(-1)!;
|
|
122
|
+
params.screen = lastRoute.name;
|
|
123
|
+
// Weirdly, this always needs to be an object. If it's undefined, it won't work.
|
|
124
|
+
params.params = lastRoute.params ?? {};
|
|
125
|
+
|
|
126
|
+
if (lastRoute.state) {
|
|
127
|
+
rewriteNavigationStateToParams(lastRoute.state, params.params);
|
|
181
128
|
}
|
|
129
|
+
|
|
130
|
+
return params;
|
|
182
131
|
}
|
|
183
132
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
133
|
+
function getNavigatePushAction(state: ResultState) {
|
|
134
|
+
const { screen, params } = rewriteNavigationStateToParams(state);
|
|
135
|
+
return {
|
|
136
|
+
type: "NAVIGATE",
|
|
137
|
+
payload: {
|
|
138
|
+
name: screen,
|
|
139
|
+
params,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
191
143
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
144
|
+
function getNavigateReplaceAction(
|
|
145
|
+
previousState: ResultState,
|
|
146
|
+
parentState: NavigationState,
|
|
147
|
+
lastNavigatorSupportingReplace: NavigationState = parentState
|
|
148
|
+
): NavigationAction {
|
|
149
|
+
// We should always have at least one route in the state
|
|
150
|
+
const state = previousState.routes.at(-1)!;
|
|
151
|
+
|
|
152
|
+
// Only these navigators support replace
|
|
153
|
+
if (parentState.type === "stack" || parentState.type === "tab") {
|
|
154
|
+
lastNavigatorSupportingReplace = parentState;
|
|
205
155
|
}
|
|
206
156
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
157
|
+
const currentRoute = parentState.routes.find(
|
|
158
|
+
(route) => route.name === state.name
|
|
159
|
+
);
|
|
160
|
+
const routesAreEqual = parentState.routes[parentState.index] === currentRoute;
|
|
161
|
+
|
|
162
|
+
// If there is nested state and the routes are equal, we should keep going down the tree
|
|
163
|
+
if (state.state && routesAreEqual && currentRoute.state) {
|
|
164
|
+
return getNavigateReplaceAction(
|
|
165
|
+
state.state,
|
|
166
|
+
currentRoute.state as any,
|
|
167
|
+
lastNavigatorSupportingReplace
|
|
168
|
+
);
|
|
169
|
+
}
|
|
217
170
|
|
|
218
|
-
|
|
219
|
-
|
|
171
|
+
// Either we reached the bottom of the state or the point where the routes diverged
|
|
172
|
+
const { screen, params } = rewriteNavigationStateToParams(previousState);
|
|
173
|
+
return {
|
|
174
|
+
type:
|
|
175
|
+
lastNavigatorSupportingReplace.type === "stack" ? "REPLACE" : "JUMP_TO",
|
|
176
|
+
payload: {
|
|
177
|
+
name: screen,
|
|
178
|
+
params,
|
|
179
|
+
// Ensure that the last navigator supporting replace is the one that handles the action
|
|
180
|
+
source: lastNavigatorSupportingReplace?.key,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
220
183
|
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { InitialState, NavigationState, ParamListBase, PartialState, getActionFromState } from "@react-navigation/native";
|
|
2
|
-
import { ResultState } from "../fork/getStateFromPath";
|
|
3
|
-
export type NavigateAction = Extract<ReturnType<typeof getActionFromState>, {
|
|
4
|
-
type: "NAVIGATE";
|
|
5
|
-
}> & {
|
|
6
|
-
payload: NavigateActionParams;
|
|
7
|
-
};
|
|
8
|
-
export type NavigateActionParams = {
|
|
9
|
-
params?: NavigateActionParams;
|
|
10
|
-
path: string;
|
|
11
|
-
initial: boolean;
|
|
12
|
-
screen: string;
|
|
13
|
-
name?: string;
|
|
14
|
-
};
|
|
15
|
-
/** Return the absolute last route to move to. */
|
|
16
|
-
export declare function findTopRouteForTarget(state: ResultState): Omit<import("@react-navigation/native").Route<string, object | undefined>, "key"> & {
|
|
17
|
-
state?: Readonly<Partial<Omit<Readonly<{
|
|
18
|
-
key: string;
|
|
19
|
-
index: number;
|
|
20
|
-
routeNames: string[];
|
|
21
|
-
history?: unknown[] | undefined;
|
|
22
|
-
routes: (Readonly<{
|
|
23
|
-
key: string;
|
|
24
|
-
name: string;
|
|
25
|
-
path?: string | undefined;
|
|
26
|
-
}> & Readonly<{
|
|
27
|
-
params?: Readonly<object | undefined>;
|
|
28
|
-
}> & {
|
|
29
|
-
state?: Readonly<any> | PartialState<Readonly<any>> | undefined;
|
|
30
|
-
})[];
|
|
31
|
-
type: string;
|
|
32
|
-
stale: false;
|
|
33
|
-
}>, "stale" | "routes">> & {
|
|
34
|
-
routes: (Omit<import("@react-navigation/native").Route<string, object | undefined>, "key"> & any)[];
|
|
35
|
-
}> | undefined;
|
|
36
|
-
};
|
|
37
|
-
/** @returns true if moving to a sibling inside the same navigator. */
|
|
38
|
-
export declare function isMovingToSiblingRoute(currentState: NavigationState | PartialState<NavigationState> | undefined, targetState: ResultState | undefined): boolean;
|
|
39
|
-
export declare function getQualifiedStateForTopOfTargetState(rootState: InitialState, targetState: ResultState): Readonly<Partial<Omit<Readonly<{
|
|
40
|
-
key: string;
|
|
41
|
-
index: number;
|
|
42
|
-
routeNames: string[];
|
|
43
|
-
history?: unknown[] | undefined;
|
|
44
|
-
routes: (Readonly<{
|
|
45
|
-
key: string;
|
|
46
|
-
name: string;
|
|
47
|
-
path?: string | undefined;
|
|
48
|
-
}> & Readonly<{
|
|
49
|
-
params?: Readonly<object | undefined>;
|
|
50
|
-
}> & {
|
|
51
|
-
state?: Readonly<any> | PartialState<Readonly<any>> | undefined;
|
|
52
|
-
})[];
|
|
53
|
-
type: string;
|
|
54
|
-
stale: false;
|
|
55
|
-
}>, "stale" | "routes">> & {
|
|
56
|
-
routes: (Omit<import("@react-navigation/native").Route<string, object | undefined>, "key"> & {
|
|
57
|
-
state?: Readonly<Partial<Omit<Readonly<{
|
|
58
|
-
key: string;
|
|
59
|
-
index: number;
|
|
60
|
-
routeNames: string[];
|
|
61
|
-
history?: unknown[] | undefined;
|
|
62
|
-
routes: (Readonly<{
|
|
63
|
-
key: string;
|
|
64
|
-
name: string;
|
|
65
|
-
path?: string | undefined;
|
|
66
|
-
}> & Readonly<{
|
|
67
|
-
params?: Readonly<object | undefined>;
|
|
68
|
-
}> & {
|
|
69
|
-
state?: Readonly<any> | PartialState<Readonly<any>> | undefined;
|
|
70
|
-
})[];
|
|
71
|
-
type: string;
|
|
72
|
-
stale: false;
|
|
73
|
-
}>, "stale" | "routes">> & any> | undefined;
|
|
74
|
-
})[];
|
|
75
|
-
}>;
|
|
76
|
-
export declare function getEarliestMismatchedRoute<T extends ParamListBase>(rootState: NavigationState<T> | undefined, actionParams: NavigateActionParams): {
|
|
77
|
-
name: string;
|
|
78
|
-
params?: any;
|
|
79
|
-
type?: string;
|
|
80
|
-
} | null;
|
|
81
|
-
//# sourceMappingURL=stateOperations.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stateOperations.d.ts","sourceRoot":"","sources":["../../src/link/stateOperations.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,OAAO,CAClC,UAAU,CAAC,OAAO,kBAAkB,CAAC,EACrC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CACrB,GAAG;IACF,OAAO,EAAE,oBAAoB,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAwBF,iDAAiD;AACjD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;EAIvD;AAED,sEAAsE;AACtE,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,eAAe,GAAG,YAAY,CAAC,eAAe,CAAC,GAAG,SAAS,EACzE,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,OAAO,CAgCT;AAKD,wBAAgB,oCAAoC,CAClD,SAAS,EAAE,YAAY,EACvB,WAAW,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyBzB;AAKD,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,aAAa,EAChE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,SAAS,EACzC,YAAY,EAAE,oBAAoB,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgCtD"}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InitialState,
|
|
3
|
-
NavigationState,
|
|
4
|
-
ParamListBase,
|
|
5
|
-
PartialState,
|
|
6
|
-
getActionFromState,
|
|
7
|
-
} from "@react-navigation/native";
|
|
8
|
-
|
|
9
|
-
import { ResultState } from "../fork/getStateFromPath";
|
|
10
|
-
|
|
11
|
-
export type NavigateAction = Extract<
|
|
12
|
-
ReturnType<typeof getActionFromState>,
|
|
13
|
-
{ type: "NAVIGATE" }
|
|
14
|
-
> & {
|
|
15
|
-
payload: NavigateActionParams;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type NavigateActionParams = {
|
|
19
|
-
params?: NavigateActionParams;
|
|
20
|
-
path: string;
|
|
21
|
-
initial: boolean;
|
|
22
|
-
screen: string;
|
|
23
|
-
name?: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Get the last state for a given target state (generated from a path).
|
|
27
|
-
function findTopStateForTarget(state: ResultState) {
|
|
28
|
-
let current: Partial<InitialState> | undefined = state;
|
|
29
|
-
let previous: Partial<InitialState> | undefined = state;
|
|
30
|
-
|
|
31
|
-
while (current?.routes?.[current?.routes?.length - 1].state != null) {
|
|
32
|
-
previous = current;
|
|
33
|
-
current = current?.routes[current?.routes.length - 1].state;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// If the last route in the target state is an index route, return the previous state (parent).
|
|
37
|
-
// NOTE: This may need to be updated to support initial route name being a non-standard value.
|
|
38
|
-
if (
|
|
39
|
-
previous &&
|
|
40
|
-
current?.routes?.[current.routes.length - 1]!.name === "index"
|
|
41
|
-
) {
|
|
42
|
-
return previous;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return current;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Return the absolute last route to move to. */
|
|
49
|
-
export function findTopRouteForTarget(state: ResultState) {
|
|
50
|
-
const nextState = findTopStateForTarget(state)!;
|
|
51
|
-
// Ensure we get the last route to prevent returning the initial route.
|
|
52
|
-
return nextState.routes?.[nextState.routes.length - 1]!;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** @returns true if moving to a sibling inside the same navigator. */
|
|
56
|
-
export function isMovingToSiblingRoute(
|
|
57
|
-
currentState: NavigationState | PartialState<NavigationState> | undefined,
|
|
58
|
-
targetState: ResultState | undefined
|
|
59
|
-
): boolean {
|
|
60
|
-
if (!currentState || !targetState) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Need to type this, as the current types are not compaitble with the `find`
|
|
65
|
-
const targetRoute = targetState.routes[0];
|
|
66
|
-
|
|
67
|
-
// Make sure we're in the same navigator
|
|
68
|
-
if (!currentState.routeNames?.includes(targetRoute.name)) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// If there's no state, we're at the end of the path
|
|
73
|
-
if (!targetRoute.state) {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Coerce the types into a more common form
|
|
78
|
-
const currentRoutes:
|
|
79
|
-
| {
|
|
80
|
-
name: string;
|
|
81
|
-
state?: NavigationState | PartialState<NavigationState>;
|
|
82
|
-
}[]
|
|
83
|
-
| undefined = currentState?.routes;
|
|
84
|
-
const locatedState = currentRoutes?.find((r) => r.name === targetRoute.name);
|
|
85
|
-
|
|
86
|
-
if (!locatedState) {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return isMovingToSiblingRoute(locatedState.state, targetRoute.state);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Given the root state and a target state from `getStateFromPath`,
|
|
94
|
-
// return the root state containing the highest target route matching the root state.
|
|
95
|
-
// This can be used to determine what type of navigator action should be used.
|
|
96
|
-
export function getQualifiedStateForTopOfTargetState(
|
|
97
|
-
rootState: InitialState,
|
|
98
|
-
targetState: ResultState
|
|
99
|
-
) {
|
|
100
|
-
let current: InitialState | undefined = targetState;
|
|
101
|
-
let currentRoot: InitialState | undefined = rootState;
|
|
102
|
-
|
|
103
|
-
while (current?.routes?.[current?.routes?.length - 1].state != null) {
|
|
104
|
-
const nextRoute: any = current?.routes?.[current?.routes?.length - 1];
|
|
105
|
-
|
|
106
|
-
const nextCurrentRoot: InitialState | undefined = currentRoot?.routes?.find(
|
|
107
|
-
(route) => route.name === nextRoute.name
|
|
108
|
-
)?.state;
|
|
109
|
-
|
|
110
|
-
if (nextCurrentRoot == null) {
|
|
111
|
-
return currentRoot;
|
|
112
|
-
// Not sure what to do -- we're tracking against the assumption that
|
|
113
|
-
// all routes in the target state are in the root state
|
|
114
|
-
// currentRoot = undefined;
|
|
115
|
-
} else {
|
|
116
|
-
currentRoot = nextCurrentRoot;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
current = nextRoute.state;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return currentRoot;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Given the root state and a target state from `getStateFromPath`,
|
|
126
|
-
// return the root state containing the highest target route matching the root state.
|
|
127
|
-
// This can be used to determine what type of navigator action should be used.
|
|
128
|
-
export function getEarliestMismatchedRoute<T extends ParamListBase>(
|
|
129
|
-
rootState: NavigationState<T> | undefined,
|
|
130
|
-
actionParams: NavigateActionParams
|
|
131
|
-
): { name: string; params?: any; type?: string } | null {
|
|
132
|
-
const actionName = actionParams.name ?? actionParams.screen;
|
|
133
|
-
if (!rootState?.routes || rootState.index == null) {
|
|
134
|
-
// This should never happen where there's more action than state.
|
|
135
|
-
return {
|
|
136
|
-
name: actionName,
|
|
137
|
-
type: "stack",
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const nextCurrentRoot = rootState.routes[rootState.index];
|
|
142
|
-
if (actionName === nextCurrentRoot.name) {
|
|
143
|
-
if (!actionParams.params) {
|
|
144
|
-
// All routes match all the way up, no change required.
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return getEarliestMismatchedRoute(
|
|
149
|
-
// @react-navigation/native types this as NavigationState | Partial<NavigationState> | undefined
|
|
150
|
-
// In our usage, it's always a NavigationState | undefined
|
|
151
|
-
nextCurrentRoot.state as NavigationState<T> | undefined,
|
|
152
|
-
actionParams.params
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// There's a selected state but it doesn't match the action state
|
|
157
|
-
// this is now the lowest point of change.
|
|
158
|
-
return {
|
|
159
|
-
name: actionName,
|
|
160
|
-
params: actionParams.params,
|
|
161
|
-
type: rootState.type,
|
|
162
|
-
};
|
|
163
|
-
}
|