@react-navigation/native 8.0.0-alpha.32 → 8.0.0-alpha.34

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 (67) hide show
  1. package/android/build.gradle +4 -6
  2. package/assets/fonts/MaterialSymbolsOutlined.codepoints +179 -24
  3. package/assets/fonts/MaterialSymbolsOutlined_100.ttf +0 -0
  4. package/assets/fonts/MaterialSymbolsOutlined_200.ttf +0 -0
  5. package/assets/fonts/MaterialSymbolsOutlined_300.ttf +0 -0
  6. package/assets/fonts/MaterialSymbolsOutlined_400.ttf +0 -0
  7. package/assets/fonts/MaterialSymbolsOutlined_500.ttf +0 -0
  8. package/assets/fonts/MaterialSymbolsOutlined_600.ttf +0 -0
  9. package/assets/fonts/MaterialSymbolsOutlined_700.ttf +0 -0
  10. package/assets/fonts/MaterialSymbolsRounded.codepoints +179 -24
  11. package/assets/fonts/MaterialSymbolsRounded_100.ttf +0 -0
  12. package/assets/fonts/MaterialSymbolsRounded_200.ttf +0 -0
  13. package/assets/fonts/MaterialSymbolsRounded_300.ttf +0 -0
  14. package/assets/fonts/MaterialSymbolsRounded_400.ttf +0 -0
  15. package/assets/fonts/MaterialSymbolsRounded_500.ttf +0 -0
  16. package/assets/fonts/MaterialSymbolsRounded_600.ttf +0 -0
  17. package/assets/fonts/MaterialSymbolsRounded_700.ttf +0 -0
  18. package/assets/fonts/MaterialSymbolsSharp.codepoints +179 -24
  19. package/assets/fonts/MaterialSymbolsSharp_100.ttf +0 -0
  20. package/assets/fonts/MaterialSymbolsSharp_200.ttf +0 -0
  21. package/assets/fonts/MaterialSymbolsSharp_300.ttf +0 -0
  22. package/assets/fonts/MaterialSymbolsSharp_400.ttf +0 -0
  23. package/assets/fonts/MaterialSymbolsSharp_500.ttf +0 -0
  24. package/assets/fonts/MaterialSymbolsSharp_600.ttf +0 -0
  25. package/assets/fonts/MaterialSymbolsSharp_700.ttf +0 -0
  26. package/lib/module/NavigationContainer.js +3 -3
  27. package/lib/module/NavigationContainer.js.map +1 -1
  28. package/lib/module/createMemoryHistory.js +7 -1
  29. package/lib/module/createMemoryHistory.js.map +1 -1
  30. package/lib/module/createStandardNavigationFactories.js.map +1 -1
  31. package/lib/module/native/CornerInset.ios.js +4 -1
  32. package/lib/module/native/CornerInset.ios.js.map +1 -1
  33. package/lib/module/native/MaterialSymbolViewNativeComponent.ts +2 -2
  34. package/lib/module/native/SFSymbolViewNativeComponent.ts +4 -4
  35. package/lib/module/useBackButton.native.js +4 -1
  36. package/lib/module/useBackButton.native.js.map +1 -1
  37. package/lib/module/useLinking.js +88 -19
  38. package/lib/module/useLinking.js.map +1 -1
  39. package/lib/typescript/src/NavigationContainer.d.ts +1 -1
  40. package/lib/typescript/src/NavigationContainer.d.ts.map +1 -1
  41. package/lib/typescript/src/createMemoryHistory.d.ts.map +1 -1
  42. package/lib/typescript/src/createStandardNavigationFactories.d.ts +4 -2
  43. package/lib/typescript/src/createStandardNavigationFactories.d.ts.map +1 -1
  44. package/lib/typescript/src/native/CornerInset.ios.d.ts.map +1 -1
  45. package/lib/typescript/src/native/MaterialSymbolData.d.ts +1 -1
  46. package/lib/typescript/src/native/MaterialSymbolData.d.ts.map +1 -1
  47. package/lib/typescript/src/native/MaterialSymbolViewNativeComponent.d.ts +2 -2
  48. package/lib/typescript/src/native/MaterialSymbolViewNativeComponent.d.ts.map +1 -1
  49. package/lib/typescript/src/native/SFSymbolViewNativeComponent.d.ts +4 -4
  50. package/lib/typescript/src/native/SFSymbolViewNativeComponent.d.ts.map +1 -1
  51. package/lib/typescript/src/useBackButton.native.d.ts.map +1 -1
  52. package/lib/typescript/src/useLinkBuilder.d.ts +30 -30
  53. package/lib/typescript/src/useLinkBuilder.d.ts.map +1 -1
  54. package/lib/typescript/src/useLinkProps.d.ts +4 -4
  55. package/lib/typescript/src/useLinkProps.d.ts.map +1 -1
  56. package/lib/typescript/src/useLinking.d.ts.map +1 -1
  57. package/package.json +7 -6
  58. package/src/NavigationContainer.tsx +5 -5
  59. package/src/createMemoryHistory.tsx +7 -1
  60. package/src/createStandardNavigationFactories.tsx +9 -8
  61. package/src/native/CornerInset.ios.tsx +2 -0
  62. package/src/native/MaterialSymbolData.tsx +155 -0
  63. package/src/native/MaterialSymbolViewNativeComponent.ts +2 -2
  64. package/src/native/SFSymbolViewNativeComponent.ts +4 -4
  65. package/src/useBackButton.native.tsx +2 -0
  66. package/src/useLinkProps.tsx +4 -4
  67. package/src/useLinking.tsx +134 -18
