@react-navigation/native 7.1.25 → 8.0.0-alpha.0
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/module/Link.js.map +1 -1
- package/lib/module/NavigationContainer.js +73 -63
- package/lib/module/NavigationContainer.js.map +1 -1
- package/lib/module/__stubs__/createStackNavigator.js.map +1 -1
- package/lib/module/extractPathFromURL.js +12 -7
- package/lib/module/extractPathFromURL.js.map +1 -1
- package/lib/module/getStateFromHref.js +30 -0
- package/lib/module/getStateFromHref.js.map +1 -0
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/theming/DarkTheme.js.map +1 -1
- package/lib/module/theming/DefaultTheme.js +1 -1
- package/lib/module/theming/DefaultTheme.js.map +1 -1
- package/lib/module/theming/fonts.js.map +1 -1
- package/lib/module/useLinkBuilder.js +5 -8
- package/lib/module/useLinkBuilder.js.map +1 -1
- package/lib/module/useLinkProps.js +20 -0
- package/lib/module/useLinkProps.js.map +1 -1
- package/lib/module/useLinking.js +23 -24
- package/lib/module/useLinking.js.map +1 -1
- package/lib/module/useLinking.native.js +21 -36
- package/lib/module/useLinking.native.js.map +1 -1
- package/lib/module/useScrollToTop.js +1 -0
- package/lib/module/useScrollToTop.js.map +1 -1
- package/lib/module/useThenable.js +9 -6
- package/lib/module/useThenable.js.map +1 -1
- package/lib/typescript/src/Link.d.ts +3 -2
- package/lib/typescript/src/Link.d.ts.map +1 -1
- package/lib/typescript/src/NavigationContainer.d.ts +35 -7
- package/lib/typescript/src/NavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/__stubs__/createStackNavigator.d.ts +1 -2
- package/lib/typescript/src/__stubs__/createStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/extractPathFromURL.d.ts +2 -1
- package/lib/typescript/src/extractPathFromURL.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromHref.d.ts +4 -0
- package/lib/typescript/src/getStateFromHref.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +0 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/theming/DarkTheme.d.ts +63 -2
- package/lib/typescript/src/theming/DarkTheme.d.ts.map +1 -1
- package/lib/typescript/src/theming/DefaultTheme.d.ts +63 -2
- package/lib/typescript/src/theming/DefaultTheme.d.ts.map +1 -1
- package/lib/typescript/src/theming/fonts.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +49 -23
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useLinkBuilder.d.ts +14 -6
- package/lib/typescript/src/useLinkBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useLinkProps.d.ts +3 -3
- package/lib/typescript/src/useLinkProps.d.ts.map +1 -1
- package/lib/typescript/src/useLinking.d.ts +48 -4
- package/lib/typescript/src/useLinking.d.ts.map +1 -1
- package/lib/typescript/src/useLinking.native.d.ts +69 -4
- package/lib/typescript/src/useLinking.native.d.ts.map +1 -1
- package/lib/typescript/src/useScrollToTop.d.ts.map +1 -1
- package/lib/typescript/src/useThenable.d.ts +4 -1
- package/lib/typescript/src/useThenable.d.ts.map +1 -1
- package/package.json +13 -13
- package/src/Link.tsx +18 -15
- package/src/NavigationContainer.tsx +141 -84
- package/src/__stubs__/createStackNavigator.tsx +0 -2
- package/src/extractPathFromURL.tsx +28 -18
- package/src/getStateFromHref.tsx +46 -0
- package/src/index.tsx +0 -1
- package/src/theming/DarkTheme.tsx +4 -3
- package/src/theming/DefaultTheme.tsx +5 -4
- package/src/theming/fonts.tsx +1 -2
- package/src/types.tsx +60 -22
- package/src/useLinkBuilder.tsx +6 -9
- package/src/useLinkProps.tsx +36 -7
- package/src/useLinking.native.tsx +25 -48
- package/src/useLinking.tsx +32 -38
- package/src/useScrollToTop.tsx +3 -1
- package/src/useThenable.tsx +14 -6
- package/lib/module/UnhandledLinkingContext.js +0 -14
- package/lib/module/UnhandledLinkingContext.js.map +0 -1
- package/lib/typescript/src/UnhandledLinkingContext.d.ts +0 -6
- package/lib/typescript/src/UnhandledLinkingContext.d.ts.map +0 -1
- package/src/UnhandledLinkingContext.tsx +0 -18
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
getActionFromState,
|
|
4
4
|
getPathFromState,
|
|
5
5
|
getStateFromPath,
|
|
6
|
+
type InitialState,
|
|
6
7
|
type NavigationContainerProps,
|
|
7
8
|
type NavigationContainerRef,
|
|
8
|
-
type NavigationState,
|
|
9
9
|
type ParamListBase,
|
|
10
|
+
type RootParamList,
|
|
10
11
|
ThemeProvider,
|
|
11
12
|
validatePathConfig,
|
|
12
13
|
} from '@react-navigation/core';
|
|
13
14
|
import * as React from 'react';
|
|
14
|
-
import { I18nManager } from 'react-native';
|
|
15
|
-
import useLatestCallback from 'use-latest-callback';
|
|
15
|
+
import { I18nManager, Platform } from 'react-native';
|
|
16
16
|
|
|
17
17
|
import { LinkingContext } from './LinkingContext';
|
|
18
18
|
import { LocaleDirContext } from './LocaleDirContext';
|
|
@@ -21,12 +21,12 @@ import type {
|
|
|
21
21
|
DocumentTitleOptions,
|
|
22
22
|
LinkingOptions,
|
|
23
23
|
LocaleDirection,
|
|
24
|
+
Persistor,
|
|
24
25
|
} from './types';
|
|
25
|
-
import { UnhandledLinkingContext } from './UnhandledLinkingContext';
|
|
26
26
|
import { useBackButton } from './useBackButton';
|
|
27
27
|
import { useDocumentTitle } from './useDocumentTitle';
|
|
28
28
|
import { useLinking } from './useLinking';
|
|
29
|
-
import { useThenable } from './useThenable';
|
|
29
|
+
import { type Thenable, useThenable } from './useThenable';
|
|
30
30
|
|
|
31
31
|
declare global {
|
|
32
32
|
var REACT_NAVIGATION_DEVTOOLS: WeakMap<
|
|
@@ -41,7 +41,9 @@ type Props<ParamList extends {}> = NavigationContainerProps & {
|
|
|
41
41
|
/**
|
|
42
42
|
* Initial state object for the navigation tree.
|
|
43
43
|
*
|
|
44
|
-
* If this is provided
|
|
44
|
+
* If this is provided:
|
|
45
|
+
* - Deep link or URLs won't be handled on the initial render.
|
|
46
|
+
* - Persisted state won't be restored.
|
|
45
47
|
*/
|
|
46
48
|
initialState?: NavigationContainerProps['initialState'];
|
|
47
49
|
/**
|
|
@@ -56,11 +58,36 @@ type Props<ParamList extends {}> = NavigationContainerProps & {
|
|
|
56
58
|
*/
|
|
57
59
|
linking?: LinkingOptions<ParamList>;
|
|
58
60
|
/**
|
|
59
|
-
*
|
|
61
|
+
* Persistor object to persist and restore navigation state.
|
|
62
|
+
*
|
|
63
|
+
* State is not restored if a deep link is handled on the initial render
|
|
64
|
+
* Not supported on web when linking is enabled.
|
|
65
|
+
*
|
|
66
|
+
* Example:
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* const persistor = {
|
|
70
|
+
* async persist(state) {
|
|
71
|
+
* await AsyncStorage.setItem('state-key-v1', JSON.stringify(state));
|
|
72
|
+
* },
|
|
73
|
+
* async restore() {
|
|
74
|
+
* const state = await AsyncStorage.getItem('state-key-v1');
|
|
75
|
+
*
|
|
76
|
+
* return state ? JSON.parse(state) : undefined;
|
|
77
|
+
* },
|
|
78
|
+
* };
|
|
79
|
+
*
|
|
80
|
+
* <NavigationContainer persistor={persistor}>...</NavigationContainer>
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
persistor?: Persistor;
|
|
84
|
+
/**
|
|
85
|
+
* Fallback element to render until initial state is resolved.
|
|
86
|
+
* Used when deep link or persisted state is being restored asynchronously.
|
|
60
87
|
*
|
|
61
88
|
* Defaults to `null`.
|
|
62
89
|
*/
|
|
63
|
-
fallback?: React.
|
|
90
|
+
fallback?: React.ReactElement | null;
|
|
64
91
|
/**
|
|
65
92
|
* Options to configure the document title on Web.
|
|
66
93
|
*
|
|
@@ -70,21 +97,22 @@ type Props<ParamList extends {}> = NavigationContainerProps & {
|
|
|
70
97
|
documentTitle?: DocumentTitleOptions;
|
|
71
98
|
};
|
|
72
99
|
|
|
100
|
+
const RESTORE_STATE_ERROR =
|
|
101
|
+
'Failed to restore navigation state. The state will be initialized based on the navigation tree.';
|
|
102
|
+
|
|
73
103
|
function NavigationContainerInner(
|
|
74
104
|
{
|
|
75
105
|
direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
|
|
76
106
|
theme = DefaultTheme,
|
|
77
107
|
linking,
|
|
108
|
+
persistor,
|
|
78
109
|
fallback = null,
|
|
79
110
|
documentTitle,
|
|
80
|
-
onReady,
|
|
81
111
|
onStateChange,
|
|
82
112
|
...rest
|
|
83
113
|
}: Props<ParamListBase>,
|
|
84
114
|
ref?: React.Ref<NavigationContainerRef<ParamListBase> | null>
|
|
85
115
|
) {
|
|
86
|
-
const isLinkingEnabled = linking ? linking.enabled !== false : false;
|
|
87
|
-
|
|
88
116
|
if (linking?.config) {
|
|
89
117
|
validatePathConfig(linking.config);
|
|
90
118
|
}
|
|
@@ -95,82 +123,108 @@ function NavigationContainerInner(
|
|
|
95
123
|
useBackButton(refContainer);
|
|
96
124
|
useDocumentTitle(refContainer, documentTitle);
|
|
97
125
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
prefixes: [],
|
|
107
|
-
...linking,
|
|
108
|
-
},
|
|
109
|
-
setLastUnhandledLink
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
const linkingContext = React.useMemo(() => ({ options: linking }), [linking]);
|
|
126
|
+
const linkingConfig = React.useMemo(() => {
|
|
127
|
+
if (linking == null) {
|
|
128
|
+
return {
|
|
129
|
+
options: {
|
|
130
|
+
enabled: false,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
113
134
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
return {
|
|
136
|
+
options: {
|
|
137
|
+
...linking,
|
|
138
|
+
enabled: linking.enabled !== false,
|
|
139
|
+
prefixes: linking.prefixes ?? ['*'],
|
|
140
|
+
getStateFromPath: linking?.getStateFromPath ?? getStateFromPath,
|
|
141
|
+
getPathFromState: linking?.getPathFromState ?? getPathFromState,
|
|
142
|
+
getActionFromState: linking?.getActionFromState ?? getActionFromState,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}, [linking]);
|
|
118
146
|
|
|
119
|
-
const
|
|
120
|
-
// If the screen path matches lastUnhandledLink, we do not track it
|
|
121
|
-
const path = refContainer.current?.getCurrentRoute()?.path;
|
|
122
|
-
setLastUnhandledLink((previousLastUnhandledLink) => {
|
|
123
|
-
if (previousLastUnhandledLink === path) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
return previousLastUnhandledLink;
|
|
127
|
-
});
|
|
128
|
-
onReady?.();
|
|
129
|
-
});
|
|
147
|
+
const { getInitialState } = useLinking(refContainer, linkingConfig.options);
|
|
130
148
|
|
|
131
|
-
const onStateChangeForLinkingHandling = useLatestCallback(
|
|
132
|
-
(state: Readonly<NavigationState> | undefined) => {
|
|
133
|
-
// If the screen path matches lastUnhandledLink, we do not track it
|
|
134
|
-
const path = refContainer.current?.getCurrentRoute()?.path;
|
|
135
|
-
setLastUnhandledLink((previousLastUnhandledLink) => {
|
|
136
|
-
if (previousLastUnhandledLink === path) {
|
|
137
|
-
return undefined;
|
|
138
|
-
}
|
|
139
|
-
return previousLastUnhandledLink;
|
|
140
|
-
});
|
|
141
|
-
onStateChange?.(state);
|
|
142
|
-
}
|
|
143
|
-
);
|
|
144
149
|
// Add additional linking related info to the ref
|
|
145
150
|
// This will be used by the devtools
|
|
146
151
|
React.useEffect(() => {
|
|
147
152
|
if (refContainer.current) {
|
|
148
153
|
REACT_NAVIGATION_DEVTOOLS.set(refContainer.current, {
|
|
149
154
|
get linking() {
|
|
150
|
-
return
|
|
151
|
-
...linking,
|
|
152
|
-
enabled: isLinkingEnabled,
|
|
153
|
-
prefixes: linking?.prefixes ?? [],
|
|
154
|
-
getStateFromPath: linking?.getStateFromPath ?? getStateFromPath,
|
|
155
|
-
getPathFromState: linking?.getPathFromState ?? getPathFromState,
|
|
156
|
-
getActionFromState:
|
|
157
|
-
linking?.getActionFromState ?? getActionFromState,
|
|
158
|
-
};
|
|
155
|
+
return linkingConfig.options;
|
|
159
156
|
},
|
|
160
157
|
});
|
|
161
158
|
}
|
|
162
159
|
});
|
|
163
160
|
|
|
164
|
-
const [
|
|
161
|
+
const [isLinkStateResolved, initialStateFromLink] = useThenable(() => {
|
|
162
|
+
if (rest.initialState != null || !linkingConfig.options.enabled) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return getInitialState();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const isPersistenceSupported =
|
|
170
|
+
Platform.OS === 'web' ? !linkingConfig.options.enabled : true;
|
|
171
|
+
|
|
172
|
+
const [isPersistedStateResolved, initialStateFromPersisted] = useThenable(
|
|
173
|
+
() => {
|
|
174
|
+
if (
|
|
175
|
+
isPersistenceSupported === false ||
|
|
176
|
+
rest.initialState != null ||
|
|
177
|
+
persistor == null
|
|
178
|
+
) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let restoredState;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
restoredState = persistor.restore();
|
|
186
|
+
} catch (e) {
|
|
187
|
+
console.error(RESTORE_STATE_ERROR, e);
|
|
188
|
+
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (restoredState == null) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if ('then' in restoredState) {
|
|
197
|
+
return restoredState.then(
|
|
198
|
+
(state) => state,
|
|
199
|
+
(error) => {
|
|
200
|
+
console.error(RESTORE_STATE_ERROR, error);
|
|
201
|
+
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const thenable: Thenable<InitialState | undefined> = {
|
|
208
|
+
then(onfulfilled) {
|
|
209
|
+
return Promise.resolve(
|
|
210
|
+
onfulfilled ? onfulfilled(restoredState) : restoredState
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return thenable;
|
|
216
|
+
}
|
|
217
|
+
);
|
|
165
218
|
|
|
166
219
|
// FIXME
|
|
167
220
|
// @ts-expect-error not sure why this is not working
|
|
168
221
|
React.useImperativeHandle(ref, () => refContainer.current);
|
|
169
222
|
|
|
170
|
-
const
|
|
171
|
-
rest.initialState != null ||
|
|
223
|
+
const isStateReady =
|
|
224
|
+
rest.initialState != null ||
|
|
225
|
+
(isLinkStateResolved && isPersistedStateResolved);
|
|
172
226
|
|
|
173
|
-
if (!
|
|
227
|
+
if (!isStateReady) {
|
|
174
228
|
return (
|
|
175
229
|
<LocaleDirContext.Provider value={direction}>
|
|
176
230
|
<ThemeProvider value={theme}>{fallback}</ThemeProvider>
|
|
@@ -180,32 +234,35 @@ function NavigationContainerInner(
|
|
|
180
234
|
|
|
181
235
|
return (
|
|
182
236
|
<LocaleDirContext.Provider value={direction}>
|
|
183
|
-
<
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
237
|
+
<LinkingContext.Provider value={linkingConfig}>
|
|
238
|
+
<BaseNavigationContainer
|
|
239
|
+
{...rest}
|
|
240
|
+
theme={theme}
|
|
241
|
+
initialState={
|
|
242
|
+
rest.initialState ??
|
|
243
|
+
initialStateFromLink ??
|
|
244
|
+
initialStateFromPersisted
|
|
245
|
+
}
|
|
246
|
+
onStateChange={(state) => {
|
|
247
|
+
onStateChange?.(state);
|
|
248
|
+
persistor?.persist(state);
|
|
249
|
+
}}
|
|
250
|
+
ref={refContainer}
|
|
251
|
+
/>
|
|
252
|
+
</LinkingContext.Provider>
|
|
197
253
|
</LocaleDirContext.Provider>
|
|
198
254
|
);
|
|
199
255
|
}
|
|
200
256
|
|
|
201
257
|
/**
|
|
202
258
|
* Container component that manages the navigation state.
|
|
259
|
+
*
|
|
203
260
|
* This should be rendered at the root wrapping the whole app.
|
|
204
261
|
*/
|
|
205
262
|
export const NavigationContainer = React.forwardRef(
|
|
206
263
|
NavigationContainerInner
|
|
207
|
-
) as <
|
|
208
|
-
props: Props<
|
|
209
|
-
ref?: React.Ref<NavigationContainerRef<
|
|
264
|
+
) as <ParamList extends {} = RootParamList>(
|
|
265
|
+
props: Props<ParamList> & {
|
|
266
|
+
ref?: React.Ref<NavigationContainerRef<ParamList>>;
|
|
210
267
|
}
|
|
211
268
|
) => React.ReactElement;
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
const StackNavigator = (
|
|
13
13
|
props: DefaultNavigatorOptions<
|
|
14
14
|
ParamListBase,
|
|
15
|
-
string | undefined,
|
|
16
15
|
StackNavigationState<ParamListBase>,
|
|
17
16
|
{},
|
|
18
17
|
{},
|
|
@@ -35,7 +34,6 @@ export function createStackNavigator<
|
|
|
35
34
|
ParamList extends ParamListBase,
|
|
36
35
|
>(): TypedNavigator<{
|
|
37
36
|
ParamList: ParamList;
|
|
38
|
-
NavigatorID: string | undefined;
|
|
39
37
|
State: StackNavigationState<ParamList>;
|
|
40
38
|
ScreenOptions: {};
|
|
41
39
|
EventMap: {};
|
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
import escapeStringRegexp from 'escape-string-regexp';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type { LinkingPrefix } from './types';
|
|
4
|
+
|
|
5
|
+
export function extractPathFromURL(prefixes: LinkingPrefix[], url: string) {
|
|
4
6
|
for (const prefix of prefixes) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
let prefixRegex;
|
|
8
|
+
|
|
9
|
+
if (prefix === '*') {
|
|
10
|
+
prefixRegex = /^(((https?:\/\/)[^/]+)|([^/]+:(\/\/)?))/;
|
|
11
|
+
} else {
|
|
12
|
+
const protocol = prefix.match(/^[^:]+:/)?.[0] ?? '';
|
|
13
|
+
const host = prefix
|
|
14
|
+
.replace(new RegExp(`^${escapeStringRegexp(protocol)}`), '')
|
|
15
|
+
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
|
|
16
|
+
.replace(/^\//, ''); // Remove extra leading slash
|
|
17
|
+
|
|
18
|
+
prefixRegex = new RegExp(
|
|
19
|
+
`^${escapeStringRegexp(protocol)}(/)*${host
|
|
20
|
+
.split('.')
|
|
21
|
+
.map((it) => (it === '*' ? '[^/]+' : escapeStringRegexp(it)))
|
|
22
|
+
.join('\\.')}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
17
25
|
|
|
18
26
|
const [originAndPath, ...searchParams] = url.split('?');
|
|
19
|
-
const normalizedURL = originAndPath
|
|
20
|
-
.replace(/\/+/g, '/')
|
|
21
|
-
.concat(searchParams.length ? `?${searchParams.join('?')}` : '');
|
|
22
27
|
|
|
23
|
-
if (prefixRegex.test(
|
|
24
|
-
|
|
28
|
+
if (prefixRegex.test(originAndPath)) {
|
|
29
|
+
const result = originAndPath
|
|
30
|
+
.replace(prefixRegex, '')
|
|
31
|
+
.replace(/\/+/g, '/')
|
|
32
|
+
.concat(searchParams.length ? `?${searchParams.join('?')}` : '');
|
|
33
|
+
|
|
34
|
+
return result.startsWith('/') ? result : `/${result}`;
|
|
25
35
|
}
|
|
26
36
|
}
|
|
27
37
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getStateFromPath, type ParamListBase } from '@react-navigation/core';
|
|
2
|
+
|
|
3
|
+
import { extractPathFromURL } from './extractPathFromURL';
|
|
4
|
+
import type { LinkingOptions } from './types';
|
|
5
|
+
|
|
6
|
+
export function getStateFromHref(
|
|
7
|
+
href: string,
|
|
8
|
+
options: LinkingOptions<ParamListBase> | undefined
|
|
9
|
+
): ReturnType<typeof getStateFromPath> {
|
|
10
|
+
const {
|
|
11
|
+
prefixes,
|
|
12
|
+
filter,
|
|
13
|
+
config,
|
|
14
|
+
getStateFromPath: getStateFromPathHelper = getStateFromPath,
|
|
15
|
+
} = options || {};
|
|
16
|
+
|
|
17
|
+
let path;
|
|
18
|
+
|
|
19
|
+
if (href.startsWith('/')) {
|
|
20
|
+
path = href;
|
|
21
|
+
} else if (href) {
|
|
22
|
+
if (filter && !filter(href)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Failed to parse href '${href}'. It doesn't match the filter specified in linking config.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (prefixes == null || prefixes.length === 0) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Failed to parse href '${href}'. It doesn't start with '/' and no prefixes are defined in linking config.`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
path = extractPathFromURL(prefixes, href);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (path == null) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Got invalid href '${href}'. It must start with '/' or match one of the prefixes: ${options?.prefixes?.map((prefix) => `'${prefix}'`).join(', ')}.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const state = getStateFromPathHelper(path, config);
|
|
44
|
+
|
|
45
|
+
return state;
|
|
46
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -7,7 +7,6 @@ export { ServerContainer } from './ServerContainer';
|
|
|
7
7
|
export { DarkTheme } from './theming/DarkTheme';
|
|
8
8
|
export { DefaultTheme } from './theming/DefaultTheme';
|
|
9
9
|
export * from './types';
|
|
10
|
-
export { UnhandledLinkingContext as UNSTABLE_UnhandledLinkingContext } from './UnhandledLinkingContext';
|
|
11
10
|
export { useLinkBuilder } from './useLinkBuilder';
|
|
12
11
|
export { type LinkProps, useLinkProps } from './useLinkProps';
|
|
13
12
|
export { useLinkTo } from './useLinkTo';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { Theme } from '
|
|
1
|
+
import type { Theme } from '@react-navigation/core';
|
|
2
|
+
|
|
2
3
|
import { fonts } from './fonts';
|
|
3
4
|
|
|
4
|
-
export const DarkTheme
|
|
5
|
+
export const DarkTheme = {
|
|
5
6
|
dark: true,
|
|
6
7
|
colors: {
|
|
7
8
|
primary: 'rgb(10, 132, 255)',
|
|
@@ -12,4 +13,4 @@ export const DarkTheme: Theme = {
|
|
|
12
13
|
notification: 'rgb(255, 69, 58)',
|
|
13
14
|
},
|
|
14
15
|
fonts,
|
|
15
|
-
};
|
|
16
|
+
} as const satisfies Theme;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type { Theme } from '
|
|
1
|
+
import type { Theme } from '@react-navigation/core';
|
|
2
|
+
|
|
2
3
|
import { fonts } from './fonts';
|
|
3
4
|
|
|
4
|
-
export const DefaultTheme
|
|
5
|
+
export const DefaultTheme = {
|
|
5
6
|
dark: false,
|
|
6
7
|
colors: {
|
|
7
8
|
primary: 'rgb(0, 122, 255)',
|
|
8
9
|
background: 'rgb(242, 242, 242)',
|
|
9
10
|
card: 'rgb(255, 255, 255)',
|
|
10
11
|
text: 'rgb(28, 28, 30)',
|
|
11
|
-
border: 'rgb(
|
|
12
|
+
border: 'rgb(178, 178, 178)',
|
|
12
13
|
notification: 'rgb(255, 59, 48)',
|
|
13
14
|
},
|
|
14
15
|
fonts,
|
|
15
|
-
};
|
|
16
|
+
} as const satisfies Theme;
|
package/src/theming/fonts.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import type { Theme } from '@react-navigation/core';
|
|
1
2
|
import { Platform } from 'react-native';
|
|
2
3
|
|
|
3
|
-
import type { Theme } from '../types';
|
|
4
|
-
|
|
5
4
|
const WEB_FONT_STACK =
|
|
6
5
|
'system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
|
|
7
6
|
|
package/src/types.tsx
CHANGED
|
@@ -2,16 +2,23 @@ import type {
|
|
|
2
2
|
getActionFromState as getActionFromStateDefault,
|
|
3
3
|
getPathFromState as getPathFromStateDefault,
|
|
4
4
|
getStateFromPath as getStateFromPathDefault,
|
|
5
|
+
InitialState,
|
|
6
|
+
NavigationState,
|
|
5
7
|
PathConfigMap,
|
|
6
8
|
Route,
|
|
7
9
|
} from '@react-navigation/core';
|
|
10
|
+
import type { ColorValue as ReactNativeColorValue } from 'react-native';
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
}
|
|
12
|
+
type ColorValue =
|
|
13
|
+
| `#${string}`
|
|
14
|
+
| `rgb(${string})`
|
|
15
|
+
| `rgba(${string})`
|
|
16
|
+
| `hsl(${string})`
|
|
17
|
+
| `hsla(${string})`
|
|
18
|
+
| `hwb(${string})`
|
|
19
|
+
| `hwba(${string})`
|
|
20
|
+
| `var(--${string})`
|
|
21
|
+
| ReactNativeColorValue;
|
|
15
22
|
|
|
16
23
|
type FontStyle = {
|
|
17
24
|
fontFamily: string;
|
|
@@ -32,12 +39,12 @@ type FontStyle = {
|
|
|
32
39
|
interface NativeTheme {
|
|
33
40
|
dark: boolean;
|
|
34
41
|
colors: {
|
|
35
|
-
primary:
|
|
36
|
-
background:
|
|
37
|
-
card:
|
|
38
|
-
text:
|
|
39
|
-
border:
|
|
40
|
-
notification:
|
|
42
|
+
primary: ColorValue;
|
|
43
|
+
background: ColorValue;
|
|
44
|
+
card: ColorValue;
|
|
45
|
+
text: ColorValue;
|
|
46
|
+
border: ColorValue;
|
|
47
|
+
notification: ColorValue;
|
|
41
48
|
};
|
|
42
49
|
fonts: {
|
|
43
50
|
regular: FontStyle;
|
|
@@ -47,19 +54,34 @@ interface NativeTheme {
|
|
|
47
54
|
};
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
declare module '@react-navigation/core' {
|
|
58
|
+
interface Theme extends NativeTheme {}
|
|
59
|
+
}
|
|
51
60
|
|
|
52
61
|
export type LocaleDirection = 'ltr' | 'rtl';
|
|
53
62
|
|
|
63
|
+
export type LinkingPrefix = '*' | (string & {});
|
|
64
|
+
|
|
54
65
|
export type LinkingOptions<ParamList extends {}> = {
|
|
55
66
|
/**
|
|
56
67
|
* Whether deep link handling should be enabled.
|
|
57
|
-
*
|
|
68
|
+
*
|
|
69
|
+
* Defaults to `true` when a linking config is specified.
|
|
58
70
|
*/
|
|
59
71
|
enabled?: boolean;
|
|
60
72
|
/**
|
|
61
|
-
* The prefixes
|
|
62
|
-
*
|
|
73
|
+
* The prefixes to match to determine whether to handle a URL.
|
|
74
|
+
*
|
|
75
|
+
* Supported prefix formats:
|
|
76
|
+
* - `${scheme}://` - App-specific scheme, e.g. `myapp://`
|
|
77
|
+
* - `${protocol}://${host}` - Universal links or app links, e.g. `https://example.com`, `https://subdomain.example.com`
|
|
78
|
+
* - `${protocol}://*.${domain}` - Any subdomain of given domain, e.g. `https://*.example.com`
|
|
79
|
+
* - `${protocol}://${host}/${path}` - Subpath of given host, e.g. `https://example.com/app`
|
|
80
|
+
* - `*` - Any domain or subdomain with `http://` and `https://` as well as any app-specific scheme
|
|
81
|
+
*
|
|
82
|
+
* The prefix will be stripped from the URL before it's parsed.
|
|
83
|
+
*
|
|
84
|
+
* Defaults to `[*]`.
|
|
63
85
|
*
|
|
64
86
|
* This is not supported on Web.
|
|
65
87
|
*
|
|
@@ -67,14 +89,14 @@ export type LinkingOptions<ParamList extends {}> = {
|
|
|
67
89
|
* ```js
|
|
68
90
|
* {
|
|
69
91
|
* prefixes: [
|
|
70
|
-
* "myapp://",
|
|
71
|
-
* "https://example.com",
|
|
72
|
-
* "https://*.example.com"
|
|
92
|
+
* "myapp://",
|
|
93
|
+
* "https://example.com",
|
|
94
|
+
* "https://*.example.com"
|
|
73
95
|
* ]
|
|
74
96
|
* }
|
|
75
97
|
* ```
|
|
76
98
|
*/
|
|
77
|
-
prefixes
|
|
99
|
+
prefixes?: LinkingPrefix[];
|
|
78
100
|
/**
|
|
79
101
|
* Optional function which takes an incoming URL returns a boolean
|
|
80
102
|
* indicating whether React Navigation should handle it.
|
|
@@ -153,9 +175,11 @@ export type LinkingOptions<ParamList extends {}> = {
|
|
|
153
175
|
* subscribe: (listener) => {
|
|
154
176
|
* const onReceiveURL = ({ url }) => listener(url);
|
|
155
177
|
*
|
|
156
|
-
* Linking.addEventListener('url', onReceiveURL);
|
|
178
|
+
* const subscription = Linking.addEventListener('url', onReceiveURL);
|
|
157
179
|
*
|
|
158
|
-
* return () =>
|
|
180
|
+
* return () => {
|
|
181
|
+
* subscription.remove();
|
|
182
|
+
* };
|
|
159
183
|
* }
|
|
160
184
|
* }
|
|
161
185
|
* ```
|
|
@@ -186,6 +210,20 @@ export type DocumentTitleOptions = {
|
|
|
186
210
|
) => string;
|
|
187
211
|
};
|
|
188
212
|
|
|
213
|
+
export type Persistor = {
|
|
214
|
+
/**
|
|
215
|
+
* Callback to persist the navigation state.
|
|
216
|
+
*/
|
|
217
|
+
persist(state: NavigationState | undefined): void;
|
|
218
|
+
/**
|
|
219
|
+
* Callback to restore the navigation state.
|
|
220
|
+
* Should return the restored state or a Promise which resolves to the restored state.
|
|
221
|
+
*
|
|
222
|
+
* If a Promise is returned, providing a `fallback` component is recommended.
|
|
223
|
+
*/
|
|
224
|
+
restore(): PromiseLike<InitialState | undefined> | InitialState | undefined;
|
|
225
|
+
};
|
|
226
|
+
|
|
189
227
|
export type ServerContainerRef = {
|
|
190
228
|
getCurrentOptions(): Record<string, any> | undefined;
|
|
191
229
|
};
|