1 | import type {
|
2 | CommonActions,
|
3 | NavigationState,
|
4 | ParamListBase,
|
5 | PartialRoute,
|
6 | PartialState,
|
7 | Route,
|
8 | } from '@react-navigation/routers';
|
9 |
|
10 | import type { NavigatorScreenParams, PathConfig, PathConfigMap } from './types';
|
11 |
|
12 | type ConfigItem = {
|
13 | initialRouteName?: string;
|
14 | screens?: Record<string, ConfigItem>;
|
15 | };
|
16 |
|
17 | type Options = {
|
18 | initialRouteName?: string;
|
19 | screens: PathConfigMap<object>;
|
20 | };
|
21 |
|
22 | type NavigateAction<State extends NavigationState> = {
|
23 | type: 'NAVIGATE';
|
24 | payload: {
|
25 | name: string;
|
26 | params?: NavigatorScreenParams<State>;
|
27 | path?: string;
|
28 | };
|
29 | };
|
30 |
|
31 | export default function getActionFromState(
|
32 | state: PartialState<NavigationState>,
|
33 | options?: Options
|
34 | ): NavigateAction<NavigationState> | CommonActions.Action | undefined {
|
35 |
|
36 | const normalizedConfig = options
|
37 | ? createNormalizedConfigItem(options as PathConfig<object> | string)
|
38 | : {};
|
39 |
|
40 | const routes =
|
41 | state.index != null ? state.routes.slice(0, state.index + 1) : state.routes;
|
42 |
|
43 | if (routes.length === 0) {
|
44 | return undefined;
|
45 | }
|
46 |
|
47 | if (
|
48 | !(
|
49 | (routes.length === 1 && routes[0].key === undefined) ||
|
50 | (routes.length === 2 &&
|
51 | routes[0].key === undefined &&
|
52 | routes[0].name === normalizedConfig?.initialRouteName &&
|
53 | routes[1].key === undefined)
|
54 | )
|
55 | ) {
|
56 | return {
|
57 | type: 'RESET',
|
58 | payload: state,
|
59 | };
|
60 | }
|
61 |
|
62 | const route = state.routes[state.index ?? state.routes.length - 1];
|
63 |
|
64 | let current: PartialState<NavigationState> | undefined = route?.state;
|
65 | let config: ConfigItem | undefined = normalizedConfig?.screens?.[route?.name];
|
66 | let params = { ...route.params } as NavigatorScreenParams<
|
67 | ParamListBase,
|
68 | NavigationState
|
69 | >;
|
70 |
|
71 | let payload = route
|
72 | ? { name: route.name, path: route.path, params }
|
73 | : undefined;
|
74 |
|
75 | while (current) {
|
76 | if (current.routes.length === 0) {
|
77 | return undefined;
|
78 | }
|
79 |
|
80 | const routes =
|
81 | current.index != null
|
82 | ? current.routes.slice(0, current.index + 1)
|
83 | : current.routes;
|
84 |
|
85 | const route: Route<string> | PartialRoute<Route<string>> =
|
86 | routes[routes.length - 1];
|
87 |
|
88 |
|
89 | Object.assign(params, {
|
90 | initial: undefined,
|
91 | screen: undefined,
|
92 | params: undefined,
|
93 | state: undefined,
|
94 | });
|
95 |
|
96 | if (routes.length === 1 && routes[0].key === undefined) {
|
97 | params.initial = true;
|
98 | params.screen = route.name;
|
99 | } else if (
|
100 | routes.length === 2 &&
|
101 | routes[0].key === undefined &&
|
102 | routes[0].name === config?.initialRouteName &&
|
103 | routes[1].key === undefined
|
104 | ) {
|
105 | params.initial = false;
|
106 | params.screen = route.name;
|
107 | } else {
|
108 | params.state = current;
|
109 | break;
|
110 | }
|
111 |
|
112 | if (route.state) {
|
113 | params.params = { ...route.params };
|
114 | params = params.params as NavigatorScreenParams<
|
115 | ParamListBase,
|
116 | NavigationState
|
117 | >;
|
118 | } else {
|
119 | params.path = route.path;
|
120 | params.params = route.params;
|
121 | }
|
122 |
|
123 | current = route.state;
|
124 | config = config?.screens?.[route.name];
|
125 | }
|
126 |
|
127 | if (!payload) {
|
128 | return;
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 | return {
|
134 | type: 'NAVIGATE',
|
135 | payload,
|
136 | };
|
137 | }
|
138 |
|
139 | const createNormalizedConfigItem = (config: PathConfig<object> | string) =>
|
140 | typeof config === 'object' && config != null
|
141 | ? {
|
142 | initialRouteName: config.initialRouteName,
|
143 | screens:
|
144 | config.screens != null
|
145 | ? createNormalizedConfigs(config.screens)
|
146 | : undefined,
|
147 | }
|
148 | : {};
|
149 |
|
150 | const createNormalizedConfigs = (options: PathConfigMap<object>) =>
|
151 | Object.entries(options).reduce<Record<string, ConfigItem>>((acc, [k, v]) => {
|
152 | acc[k] = createNormalizedConfigItem(v);
|
153 | return acc;
|
154 | }, {});
|
155 |
|
\ | No newline at end of file |