@@ -9,13 +9,13 @@ import {
9
9
 
10
10
  export interface NativeProps extends ViewProps {
11
11
  name: string;
12
- variant?: string;
12
+ variant?: string | undefined;
13
13
  weight?: CodegenTypes.WithDefault<
14
14
  0 | 100 | 200 | 300 | 400 | 500 | 600 | 700,
15
15
  0
16
16
  >;
17
17
  size: CodegenTypes.Float;
18
- color?: ColorValue;
18
+ color?: ColorValue | undefined;
19
19
  }
20
20
 
21
21
  export default codegenNativeComponent<NativeProps>(
@@ -10,16 +10,16 @@ import {
10
10
  export interface NativeProps extends ViewProps {
11
11
  name: string;
12
12
  size: CodegenTypes.Float;
13
- color?: ColorValue;
13
+ color?: ColorValue | undefined;
14
14
  weight: CodegenTypes.Int32;
15
15
  scale: string;
16
16
  variableValue: CodegenTypes.Float;
17
17
  variableValueMode: string;
18
18
  colorRenderingMode: string;
19
19
  renderingMode: string;
20
- colorPrimary?: ColorValue;
21
- colorSecondary?: ColorValue;
22
- colorTertiary?: ColorValue;
20
+ colorPrimary?: ColorValue | undefined;
21
+ colorSecondary?: ColorValue | undefined;
22
+ colorTertiary?: ColorValue | undefined;
23
23
  effect: string;
24
24
  effectRepeat: string;
25
25
  effectRepeatCount: CodegenTypes.Int32;
@@ -9,8 +9,10 @@ export function useBackButton(
9
9
  ref: React.RefObject<NavigationContainerRef<ParamListBase> | null>
10
10
  ) {
11
11
  React.useEffect(() => {
12
+ // eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
12
13
  const subscription = BackHandler.addEventListener(
13
14
  'hardwareBackPress',
15
+ // eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
14
16
  () => {
15
17
  const navigation = ref.current;
16
18
 
@@ -15,15 +15,15 @@ export type LinkProps<
15
15
  RouteName extends keyof ParamList = keyof ParamList,
16
16
  > =
17
17
  | ({
18
- href?: string;
19
- action?: NavigationAction;
18
+ href?: string | undefined;
19
+ action?: NavigationAction | undefined;
20
20
  } & (RouteName extends unknown
21
21
  ? undefined extends ParamList[RouteName]
22
- ? { screen: RouteName; params?: ParamList[RouteName] }
22
+ ? { screen: RouteName; params?: ParamList[RouteName] | undefined }
23
23
  : { screen: RouteName; params: ParamList[RouteName] }
24
24
  : never))
25
25
  | {
26
- href?: string;
26
+ href?: string | undefined;
27
27
  action: NavigationAction;
28
28
  screen?: undefined;
29
29
  params?: undefined;
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CommonActions,
2
3
  findFocusedRoute,
3
4
  getActionFromState as getActionFromStateDefault,
4
5
  getPathFromState as getPathFromStateDefault,
@@ -18,6 +19,12 @@ import type { Thenable } from './useThenable';
18
19
 
19
20
  type ResultState = ReturnType<typeof getStateFromPathDefault>;
20
21
 
22
+ /**
23
+ * History delta already applied by the browser when handling `popstate`
24
+ * The value 'replace' means the delta is unknown, so we can only replace
25
+ */
26
+ type PopStateDelta = number | 'replace';
27
+
21
28
  const getRoutesUntilIndex = (state: NavigationState) =>
22
29
  state.routes.slice(0, state.index + 1);
23
30
 
@@ -130,10 +137,14 @@ const isPoppingLastEntry = (
130
137
  */
131
138
  export const series = (cb: () => Promise<void>) => {
132
139
  let queue = Promise.resolve();
140
+
133
141
  const callback = () => {
134
142
  // eslint-disable-next-line promise/no-callback-in-promise
135
- queue = queue.then(cb);
143
+ queue = queue.then(cb).catch((e) => {
144
+ console.error(e);
145
+ });
136
146
  };
147
+
137
148
  return callback;
138
149
  };
139
150
 
@@ -256,7 +267,9 @@ export function useLinking<ParamList extends ParamListBase>(
256
267
 
257
268
  const previousIndexRef = React.useRef<number | undefined>(undefined);
258
269
  const previousStateRef = React.useRef<NavigationState | undefined>(undefined);
259
- const pendingPopStatePathRef = React.useRef<string | undefined>(undefined);
270
+ const pendingPopStateDeltaRef = React.useRef<PopStateDelta | undefined>(
271
+ undefined
272
+ );
260
273
 
261
274
  React.useEffect(() => {
262
275
  if (!history) {
@@ -280,7 +293,88 @@ export function useLinking<ParamList extends ParamListBase>(
280
293
  const previousIndex = previousIndexRef.current ?? 0;
281
294
 
282
295
  previousIndexRef.current = index;
283
- pendingPopStatePathRef.current = path;
296
+
297
+ const rollbackHistory = () => {
298
+ const delta = previousIndex - index;
299
+
300
+ if (delta === 0) {
301
+ return;
302
+ }
303
+
304
+ history
305
+ .go(delta)
306
+ // eslint-disable-next-line promise/always-return
307
+ ?.then(() => {
308
+ previousIndexRef.current = history.index;
309
+ })
310
+ .catch(() => {
311
+ // The navigation was interrupted
312
+ });
313
+ };
314
+
315
+ const rollbackHistoryIfPrevented = (
316
+ callback: () => void,
317
+ pendingDelta: PopStateDelta
318
+ ) => {
319
+ let removePrevented = false;
320
+ let actionChangedState = false;
321
+
322
+ const unsubscribeEvent = navigation.addListener(
323
+ '__unsafe_event__',
324
+ (e) => {
325
+ if (e.data.type === 'beforeRemove' && e.data.defaultPrevented) {
326
+ removePrevented = true;
327
+ }
328
+ }
329
+ );
330
+
331
+ // After preventing remove, user may synchronously continue navigation
332
+ // This is common when showing a confirmation dialog
333
+ // If this action produces an update, we don't want to rollback the history
334
+ const unsubscribeAction = navigation.addListener(
335
+ '__unsafe_action__',
336
+ (e) => {
337
+ if (!e.data.noop) {
338
+ actionChangedState = true;
339
+ }
340
+ }
341
+ );
342
+
343
+ try {
344
+ callback();
345
+ } finally {
346
+ unsubscribeEvent();
347
+ unsubscribeAction();
348
+ }
349
+
350
+ if (actionChangedState) {
351
+ // The change may be committed later, e.g. with transitions
352
+ // Remember the delta so it can be subtracted when syncing the commit
353
+ pendingPopStateDeltaRef.current = pendingDelta;
354
+ } else if (removePrevented) {
355
+ rollbackHistory();
356
+ }
357
+ };
358
+
359
+ const dispatch = (
360
+ action: Parameters<typeof navigation.dispatch>[0],
361
+ pendingDelta: PopStateDelta
362
+ ) => {
363
+ rollbackHistoryIfPrevented(
364
+ () => navigation.dispatch(action),
365
+ pendingDelta
366
+ );
367
+ };
368
+
369
+ const resetRoot = (
370
+ state: Parameters<typeof navigation.resetRoot>[0],
371
+ pendingDelta: PopStateDelta
372
+ ) => {
373
+ rollbackHistoryIfPrevented(
374
+ () => navigation.resetRoot(state),
375
+ pendingDelta
376
+ );
377
+ };
284
378
 
285
379
  // When browser back/forward is clicked, we first need to check if state object for this index exists
286
380
  // If it does we'll reset to that state object
@@ -301,12 +395,25 @@ export function useLinking<ParamList extends ParamListBase>(
301
395
  recordFocused &&
302
396
  isPoppingLastEntry(currentFocused, recordFocused)
303
397
  ) {
398
+ const pending = pendingPopStateDeltaRef.current;
399
+
304
400
  // If we detect that the state change is popping the last entry
305
401
  // Dispatch a back action instead of resetting to the state
306
402
  // This makes sure changes to history state since the entry was added don't get lost
307
- navigation.goBack();
403
+ dispatch(
404
+ CommonActions.goBack(),
405
+ // Stack on any delta from a previous `popstate` that hasn't committed yet
406
+ pending === 'replace' ? 'replace' : (pending ?? 0) - 1
407
+ );
308
408
  } else {
309
- navigation.resetRoot(record.state);
409
+ // The browser already moved from the current state to the record's state
410
+ resetRoot(
411
+ record.state,
412
+ currentFocused && recordFocused
413
+ ? getTotalHistoryLength(recordFocused) -
414
+ getTotalHistoryLength(currentFocused)
415
+ : 'replace'
416
+ );
310
417
  }
311
418
 
312
419
  return;
@@ -343,7 +450,7 @@ export function useLinking<ParamList extends ParamListBase>(
343
450
 
344
451
  if (action !== undefined) {
345
452
  try {
346
- navigation.dispatch(action);
453
+ dispatch(action, 'replace');
347
454
  } catch (e) {
348
455
  // Ignore any errors from deep linking.
349
456
  // This could happen in case of malformed links, navigation object not being initialized etc.
@@ -356,14 +463,14 @@ export function useLinking<ParamList extends ParamListBase>(
356
463
  );
357
464
  }
358
465
  } else {
359
- navigation.resetRoot(state);
466
+ resetRoot(state, 'replace');
360
467
  }
361
468
  } else {
362
- navigation.resetRoot(state);
469
+ resetRoot(state, 'replace');
363
470
  }
364
471
  } else {
365
472
  // if current path didn't return any state, we should revert to initial state
366
- navigation.resetRoot(state);
473
+ resetRoot(state, 'replace');
367
474
  }
368
475
  });
369
476
  }, [enabled, history, ref, validateRoutesNotExistInRootState]);
@@ -448,7 +555,7 @@ export function useLinking<ParamList extends ParamListBase>(
448
555
  }
449
556
  }
450
557
 
451
- const onStateChange = async () => {
558
+ const onUpdate = async () => {
452
559
  const navigation = ref.current;
453
560
 
454
561
  if (!navigation || !enabled) {
@@ -463,12 +570,18 @@ export function useLinking<ParamList extends ParamListBase>(
463
570
  return;
464
571
  }
465
572
 
466
- const pendingPath = pendingPopStatePathRef.current;
573
+ // Skip if the state hasn't changed since we last synced it
574
+ if (previousState === state) {
575
+ return;
576
+ }
577
+
467
578
  const route = findFocusedRoute(state);
468
579
  const path = getPathForRoute(route, state);
469
580
 
581
+ const pendingPopStateDelta = pendingPopStateDeltaRef.current;
582
+
470
583
  previousStateRef.current = state;
471
- pendingPopStatePathRef.current = undefined;
584
+ pendingPopStateDeltaRef.current = undefined;
472
585
 
473
586
  // To detect the kind of state change, we need to:
474
587
  // - Find the common focused navigation state in previous and current state
@@ -482,13 +595,14 @@ export function useLinking<ParamList extends ParamListBase>(
482
595
  if (
483
596
  previousFocusedState &&
484
597
  focusedState &&
485
- // We should only handle push/pop if path changed from what was in last `popstate`
486
- // Otherwise it's likely a change triggered by `popstate`
487
- path !== pendingPath
598
+ // If the delta from `popstate` is unknown, we can only replace
599
+ pendingPopStateDelta !== 'replace'
488
600
  ) {
489
601
  const historyDelta =
490
602
  getTotalHistoryLength(focusedState) -
491
- getTotalHistoryLength(previousFocusedState);
603
+ getTotalHistoryLength(previousFocusedState) -
604
+ // Subtract the delta already applied by the browser to sync only the remaining changes
605
+ (pendingPopStateDelta ?? 0);
492
606
 
493
607
  if (historyDelta > 0) {
494
608
  // If history length is increased, we should pushState
@@ -534,10 +648,12 @@ export function useLinking<ParamList extends ParamListBase>(
534
648
  previousIndexRef.current = history.index;
535
649
  };
536
650
 
537
- // We debounce onStateChange coz we don't want multiple state changes to be handled at one time
651
+ // We debounce onUpdate coz we don't want multiple state changes to be handled at one time
538
652
  // This could happen since `history.go(n)` is asynchronous
539
653
  // If `pushState` or `replaceState` were called before `history.go(n)` completes, it'll mess stuff up
540
- return ref.current?.addListener('state', series(onStateChange));
654
+ const handleUpdate = series(onUpdate);
655
+
656
+ return ref.current?.addListener('state', handleUpdate);
541
657
  }, [enabled, history, ref]);
542
658
 
543
659
  return {