Compare commits

..

1 Commits

Author SHA1 Message Date
Satyajit Sahoo
515e652b37 wip 2020-04-27 17:46:42 +02:00
160 changed files with 3722 additions and 9174 deletions

View File

@@ -1,55 +1,46 @@
version: 2.1 version: 2
executors: defaults: &defaults
default:
docker: docker:
- image: circleci/node:10 - image: circleci/node:10
working_directory: ~/project working_directory: ~/project
environment:
YARN_CACHE_FOLDER: "~/.cache/yarn"
commands:
attach_project:
steps:
- attach_workspace:
at: ~/project
jobs: jobs:
install-dependencies: install-dependencies:
executor: default <<: *defaults
steps: steps:
- checkout - checkout
- attach_project - attach_workspace:
at: ~/project
- restore_cache: - restore_cache:
keys: keys:
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }} - v1-dependencies-{{ checksum "yarn.lock" }}
- yarn-packages-v1-{{ .Branch }}- - v1-dependencies-
- yarn-packages-v1-
- run: - run:
name: Install project dependencies name: Install project dependencies
command: yarn install --frozen-lockfile command: yarn install --frozen-lockfile
- save_cache: - save_cache:
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }} key: v1-dependencies-{{ checksum "yarn.lock" }}
paths: ~/.cache/yarn paths: node_modules
- persist_to_workspace: - persist_to_workspace:
root: . root: .
paths: . paths: .
lint-and-typecheck: lint-and-typecheck:
executor: default <<: *defaults
steps: steps:
- attach_project - attach_workspace:
at: ~/project
- run: - run:
name: Lint files name: Lint files
command: yarn lint command: yarn lint
- run: - run:
name: Typecheck files name: Typecheck files
command: yarn typescript command: yarn typescript
unit-tests: unit-tests:
executor: default <<: *defaults
steps: steps:
- attach_project - attach_workspace:
at: ~/project
- run: - run:
name: Run unit tests name: Run unit tests
command: yarn test --coverage command: yarn test --coverage
@@ -59,11 +50,11 @@ jobs:
- store_artifacts: - store_artifacts:
path: coverage path: coverage
destination: coverage destination: coverage
integration-tests: integration-tests:
executor: default <<: *defaults
steps: steps:
- attach_project - attach_workspace:
at: ~/project
- run: - run:
name: Install Headless Chrome dependencies name: Install Headless Chrome dependencies
command: | command: |
@@ -79,11 +70,11 @@ jobs:
- run: - run:
name: Run integration tests name: Run integration tests
command: yarn example test --maxWorkers=2 command: yarn example test --maxWorkers=2
build-packages: build-packages:
executor: default <<: *defaults
steps: steps:
- attach_project - attach_workspace:
at: ~/project
- run: - run:
name: Build packages in the monorepo name: Build packages in the monorepo
command: yarn lerna run prepare command: yarn lerna run prepare
@@ -92,6 +83,7 @@ jobs:
command: node scripts/check-types-path.js command: node scripts/check-types-path.js
workflows: workflows:
version: 2
build-and-test: build-and-test:
jobs: jobs:
- install-dependencies - install-dependencies

View File

@@ -8,6 +8,7 @@
"@react-navigation/routers", "@react-navigation/routers",
"@react-navigation/compat", "@react-navigation/compat",
"@react-navigation/stack", "@react-navigation/stack",
"@react-navigation/web-stack",
"@react-navigation/drawer", "@react-navigation/drawer",
"@react-navigation/bottom-tabs", "@react-navigation/bottom-tabs",
"@react-navigation/material-top-tabs", "@react-navigation/material-top-tabs",

View File

@@ -23,16 +23,20 @@ jobs:
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }} expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
expo-cache: true expo-cache: true
- name: Restore yarn cache - name: Get yarn cache
id: yarn-cache id: yarn-cache
uses: actions/cache@master run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Check yarn cache
uses: actions/cache@v1
with: with:
path: '**/node_modules' path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies - name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn
run: yarn install --frozen-lockfile
- name: Publish Expo app - name: Publish Expo app
working-directory: ./example working-directory: ./example

View File

@@ -25,16 +25,19 @@ jobs:
expo-password: ${{ secrets.EXPO_CLI_PASSWORD }} expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
expo-cache: true expo-cache: true
- name: Restore yarn cache - name: Get yarn cache
id: yarn-cache id: yarn-cache
uses: actions/cache@master run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
with: with:
path: '**/node_modules' path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies - name: Install dependencies
if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn
run: yarn install --frozen-lockfile
- name: Publish Expo app - name: Publish Expo app
working-directory: ./example working-directory: ./example

View File

@@ -1,10 +1,6 @@
import 'react-native-gesture-handler'; import 'react-native-gesture-handler';
import { registerRootComponent } from 'expo'; import { registerRootComponent } from 'expo';
import { Asset } from 'expo-asset';
import { Assets as StackAssets } from '@react-navigation/stack';
import App from './src/index'; import App from './src/index';
Asset.loadAsync(StackAssets);
registerRootComponent(App); registerRootComponent(App);

View File

@@ -1,41 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.1.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/example@5.0.0-alpha.23...@react-navigation/example@5.1.0) (2020-05-20)
### Bug Fixes
* add config to enable redux devtools integration ([c9c825b](https://github.com/react-navigation/react-navigation/commit/c9c825bee61426635a28ee149eeeff3d628171cd))
* clamp interpolated styles ([67798af](https://github.com/react-navigation/react-navigation/commit/67798af869dcbbf323629fc7e7cc9062d1e12c29))
* disable screens when mode is modal on older expo versions ([94d7b28](https://github.com/react-navigation/react-navigation/commit/94d7b28c0b2ce0d56c99b224610f305be6451626))
* dispatch pop early when screen is closed with gesture ([#336](https://github.com/react-navigation/react-navigation/issues/336)) ([3d937d1](https://github.com/react-navigation/react-navigation/commit/3d937d1e6571cd613e830d64f7b2e7426076d371)), closes [#267](https://github.com/react-navigation/react-navigation/issues/267)
* provide initial values for safe area to prevent blank screen ([#238](https://github.com/react-navigation/react-navigation/issues/238)) ([77b7570](https://github.com/react-navigation/react-navigation/commit/77b757091c0451e20bca01138629669c7da544a8))
* render fallback only if linking is enabled. closes [#8161](https://github.com/react-navigation/react-navigation/issues/8161) ([1c075ff](https://github.com/react-navigation/react-navigation/commit/1c075ffb169d233ed0515efea264a5a69b4de52e))
* return onPress instead of onClick for useLinkProps ([ae5442e](https://github.com/react-navigation/react-navigation/commit/ae5442ebe812b91fa1f12164f27d1aeed918ab0e))
* rtl in native app example ([50b366e](https://github.com/react-navigation/react-navigation/commit/50b366e7341f201d29a44f20b7771b3a832b0045))
* screens integration on Android ([#294](https://github.com/react-navigation/react-navigation/issues/294)) ([9bfb295](https://github.com/react-navigation/react-navigation/commit/9bfb29562020c61b4d5c9bee278bcb1c7bdb8b67))
* spread parent params to children in compat navigator ([24febf6](https://github.com/react-navigation/react-navigation/commit/24febf6ea99be2e5f22005fdd2a82136d647255c)), closes [#6785](https://github.com/react-navigation/react-navigation/issues/6785)
* update screens for native stack ([5411816](https://github.com/react-navigation/react-navigation/commit/54118161885738a6d20b062c7e6679f3bace8424))
* wrap navigators in gesture handler root ([41a5e1a](https://github.com/react-navigation/react-navigation/commit/41a5e1a385aa5180abc3992a4c67077c37b998b9))
### Features
* add `animationTypeForReplace` option ([#297](https://github.com/react-navigation/react-navigation/issues/297)) ([6262f72](https://github.com/react-navigation/react-navigation/commit/6262f7298bff843571fb4b1a677d3beabe29833e))
* add `screens` prop for nested configs ([#308](https://github.com/react-navigation/react-navigation/issues/308)) ([b931ae6](https://github.com/react-navigation/react-navigation/commit/b931ae62dfb2c5253c94ea5ace73e9070ec17c4a))
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
* add a useLinkProps hook ([f2291d1](https://github.com/react-navigation/react-navigation/commit/f2291d110faa2aa8e10c9133c1c0c28d54af7917))
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/commit/942d2be2c72720469475ce12ec8df23825994dbf))
* add custom theme support ([#211](https://github.com/react-navigation/react-navigation/issues/211)) ([00fc616](https://github.com/react-navigation/react-navigation/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
* add deeplinking to native example ([#309](https://github.com/react-navigation/react-navigation/issues/309)) ([e55e866](https://github.com/react-navigation/react-navigation/commit/e55e866af2f2163ee89bc527997cda13ffeb2abe))
* add headerStatusBarHeight option to stack ([b201fd2](https://github.com/react-navigation/react-navigation/commit/b201fd20716a2f03cb9373c72281f5d396a9356d))
* add Link component as useLinkTo hook for navigating to links ([2573b5b](https://github.com/react-navigation/react-navigation/commit/2573b5beaac1240434e52f3f57bb29da2f541c88))
* add openByDefault option to drawer ([36689e2](https://github.com/react-navigation/react-navigation/commit/36689e24c21b474692bb7ecd0b901c8afbbe9a20))
* add permanent drawer type ([#7818](https://github.com/react-navigation/react-navigation/issues/7818)) ([6a5d0a0](https://github.com/react-navigation/react-navigation/commit/6a5d0a035afae60d91aef78401ec8826295746fe))
* add preventDefault functionality in material bottom tabs ([3dede31](https://github.com/react-navigation/react-navigation/commit/3dede316ccab3b2403a475f60ce20b5c4e4cc068))
* emit appear and dismiss events for native stack ([f1df4a0](https://github.com/react-navigation/react-navigation/commit/f1df4a080877b3642e748a41a5ffc2da8c449a8c))
* initialState should take priority over deep link ([039017b](https://github.com/react-navigation/react-navigation/commit/039017bc2af69120d2d10e8f2c8a62919c37eb65))
* integrate with history API on web ([5a3f835](https://github.com/react-navigation/react-navigation/commit/5a3f8356b05bff7ed20893a5db6804612da3e568))

View File

@@ -7,6 +7,12 @@
"slug": "react-navigation-example", "slug": "react-navigation-example",
"description": "Demo app to showcase various functionality of React Navigation", "description": "Demo app to showcase various functionality of React Navigation",
"privacy": "public", "privacy": "public",
"sdkVersion": "37.0.0",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0", "version": "1.0.0",
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
"splash": { "splash": {
@@ -14,16 +20,15 @@
"resizeMode": "contain", "resizeMode": "contain",
"backgroundColor": "#ffffff" "backgroundColor": "#ffffff"
}, },
"sdkVersion": "37.0.0",
"platforms": ["ios", "android", "web"],
"ios": {
"supportsTablet": true
},
"updates": { "updates": {
"fallbackToCacheTimeout": 0 "fallbackToCacheTimeout": 0
}, },
"assetBundlePatterns": ["**/*"], "assetBundlePatterns": [
"scheme": "rne", "**/*"
],
"ios": {
"supportsTablet": true
},
"entryPoint": "App.tsx" "entryPoint": "App.tsx"
} }
} }

View File

@@ -6,7 +6,7 @@ beforeEach(async () => {
it('loads the article page', async () => { it('loads the article page', async () => {
expect(await page.evaluate(() => location.pathname + location.search)).toBe( expect(await page.evaluate(() => location.pathname + location.search)).toBe(
'/link-component/article/gandalf' '/link-component/Article?author=Gandalf'
); );
expect( expect(
((await page.accessibility.snapshot()) as any)?.children?.find( ((await page.accessibility.snapshot()) as any)?.children?.find(
@@ -16,24 +16,24 @@ it('loads the article page', async () => {
}); });
it('goes to the album page and goes back', async () => { it('goes to the album page and goes back', async () => {
await page.click('[href="/link-component/music"]'); await page.click('[href="/link-component/Album"]');
expect(await page.evaluate(() => location.pathname + location.search)).toBe( expect(await page.evaluate(() => location.pathname + location.search)).toBe(
'/link-component/music' '/link-component/Album'
); );
expect( expect(
((await page.accessibility.snapshot()) as any)?.children?.find( ((await page.accessibility.snapshot()) as any)?.children?.find(
(it: any) => it.role === 'heading' (it: any) => it.role === 'heading'
)?.name )?.name
).toBe('Albums'); ).toBe('Album');
await page.click('[aria-label="Article by Gandalf, back"]'); await page.click('[aria-label="Article by Gandalf, back"]');
await page.waitForNavigation(); await page.waitForNavigation();
expect(await page.evaluate(() => location.pathname + location.search)).toBe( expect(await page.evaluate(() => location.pathname + location.search)).toBe(
'/link-component/article/gandalf' '/link-component/Article?author=Gandalf'
); );
expect( expect(

View File

@@ -1,13 +0,0 @@
import fetch from 'node-fetch';
import cheerio from 'cheerio';
const server = 'http://localhost:3275';
it('renders the home page', async () => {
const res = await fetch(server);
const html = await res.text();
const $ = cheerio.load(html);
expect($('title').text()).toBe('Examples');
});

View File

@@ -1,16 +1,8 @@
import { setup } from 'jest-dev-server'; import { setup } from 'jest-dev-server';
export default async function () { export default async function () {
await setup([ await setup({
{
command: 'yarn serve -l 3579 web-build', command: 'yarn serve -l 3579 web-build',
launchTimeout: 50000,
port: 3579, port: 3579,
}, });
{
command: 'yarn server',
launchTimeout: 50000,
port: 3275,
},
]);
} }

View File

@@ -1,38 +1,35 @@
PODS: PODS:
- boost-for-react-native (1.63.0) - boost-for-react-native (1.63.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EXBlur (8.1.0): - EXAppLoaderProvider (8.0.0)
- EXBlur (8.0.0):
- UMCore - UMCore
- EXConstants (9.0.0): - EXConstants (8.0.0):
- UMConstantsInterface - UMConstantsInterface
- UMCore - UMCore
- EXErrorRecovery (1.1.0): - EXErrorRecovery (1.0.0):
- UMCore - UMCore
- EXFileSystem (8.1.0): - EXFileSystem (8.0.0):
- UMCore - UMCore
- UMFileSystemInterface - UMFileSystemInterface
- EXFont (8.1.0): - EXFont (8.0.0):
- UMCore - UMCore
- UMFontInterface - UMFontInterface
- EXImageLoader (1.0.1): - EXKeepAwake (8.0.0):
- React-Core
- UMCore - UMCore
- UMImageLoaderInterface - EXLinearGradient (8.0.0):
- EXKeepAwake (8.1.0):
- UMCore - UMCore
- EXLinearGradient (8.1.0): - EXLocation (8.0.0):
- UMCore
- EXLocation (8.1.0):
- UMCore - UMCore
- UMPermissionsInterface - UMPermissionsInterface
- UMTaskManagerInterface - UMTaskManagerInterface
- EXPermissions (8.1.0): - EXPermissions (8.0.0):
- UMCore - UMCore
- UMPermissionsInterface - UMPermissionsInterface
- EXSQLite (8.1.0): - EXSQLite (8.0.0):
- UMCore - UMCore
- UMFileSystemInterface - UMFileSystemInterface
- EXWebBrowser (8.2.1): - EXWebBrowser (8.0.0):
- UMCore - UMCore
- FBLazyVector (0.61.5) - FBLazyVector (0.61.5)
- FBReactNativeSpec (0.61.5): - FBReactNativeSpec (0.61.5):
@@ -53,6 +50,8 @@ PODS:
- glog - glog
- glog (0.3.5) - glog (0.3.5)
- RCTRequired (0.61.5) - RCTRequired (0.61.5)
- RCTRestart (0.0.13):
- React
- RCTTypeSafety (0.61.5): - RCTTypeSafety (0.61.5):
- FBLazyVector (= 0.61.5) - FBLazyVector (= 0.61.5)
- Folly (= 2018.10.22.00) - Folly (= 2018.10.22.00)
@@ -215,9 +214,7 @@ PODS:
- React-cxxreact (= 0.61.5) - React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5) - React-jsi (= 0.61.5)
- React-jsinspector (0.61.5) - React-jsinspector (0.61.5)
- react-native-restart (0.0.15): - react-native-safe-area-context (0.6.2):
- React
- react-native-safe-area-context (1.0.0):
- React - React
- React-RCTActionSheet (0.61.5): - React-RCTActionSheet (0.61.5):
- React-Core/RCTActionSheetHeaders (= 0.61.5) - React-Core/RCTActionSheetHeaders (= 0.61.5)
@@ -254,41 +251,39 @@ PODS:
- React-cxxreact (= 0.61.5) - React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5) - React-jsi (= 0.61.5)
- ReactCommon/jscallinvoker (= 0.61.5) - ReactCommon/jscallinvoker (= 0.61.5)
- RNCMaskedView (0.1.10): - RNCMaskedView (0.1.5):
- React - React
- RNGestureHandler (1.6.1): - RNGestureHandler (1.5.5):
- React - React
- RNReanimated (1.8.0): - RNReanimated (1.4.0):
- React - React
- RNScreens (2.7.0): - RNScreens (2.0.0-alpha.33):
- React - React
- UMAppLoader (1.0.2) - UMBarCodeScannerInterface (5.0.0)
- UMBarCodeScannerInterface (5.1.0) - UMCameraInterface (5.0.0)
- UMCameraInterface (5.1.0) - UMConstantsInterface (5.0.0)
- UMConstantsInterface (5.1.0) - UMCore (5.0.0)
- UMCore (5.1.2) - UMFaceDetectorInterface (5.0.0)
- UMFaceDetectorInterface (5.1.0) - UMFileSystemInterface (5.0.0)
- UMFileSystemInterface (5.1.0) - UMFontInterface (5.0.0)
- UMFontInterface (5.1.0) - UMImageLoaderInterface (5.0.0)
- UMImageLoaderInterface (5.1.0) - UMPermissionsInterface (5.0.0)
- UMPermissionsInterface (5.1.0): - UMReactNativeAdapter (5.0.0):
- UMCore
- UMReactNativeAdapter (5.2.0):
- React-Core - React-Core
- UMCore - UMCore
- UMFontInterface - UMFontInterface
- UMSensorsInterface (5.1.0) - UMSensorsInterface (5.0.0)
- UMTaskManagerInterface (5.1.0) - UMTaskManagerInterface (5.0.0)
- Yoga (1.14.0) - Yoga (1.14.0)
DEPENDENCIES: DEPENDENCIES:
- DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXAppLoaderProvider (from `../../node_modules/expo-app-loader-provider/ios`)
- EXBlur (from `../../node_modules/expo-blur/ios`) - EXBlur (from `../../node_modules/expo-blur/ios`)
- EXConstants (from `../../node_modules/expo-constants/ios`) - EXConstants (from `../../node_modules/expo-constants/ios`)
- EXErrorRecovery (from `../../node_modules/expo-error-recovery/ios`) - EXErrorRecovery (from `../../node_modules/expo-error-recovery/ios`)
- EXFileSystem (from `../../node_modules/expo-file-system/ios`) - EXFileSystem (from `../../node_modules/expo-file-system/ios`)
- EXFont (from `../../node_modules/expo-font/ios`) - EXFont (from `../../node_modules/expo-font/ios`)
- EXImageLoader (from `../../node_modules/expo-image-loader/ios`)
- EXKeepAwake (from `../../node_modules/expo-keep-awake/ios`) - EXKeepAwake (from `../../node_modules/expo-keep-awake/ios`)
- EXLinearGradient (from `../../node_modules/expo-linear-gradient/ios`) - EXLinearGradient (from `../../node_modules/expo-linear-gradient/ios`)
- EXLocation (from `../../node_modules/expo-location/ios`) - EXLocation (from `../../node_modules/expo-location/ios`)
@@ -300,6 +295,7 @@ DEPENDENCIES:
- Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`) - Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`) - glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`) - RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
- RCTRestart (from `../../node_modules/react-native-restart/ios`)
- RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`) - RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../../node_modules/react-native/`) - React (from `../../node_modules/react-native/`)
- React-Core (from `../../node_modules/react-native/`) - React-Core (from `../../node_modules/react-native/`)
@@ -310,7 +306,6 @@ DEPENDENCIES:
- React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`) - React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`) - React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-restart (from `../../node_modules/react-native-restart`)
- react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`) - react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`)
- React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`) - React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
@@ -327,11 +322,10 @@ DEPENDENCIES:
- RNGestureHandler (from `../../node_modules/react-native-gesture-handler`) - RNGestureHandler (from `../../node_modules/react-native-gesture-handler`)
- RNReanimated (from `../../node_modules/react-native-reanimated`) - RNReanimated (from `../../node_modules/react-native-reanimated`)
- RNScreens (from `../../node_modules/react-native-screens`) - RNScreens (from `../../node_modules/react-native-screens`)
- UMAppLoader (from `../../node_modules/unimodules-app-loader/ios`)
- UMBarCodeScannerInterface (from `../../node_modules/unimodules-barcode-scanner-interface/ios`) - UMBarCodeScannerInterface (from `../../node_modules/unimodules-barcode-scanner-interface/ios`)
- UMCameraInterface (from `../../node_modules/unimodules-camera-interface/ios`) - UMCameraInterface (from `../../node_modules/unimodules-camera-interface/ios`)
- UMConstantsInterface (from `../../node_modules/unimodules-constants-interface/ios`) - UMConstantsInterface (from `../../node_modules/unimodules-constants-interface/ios`)
- "UMCore (from `../../node_modules/react-native-unimodules/node_modules/@unimodules/core/ios`)" - "UMCore (from `../../node_modules/@unimodules/core/ios`)"
- UMFaceDetectorInterface (from `../../node_modules/unimodules-face-detector-interface/ios`) - UMFaceDetectorInterface (from `../../node_modules/unimodules-face-detector-interface/ios`)
- UMFileSystemInterface (from `../../node_modules/unimodules-file-system-interface/ios`) - UMFileSystemInterface (from `../../node_modules/unimodules-file-system-interface/ios`)
- UMFontInterface (from `../../node_modules/unimodules-font-interface/ios`) - UMFontInterface (from `../../node_modules/unimodules-font-interface/ios`)
@@ -349,30 +343,42 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
DoubleConversion: DoubleConversion:
:podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" :podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXAppLoaderProvider:
:path: !ruby/object:Pathname
path: "../../node_modules/expo-app-loader-provider/ios"
EXBlur: EXBlur:
:path: "../../node_modules/expo-blur/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-blur/ios"
EXConstants: EXConstants:
:path: "../../node_modules/expo-constants/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-constants/ios"
EXErrorRecovery: EXErrorRecovery:
:path: "../../node_modules/expo-error-recovery/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-error-recovery/ios"
EXFileSystem: EXFileSystem:
:path: "../../node_modules/expo-file-system/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-file-system/ios"
EXFont: EXFont:
:path: "../../node_modules/expo-font/ios" :path: !ruby/object:Pathname
EXImageLoader: path: "../../node_modules/expo-font/ios"
:path: "../../node_modules/expo-image-loader/ios"
EXKeepAwake: EXKeepAwake:
:path: "../../node_modules/expo-keep-awake/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-keep-awake/ios"
EXLinearGradient: EXLinearGradient:
:path: "../../node_modules/expo-linear-gradient/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-linear-gradient/ios"
EXLocation: EXLocation:
:path: "../../node_modules/expo-location/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-location/ios"
EXPermissions: EXPermissions:
:path: "../../node_modules/expo-permissions/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-permissions/ios"
EXSQLite: EXSQLite:
:path: "../../node_modules/expo-sqlite/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-sqlite/ios"
EXWebBrowser: EXWebBrowser:
:path: "../../node_modules/expo-web-browser/ios" :path: !ruby/object:Pathname
path: "../../node_modules/expo-web-browser/ios"
FBLazyVector: FBLazyVector:
:path: "../../node_modules/react-native/Libraries/FBLazyVector" :path: "../../node_modules/react-native/Libraries/FBLazyVector"
FBReactNativeSpec: FBReactNativeSpec:
@@ -383,6 +389,8 @@ EXTERNAL SOURCES:
:podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec" :podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec"
RCTRequired: RCTRequired:
:path: "../../node_modules/react-native/Libraries/RCTRequired" :path: "../../node_modules/react-native/Libraries/RCTRequired"
RCTRestart:
:path: "../../node_modules/react-native-restart/ios"
RCTTypeSafety: RCTTypeSafety:
:path: "../../node_modules/react-native/Libraries/TypeSafety" :path: "../../node_modules/react-native/Libraries/TypeSafety"
React: React:
@@ -399,8 +407,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/ReactCommon/jsiexecutor" :path: "../../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector: React-jsinspector:
:path: "../../node_modules/react-native/ReactCommon/jsinspector" :path: "../../node_modules/react-native/ReactCommon/jsinspector"
react-native-restart:
:path: "../../node_modules/react-native-restart"
react-native-safe-area-context: react-native-safe-area-context:
:path: "../../node_modules/react-native-safe-area-context" :path: "../../node_modules/react-native-safe-area-context"
React-RCTActionSheet: React-RCTActionSheet:
@@ -431,55 +437,66 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-reanimated" :path: "../../node_modules/react-native-reanimated"
RNScreens: RNScreens:
:path: "../../node_modules/react-native-screens" :path: "../../node_modules/react-native-screens"
UMAppLoader:
:path: "../../node_modules/unimodules-app-loader/ios"
UMBarCodeScannerInterface: UMBarCodeScannerInterface:
:path: "../../node_modules/unimodules-barcode-scanner-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-barcode-scanner-interface/ios"
UMCameraInterface: UMCameraInterface:
:path: "../../node_modules/unimodules-camera-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-camera-interface/ios"
UMConstantsInterface: UMConstantsInterface:
:path: "../../node_modules/unimodules-constants-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-constants-interface/ios"
UMCore: UMCore:
:path: "../../node_modules/react-native-unimodules/node_modules/@unimodules/core/ios" :path: !ruby/object:Pathname
path: "../../node_modules/@unimodules/core/ios"
UMFaceDetectorInterface: UMFaceDetectorInterface:
:path: "../../node_modules/unimodules-face-detector-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-face-detector-interface/ios"
UMFileSystemInterface: UMFileSystemInterface:
:path: "../../node_modules/unimodules-file-system-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-file-system-interface/ios"
UMFontInterface: UMFontInterface:
:path: "../../node_modules/unimodules-font-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-font-interface/ios"
UMImageLoaderInterface: UMImageLoaderInterface:
:path: "../../node_modules/unimodules-image-loader-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-image-loader-interface/ios"
UMPermissionsInterface: UMPermissionsInterface:
:path: "../../node_modules/unimodules-permissions-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-permissions-interface/ios"
UMReactNativeAdapter: UMReactNativeAdapter:
:path: "../../node_modules/@unimodules/react-native-adapter/ios" :path: !ruby/object:Pathname
path: "../../node_modules/@unimodules/react-native-adapter/ios"
UMSensorsInterface: UMSensorsInterface:
:path: "../../node_modules/unimodules-sensors-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-sensors-interface/ios"
UMTaskManagerInterface: UMTaskManagerInterface:
:path: "../../node_modules/unimodules-task-manager-interface/ios" :path: !ruby/object:Pathname
path: "../../node_modules/unimodules-task-manager-interface/ios"
Yoga: Yoga:
:path: "../../node_modules/react-native/ReactCommon/yoga" :path: "../../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
EXBlur: aa14d84bff6e9c2232fbcaf54ad809eee1cc41dc EXAppLoaderProvider: ebdb6bc2632c1ccadbe49f5e4104d8d690969c49
EXConstants: 5304709b1bea70a4828f48ba4c7fc3ec3b2d9b17 EXBlur: d1604f66f89a9414f5ee65dfb23874437c1bb147
EXErrorRecovery: 8f4c21ab2f51bf75defe4536f841a37de59b0661 EXConstants: 4051b16c17ef3defa03c541d42811dc92b249146
EXFileSystem: cf4232ba7c62dc49b78c2d36005f97b6fddf0b01 EXErrorRecovery: d36db99ec6a3808f313f01b0890eb443796dd1c2
EXFont: 8326ecf966be559f7ced7c8e221a32fc4d9ed8b0 EXFileSystem: 6e0d9bb6cc4ea404dbb8f583c1a8a2dcdf4b83b6
EXImageLoader: 5ad6896fa1ef2ee814b551873cbf7a7baccc694a EXFont: 6187b5ab46ee578d5f8e7f2ea092752e78772235
EXKeepAwake: d045bc2cf1ad5a04f0323cc7c894b95b414042e0 EXKeepAwake: 66e9f80b6d129633725a0e42f8d285c229876811
EXLinearGradient: 97d8095d1e4ad96f7893e010e564796ed8aeea42 EXLinearGradient: 75f302f9d6484267a3f6d3252df2e7a5f00e716a
EXLocation: bbd487fd96a18a3ad9725389bbb94c4a5f78edf3 EXLocation: 3c75d012ca92eed94d4338778d79c49d1252393a
EXPermissions: 24b97f734ce9172d245a5be38ad9ccfcb6135964 EXPermissions: 9bc08859a675d291e89be9a0870155c27c16ac35
EXSQLite: 877ad6c8eb169353a2f94d5ad26510ffadd46a1f EXSQLite: 220226a354912b100dfe913f5fe6f31762c8927e
EXWebBrowser: 5902f99ac5ac551e5c82ff46f13a337b323aa9ea EXWebBrowser: db32607359fb7b55b7b7b91df32dd3d8355bb3b7
FBLazyVector: aaeaf388755e4f29cd74acbc9e3b8da6d807c37f FBLazyVector: aaeaf388755e4f29cd74acbc9e3b8da6d807c37f
FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75 FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28 glog: 1f3da668190260b06b429bb211bfbee5cd790c28
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1 RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
RCTRestart: dd19aab87fc1118e05b6b5b91b959105647f56b4
RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320 RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320
React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78 React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78
React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04 React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04
@@ -488,8 +505,7 @@ SPEC CHECKSUMS:
React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7 React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386 React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0 React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
react-native-restart: fff228304625f55de2ebd4de43938110f4c888ed react-native-safe-area-context: 25260c5d0b9c53fd7aa88e569e2edae72af1f6a3
react-native-safe-area-context: a346c75f2288147527365ce27b59ca6d38c27805
React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76 React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360 React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72 React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72
@@ -500,25 +516,24 @@ SPEC CHECKSUMS:
React-RCTText: 9ccc88273e9a3aacff5094d2175a605efa854dbe React-RCTText: 9ccc88273e9a3aacff5094d2175a605efa854dbe
React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad
ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f RNCMaskedView: dd13f9f7b146a9ad82f9b7eb6c9b5548fcf6e990
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38 RNGestureHandler: d2270608171c868581b840cfc692f2962c05cd17
RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c
RNScreens: cf198f915f8a2bf163de94ca9f5bfc8d326c3706 RNScreens: 1c7fd499b915c77c21e8e6c327890c5af9b4cf7e
UMAppLoader: ee77a072f9e15128f777ccd6d2d00f52ab4387e6 UMBarCodeScannerInterface: 3802c8574ef119c150701d679ab386e2266d6a54
UMBarCodeScannerInterface: 9dc692b87e5f20fe277fa57aa47f45d418c3cc6c UMCameraInterface: 985d301f688ed392f815728f0dd906ca34b7ccb1
UMCameraInterface: 625878bbf2ba188a8548675e1d1d2e438a653e6d UMConstantsInterface: bda5f8bd3403ad99e663eb3c4da685d063c5653c
UMConstantsInterface: 64060cf86587bcd90b1dbd804cceb6d377a308c1 UMCore: 7ab08669a8bb2e61f557c1fe9784521cb5aa28e3
UMCore: eb200e882eadafcd31ead290770835fd648c0945 UMFaceDetectorInterface: ce14e8e597f6a52aa66e4ab956cb5bff4fa8acf8
UMFaceDetectorInterface: d6677d6ddc9ab95a0ca857aa7f8ba76656cc770f UMFileSystemInterface: 2ed004c9620f43f0b36b33c42ce668500850d6a4
UMFileSystemInterface: c70ea7147198b9807080f3597f26236be49b0165 UMFontInterface: 24fbc0a02ade6c60ad3ee3e2b5d597c8dcfc3208
UMFontInterface: d9d3b27af698c5389ae9e20b99ef56a083f491fb UMImageLoaderInterface: 3976a14c588341228881ff75970fbabf122efca4
UMImageLoaderInterface: 14dd2c46c67167491effc9e91250e9510f12709e UMPermissionsInterface: 2abf9f7f4aa7110e27beaf634a7deda2d50ff3d7
UMPermissionsInterface: 5e83a9167c177e4a0f0a3539345983cc749efb3e UMReactNativeAdapter: 230406e3335a8dbd4c9c0e654488a1cf3b44552f
UMReactNativeAdapter: 126da3486c1a1f11945b649d557d6c2ebb9407b2 UMSensorsInterface: d708a892ef1500bdd9fc3ff03f7836c66d1634d3
UMSensorsInterface: 48941f70175e2975af1a9386c6d6cb16d8126805 UMTaskManagerInterface: a98e37a576a5220bf43b8faf33cfdc129d2f441d
UMTaskManagerInterface: cb890c79c63885504ddc0efd7a7d01481760aca2
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
PODFILE CHECKSUM: c48a21ff513d3eadafa50f8797207ef2be75e234 PODFILE CHECKSUM: c48a21ff513d3eadafa50f8797207ef2be75e234
COCOAPODS: 1.9.1 COCOAPODS: 1.8.4

View File

@@ -8,24 +8,25 @@ const blacklist = require('metro-config/src/defaults/blacklist');
const root = path.resolve(__dirname, '..'); const root = path.resolve(__dirname, '..');
const packages = path.resolve(root, 'packages'); const packages = path.resolve(root, 'packages');
const workspaces = fs // Get the list of dependencies for all packages in the monorepo
const modules = ['@expo/vector-icons']
.concat(
...fs
// List all packages under `packages/` // List all packages under `packages/`
.readdirSync(packages) .readdirSync(packages)
// Ignore hidden files such as .DS_Store // Ignore hidden files such as .DS_Store
.filter((p) => !p.startsWith('.')) .filter((p) => !p.startsWith('.'))
.map((p) => path.join(packages, p)); .map((p) => {
// Get the list of dependencies for all packages in the monorepo
const modules = ['@expo/vector-icons']
.concat(
...workspaces.map((it) => {
const pak = JSON.parse( const pak = JSON.parse(
fs.readFileSync(path.join(it, 'package.json'), 'utf8') fs.readFileSync(path.join(packages, p, 'package.json'), 'utf8')
); );
// We need to make sure that only one version is loaded for peerDependencies // We need to collect list of deps that this package imports
// So we blacklist them at the root, and alias them to the versions in example's node_modules // Collecting both dependencies are peerDependencies should do it
return pak.peerDependencies ? Object.keys(pak.peerDependencies) : []; return Object.keys({
...pak.dependencies,
...pak.peerDependencies,
});
}) })
) )
.sort() .sort()
@@ -44,15 +45,14 @@ module.exports = {
watchFolders: [root], watchFolders: [root],
resolver: { resolver: {
// We need to blacklist the peerDependencies we've collected in packages' node_modules // We need to blacklist `node_modules` of all our packages
// This will avoid Metro throwing duplicate module errors
blacklistRE: blacklist( blacklistRE: blacklist(
[].concat( fs
...workspaces.map((it) => .readdirSync(packages)
modules.map( .map((p) => path.join(packages, p))
(m) => .map(
new RegExp(`^${escape(path.join(it, 'node_modules', m))}\\/.*$`) (it) => new RegExp(`^${escape(path.join(it, 'node_modules'))}\\/.*$`)
)
)
) )
), ),

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/example", "name": "@react-navigation/example",
"description": "Demo app to showcase various functionality of React Navigation", "description": "Demo app to showcase various functionality of React Navigation",
"version": "5.1.0", "version": "5.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "expo start", "start": "expo start",
@@ -9,50 +9,37 @@
"native": "react-native start", "native": "react-native start",
"android": "react-native run-android", "android": "react-native run-android",
"ios": "react-native run-ios", "ios": "react-native run-ios",
"server": "nodemon -e '.js,.ts,.tsx' --exec \"babel-node -i '/node_modules[/\\](?react-native)/' -x '.web.tsx,.web.ts,.web.js,.tsx,.ts,.js' --config-file ./server/babel.config.js server\"",
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@expo/vector-icons": "^10.2.0", "@expo/vector-icons": "^10.0.0",
"@react-native-community/masked-view": "^0.1.10", "@react-native-community/masked-view": "^0.1.7",
"color": "^3.1.2", "color": "^3.1.2",
"expo": "^37.0.8", "expo": "^37.0.0",
"expo-asset": "~8.1.3", "expo-asset": "~8.1.3",
"expo-blur": "~8.1.0", "expo-blur": "~8.1.0",
"koa": "^2.12.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-dom": "~16.9.0", "react-dom": "~16.9.0",
"react-native": "~0.61.5", "react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0", "react-native-gesture-handler": "^1.6.0",
"react-native-paper": "^3.10.1", "react-native-paper": "^3.7.0",
"react-native-reanimated": "^1.8.0", "react-native-reanimated": "^1.7.0",
"react-native-restart": "^0.0.15", "react-native-restart": "^0.0.14",
"react-native-safe-area-context": "^1.0.0", "react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.7.0", "react-native-screens": "^2.3.0",
"react-native-tab-view": "2.14.0", "react-native-tab-view": "2.14.0",
"react-native-unimodules": "~0.9.1", "react-native-unimodules": "~0.8.1",
"react-native-vector-icons": "^6.6.0",
"react-native-web": "^0.11.7" "react-native-web": "^0.11.7"
}, },
"devDependencies": { "devDependencies": {
"@babel/node": "^7.8.7",
"@expo/webpack-config": "^0.11.19", "@expo/webpack-config": "^0.11.19",
"@types/cheerio": "^0.22.18",
"@types/jest-dev-server": "^4.2.0", "@types/jest-dev-server": "^4.2.0",
"@types/koa": "^2.11.3", "@types/react": "^16.9.23",
"@types/node-fetch": "^2.5.7", "@types/react-native": "^0.60.22",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.8",
"@types/react-native": "^0.62.7",
"babel-plugin-module-resolver": "^4.0.0",
"babel-preset-expo": "^8.1.0", "babel-preset-expo": "^8.1.0",
"cheerio": "^1.0.0-rc.3", "expo-cli": "^3.17.18",
"expo-cli": "^3.20.1", "jest": "^25.2.7",
"jest": "^26.0.1",
"jest-dev-server": "^4.4.0", "jest-dev-server": "^4.4.0",
"mock-require-assets": "^0.0.1",
"node-fetch": "^2.6.0",
"nodemon": "^2.0.4",
"playwright": "^0.14.0", "playwright": "^0.14.0",
"serve": "^11.3.0", "serve": "^11.3.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"

View File

@@ -1,40 +0,0 @@
const path = require('path');
const fs = require('fs');
const packages = path.resolve(__dirname, '..', '..', 'packages');
const alias = Object.fromEntries(
fs
.readdirSync(packages)
.filter((name) => !name.startsWith('.'))
.map((name) => [
`@react-navigation/${name}`,
path.resolve(
packages,
name,
require(`../../packages/${name}/package.json`).source
),
])
);
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-flow',
'@babel/preset-typescript',
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-class-properties',
[
'module-resolver',
{
root: ['..'],
alias: {
'react-native': 'react-native-web',
...alias,
},
},
],
],
};

View File

@@ -1,54 +0,0 @@
import './resolve-hooks';
import Koa from 'koa';
import * as React from 'react';
import ReactDOMServer from 'react-dom/server';
import { AppRegistry } from 'react-native-web';
import { ServerContainer, ServerContainerRef } from '@react-navigation/native';
import App from '../src/index';
AppRegistry.registerComponent('App', () => App);
const PORT = process.env.PORT || 3275;
const app = new Koa();
app.use(async (ctx) => {
const { element, getStyleElement } = AppRegistry.getApplication('App');
const ref = React.createRef<ServerContainerRef>();
const html = ReactDOMServer.renderToString(
<ServerContainer
ref={ref}
location={{ pathname: ctx.path, search: ctx.search }}
>
{element}
</ServerContainer>
);
const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());
const document = `
<!DOCTYPE html>
<html style="height: 100%">
<meta charset="utf-8">
<meta httpEquiv="X-UA-Compatible" content="IE=edge">
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover"
>
${css}
<title>${ref.current?.getCurrentOptions()?.title}</title>
<body style="min-height: 100%">
<div id="root" style="display: flex; min-height: 100vh">
${html}
</div>
`;
ctx.body = document;
});
app.listen(PORT, () => {
console.log(`Running at http://localhost:${PORT}`);
});

View File

@@ -1,12 +0,0 @@
import 'mock-require-assets';
import Module from 'module';
// We need to make sure that .web.xx extensions are resolved before .xx
// @ts-ignore
Module._extensions = Object.fromEntries(
// @ts-ignore
Object.entries(Module._extensions).sort((a, b) => {
return b[0].split('.').length - a[0].split('.').length;
})
);

View File

@@ -1,3 +0,0 @@
import { AsyncStorage } from 'react-native';
export default AsyncStorage;

View File

@@ -1,14 +0,0 @@
export default {
getItem(key: string) {
return Promise.resolve(localStorage.getItem(key));
},
setItem(key: string, value: string) {
return Promise.resolve(localStorage.setItem(key, value));
},
removeItem(key: string) {
return Promise.resolve(localStorage.removeItem(key));
},
clear() {
return Promise.resolve(localStorage.clear());
},
};

View File

@@ -1,11 +0,0 @@
import RNRestart from 'react-native-restart';
import { Updates } from 'expo';
export function restartApp() {
// @ts-ignore
if (global.Expo) {
Updates.reloadFromCache();
} else {
RNRestart.Restart();
}
}

View File

@@ -1 +0,0 @@
export function restartApp() {}

View File

@@ -1,11 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { View, ScrollView, StyleSheet, Platform } from 'react-native'; import { Platform } from 'react-native';
import { Button } from 'react-native-paper'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import {
createBottomTabNavigator,
BottomTabNavigationProp,
} from '@react-navigation/bottom-tabs';
import TouchableBounce from '../Shared/TouchableBounce'; import TouchableBounce from '../Shared/TouchableBounce';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
import Contacts from '../Shared/Contacts'; import Contacts from '../Shared/Contacts';
@@ -27,36 +23,6 @@ type BottomTabParams = {
Chat: undefined; Chat: undefined;
}; };
const scrollEnabled = Platform.select({ web: true, default: false });
const AlbumsScreen = ({
navigation,
}: {
navigation: BottomTabNavigationProp<BottomTabParams>;
}) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="outlined"
onPress={() => navigation.setOptions({ tabBarVisible: false })}
style={styles.button}
>
Hide tab bar
</Button>
<Button
mode="outlined"
onPress={() => navigation.setOptions({ tabBarVisible: true })}
style={styles.button}
>
Show tab bar
</Button>
</View>
<Albums scrollEnabled={scrollEnabled} />
</ScrollView>
);
};
const BottomTabs = createBottomTabNavigator<BottomTabParams>(); const BottomTabs = createBottomTabNavigator<BottomTabParams>();
export default function BottomTabsScreen() { export default function BottomTabsScreen() {
@@ -96,7 +62,7 @@ export default function BottomTabsScreen() {
/> />
<BottomTabs.Screen <BottomTabs.Screen
name="Albums" name="Albums"
component={AlbumsScreen} component={Albums}
options={{ options={{
title: 'Albums', title: 'Albums',
tabBarIcon: getTabBarIcon('image-album'), tabBarIcon: getTabBarIcon('image-album'),
@@ -105,13 +71,3 @@ export default function BottomTabsScreen() {
</BottomTabs.Navigator> </BottomTabs.Navigator>
); );
} }
const styles = StyleSheet.create({
buttons: {
flexDirection: 'row',
padding: 8,
},
button: {
margin: 8,
},
});

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { View, ScrollView, StyleSheet, Platform } from 'react-native'; import { View, ScrollView, StyleSheet } from 'react-native';
import { Button } from 'react-native-paper'; import { Button } from 'react-native-paper';
import { import {
createCompatNavigatorFactory, createCompatNavigatorFactory,
@@ -23,8 +23,6 @@ type NestedStackParams = {
Article: { author: string }; Article: { author: string };
}; };
const scrollEnabled = Platform.select({ web: true, default: false });
const AlbumsScreen: CompatScreenType<StackNavigationProp< const AlbumsScreen: CompatScreenType<StackNavigationProp<
CompatStackParams CompatStackParams
>> = ({ navigation }) => { >> = ({ navigation }) => {
@@ -46,7 +44,7 @@ const AlbumsScreen: CompatScreenType<StackNavigationProp<
Go back Go back
</Button> </Button>
</View> </View>
<Albums scrollEnabled={scrollEnabled} /> <Albums scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -72,7 +70,7 @@ const FeedScreen: CompatScreenType<StackNavigationProp<NestedStackParams>> = ({
Go back Go back
</Button> </Button>
</View> </View>
<NewsFeed scrollEnabled={scrollEnabled} /> <NewsFeed scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -102,7 +100,7 @@ const ArticleScreen: CompatScreenType<StackNavigationProp<
</View> </View>
<Article <Article
author={{ name: navigation.getParam('author') }} author={{ name: navigation.getParam('author') }}
scrollEnabled={scrollEnabled} scrollEnabled={false}
/> />
</ScrollView> </ScrollView>
); );

View File

@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { View, StyleSheet } from 'react-native'; import { View, StyleSheet } from 'react-native';
import { Title, Button } from 'react-native-paper'; import { Title, Button } from 'react-native-paper';
import Feather from 'react-native-vector-icons/Feather'; import { Feather } from '@expo/vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
type BottomTabParams = { type BottomTabParams = {

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { View, StyleSheet, ScrollView, Platform } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import { Button } from 'react-native-paper'; import { Button } from 'react-native-paper';
import { import {
Link, Link,
@@ -17,29 +17,18 @@ import Albums from '../Shared/Albums';
type SimpleStackParams = { type SimpleStackParams = {
Article: { author: string }; Article: { author: string };
Albums: undefined; Album: undefined;
}; };
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>; type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
const scrollEnabled = Platform.select({ web: true, default: false });
const LinkButton = ({ const LinkButton = ({
to, to,
...rest ...rest
}: React.ComponentProps<typeof Button> & { to: string }) => { }: React.ComponentProps<typeof Button> & { to: string }) => {
const { onPress, ...props } = useLinkProps({ to }); const props = useLinkProps({ to });
return ( return <Button {...props} {...rest} />;
<Button
{...props}
{...rest}
{...Platform.select({
web: { onClick: onPress } as any,
default: { onPress },
})}
/>
);
}; };
const ArticleScreen = ({ const ArticleScreen = ({
@@ -53,24 +42,24 @@ const ArticleScreen = ({
<ScrollView> <ScrollView>
<View style={styles.buttons}> <View style={styles.buttons}>
<Link <Link
to="/link-component/music" to="/link-component/Album"
style={[styles.button, { padding: 8 }]} style={[styles.button, { padding: 8 }]}
> >
Go to /link-component/music Go to /link-component/Album
</Link> </Link>
<Link <Link
to="/link-component/music" to="/link-component/Album"
action={StackActions.replace('Albums')} action={StackActions.replace('Album')}
style={[styles.button, { padding: 8 }]} style={[styles.button, { padding: 8 }]}
> >
Replace with /link-component/music Replace with /link-component/Album
</Link> </Link>
<LinkButton <LinkButton
to="/link-component/music" to="/link-component/Album"
mode="contained" mode="contained"
style={styles.button} style={styles.button}
> >
Go to /link-component/music Go to /link-component/Album
</LinkButton> </LinkButton>
<Button <Button
mode="outlined" mode="outlined"
@@ -80,10 +69,7 @@ const ArticleScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<Article <Article author={{ name: route.params.author }} scrollEnabled={false} />
author={{ name: route.params.author }}
scrollEnabled={scrollEnabled}
/>
</ScrollView> </ScrollView>
); );
}; };
@@ -97,17 +83,17 @@ const AlbumsScreen = ({
<ScrollView> <ScrollView>
<View style={styles.buttons}> <View style={styles.buttons}>
<Link <Link
to="/link-component/article/babel" to="/link-component/Article?author=Babel"
style={[styles.button, { padding: 8 }]} style={[styles.button, { padding: 8 }]}
> >
Go to /link-component/article Go to /link-component/Article
</Link> </Link>
<LinkButton <LinkButton
to="/link-component/article/babel" to="/link-component/Article?author=Babel"
mode="contained" mode="contained"
style={styles.button} style={styles.button}
> >
Go to /link-component/article Go to /link-component/Article
</LinkButton> </LinkButton>
<Button <Button
mode="outlined" mode="outlined"
@@ -117,7 +103,7 @@ const AlbumsScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<Albums scrollEnabled={scrollEnabled} /> <Albums scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -144,9 +130,9 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
initialParams={{ author: 'Gandalf' }} initialParams={{ author: 'Gandalf' }}
/> />
<SimpleStack.Screen <SimpleStack.Screen
name="Albums" name="Album"
component={AlbumsScreen} component={AlbumsScreen}
options={{ title: 'Albums' }} options={{ title: 'Album' }}
/> />
</SimpleStack.Navigator> </SimpleStack.Navigator>
); );

View File

@@ -1,18 +1,12 @@
import * as React from 'react'; import * as React from 'react';
import { Dimensions, ScaledSize } from 'react-native'; import { Dimensions, ScaledSize } from 'react-native';
import { Appbar } from 'react-native-paper'; import { Appbar } from 'react-native-paper';
import { import { ParamListBase } from '@react-navigation/native';
useTheme,
useNavigation,
ParamListBase,
} from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { import {
createDrawerNavigator, createDrawerNavigator,
DrawerNavigationProp, DrawerNavigationProp,
DrawerContent, DrawerContent,
DrawerContentComponentProps,
DrawerContentOptions,
} from '@react-navigation/drawer'; } from '@react-navigation/drawer';
import Article from '../Shared/Article'; import Article from '../Shared/Article';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
@@ -21,7 +15,7 @@ import NewsFeed from '../Shared/NewsFeed';
type DrawerParams = { type DrawerParams = {
Article: undefined; Article: undefined;
NewsFeed: undefined; NewsFeed: undefined;
Albums: undefined; Album: undefined;
}; };
type DrawerNavigation = DrawerNavigationProp<DrawerParams>; type DrawerNavigation = DrawerNavigationProp<DrawerParams>;
@@ -49,11 +43,10 @@ const Header = ({
onGoBack: () => void; onGoBack: () => void;
title: string; title: string;
}) => { }) => {
const { colors } = useTheme();
const isLargeScreen = useIsLargeScreen(); const isLargeScreen = useIsLargeScreen();
return ( return (
<Appbar.Header style={{ backgroundColor: colors.card, elevation: 1 }}> <Appbar.Header>
{isLargeScreen ? null : <Appbar.BackAction onPress={onGoBack} />} {isLargeScreen ? null : <Appbar.BackAction onPress={onGoBack} />}
<Appbar.Content title={title} /> <Appbar.Content title={title} />
</Appbar.Header> </Appbar.Header>
@@ -87,23 +80,6 @@ const AlbumsScreen = ({ navigation }: { navigation: DrawerNavigation }) => {
); );
}; };
const CustomDrawerContent = (
props: DrawerContentComponentProps<DrawerContentOptions>
) => {
const { colors } = useTheme();
const navigation = useNavigation();
return (
<>
<Appbar.Header style={{ backgroundColor: colors.card, elevation: 1 }}>
<Appbar.Action icon="close" onPress={() => navigation.goBack()} />
<Appbar.Content title="Pages" />
</Appbar.Header>
<DrawerContent {...props} />
</>
);
};
const Drawer = createDrawerNavigator<DrawerParams>(); const Drawer = createDrawerNavigator<DrawerParams>();
type Props = Partial<React.ComponentProps<typeof Drawer.Navigator>> & { type Props = Partial<React.ComponentProps<typeof Drawer.Navigator>> & {
@@ -124,7 +100,15 @@ export default function DrawerScreen({ navigation, ...rest }: Props) {
drawerType={isLargeScreen ? 'permanent' : 'back'} drawerType={isLargeScreen ? 'permanent' : 'back'}
drawerStyle={isLargeScreen ? null : { width: '100%' }} drawerStyle={isLargeScreen ? null : { width: '100%' }}
overlayColor="transparent" overlayColor="transparent"
drawerContent={(props) => <CustomDrawerContent {...props} />} drawerContent={(props) => (
<>
<Appbar.Header>
<Appbar.Action icon="close" onPress={() => navigation.goBack()} />
<Appbar.Content title="Pages" />
</Appbar.Header>
<DrawerContent {...props} />
</>
)}
{...rest} {...rest}
> >
<Drawer.Screen name="Article" component={ArticleScreen} /> <Drawer.Screen name="Article" component={ArticleScreen} />
@@ -134,9 +118,9 @@ export default function DrawerScreen({ navigation, ...rest }: Props) {
options={{ title: 'Feed' }} options={{ title: 'Feed' }}
/> />
<Drawer.Screen <Drawer.Screen
name="Albums" name="Album"
component={AlbumsScreen} component={AlbumsScreen}
options={{ title: 'Albums' }} options={{ title: 'Album' }}
/> />
</Drawer.Navigator> </Drawer.Navigator>
); );

View File

@@ -1,6 +1,4 @@
import * as React from 'react'; import * as React from 'react';
import { ParamListBase } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
import Contacts from '../Shared/Contacts'; import Contacts from '../Shared/Contacts';
@@ -14,15 +12,7 @@ type MaterialTopTabParams = {
const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>(); const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>();
type Props = { export default function MaterialTopTabsScreen() {
navigation: StackNavigationProp<ParamListBase>;
};
export default function MaterialTopTabsScreen({ navigation }: Props) {
navigation.setOptions({
cardStyle: { flex: 1 },
});
return ( return (
<MaterialTopTabs.Navigator> <MaterialTopTabs.Navigator>
<MaterialTopTabs.Screen <MaterialTopTabs.Screen

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { View, StyleSheet, ScrollView, Platform } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import { Button } from 'react-native-paper'; import { Button } from 'react-native-paper';
import { RouteProp, ParamListBase } from '@react-navigation/native'; import { RouteProp, ParamListBase } from '@react-navigation/native';
import { import {
@@ -12,13 +12,11 @@ import Albums from '../Shared/Albums';
type ModalStackParams = { type ModalStackParams = {
Article: { author: string }; Article: { author: string };
Albums: undefined; Album: undefined;
}; };
type ModalStackNavigation = StackNavigationProp<ModalStackParams>; type ModalStackNavigation = StackNavigationProp<ModalStackParams>;
const scrollEnabled = Platform.select({ web: true, default: false });
const ArticleScreen = ({ const ArticleScreen = ({
navigation, navigation,
route, route,
@@ -31,7 +29,7 @@ const ArticleScreen = ({
<View style={styles.buttons}> <View style={styles.buttons}>
<Button <Button
mode="contained" mode="contained"
onPress={() => navigation.push('Albums')} onPress={() => navigation.push('Album')}
style={styles.button} style={styles.button}
> >
Push album Push album
@@ -44,10 +42,7 @@ const ArticleScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<Article <Article author={{ name: route.params.author }} scrollEnabled={false} />
author={{ name: route.params.author }}
scrollEnabled={scrollEnabled}
/>
</ScrollView> </ScrollView>
); );
}; };
@@ -71,7 +66,7 @@ const AlbumsScreen = ({ navigation }: { navigation: ModalStackNavigation }) => {
Go back Go back
</Button> </Button>
</View> </View>
<Albums scrollEnabled={scrollEnabled} /> <Albums scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -112,9 +107,9 @@ export default function SimpleStackScreen({ navigation, options }: Props) {
initialParams={{ author: 'Gandalf' }} initialParams={{ author: 'Gandalf' }}
/> />
<ModalPresentationStack.Screen <ModalPresentationStack.Screen
name="Albums" name="Album"
component={AlbumsScreen} component={AlbumsScreen}
options={{ title: 'Albums' }} options={{ title: 'Album' }}
/> />
</ModalPresentationStack.Navigator> </ModalPresentationStack.Navigator>
); );

View File

@@ -1,40 +0,0 @@
import { StackNavigationProp } from '@react-navigation/stack';
import * as React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Button } from 'react-native-paper';
const NotFoundScreen = ({
navigation,
}: {
navigation: StackNavigationProp<{ Home: undefined }>;
}) => {
return (
<View style={styles.container}>
<Text style={styles.title}>404 Not Found</Text>
<Button
mode="contained"
onPress={() => navigation.navigate('Home')}
style={styles.button}
>
Go to home
</Button>
</View>
);
};
export default NotFoundScreen;
const styles = StyleSheet.create({
title: {
fontSize: 36,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 8,
},
button: {
margin: 24,
},
});

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { View, Platform, StyleSheet, ScrollView } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import { Button } from 'react-native-paper'; import { Button } from 'react-native-paper';
import { RouteProp, ParamListBase } from '@react-navigation/native'; import { RouteProp, ParamListBase } from '@react-navigation/native';
import { import {
@@ -13,13 +13,11 @@ import NewsFeed from '../Shared/NewsFeed';
type SimpleStackParams = { type SimpleStackParams = {
Article: { author: string }; Article: { author: string };
NewsFeed: undefined; NewsFeed: undefined;
Albums: undefined; Album: undefined;
}; };
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>; type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
const scrollEnabled = Platform.select({ web: true, default: false });
const ArticleScreen = ({ const ArticleScreen = ({
navigation, navigation,
route, route,
@@ -45,10 +43,7 @@ const ArticleScreen = ({
Pop screen Pop screen
</Button> </Button>
</View> </View>
<Article <Article author={{ name: route.params.author }} scrollEnabled={false} />
author={{ name: route.params.author }}
scrollEnabled={scrollEnabled}
/>
</ScrollView> </ScrollView>
); );
}; };
@@ -63,7 +58,7 @@ const NewsFeedScreen = ({
<View style={styles.buttons}> <View style={styles.buttons}>
<Button <Button
mode="contained" mode="contained"
onPress={() => navigation.navigate('Albums')} onPress={() => navigation.navigate('Album')}
style={styles.button} style={styles.button}
> >
Navigate to album Navigate to album
@@ -76,7 +71,7 @@ const NewsFeedScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<NewsFeed scrollEnabled={scrollEnabled} /> <NewsFeed scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -104,7 +99,7 @@ const AlbumsScreen = ({
Pop by 2 Pop by 2
</Button> </Button>
</View> </View>
<Albums scrollEnabled={scrollEnabled} /> <Albums scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -136,9 +131,9 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
options={{ title: 'Feed' }} options={{ title: 'Feed' }}
/> />
<SimpleStack.Screen <SimpleStack.Screen
name="Albums" name="Album"
component={AlbumsScreen} component={AlbumsScreen}
options={{ title: 'Albums' }} options={{ title: 'Album' }}
/> />
</SimpleStack.Navigator> </SimpleStack.Navigator>
); );

View File

@@ -1,36 +1,25 @@
import * as React from 'react'; import * as React from 'react';
import { import { View, StyleSheet, ScrollView, Alert, Platform } from 'react-native';
Animated,
View,
StyleSheet,
ScrollView,
Alert,
Platform,
} from 'react-native';
import { Button, Appbar } from 'react-native-paper'; import { Button, Appbar } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import { BlurView } from 'expo-blur';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { RouteProp, ParamListBase } from '@react-navigation/native'; import { RouteProp, ParamListBase } from '@react-navigation/native';
import { import {
createStackNavigator, createStackNavigator,
StackNavigationProp, StackNavigationProp,
HeaderBackground, HeaderBackground,
useHeaderHeight, useHeaderHeight,
Header,
StackHeaderProps,
} from '@react-navigation/stack'; } from '@react-navigation/stack';
import BlurView from '../Shared/BlurView';
import Article from '../Shared/Article'; import Article from '../Shared/Article';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
type SimpleStackParams = { type SimpleStackParams = {
Article: { author: string }; Article: { author: string };
Albums: undefined; Album: undefined;
}; };
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>; type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
const scrollEnabled = Platform.select({ web: true, default: false });
const ArticleScreen = ({ const ArticleScreen = ({
navigation, navigation,
route, route,
@@ -43,7 +32,7 @@ const ArticleScreen = ({
<View style={styles.buttons}> <View style={styles.buttons}>
<Button <Button
mode="contained" mode="contained"
onPress={() => navigation.push('Albums')} onPress={() => navigation.push('Album')}
style={styles.button} style={styles.button}
> >
Push album Push album
@@ -56,10 +45,7 @@ const ArticleScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<Article <Article author={{ name: route.params.author }} scrollEnabled={false} />
author={{ name: route.params.author }}
scrollEnabled={scrollEnabled}
/>
</ScrollView> </ScrollView>
); );
}; };
@@ -89,7 +75,7 @@ const AlbumsScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<Albums scrollEnabled={scrollEnabled} /> <Albums scrollEnabled={false} />
</ScrollView> </ScrollView>
); );
}; };
@@ -100,25 +86,6 @@ type Props = Partial<React.ComponentProps<typeof SimpleStack.Navigator>> & {
navigation: StackNavigationProp<ParamListBase>; navigation: StackNavigationProp<ParamListBase>;
}; };
function CustomHeader(props: StackHeaderProps) {
const { current, next } = props.scene.progress;
const progress = Animated.add(current, next || 0);
const opacity = progress.interpolate({
inputRange: [0, 1, 2],
outputRange: [0, 1, 0],
});
return (
<>
<Header {...props} />
<Animated.Text style={[styles.banner, { opacity }]}>
Why hello there, pardner!
</Animated.Text>
</>
);
}
export default function SimpleStackScreen({ navigation, ...rest }: Props) { export default function SimpleStackScreen({ navigation, ...rest }: Props) {
navigation.setOptions({ navigation.setOptions({
headerShown: false, headerShown: false,
@@ -131,7 +98,6 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
component={ArticleScreen} component={ArticleScreen}
options={({ route }) => ({ options={({ route }) => ({
title: `Article by ${route.params?.author}`, title: `Article by ${route.params?.author}`,
header: CustomHeader,
headerTintColor: '#fff', headerTintColor: '#fff',
headerStyle: { backgroundColor: '#ff005d' }, headerStyle: { backgroundColor: '#ff005d' },
headerBackTitleVisible: false, headerBackTitleVisible: false,
@@ -160,10 +126,10 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) {
initialParams={{ author: 'Gandalf' }} initialParams={{ author: 'Gandalf' }}
/> />
<SimpleStack.Screen <SimpleStack.Screen
name="Albums" name="Album"
component={AlbumsScreen} component={AlbumsScreen}
options={{ options={{
title: 'Albums', title: 'Album',
headerBackTitle: 'Back', headerBackTitle: 'Back',
headerTransparent: true, headerTransparent: true,
headerBackground: () => ( headerBackground: () => (
@@ -189,10 +155,4 @@ const styles = StyleSheet.create({
button: { button: {
margin: 8, margin: 8,
}, },
banner: {
textAlign: 'center',
color: 'tomato',
backgroundColor: 'papayawhip',
padding: 4,
},
}); });

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { View, StyleSheet, ScrollView, Platform } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import { Button, Paragraph } from 'react-native-paper'; import { Button, Paragraph } from 'react-native-paper';
import { RouteProp, ParamListBase, useTheme } from '@react-navigation/native'; import { RouteProp, ParamListBase, useTheme } from '@react-navigation/native';
import { import {
@@ -15,8 +15,6 @@ type SimpleStackParams = {
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>; type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
const scrollEnabled = Platform.select({ web: true, default: false });
const ArticleScreen = ({ const ArticleScreen = ({
navigation, navigation,
route, route,
@@ -42,10 +40,7 @@ const ArticleScreen = ({
Go back Go back
</Button> </Button>
</View> </View>
<Article <Article author={{ name: route.params.author }} scrollEnabled={false} />
author={{ name: route.params.author }}
scrollEnabled={scrollEnabled}
/>
</ScrollView> </ScrollView>
); );
}; };

View File

@@ -0,0 +1,3 @@
export default function WebStack() {
return null;
}

View File

@@ -0,0 +1,142 @@
import * as React from 'react';
import { View, StyleSheet, ScrollView } from 'react-native';
import { Button } from 'react-native-paper';
import { RouteProp, ParamListBase } from '@react-navigation/native';
import {
createWebStackNavigator,
WebStackNavigationProp,
} from '@react-navigation/web-stack';
import Article from '../Shared/Article';
import Albums from '../Shared/Albums';
import NewsFeed from '../Shared/NewsFeed';
type WebStackParams = {
Article: { author: string };
NewsFeed: undefined;
Album: undefined;
};
type WebStackNavigation = WebStackNavigationProp<WebStackParams>;
const ArticleScreen = ({
navigation,
route,
}: {
navigation: WebStackNavigation;
route: RouteProp<WebStackParams, 'Article'>;
}) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.replace('NewsFeed')}
style={styles.button}
>
Replace with feed
</Button>
<Button
mode="outlined"
onPress={() => navigation.pop()}
style={styles.button}
>
Pop screen
</Button>
</View>
<Article author={{ name: route.params.author }} scrollEnabled={false} />
</ScrollView>
);
};
const NewsFeedScreen = ({ navigation }: { navigation: WebStackNavigation }) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.navigate('Album')}
style={styles.button}
>
Navigate to album
</Button>
<Button
mode="outlined"
onPress={() => navigation.goBack()}
style={styles.button}
>
Go back
</Button>
</View>
<NewsFeed scrollEnabled={false} />
</ScrollView>
);
};
const AlbumsScreen = ({ navigation }: { navigation: WebStackNavigation }) => {
return (
<ScrollView>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.push('Article', { author: 'Babel fish' })}
style={styles.button}
>
Push article
</Button>
<Button
mode="outlined"
onPress={() => navigation.pop(2)}
style={styles.button}
>
Pop by 2
</Button>
</View>
<Albums scrollEnabled={false} />
</ScrollView>
);
};
const WebStack = createWebStackNavigator<WebStackParams>();
type Props = Partial<React.ComponentProps<typeof WebStack.Navigator>> & {
navigation: WebStackNavigationProp<ParamListBase>;
};
export default function WebStackScreen({ navigation, ...rest }: Props) {
navigation.setOptions({
headerShown: false,
});
return (
<WebStack.Navigator {...rest}>
<WebStack.Screen
name="Article"
component={ArticleScreen}
options={({ route }) => ({
title: `Article by ${route.params.author}`,
})}
initialParams={{ author: 'Gandalf' }}
/>
<WebStack.Screen
name="NewsFeed"
component={NewsFeedScreen}
options={{ title: 'Feed' }}
/>
<WebStack.Screen
name="Album"
component={AlbumsScreen}
options={{ title: 'Album' }}
/>
</WebStack.Navigator>
);
}
const styles = StyleSheet.create({
buttons: {
flexDirection: 'row',
padding: 8,
},
button: {
margin: 8,
},
});

View File

@@ -1,3 +0,0 @@
import { BlurView } from 'expo-blur';
export default BlurView;

View File

@@ -1,12 +0,0 @@
import * as React from 'react';
import { View, ViewProps } from 'react-native';
type Props = ViewProps & {
tint: 'light' | 'dark';
intensity: number;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function BlurView({ tint, intensity, ...rest }: Props) {
return <View {...rest} />;
}

View File

@@ -1,17 +1,20 @@
import * as React from 'react'; import * as React from 'react';
import { import {
ScrollView, ScrollView,
AsyncStorage,
YellowBox, YellowBox,
Platform, Platform,
StatusBar, StatusBar,
I18nManager, I18nManager,
Dimensions, Dimensions,
ScaledSize, ScaledSize,
Linking,
} from 'react-native'; } from 'react-native';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { enableScreens } from 'react-native-screens'; import { enableScreens } from 'react-native-screens';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import RNRestart from 'react-native-restart';
import { Updates } from 'expo';
import { Asset } from 'expo-asset';
import { MaterialIcons } from '@expo/vector-icons';
import { import {
Provider as PaperProvider, Provider as PaperProvider,
DefaultTheme as PaperLightTheme, DefaultTheme as PaperLightTheme,
@@ -23,10 +26,10 @@ import {
} from 'react-native-paper'; } from 'react-native-paper';
import { import {
InitialState, InitialState,
NavigationContainerRef,
NavigationContainer, NavigationContainer,
DefaultTheme, DefaultTheme,
DarkTheme, DarkTheme,
PathConfig,
} from '@react-navigation/native'; } from '@react-navigation/native';
import { import {
createDrawerNavigator, createDrawerNavigator,
@@ -34,14 +37,14 @@ import {
} from '@react-navigation/drawer'; } from '@react-navigation/drawer';
import { import {
createStackNavigator, createStackNavigator,
Assets as StackAssets,
StackNavigationProp, StackNavigationProp,
HeaderStyleInterpolators, HeaderStyleInterpolators,
} from '@react-navigation/stack'; } from '@react-navigation/stack';
import { restartApp } from './Restart';
import AsyncStorage from './AsyncStorage';
import LinkingPrefixes from './LinkingPrefixes'; import LinkingPrefixes from './LinkingPrefixes';
import SettingsItem from './Shared/SettingsItem'; import SettingsItem from './Shared/SettingsItem';
import WebStack from './Screens/WebStack';
import SimpleStack from './Screens/SimpleStack'; import SimpleStack from './Screens/SimpleStack';
import ModalPresentationStack from './Screens/ModalPresentationStack'; import ModalPresentationStack from './Screens/ModalPresentationStack';
import StackTransparent from './Screens/StackTransparent'; import StackTransparent from './Screens/StackTransparent';
@@ -49,7 +52,6 @@ import StackHeaderCustomization from './Screens/StackHeaderCustomization';
import BottomTabs from './Screens/BottomTabs'; import BottomTabs from './Screens/BottomTabs';
import MaterialTopTabsScreen from './Screens/MaterialTopTabs'; import MaterialTopTabsScreen from './Screens/MaterialTopTabs';
import MaterialBottomTabs from './Screens/MaterialBottomTabs'; import MaterialBottomTabs from './Screens/MaterialBottomTabs';
import NotFound from './Screens/NotFound';
import DynamicTabs from './Screens/DynamicTabs'; import DynamicTabs from './Screens/DynamicTabs';
import AuthFlow from './Screens/AuthFlow'; import AuthFlow from './Screens/AuthFlow';
import CompatAPI from './Screens/CompatAPI'; import CompatAPI from './Screens/CompatAPI';
@@ -70,12 +72,16 @@ type RootDrawerParamList = {
type RootStackParamList = { type RootStackParamList = {
Home: undefined; Home: undefined;
NotFound: undefined;
} & { } & {
[P in keyof typeof SCREENS]: undefined; [P in keyof typeof SCREENS]: undefined;
}; };
const SCREENS = { const SCREENS = {
...(Platform.OS === 'web'
? {
WebStack: { title: 'Web Stack', component: WebStack },
}
: null),
SimpleStack: { title: 'Simple Stack', component: SimpleStack }, SimpleStack: { title: 'Simple Stack', component: SimpleStack },
ModalPresentationStack: { ModalPresentationStack: {
title: 'Modal Presentation Stack', title: 'Modal Presentation Stack',
@@ -126,10 +132,14 @@ const Stack = createStackNavigator<RootStackParamList>();
const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE'; const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
const THEME_PERSISTENCE_KEY = 'THEME_TYPE'; const THEME_PERSISTENCE_KEY = 'THEME_TYPE';
Asset.loadAsync(StackAssets);
export default function App() { export default function App() {
const containerRef = React.useRef<NavigationContainerRef>(null);
const [theme, setTheme] = React.useState(DefaultTheme); const [theme, setTheme] = React.useState(DefaultTheme);
const [isReady, setIsReady] = React.useState(Platform.OS === 'web'); const [isReady, setIsReady] = React.useState(false);
const [initialState, setInitialState] = React.useState< const [initialState, setInitialState] = React.useState<
InitialState | undefined InitialState | undefined
>(); >();
@@ -137,19 +147,19 @@ export default function App() {
React.useEffect(() => { React.useEffect(() => {
const restoreState = async () => { const restoreState = async () => {
try { try {
const initialUrl = await Linking.getInitialURL(); let state;
if (Platform.OS !== 'web' || initialUrl === null) { if (Platform.OS !== 'web' && state === undefined) {
const savedState = await AsyncStorage.getItem( const savedState = await AsyncStorage.getItem(
NAVIGATION_PERSISTENCE_KEY NAVIGATION_PERSISTENCE_KEY
); );
const state = savedState ? JSON.parse(savedState) : undefined; state = savedState ? JSON.parse(savedState) : undefined;
}
if (state !== undefined) { if (state !== undefined) {
setInitialState(state); setInitialState(state);
} }
}
} finally { } finally {
try { try {
const themeName = await AsyncStorage.getItem(THEME_PERSISTENCE_KEY); const themeName = await AsyncStorage.getItem(THEME_PERSISTENCE_KEY);
@@ -204,6 +214,7 @@ export default function App() {
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} /> <StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
)} )}
<NavigationContainer <NavigationContainer
ref={containerRef}
initialState={initialState} initialState={initialState}
onStateChange={(state) => onStateChange={(state) =>
AsyncStorage.setItem( AsyncStorage.setItem(
@@ -224,43 +235,17 @@ export default function App() {
Root: { Root: {
path: '', path: '',
initialRouteName: 'Home', initialRouteName: 'Home',
screens: Object.keys(SCREENS).reduce<PathConfig>( screens: Object.keys(SCREENS).reduce<{ [key: string]: string }>(
(acc, name) => { (acc, name) => {
// Convert screen names such as SimpleStack to kebab case (simple-stack) // Convert screen names such as SimpleStack to kebab case (simple-stack)
const path = name acc[name] = name
.replace(/([A-Z]+)/g, '-$1') .replace(/([A-Z]+)/g, '-$1')
.replace(/^-/, '') .replace(/^-/, '')
.toLowerCase(); .toLowerCase();
acc[name] = {
path,
screens: {
Article: {
path: 'article/:author?',
parse: {
author: (author) =>
author.charAt(0).toUpperCase() +
author.slice(1).replace(/-/g, ' '),
},
stringify: {
author: (author: string) =>
author.toLowerCase().replace(/\s/g, '-'),
},
},
Albums: 'music',
Chat: 'chat',
Contacts: 'people',
NewsFeed: 'feed',
Dialog: 'dialog',
},
};
return acc; return acc;
}, },
{ { Home: '' }
Home: '',
NotFound: '*',
}
), ),
}, },
}, },
@@ -315,7 +300,12 @@ export default function App() {
value={I18nManager.isRTL} value={I18nManager.isRTL}
onValueChange={() => { onValueChange={() => {
I18nManager.forceRTL(!I18nManager.isRTL); I18nManager.forceRTL(!I18nManager.isRTL);
restartApp(); // @ts-ignore
if (global.Expo) {
Updates.reloadFromCache();
} else {
RNRestart.Restart();
}
}} }}
/> />
<Divider /> <Divider />
@@ -345,11 +335,6 @@ export default function App() {
</ScrollView> </ScrollView>
)} )}
</Stack.Screen> </Stack.Screen>
<Stack.Screen
name="NotFound"
component={NotFound}
options={{ title: 'Oops!' }}
/>
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map( {(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
(name) => ( (name) => (
<Stack.Screen <Stack.Screen

View File

@@ -1,16 +0,0 @@
declare module 'react-native-web' {
export const AppRegistry: {
registerComponent(
name: string,
callback: () => React.ComponentType<any>
): void;
getApplication(
name: string,
options?: { initialProps: object }
): {
element: React.ReactElement;
getStyleElement(): React.ReactElement;
};
};
}

View File

@@ -1 +0,0 @@
/* /index.html 200

View File

@@ -24,19 +24,17 @@ module.exports = async function (env, argv) {
); );
Object.assign(config.resolve.alias, { Object.assign(config.resolve.alias, {
'react': path.resolve(node_modules, 'react'), react: path.resolve(node_modules, 'react'),
'react-native': path.resolve(node_modules, 'react-native-web'), 'react-native': path.resolve(node_modules, 'react-native-web'),
'react-native-web': path.resolve(node_modules, 'react-native-web'), 'react-native-web': path.resolve(node_modules, 'react-native-web'),
'@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'), '@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'),
}); });
fs.readdirSync(packages) fs.readdirSync(packages).forEach((name) => {
.filter((name) => !name.startsWith('.'))
.forEach((name) => {
config.resolve.alias[`@react-navigation/${name}`] = path.resolve( config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
packages, packages,
name, name,
require(`../packages/${name}/package.json`).source 'src'
); );
}); });

View File

@@ -13,7 +13,7 @@
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/react-navigation/react-navigation.git" "url": "git+https://github.com/satya164/react-navigation.git"
}, },
"author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/), Michał Osadnik <micosa97@gmail.com> (https://github.com/osdnk/)", "author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/), Michał Osadnik <micosa97@gmail.com> (https://github.com/osdnk/)",
"scripts": { "scripts": {
@@ -27,23 +27,23 @@
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.8.3", "@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.9.0", "@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-env": "^7.9.6", "@babel/preset-env": "^7.9.0",
"@babel/preset-flow": "^7.9.0", "@babel/preset-flow": "^7.9.0",
"@babel/preset-react": "^7.9.4", "@babel/preset-react": "^7.9.4",
"@babel/preset-typescript": "^7.9.0", "@babel/preset-typescript": "^7.9.0",
"@babel/runtime": "^7.9.6", "@babel/runtime": "^7.9.2",
"@commitlint/config-conventional": "^8.3.4", "@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.2.1", "@types/jest": "^25.2.1",
"babel-jest": "^26.0.1", "babel-jest": "^25.2.6",
"codecov": "^3.6.5", "codecov": "^3.6.5",
"commitlint": "^8.3.5", "commitlint": "^8.3.5",
"core-js": "^3.6.5", "core-js": "^3.6.4",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-satya164": "^3.1.7", "eslint-config-satya164": "^3.1.6",
"husky": "^4.2.5", "husky": "^4.2.3",
"jest": "^26.0.1", "jest": "^25.2.7",
"lerna": "^3.20.2", "lerna": "^3.20.2",
"prettier": "^2.0.5", "prettier": "^2.0.4",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },
"resolutions": { "resolutions": {

View File

@@ -3,151 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.5.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.5.1...@react-navigation/bottom-tabs@5.5.2) (2020-06-06)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.5.0...@react-navigation/bottom-tabs@5.5.1) (2020-05-27)
### Bug Fixes
* fix type of style for various options ([9d822b9](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/9d822b95a6df797e2e63e481573e64ea7d0f9386))
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.7...@react-navigation/bottom-tabs@5.5.0) (2020-05-23)
### Features
* animate changes to tabBarVisible in BottomTabBar ([#8286](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/8286)) ([c1e46f8](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/c1e46f8e331e0054995aa476455af204d02d4170))
* update react-native-safe-area-context to 1.0.0 ([#8182](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/8182)) ([d62fbfe](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/d62fbfe255140f16b182e8b54b276a7c96f2aec6))
## [5.4.7](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.6...@react-navigation/bottom-tabs@5.4.7) (2020-05-20)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.4.6](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.5...@react-navigation/bottom-tabs@5.4.6) (2020-05-20)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.4.5](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.4...@react-navigation/bottom-tabs@5.4.5) (2020-05-16)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.4.4](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.3...@react-navigation/bottom-tabs@5.4.4) (2020-05-14)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.4.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.2...@react-navigation/bottom-tabs@5.4.3) (2020-05-14)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.1...@react-navigation/bottom-tabs@5.4.2) (2020-05-10)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.4.0...@react-navigation/bottom-tabs@5.4.1) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.4...@react-navigation/bottom-tabs@5.4.0) (2020-05-08)
### Features
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/7971)
## [5.3.4](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.3...@react-navigation/bottom-tabs@5.3.4) (2020-05-05)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.2...@react-navigation/bottom-tabs@5.3.3) (2020-05-01)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.1...@react-navigation/bottom-tabs@5.3.2) (2020-05-01)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.3.0...@react-navigation/bottom-tabs@5.3.1) (2020-04-30)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.8...@react-navigation/bottom-tabs@5.3.0) (2020-04-30)
### Features
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/942d2be2c72720469475ce12ec8df23825994dbf))
## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.7...@react-navigation/bottom-tabs@5.2.8) (2020-04-27) ## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.7...@react-navigation/bottom-tabs@5.2.8) (2020-04-27)
**Note:** Version bump only for package @react-navigation/bottom-tabs **Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/bottom-tabs", "name": "@react-navigation/bottom-tabs",
"description": "Bottom tab navigator following iOS design guidelines", "description": "Bottom tab navigator following iOS design guidelines",
"version": "5.5.2", "version": "5.2.8",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -15,13 +15,11 @@
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs", "repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
@@ -36,16 +34,16 @@
"react-native-iphone-x-helper": "^1.2.1" "react-native-iphone-x-helper": "^1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.5.1", "@react-navigation/native": "^5.1.7",
"@types/color": "^3.0.1", "@types/color": "^3.0.1",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"@types/react-native": "^0.62.7", "@types/react-native": "^0.61.22",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-native": "~0.61.5", "react-native": "~0.61.5",
"react-native-safe-area-context": "^1.0.0", "react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.7.0", "react-native-screens": "^2.3.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -15,7 +15,6 @@ export { default as BottomTabBar } from './views/BottomTabBar';
export type { export type {
BottomTabNavigationOptions, BottomTabNavigationOptions,
BottomTabNavigationProp, BottomTabNavigationProp,
BottomTabScreenProps,
BottomTabBarProps, BottomTabBarProps,
BottomTabBarOptions, BottomTabBarOptions,
} from './types'; } from './types';

View File

@@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { import {
Animated,
TouchableWithoutFeedbackProps, TouchableWithoutFeedbackProps,
StyleProp, StyleProp,
TextStyle, TextStyle,
@@ -14,7 +13,6 @@ import {
Descriptor, Descriptor,
TabNavigationState, TabNavigationState,
TabActionHelpers, TabActionHelpers,
RouteProp,
} from '@react-navigation/native'; } from '@react-navigation/native';
export type BottomTabNavigationEventMap = { export type BottomTabNavigationEventMap = {
@@ -47,14 +45,6 @@ export type BottomTabNavigationProp<
> & > &
TabActionHelpers<ParamList>; TabActionHelpers<ParamList>;
export type BottomTabScreenProps<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = {
navigation: BottomTabNavigationProp<ParamList, RouteName>;
route: RouteProp<ParamList, RouteName>;
};
export type BottomTabNavigationOptions = { export type BottomTabNavigationOptions = {
/** /**
* Title text for the screen. * Title text for the screen.
@@ -198,7 +188,7 @@ export type BottomTabBarOptions = {
/** /**
* Style object for the tab bar container. * Style object for the tab bar container.
*/ */
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>; style?: StyleProp<ViewStyle>;
}; };
export type BottomTabBarProps = BottomTabBarOptions & { export type BottomTabBarProps = BottomTabBarOptions & {

View File

@@ -53,59 +53,51 @@ export default function BottomTabBar({
const { colors } = useTheme(); const { colors } = useTheme();
const buildLink = useLinkBuilder(); const buildLink = useLinkBuilder();
const focusedRoute = state.routes[state.index];
const focusedDescriptor = descriptors[focusedRoute.key];
const focusedOptions = focusedDescriptor.options;
const [isKeyboardShown, setIsKeyboardShown] = React.useState(false);
const shouldShowTabBar =
focusedOptions.tabBarVisible !== false &&
!(keyboardHidesTabBar && isKeyboardShown);
const [isTabBarHidden, setIsTabBarHidden] = React.useState(!shouldShowTabBar);
const [visible] = React.useState(
() => new Animated.Value(shouldShowTabBar ? 1 : 0)
);
React.useEffect(() => {
if (shouldShowTabBar) {
Animated.timing(visible, {
toValue: 1,
duration: 250,
useNativeDriver,
}).start(({ finished }) => {
if (finished) {
setIsTabBarHidden(false);
}
});
} else {
setIsTabBarHidden(true);
Animated.timing(visible, {
toValue: 0,
duration: 200,
useNativeDriver,
}).start();
}
}, [shouldShowTabBar, visible]);
const [dimensions, setDimensions] = React.useState(() => { const [dimensions, setDimensions] = React.useState(() => {
const { height = 0, width = 0 } = Dimensions.get('window'); const { height = 0, width = 0 } = Dimensions.get('window');
return { height, width }; return { height, width };
}); });
const [layout, setLayout] = React.useState({
height: 0,
width: dimensions.width,
});
const [keyboardShown, setKeyboardShown] = React.useState(false);
const [visible] = React.useState(() => new Animated.Value(1));
const { routes } = state;
React.useEffect(() => {
if (keyboardShown) {
Animated.timing(visible, {
toValue: 0,
duration: 200,
useNativeDriver,
}).start();
}
}, [keyboardShown, visible]);
React.useEffect(() => { React.useEffect(() => {
const handleOrientationChange = ({ window }: { window: ScaledSize }) => { const handleOrientationChange = ({ window }: { window: ScaledSize }) => {
setDimensions(window); setDimensions(window);
}; };
Dimensions.addEventListener('change', handleOrientationChange); const handleKeyboardShow = () => setKeyboardShown(true);
const handleKeyboardShow = () => setIsKeyboardShown(true); const handleKeyboardHide = () =>
const handleKeyboardHide = () => setIsKeyboardShown(false); Animated.timing(visible, {
toValue: 1,
duration: 250,
useNativeDriver,
}).start(({ finished }) => {
if (finished) {
setKeyboardShown(false);
}
});
Dimensions.addEventListener('change', handleOrientationChange);
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
Keyboard.addListener('keyboardWillShow', handleKeyboardShow); Keyboard.addListener('keyboardWillShow', handleKeyboardShow);
@@ -126,12 +118,7 @@ export default function BottomTabBar({
Keyboard.removeListener('keyboardDidHide', handleKeyboardHide); Keyboard.removeListener('keyboardDidHide', handleKeyboardHide);
} }
}; };
}, []); }, [visible]);
const [layout, setLayout] = React.useState({
height: 0,
width: dimensions.width,
});
const handleLayout = (e: LayoutChangeEvent) => { const handleLayout = (e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout; const { height, width } = e.nativeEvent.layout;
@@ -148,7 +135,6 @@ export default function BottomTabBar({
}); });
}; };
const { routes } = state;
const shouldUseHorizontalLabels = () => { const shouldUseHorizontalLabels = () => {
if (labelPosition) { if (labelPosition) {
return labelPosition === 'beside-icon'; return labelPosition === 'beside-icon';
@@ -197,19 +183,22 @@ export default function BottomTabBar({
backgroundColor: colors.card, backgroundColor: colors.card,
borderTopColor: colors.border, borderTopColor: colors.border,
}, },
{ keyboardHidesTabBar
? {
// When the keyboard is shown, slide down the tab bar
transform: [ transform: [
{ {
translateY: visible.interpolate({ translateY: visible.interpolate({
inputRange: [0, 1], inputRange: [0, 1],
outputRange: [layout.height + insets.bottom, 0], outputRange: [layout.height, 0],
}), }),
}, },
], ],
// Absolutely position the tab bar so that the content is below it // Absolutely position the tab bar so that the content is below it
// This is needed to avoid gap at bottom when the tab bar is hidden // This is needed to avoid gap at bottom when the tab bar is hidden
position: isTabBarHidden ? 'absolute' : null, position: keyboardShown ? 'absolute' : null,
}, }
: null,
{ {
height: DEFAULT_TABBAR_HEIGHT + insets.bottom, height: DEFAULT_TABBAR_HEIGHT + insets.bottom,
paddingBottom: insets.bottom, paddingBottom: insets.bottom,
@@ -217,7 +206,7 @@ export default function BottomTabBar({
}, },
style, style,
]} ]}
pointerEvents={isTabBarHidden ? 'none' : 'auto'} pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
> >
<View style={styles.content} onLayout={handleLayout}> <View style={styles.content} onLayout={handleLayout}>
{routes.map((route, index) => { {routes.map((route, index) => {

View File

@@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import { import {
View, View,
Text,
TouchableWithoutFeedback, TouchableWithoutFeedback,
Animated,
StyleSheet, StyleSheet,
Platform, Platform,
StyleProp, StyleProp,
@@ -191,7 +191,7 @@ export default function BottomTabBarItem({
if (typeof label === 'string') { if (typeof label === 'string') {
return ( return (
<Text <Animated.Text
numberOfLines={1} numberOfLines={1}
style={[ style={[
styles.label, styles.label,
@@ -202,10 +202,14 @@ export default function BottomTabBarItem({
allowFontScaling={allowFontScaling} allowFontScaling={allowFontScaling}
> >
{label} {label}
</Text> </Animated.Text>
); );
} }
if (typeof label === 'string') {
return label;
}
return label({ focused, color }); return label({ focused, color });
}; };

View File

@@ -75,8 +75,17 @@ export default class BottomTabView extends React.Component<Props, State> {
tabBarOptions, tabBarOptions,
state, state,
navigation, navigation,
descriptors,
} = this.props; } = this.props;
const { descriptors } = this.props;
const route = state.routes[state.index];
const descriptor = descriptors[route.key];
const options = descriptor.options;
if (options.tabBarVisible === false) {
return null;
}
return tabBar({ return tabBar({
...tabBarOptions, ...tabBarOptions,
state: state, state: state,

View File

@@ -3,137 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.26](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.25...@react-navigation/compat@5.1.26) (2020-06-06)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.25](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.24...@react-navigation/compat@5.1.25) (2020-05-27)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.24](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.23...@react-navigation/compat@5.1.24) (2020-05-23)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.23](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.22...@react-navigation/compat@5.1.23) (2020-05-20)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.22](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.21...@react-navigation/compat@5.1.22) (2020-05-20)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.21](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.20...@react-navigation/compat@5.1.21) (2020-05-16)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.20](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.19...@react-navigation/compat@5.1.20) (2020-05-14)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.19](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.18...@react-navigation/compat@5.1.19) (2020-05-14)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.18](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.17...@react-navigation/compat@5.1.18) (2020-05-10)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.17](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.16...@react-navigation/compat@5.1.17) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
## [5.1.16](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.15...@react-navigation/compat@5.1.16) (2020-05-08)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.15](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.14...@react-navigation/compat@5.1.15) (2020-05-05)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.14](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.13...@react-navigation/compat@5.1.14) (2020-05-01)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.13](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.12...@react-navigation/compat@5.1.13) (2020-05-01)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.12](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.11...@react-navigation/compat@5.1.12) (2020-04-30)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.11](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.10...@react-navigation/compat@5.1.11) (2020-04-30)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.9...@react-navigation/compat@5.1.10) (2020-04-27) ## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.9...@react-navigation/compat@5.1.10) (2020-04-27)

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/compat", "name": "@react-navigation/compat",
"description": "Compatibility layer to write navigator definitions in static configuration format", "description": "Compatibility layer to write navigator definitions in static configuration format",
"version": "5.1.26", "version": "5.1.10",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat", "repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
"bugs": { "bugs": {
@@ -10,13 +10,11 @@
"homepage": "https://reactnavigation.org/docs/compatibility.html", "homepage": "https://reactnavigation.org/docs/compatibility.html",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
@@ -27,9 +25,9 @@
"clean": "del lib" "clean": "del lib"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.5.1", "@react-navigation/native": "^5.1.7",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"react": "~16.9.0", "react": "~16.9.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },

View File

@@ -3,159 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.10.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.9.0...@react-navigation/core@5.10.0) (2020-06-06)
### Bug Fixes
* catch missing params when they are required in navigate ([#8389](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8389)) ([8774ca9](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/8774ca97e1da91e97677ecd816c85f66af296b93))
* make sure the wildcard pattern catches nested unmatched routes ([c3bd349](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/c3bd349d77688011c9c55027edd66c6f39de2ade))
* only use the query params for focused route in path ([2d66ef9](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2d66ef93ec9923a452415c482c40e7c6b769917c))
* prevent state change being emitted unnecessarily ([ab1f79c](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/ab1f79c096e94475a4da1acf1c850d04fb1bc4cf))
### Features
* add wildcard patterns for paths ([4fe72e3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/4fe72e3ce7bae9120d04e490401f3bad58ebdf5c)), closes [#8019](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8019)
# [5.9.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.8.2...@react-navigation/core@5.9.0) (2020-05-27)
### Features
* add ref to get current options in `ServerContainer` ([#8333](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8333)) ([0b1a718](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/0b1a718756e208d84b20e45ca56004332308ad54))
## [5.8.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.8.1...@react-navigation/core@5.8.2) (2020-05-23)
**Note:** Version bump only for package @react-navigation/core
## [5.8.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.8.0...@react-navigation/core@5.8.1) (2020-05-20)
**Note:** Version bump only for package @react-navigation/core
# [5.8.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.7.0...@react-navigation/core@5.8.0) (2020-05-20)
### Features
* add getCurrentOptions ([#8277](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8277)) ([d024ec6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/d024ec6d74dffe481ce6fde732c729e20c1668f4))
* add getCurrentRoute ([#8254](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8254)) ([7b25c8e](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/7b25c8eb2e6f96128fd86b92615346ce55bedeca))
# [5.7.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.6.1...@react-navigation/core@5.7.0) (2020-05-16)
### Bug Fixes
* don't use Object.fromEntries ([51f4d11](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/51f4d11fdf4bd2bb06f8cd4094f051816590e62c))
### Features
* add a PathConfig type ([60cb3c9](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/60cb3c9ba76d7ef166c9fe8b55f23728975b5b6e))
## [5.6.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.6.0...@react-navigation/core@5.6.1) (2020-05-14)
### Bug Fixes
* don't use flat since it's not supported in node ([21b397f](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/21b397f0d6b96ec4875d3172f47533130bb08009))
# [5.6.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.5.2...@react-navigation/core@5.6.0) (2020-05-14)
### Bug Fixes
* ignore extra slashes in the pattern ([3c47716](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3c47716826d0dfa69dfa6112141c116723372ea1))
* ignore state updates when we're not mounted ([0149e85](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/0149e85a95b90c6a9d487fa753ddbf5d01c03e3d)), closes [#8226](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8226)
### Features
* merge path patterns for nested screens ([#8253](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8253)) ([acc9646](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/acc9646426fee53558d686dfbe5fd0e35361d8c0))
## [5.5.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.5.1...@react-navigation/core@5.5.2) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.5.0...@react-navigation/core@5.5.1) (2020-05-08)
### Bug Fixes
* avoid cleaning up state when a new navigator is mounted. fixes [#8195](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8195) ([f6d0676](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/f6d06768d3c36d1f5beaffcb660f3c259209f2e7))
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.4.0...@react-navigation/core@5.5.0) (2020-05-05)
### Features
* add support for optional params to linking ([#8196](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8196)) ([fcd1cc6](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/fcd1cc64c151e4941f3f544a54b5048d853821f6))
* support params anywhere in path segement ([#8184](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/8184)) ([3999fc2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3999fc28365c3a06a17d963c7be7fb7e897f99e0))
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.5...@react-navigation/core@5.4.0) (2020-04-30)
### Bug Fixes
* handle empty paths when parsing ([c3fa83e](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/c3fa83efe0d73db76365f8be3d6a8ca1d1289b71))
* parsing url ([bd35b4f](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/bd35b4fc202c3868fb75c3675b62de67557089e1))
### Features
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
## [5.3.5](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.4...@react-navigation/core@5.3.5) (2020-04-27) ## [5.3.5](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.3.4...@react-navigation/core@5.3.5) (2020-04-27)

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/core", "name": "@react-navigation/core",
"description": "Core utilities for building navigators", "description": "Core utilities for building navigators",
"version": "5.10.0", "version": "5.3.5",
"keywords": [ "keywords": [
"react", "react",
"react-native", "react-native",
@@ -15,13 +15,11 @@
"homepage": "https://reactnavigation.org", "homepage": "https://reactnavigation.org",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -31,21 +29,21 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.4.7", "@react-navigation/routers": "^5.4.1",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^2.0.0",
"nanoid": "^3.1.5", "nanoid": "^3.0.2",
"query-string": "^6.12.1", "query-string": "^6.12.0",
"react-is": "^16.13.0", "react-is": "^16.13.0",
"use-subscription": "^1.4.0" "use-subscription": "^1.4.0"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"@types/react-is": "^16.7.1", "@types/react-is": "^16.7.1",
"@types/use-subscription": "^1.0.0", "@types/use-subscription": "^1.0.0",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-native-testing-library": "^1.13.2", "react-native-testing-library": "^1.12.0",
"react-test-renderer": "~16.13.1", "react-test-renderer": "~16.13.1",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },

View File

@@ -13,22 +13,49 @@ import { ScheduleUpdateContext } from './useScheduleUpdate';
import useFocusedListeners from './useFocusedListeners'; import useFocusedListeners from './useFocusedListeners';
import useDevTools from './useDevTools'; import useDevTools from './useDevTools';
import useStateGetters from './useStateGetters'; import useStateGetters from './useStateGetters';
import useOptionsGetters from './useOptionsGetters';
import useEventEmitter from './useEventEmitter'; import useEventEmitter from './useEventEmitter';
import useSyncState from './useSyncState'; import useSyncState from './useSyncState';
import isSerializable from './isSerializable'; import isSerializable from './isSerializable';
import { NavigationContainerRef, NavigationContainerProps } from './types'; import { NavigationContainerRef, NavigationContainerProps } from './types';
import NavigationStateContext from './NavigationStateContext';
type State = NavigationState | PartialState<NavigationState> | undefined; type State = NavigationState | PartialState<NavigationState> | undefined;
const DEVTOOLS_CONFIG_KEY = const DEVTOOLS_CONFIG_KEY =
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED'; 'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED';
const MISSING_CONTEXT_ERROR =
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
const NOT_INITIALIZED_ERROR = const NOT_INITIALIZED_ERROR =
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details."; "The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";
export const NavigationStateContext = React.createContext<{
isDefault?: true;
state?: NavigationState | PartialState<NavigationState>;
getKey: () => string | undefined;
setKey: (key: string) => void;
getState: () => NavigationState | PartialState<NavigationState> | undefined;
setState: (
state: NavigationState | PartialState<NavigationState> | undefined
) => void;
}>({
isDefault: true,
get getKey(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setKey(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get getState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
});
let hasWarnedForSerialization = false; let hasWarnedForSerialization = false;
/** /**
@@ -172,21 +199,8 @@ const BaseNavigationContainer = React.forwardRef(
return getStateForRoute('root'); return getStateForRoute('root');
}, [getStateForRoute]); }, [getStateForRoute]);
const getCurrentRoute = React.useCallback(() => {
let state = getRootState();
if (state === undefined) {
return undefined;
}
while (state.routes[state.index].state !== undefined) {
state = state.routes[state.index].state as NavigationState;
}
return state.routes[state.index];
}, [getRootState]);
const emitter = useEventEmitter(); const emitter = useEventEmitter();
const { addOptionsGetter, getCurrentOptions } = useOptionsGetters({});
React.useImperativeHandle(ref, () => ({ React.useImperativeHandle(ref, () => ({
...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce< ...(Object.keys(CommonActions) as (keyof typeof CommonActions)[]).reduce<
any any
@@ -205,10 +219,6 @@ const BaseNavigationContainer = React.forwardRef(
dispatch, dispatch,
canGoBack, canGoBack,
getRootState, getRootState,
dangerouslyGetState: () => state,
dangerouslyGetParent: () => undefined,
getCurrentRoute,
getCurrentOptions,
})); }));
const builderContext = React.useMemo( const builderContext = React.useMemo(
@@ -232,17 +242,10 @@ const BaseNavigationContainer = React.forwardRef(
setState, setState,
getKey, getKey,
setKey, setKey,
addOptionsGetter,
}), }),
[getKey, getState, setKey, setState, state, addOptionsGetter] [getKey, getState, setKey, setState, state]
); );
const onStateChangeRef = React.useRef(onStateChange);
React.useEffect(() => {
onStateChangeRef.current = onStateChange;
});
React.useEffect(() => { React.useEffect(() => {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
if ( if (
@@ -269,12 +272,12 @@ const BaseNavigationContainer = React.forwardRef(
trackState(getRootState); trackState(getRootState);
} }
if (!isFirstMountRef.current && onStateChangeRef.current) { if (!isFirstMountRef.current && onStateChange) {
onStateChangeRef.current(getRootState()); onStateChange(getRootState());
} }
isFirstMountRef.current = false; isFirstMountRef.current = false;
}, [trackState, getRootState, emitter, state]); }, [onStateChange, trackState, getRootState, emitter, state]);
return ( return (
<ScheduleUpdateContext.Provider value={scheduleContext}> <ScheduleUpdateContext.Provider value={scheduleContext}>

View File

@@ -1,11 +0,0 @@
import * as React from 'react';
/**
* Context which holds the values for the current navigation tree.
* Intended for use in SSR. This is not safe to use on the client.
*/
const CurrentRenderContext = React.createContext<
{ options?: object } | undefined
>(undefined);
export default CurrentRenderContext;

View File

@@ -1,35 +0,0 @@
import * as React from 'react';
import { NavigationState, PartialState } from '@react-navigation/routers';
const MISSING_CONTEXT_ERROR =
"Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/getting-started for setup instructions.";
export default React.createContext<{
isDefault?: true;
state?: NavigationState | PartialState<NavigationState>;
getKey: () => string | undefined;
setKey: (key: string) => void;
getState: () => NavigationState | PartialState<NavigationState> | undefined;
setState: (
state: NavigationState | PartialState<NavigationState> | undefined
) => void;
addOptionsGetter?: (
key: string,
getter: () => object | undefined | null
) => void;
}>({
isDefault: true,
get getKey(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setKey(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get getState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
get setState(): any {
throw new Error(MISSING_CONTEXT_ERROR);
},
});

View File

@@ -5,13 +5,12 @@ import {
NavigationState, NavigationState,
PartialState, PartialState,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import NavigationStateContext from './NavigationStateContext'; import { NavigationStateContext } from './BaseNavigationContainer';
import NavigationContext from './NavigationContext'; import NavigationContext from './NavigationContext';
import NavigationRouteContext from './NavigationRouteContext'; import NavigationRouteContext from './NavigationRouteContext';
import StaticContainer from './StaticContainer'; import StaticContainer from './StaticContainer';
import EnsureSingleNavigator from './EnsureSingleNavigator'; import EnsureSingleNavigator from './EnsureSingleNavigator';
import { NavigationProp, RouteConfig, EventMapBase } from './types'; import { NavigationProp, RouteConfig, EventMapBase } from './types';
import useOptionsGetters from './useOptionsGetters';
type Props< type Props<
State extends NavigationState, State extends NavigationState,
@@ -25,7 +24,6 @@ type Props<
}; };
getState: () => State; getState: () => State;
setState: (state: State) => void; setState: (state: State) => void;
options: object;
}; };
/** /**
@@ -42,24 +40,11 @@ export default function SceneView<
navigation, navigation,
getState, getState,
setState, setState,
options,
}: Props<State, ScreenOptions, EventMap>) { }: Props<State, ScreenOptions, EventMap>) {
const navigatorKeyRef = React.useRef<string | undefined>(); const navigatorKeyRef = React.useRef<string | undefined>();
const getKey = React.useCallback(() => navigatorKeyRef.current, []); const getKey = React.useCallback(() => navigatorKeyRef.current, []);
const optionsRef = React.useRef<object | undefined>(options);
React.useEffect(() => {
optionsRef.current = options;
}, [options]);
const getOptions = React.useCallback(() => optionsRef.current, []);
const { addOptionsGetter } = useOptionsGetters({
key: route.key,
getOptions,
});
const setKey = React.useCallback((key: string) => { const setKey = React.useCallback((key: string) => {
navigatorKeyRef.current = key; navigatorKeyRef.current = key;
}, []); }, []);
@@ -92,16 +77,8 @@ export default function SceneView<
setState: setCurrentState, setState: setCurrentState,
getKey, getKey,
setKey, setKey,
addOptionsGetter,
}), }),
[ [getCurrentState, getKey, route.state, setCurrentState, setKey]
getCurrentState,
getKey,
route.state,
setCurrentState,
setKey,
addOptionsGetter,
]
); );
return ( return (

View File

@@ -5,8 +5,9 @@ import {
NavigationState, NavigationState,
Router, Router,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import BaseNavigationContainer from '../BaseNavigationContainer'; import BaseNavigationContainer, {
import NavigationStateContext from '../NavigationStateContext'; NavigationStateContext,
} from '../BaseNavigationContainer';
import MockRouter, { MockActions } from './__fixtures__/MockRouter'; import MockRouter, { MockActions } from './__fixtures__/MockRouter';
import useNavigationBuilder from '../useNavigationBuilder'; import useNavigationBuilder from '../useNavigationBuilder';
import Screen from '../Screen'; import Screen from '../Screen';

View File

@@ -117,82 +117,12 @@ it("doesn't add query param for empty params", () => {
}); });
it('handles state with config with nested screens', () => { it('handles state with config with nested screens', () => {
const path =
'/foo/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: 'foe',
},
},
Bar: 'bar/:type/:fruit',
Baz: {
path: 'baz/:author',
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
stringify: {
author: (author: string) => author.toLowerCase(),
id: (id: number) => `x${id}`,
unknown: (_: unknown) => 'x',
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Foe',
state: {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Baz',
params: {
author: 'Jane',
count: '10',
answer: '42',
valid: true,
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles state with config with nested screens and exact', () => {
const path = '/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true'; const path = '/foe/bar/sweet/apple/baz/jane?answer=42&count=10&valid=true';
const config = { const config = {
Foo: { Foo: {
path: 'foo', path: 'foo',
screens: { screens: {
Foe: { Foe: 'foe',
path: 'foe',
exact: true,
},
}, },
}, },
Bar: 'bar/:type/:fruit', Bar: 'bar/:type/:fruit',
@@ -253,72 +183,12 @@ it('handles state with config with nested screens and exact', () => {
}); });
it('handles state with config with nested screens and unused configs', () => { it('handles state with config with nested screens and unused configs', () => {
const path = '/foo/foe/baz/jane?answer=42&count=10&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: 'foe',
},
},
Baz: {
path: 'baz/:author',
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
stringify: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
unknown: (_: unknown) => 'x',
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Foe',
state: {
routes: [
{
name: 'Baz',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles state with config with nested screens and unused configs with exact', () => {
const path = '/foe/baz/jane?answer=42&count=10&valid=true'; const path = '/foe/baz/jane?answer=42&count=10&valid=true';
const config = { const config = {
Foo: { Foo: {
path: 'foo', path: 'foo',
screens: { screens: {
Foe: { Foe: 'foe',
path: 'foe',
exact: true,
},
}, },
}, },
Baz: { Baz: {
@@ -370,78 +240,6 @@ it('handles state with config with nested screens and unused configs with exact'
}); });
it('handles nested object with stringify in it', () => { it('handles nested object with stringify in it', () => {
const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: {
path: 'foe',
},
},
},
Bar: 'bar/:type/:fruit',
Baz: {
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Baz',
state: {
routes: [
{
name: 'Bis',
params: {
author: 'Jane',
count: 10,
answer: '42',
valid: true,
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles nested object with stringify in it with exact', () => {
const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true'; const path = '/bar/sweet/apple/foo/bis/jane?answer=42&count=10&valid=true';
const config = { const config = {
Foo: { Foo: {
@@ -459,7 +257,6 @@ it('handles nested object with stringify in it with exact', () => {
Bos: 'bos', Bos: 'bos',
Bis: { Bis: {
path: 'bis/:author', path: 'bis/:author',
exact: true,
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
@@ -516,45 +313,6 @@ it('handles nested object with stringify in it with exact', () => {
}); });
it('handles nested object for second route depth', () => { it('handles nested object for second route depth', () => {
const path = '/foo/bar/baz';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: 'foe',
Bar: {
path: 'bar',
screens: {
Baz: 'baz',
},
},
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: 'Baz' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles nested object for second route depth with exact', () => {
const path = '/baz'; const path = '/baz';
const config = { const config = {
Foo: { Foo: {
@@ -564,10 +322,7 @@ it('handles nested object for second route depth with exact', () => {
Bar: { Bar: {
path: 'bar', path: 'bar',
screens: { screens: {
Baz: { Baz: 'baz',
path: 'baz',
exact: true,
},
}, },
}, },
}, },
@@ -596,53 +351,7 @@ it('handles nested object for second route depth with exact', () => {
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path); expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
}); });
it('handles nested object for second route depth and path and stringify in roots', () => { it('handles nested object for second route depth and and path and stringify in roots', () => {
const path = '/foo/dathomir/bar/42/baz';
const config = {
Foo: {
path: 'foo/:planet',
stringify: {
id: (id: number) => `planet=${id}`,
},
screens: {
Foe: 'foe',
Bar: {
path: 'bar/:id',
parse: {
id: Number,
},
screens: {
Baz: 'baz',
},
},
},
},
};
const state = {
routes: [
{
name: 'Foo',
params: { planet: 'dathomir' },
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: 'Baz', params: { id: 42 } }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles nested object for second route depth and path and stringify in roots with exact', () => {
const path = '/baz'; const path = '/baz';
const config = { const config = {
Foo: { Foo: {
@@ -661,10 +370,7 @@ it('handles nested object for second route depth and path and stringify in roots
id: Number, id: Number,
}, },
screens: { screens: {
Baz: { Baz: 'baz',
path: 'baz',
exact: true,
},
}, },
}, },
}, },
@@ -720,51 +426,8 @@ it('ignores empty string paths', () => {
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path); expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
}); });
it('keeps query params if path is empty', () => {
const path = '/?foo=42';
const config = {
Foo: {
screens: {
Foe: 'foe',
Bar: {
screens: {
Qux: {
path: '',
parse: { foo: Number },
},
Baz: 'baz',
},
},
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: 'Qux', params: { foo: 42 } }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toEqual(
path
);
});
it('cuts nested configs too', () => { it('cuts nested configs too', () => {
const path = '/foo/baz'; const path = '/baz';
const config = { const config = {
Foo: { Foo: {
path: 'foo', path: 'foo',
@@ -772,48 +435,7 @@ it('cuts nested configs too', () => {
Bar: '', Bar: '',
}, },
}, },
Baz: { Baz: { path: 'baz' },
path: 'baz',
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: 'Baz' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('cuts nested configs too with exact', () => {
const path = '/baz';
const config = {
Foo: {
path: 'foo',
screens: {
Bar: {
path: '',
exact: true,
},
},
},
Baz: {
path: 'baz',
},
}; };
const state = { const state = {
@@ -839,7 +461,7 @@ it('cuts nested configs too with exact', () => {
}); });
it('handles empty path at the end', () => { it('handles empty path at the end', () => {
const path = '/foo/bar'; const path = '/bar';
const config = { const config = {
Foo: { Foo: {
path: 'foo', path: 'foo',
@@ -873,8 +495,6 @@ it('handles empty path at the end', () => {
}); });
it('returns "/" for empty path', () => { it('returns "/" for empty path', () => {
const path = '/';
const config = { const config = {
Foo: { Foo: {
path: '', path: '',
@@ -899,8 +519,7 @@ it('returns "/" for empty path', () => {
], ],
}; };
expect(getPathFromState(state, config)).toBe(path); expect(getPathFromState(state, config)).toBe('/');
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
}); });
it('parses no path specified', () => { it('parses no path specified', () => {
@@ -964,77 +583,6 @@ it('parses no path specified in nested config', () => {
}); });
it('strips undefined query params', () => { it('strips undefined query params', () => {
const path = '/bar/sweet/apple/foo/bis/jane?count=10&valid=true';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: {
path: 'foe',
},
},
},
Bar: 'bar/:type/:fruit',
Baz: {
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Baz',
state: {
routes: [
{
name: 'Bis',
params: {
author: 'Jane',
count: 10,
valid: true,
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('strips undefined query params with exact', () => {
const path = '/bar/sweet/apple/foo/bis/jane?count=10&valid=true'; const path = '/bar/sweet/apple/foo/bis/jane?count=10&valid=true';
const config = { const config = {
Foo: { Foo: {
@@ -1052,7 +600,6 @@ it('strips undefined query params with exact', () => {
Bos: 'bos', Bos: 'bos',
Bis: { Bis: {
path: 'bis/:author', path: 'bis/:author',
exact: true,
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
@@ -1088,6 +635,7 @@ it('strips undefined query params with exact', () => {
params: { params: {
author: 'Jane', author: 'Jane',
count: 10, count: 10,
answer: undefined,
valid: true, valid: true,
}, },
}, },
@@ -1108,75 +656,6 @@ it('strips undefined query params with exact', () => {
}); });
it('handles stripping all query params', () => { it('handles stripping all query params', () => {
const path = '/bar/sweet/apple/foo/bis/jane';
const config = {
Foo: {
path: 'foo',
screens: {
Foe: {
path: 'foe',
},
},
},
Bar: 'bar/:type/:fruit',
Baz: {
screens: {
Bos: 'bos',
Bis: {
path: 'bis/:author',
stringify: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
},
parse: {
author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number,
valid: Boolean,
},
},
},
},
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple', type: 'sweet' },
state: {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Baz',
state: {
routes: [
{
name: 'Bis',
params: {
author: 'Jane',
},
},
],
},
},
],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('handles stripping all query params with exact', () => {
const path = '/bar/sweet/apple/foo/bis/jane'; const path = '/bar/sweet/apple/foo/bis/jane';
const config = { const config = {
Foo: { Foo: {
@@ -1194,7 +673,6 @@ it('handles stripping all query params with exact', () => {
Bos: 'bos', Bos: 'bos',
Bis: { Bis: {
path: 'bis/:author', path: 'bis/:author',
exact: true,
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
@@ -1229,6 +707,9 @@ it('handles stripping all query params with exact', () => {
name: 'Bis', name: 'Bis',
params: { params: {
author: 'Jane', author: 'Jane',
count: undefined,
answer: undefined,
valid: undefined,
}, },
}, },
], ],
@@ -1246,194 +727,3 @@ it('handles stripping all query params with exact', () => {
expect(getPathFromState(state, config)).toBe(path); expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path); expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
}); });
it('replaces undefined query params', () => {
const path = '/bar/undefined/apple';
const config = {
Bar: 'bar/:type/:fruit',
};
const state = {
routes: [
{
name: 'Bar',
params: { fruit: 'apple' },
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('matches wildcard patterns at root', () => {
const path = '/test/bar/42/whatever';
const config = {
404: '*',
Foo: {
screens: {
Bar: {
path: '/bar/:id/',
},
},
},
};
const state = {
routes: [{ name: '404' }],
};
expect(getPathFromState(state, config)).toBe('/404');
expect(getPathFromState(getStateFromPath(path, config), config)).toBe('/404');
});
it('matches wildcard patterns at nested level', () => {
const path = '/bar/42/whatever/baz/initt';
const config = {
Foo: {
screens: {
Bar: {
path: '/bar/:id/',
screens: {
404: '*',
},
},
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
params: { id: '42' },
state: {
routes: [{ name: '404' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe('/bar/42/404');
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(
'/bar/42/404'
);
});
it('matches wildcard patterns at nested level with exact', () => {
const path = '/whatever';
const config = {
Foo: {
screens: {
Bar: {
path: '/bar/:id/',
screens: {
404: {
path: '*',
exact: true,
},
},
},
Baz: {},
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
state: {
routes: [{ name: '404' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe('/404');
expect(getPathFromState(getStateFromPath(path, config), config)).toBe('/404');
});
it('tries to match wildcard patterns at the end', () => {
const path = '/bar/42/test';
const config = {
Foo: {
screens: {
Bar: {
path: '/bar/:id/',
screens: {
404: '*',
Test: 'test',
},
},
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [
{
name: 'Bar',
params: { id: '42' },
state: {
routes: [{ name: 'Test' }],
},
},
],
},
},
],
};
expect(getPathFromState(state, config)).toBe(path);
expect(getPathFromState(getStateFromPath(path, config), config)).toBe(path);
});
it('uses nearest parent wildcard match for unmatched paths', () => {
const path = '/bar/42/baz/test';
const config = {
Foo: {
screens: {
Bar: {
path: '/bar/:id/',
screens: {
Baz: 'baz',
},
},
404: '*',
},
},
};
const state = {
routes: [
{
name: 'Foo',
state: {
routes: [{ name: '404' }],
},
},
],
};
expect(getPathFromState(state, config)).toBe('/404');
expect(getPathFromState(getStateFromPath(path, config), config)).toBe('/404');
});

File diff suppressed because it is too large Load Diff

View File

@@ -379,8 +379,6 @@ it("doesn't update state if action wasn't handled", () => {
}); });
it('cleans up state when the navigator unmounts', () => { it('cleans up state when the navigator unmounts', () => {
jest.useFakeTimers();
const TestNavigator = (props: any) => { const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
@@ -428,8 +426,6 @@ it('cleans up state when the navigator unmounts', () => {
<BaseNavigationContainer onStateChange={onStateChange} children={null} /> <BaseNavigationContainer onStateChange={onStateChange} children={null} />
); );
act(() => jest.runAllTimers());
expect(onStateChange).toBeCalledTimes(2); expect(onStateChange).toBeCalledTimes(2);
expect(onStateChange).lastCalledWith(undefined); expect(onStateChange).lastCalledWith(undefined);
}); });
@@ -1490,128 +1486,3 @@ it("doesn't throw if children is null", () => {
expect(() => render(element).update(element)).not.toThrowError(); expect(() => render(element).update(element)).not.toThrowError();
}); });
it('returns currently focused route with getCurrentRoute', () => {
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const TestScreen = () => null;
const navigation = React.createRef<NavigationContainerRef>();
const container = (
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="bar" options={{ a: 'b' }}>
{() => (
<TestNavigator initialRouteName="bar-a">
<Screen
name="bar-a"
component={TestScreen}
options={{ sample: 'data' }}
/>
</TestNavigator>
)}
</Screen>
<Screen name="xux" component={TestScreen} />
</TestNavigator>
</BaseNavigationContainer>
);
render(container).update(container);
expect(navigation.current?.getCurrentRoute()).toEqual({
key: 'bar-a',
name: 'bar-a',
});
});
it("returns currently focused route's options with getCurrentOptions", () => {
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const TestScreen = () => null;
const navigation = React.createRef<NavigationContainerRef>();
const container = (
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="bar" options={{ a: 'b' }}>
{() => (
<TestNavigator
initialRouteName="bar-a"
screenOptions={() => ({ sample2: 'data' })}
>
<Screen
name="bar-a"
component={TestScreen}
options={{ sample: 'data' }}
/>
</TestNavigator>
)}
</Screen>
<Screen name="xux" component={TestScreen} />
</TestNavigator>
</BaseNavigationContainer>
);
render(container).update(container);
expect(navigation.current?.getCurrentOptions()).toEqual({
sample: 'data',
sample2: 'data',
});
});
it('does not throw if while getting current options with no options defined', () => {
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const TestScreen = () => null;
const navigation = React.createRef<NavigationContainerRef>();
const container = (
<BaseNavigationContainer ref={navigation}>
<TestNavigator>
<Screen name="bar" options={{ a: 'b' }}>
{() => (
<TestNavigator initialRouteName="bar-a">
<Screen
name="bar-b"
component={TestScreen}
options={{ wrongKey: true }}
/>
<Screen name="bar-a" component={TestScreen} />
</TestNavigator>
)}
</Screen>
</TestNavigator>
</BaseNavigationContainer>
);
render(container).update(container);
expect(navigation.current?.getCurrentOptions()).toEqual({});
});
it('does not throw if while getting current options with empty container', () => {
const navigation = React.createRef<NavigationContainerRef>();
// @ts-ignore
const container = <BaseNavigationContainer ref={navigation} />;
render(container).update(container);
expect(navigation.current?.getCurrentOptions()).toEqual(undefined);
});

View File

@@ -4,31 +4,19 @@ import {
PartialState, PartialState,
Route, Route,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import { PathConfig } from './types';
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>; type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
type StringifyConfig = Record<string, (value: any) => string>; type StringifyConfig = Record<string, (value: any) => string>;
type OptionsItem = PathConfig[string]; type Options = {
[routeName: string]:
type ConfigItem = { | string
pattern?: string; | {
path?: string;
stringify?: StringifyConfig; stringify?: StringifyConfig;
screens?: Record<string, ConfigItem>; screens?: Options;
}; };
const getActiveRoute = (state: State): { name: string; params?: object } => {
const route =
typeof state.index === 'number'
? state.routes[state.index]
: state.routes[state.routes.length - 1];
if (route.state) {
return getActiveRoute(route.state);
}
return route;
}; };
/** /**
@@ -60,126 +48,103 @@ const getActiveRoute = (state: State): { name: string; params?: object } => {
*/ */
export default function getPathFromState( export default function getPathFromState(
state?: State, state?: State,
options: PathConfig = {} options: Options = {}
): string { ): string {
if (state === undefined) { if (state === undefined) {
throw Error('NavigationState not passed'); throw Error('NavigationState not passed');
} }
// Create a normalized configs array which will be easier to use
const configs = createNormalizedConfigs(options);
let path = '/'; let path = '/';
let current: State | undefined = state;
const allParams: Record<string, any> = {}; let current: State | undefined = state;
while (current) { while (current) {
let index = typeof current.index === 'number' ? current.index : 0; let index = typeof current.index === 'number' ? current.index : 0;
let route = current.routes[index] as Route<string> & { let route = current.routes[index] as Route<string> & {
state?: State; state?: State;
}; };
let currentOptions = options;
let pattern = route.name;
// we keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
let nestedRouteNames = '';
let pattern: string | undefined; while (route.name in currentOptions) {
if (typeof currentOptions[route.name] === 'string') {
let focusedParams: Record<string, any> | undefined; pattern = currentOptions[route.name] as string;
let focusedRoute = getActiveRoute(state); break;
let currentOptions = configs; } else if (typeof currentOptions[route.name] === 'object') {
// if there is no `screens` property, we return pattern
// Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined if (
let nestedRouteNames = []; !(currentOptions[route.name] as {
screens: Options;
let hasNext = true; }).screens
) {
while (route.name in currentOptions && hasNext) { pattern = (currentOptions[route.name] as { path: string }).path;
pattern = currentOptions[route.name].pattern; nestedRouteNames = `${nestedRouteNames}/${route.name}`;
break;
nestedRouteNames.push(route.name); } else {
// if it is the end of state, we return pattern
if (route.params) { if (route.state === undefined) {
const stringify = currentOptions[route.name]?.stringify; pattern = (currentOptions[route.name] as { path: string }).path;
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
const currentParams = fromEntries( break;
Object.entries(route.params).map(([key, value]) => [
key,
stringify?.[key] ? stringify[key](value) : String(value),
])
);
if (pattern) {
Object.assign(allParams, currentParams);
}
if (focusedRoute === route) {
// If this is the focused route, keep the params for later use
// We save it here since it's been stringified already
focusedParams = { ...currentParams };
pattern
?.split('/')
.filter((p) => p.startsWith(':'))
// eslint-disable-next-line no-loop-func
.forEach((p) => {
const name = getParamName(p);
// Remove the params present in the pattern since we'll only use the rest for query string
if (focusedParams) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete focusedParams[name];
}
});
}
}
// If there is no `screens` property or no nested state, we return pattern
if (!currentOptions[route.name].screens || route.state === undefined) {
hasNext = false;
} else { } else {
index = index =
typeof route.state.index === 'number' typeof route.state.index === 'number' ? route.state.index : 0;
? route.state.index
: route.state.routes.length - 1;
const nextRoute = route.state.routes[index]; const nextRoute = route.state.routes[index];
const nestedConfig = currentOptions[route.name].screens; const deeperConfig = (currentOptions[route.name] as {
screens: Options;
}).screens;
// if there is config for next route name, we go deeper // if there is config for next route name, we go deeper
if (nestedConfig && nextRoute.name in nestedConfig) { if (nextRoute.name in deeperConfig) {
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
route = nextRoute as Route<string> & { state?: State }; route = nextRoute as Route<string> & { state?: State };
currentOptions = nestedConfig; currentOptions = deeperConfig;
} else { } else {
// If not, there is no sense in going deeper in config // if not, there is no sense in going deeper in config
hasNext = false; pattern = (currentOptions[route.name] as { path: string }).path;
nestedRouteNames = `${nestedRouteNames}/${route.name}`;
break;
}
}
} }
} }
} }
if (pattern === undefined) { if (pattern === undefined) {
pattern = nestedRouteNames.join('/'); // cut the first `/`
pattern = nestedRouteNames.substring(1);
} }
// we don't add empty path strings to path
if (pattern !== '') {
const config =
currentOptions[route.name] !== undefined
? (currentOptions[route.name] as { stringify?: StringifyConfig })
.stringify
: undefined;
const params = route.params
? // Stringify all of the param values before we use them
Object.entries(route.params).reduce<{
[key: string]: string;
}>((acc, [key, value]) => {
acc[key] = config?.[key] ? config[key](value) : String(value);
return acc;
}, {})
: undefined;
if (currentOptions[route.name] !== undefined) { if (currentOptions[route.name] !== undefined) {
path += pattern path += pattern
.split('/') .split('/')
.map((p) => { .map((p) => {
const name = getParamName(p); const name = p.replace(/^:/, '');
// We don't know what to show for wildcard patterns
// Showing the route name seems ok, though whatever we show here will be incorrect
// Since the page doesn't actually exist
if (p === '*') {
return route.name;
}
// If the path has a pattern for a param, put the param in the path // If the path has a pattern for a param, put the param in the path
if (p.startsWith(':')) { if (params && name in params && p.startsWith(':')) {
const value = allParams[name]; const value = params[name];
// Remove the used value from the params object since we'll use the rest for query string
if (value === undefined && p.endsWith('?')) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
// Optional params without value assigned in route.params should be ignored delete params[name];
return '';
}
return encodeURIComponent(value); return encodeURIComponent(value);
} }
@@ -190,91 +155,30 @@ export default function getPathFromState(
path += encodeURIComponent(route.name); path += encodeURIComponent(route.name);
} }
if (!focusedParams) {
focusedParams = focusedRoute.params;
}
if (route.state) { if (route.state) {
path += '/'; path += '/';
} else if (focusedParams) { } else if (params) {
for (let param in focusedParams) { for (let param in params) {
if (focusedParams[param] === 'undefined') { if (params[param] === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete focusedParams[param]; delete params[param];
} }
} }
const query = queryString.stringify(params);
const query = queryString.stringify(focusedParams);
if (query) { if (query) {
path += `?${query}`; path += `?${query}`;
} }
} }
}
current = route.state; current = route.state;
} }
// Remove multiple as well as trailing slashes path =
path = path.replace(/\/+/g, '/'); path !== '/' && path.slice(path.length - 1) === '/'
path = path.length > 1 ? path.replace(/\/$/, '') : path; ? path.slice(0, -1)
: path;
return path; return path;
} }
// Object.fromEntries is not available in older iOS versions
const fromEntries = <K extends string, V>(entries: (readonly [K, V])[]) =>
entries.reduce((acc, [k, v]) => {
acc[k] = v;
return acc;
}, {} as Record<K, V>);
const getParamName = (pattern: string) =>
pattern.replace(/^:/, '').replace(/\?$/, '');
const joinPaths = (...paths: string[]): string =>
([] as string[])
.concat(...paths.map((p) => p.split('/')))
.filter(Boolean)
.join('/');
const createConfigItem = (
config: OptionsItem | string,
parentPattern?: string
): ConfigItem => {
if (typeof config === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
return { pattern };
}
// If an object is specified as the value (e.g. Foo: { ... }),
// It can have `path` property and `screens` prop which has nested configs
const pattern =
config.exact !== true && parentPattern && config.path
? joinPaths(parentPattern, config.path)
: config.path;
const screens = config.screens
? createNormalizedConfigs(config.screens, pattern)
: undefined;
return {
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
pattern: pattern?.split('/').filter(Boolean).join('/'),
stringify: config.stringify,
screens,
};
};
const createNormalizedConfigs = (
options: PathConfig,
pattern?: string
): Record<string, ConfigItem> =>
fromEntries(
Object.entries(options).map(([name, c]) => {
const result = createConfigItem(c, pattern);
return [name, result];
})
);

View File

@@ -5,17 +5,25 @@ import {
PartialState, PartialState,
InitialState, InitialState,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import { PathConfig } from './types';
type ParseConfig = Record<string, (value: string) => any>; type ParseConfig = Record<string, (value: string) => any>;
type Options = {
[routeName: string]:
| string
| {
path?: string;
parse?: ParseConfig;
screens?: Options;
initialRouteName?: string;
};
};
type RouteConfig = { type RouteConfig = {
screen: string; match: RegExp;
regex?: RegExp;
path: string;
pattern: string; pattern: string;
routeNames: string[]; routeNames: string[];
parse?: ParseConfig; parse: ParseConfig | undefined;
}; };
type InitialRouteConfig = { type InitialRouteConfig = {
@@ -48,128 +56,63 @@ type ResultState = PartialState<NavigationState> & {
*/ */
export default function getStateFromPath( export default function getStateFromPath(
path: string, path: string,
options: PathConfig = {} options: Options = {}
): ResultState | undefined { ): ResultState | undefined {
if (path === '') {
return undefined;
}
let initialRoutes: InitialRouteConfig[] = []; let initialRoutes: InitialRouteConfig[] = [];
// Create a normalized configs array which will be easier to use // Create a normalized configs array which will be easier to use
const configs = ([] as RouteConfig[]) const configs = ([] as RouteConfig[]).concat(
.concat(
...Object.keys(options).map((key) => ...Object.keys(options).map((key) =>
createNormalizedConfigs(key, options, [], initialRoutes) createNormalizedConfigs(key, options, [], initialRoutes)
) )
)
.sort((a, b) => {
// Sort config so that:
// - the most exhaustive ones are always at the beginning
// - patterns with wildcard are always at the end
// If one of the patterns starts with the other, it's more exhaustive
// So move it up
if (a.pattern.startsWith(b.pattern)) {
return 1;
}
if (b.pattern.startsWith(a.pattern)) {
return 1;
}
const aParts = a.pattern.split('/');
const bParts = b.pattern.split('/');
const aWildcardIndex = aParts.indexOf('*');
const bWildcardIndex = bParts.indexOf('*');
// If only one of the patterns has a wildcard, move it down in the list
if (aWildcardIndex === -1 && bWildcardIndex !== -1) {
return -1;
}
if (aWildcardIndex !== -1 && bWildcardIndex === -1) {
return 1;
}
if (aWildcardIndex === bWildcardIndex) {
// If `b` has more `/`, it's more exhaustive
// So we move it up in the list
return bParts.length - aParts.length;
}
// If the wildcard appears later in the pattern (has higher index), it's more specific
// So we move it up in the list
return bWildcardIndex - aWildcardIndex;
});
let remaining = path
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
.replace(/^\//, '') // Remove extra leading slash
.replace(/\?.*$/, ''); // Remove query params which we will handle later
// Make sure there is a trailing slash
remaining = remaining.endsWith('/') ? remaining : `${remaining}/`;
if (remaining === '/') {
// We need to add special handling of empty path so navigation to empty path also works
// When handling empty path, we should only look at the root level config
const match = configs.find(
(config) =>
config.path === '' &&
config.routeNames.every(
// Make sure that none of the parent configs have a non-empty path defined
(name) => !configs.find((c) => c.screen === name)?.path
)
); );
if (match) {
return createNestedStateObject(
match.routeNames.map((name, i, self) => {
if (i === self.length - 1) {
return { name, params: parseQueryParams(path, match.parse) };
}
return { name };
}),
initialRoutes
);
}
return undefined;
}
let result: PartialState<NavigationState> | undefined; let result: PartialState<NavigationState> | undefined;
let current: PartialState<NavigationState> | undefined; let current: PartialState<NavigationState> | undefined;
// We try to match the paths in 2 passes let remaining = path
// In first pass, we match the whole path against the regex instead of segments .replace(/[/]+/, '/') // Replace multiple slash (//) with single ones
// This makes sure matches such as wildcard will catch any unmatched routes, even if nested .replace(/^\//, '') // Remove extra leading slash
const { routeNames, allParams, remainingPath } = matchAgainstConfigs( .replace(/\?.*/, ''); // Remove query params which we will handle later
remaining,
configs.map((c) => ({
...c,
// Add `$` to the regex to make sure it matches till end of the path and not just beginning
regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
}))
);
if (routeNames !== undefined) { while (remaining) {
// This will always be empty if full path matched let routeNames: string[] | undefined;
remaining = remainingPath; let params: Record<string, any> | undefined;
current = createNestedStateObject(
createRouteObjects(configs, routeNames, allParams), // Go through all configs, and see if the next path segment matches our regex
initialRoutes for (const config of configs) {
); const match = remaining.match(config.match);
result = current;
// If our regex matches, we need to extract params from the path
if (match) {
routeNames = [...config.routeNames];
const paramPatterns = config.pattern
.split('/')
.filter((p) => p.startsWith(':'));
if (paramPatterns.length) {
params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
const key = p.replace(/^:/, '');
const value = match[i + 1]; // The param segments start from index 1 in the regex match result
acc[key] =
config.parse && config.parse[key]
? config.parse[key](value)
: value;
return acc;
}, {});
} }
// In second pass, we divide the path into segments and match piece by piece // Remove the matched segment from the remaining path
// This preserves the old behaviour, but we should remove it in next major remaining = remaining.replace(match[0], '');
while (remaining) {
let { routeNames, allParams, remainingPath } = matchAgainstConfigs(
remaining,
configs
);
remaining = remainingPath; break;
}
}
// If we hadn't matched any segments earlier, use the path as route name // If we hadn't matched any segments earlier, use the path as route name
if (routeNames === undefined) { if (routeNames === undefined) {
@@ -180,11 +123,35 @@ export default function getStateFromPath(
remaining = segments.join('/'); remaining = segments.join('/');
} }
const state = createNestedStateObject( let state: InitialState;
createRouteObjects(configs, routeNames, allParams), let routeName = routeNames.shift() as string;
initialRoutes let initialRoute = findInitialRoute(routeName, initialRoutes);
state = createNestedState(
initialRoute,
routeName,
routeNames.length === 0,
params
); );
if (routeNames.length > 0) {
let nestedState = state;
while ((routeName = routeNames.shift() as string)) {
initialRoute = findInitialRoute(routeName, initialRoutes);
nestedState.routes[nestedState.index || 0].state = createNestedState(
initialRoute,
routeName,
routeNames.length === 0,
params
);
if (routeNames.length > 0) {
nestedState = nestedState.routes[nestedState.index || 0]
.state as InitialState;
}
}
}
if (current) { if (current) {
// The state should be nested inside the deepest route we parsed before // The state should be nested inside the deepest route we parsed before
while (current?.routes[current.index || 0].state) { while (current?.routes[current.index || 0].state) {
@@ -205,315 +172,9 @@ export default function getStateFromPath(
return undefined; return undefined;
} }
const route = findFocusedRoute(current); const query = path.split('?')[1];
const params = parseQueryParams(
path,
findParseConfigForRoute(route.name, configs)
);
if (params) {
route.params = { ...route.params, ...params };
}
return result;
}
const joinPaths = (...paths: string[]): string =>
([] as string[])
.concat(...paths.map((p) => p.split('/')))
.filter(Boolean)
.join('/');
const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
let routeNames: string[] | undefined;
let allParams: Record<string, any> | undefined;
let remainingPath = remaining;
// Go through all configs, and see if the next path segment matches our regex
for (const config of configs) {
if (!config.regex) {
continue;
}
const match = remainingPath.match(config.regex);
// If our regex matches, we need to extract params from the path
if (match) {
routeNames = [...config.routeNames];
const paramPatterns = config.pattern
.split('/')
.filter((p) => p.startsWith(':'));
if (paramPatterns.length) {
allParams = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
const value = match![(i + 1) * 2].replace(/\//, ''); // The param segments appear every second item starting from 2 in the regex match result
acc[p] = value;
return acc;
}, {});
}
remainingPath = remainingPath.replace(match[1], '');
break;
}
}
return { routeNames, allParams, remainingPath };
};
const createNormalizedConfigs = (
screen: string,
routeConfig: PathConfig,
routeNames: string[] = [],
initials: InitialRouteConfig[],
parentPattern?: string
): RouteConfig[] => {
const configs: RouteConfig[] = [];
routeNames.push(screen);
const config = routeConfig[screen];
if (typeof config === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
configs.push(createConfigItem(screen, routeNames, pattern, config));
} else if (typeof config === 'object') {
let pattern: string | undefined;
// if an object is specified as the value (e.g. Foo: { ... }),
// it can have `path` property and
// it could have `screens` prop which has nested configs
if (typeof config.path === 'string') {
pattern =
config.exact !== true && parentPattern
? joinPaths(parentPattern, config.path)
: config.path;
configs.push(
createConfigItem(screen, routeNames, pattern, config.path, config.parse)
);
}
if (config.screens) {
// property `initialRouteName` without `screens` has no purpose
if (config.initialRouteName) {
initials.push({
initialRouteName: config.initialRouteName,
connectedRoutes: Object.keys(config.screens),
});
}
Object.keys(config.screens).forEach((nestedConfig) => {
const result = createNormalizedConfigs(
nestedConfig,
config.screens as PathConfig,
routeNames,
initials,
pattern
);
configs.push(...result);
});
}
}
routeNames.pop();
return configs;
};
const createConfigItem = (
screen: string,
routeNames: string[],
pattern: string,
path: string,
parse?: ParseConfig
): RouteConfig => {
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
pattern = pattern.split('/').filter(Boolean).join('/');
const regex = pattern
? new RegExp(
`^(${pattern
.split('/')
.map((it) => {
if (it.startsWith(':')) {
return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
}
return `${it === '*' ? '.*' : escape(it)}\\/`;
})
.join('')})`
)
: undefined;
return {
screen,
regex,
pattern,
path,
// The routeNames array is mutated, so copy it to keep the current state
routeNames: [...routeNames],
parse,
};
};
const findParseConfigForRoute = (
routeName: string,
flatConfig: RouteConfig[]
): ParseConfig | undefined => {
for (const config of flatConfig) {
if (routeName === config.routeNames[config.routeNames.length - 1]) {
return config.parse;
}
}
return undefined;
};
// Try to find an initial route connected with the one passed
const findInitialRoute = (
routeName: string,
initialRoutes: InitialRouteConfig[]
): string | undefined => {
for (const config of initialRoutes) {
if (config.connectedRoutes.includes(routeName)) {
return config.initialRouteName === routeName
? undefined
: config.initialRouteName;
}
}
return undefined;
};
// returns state object with values depending on whether
// it is the end of state and if there is initialRoute for this level
const createStateObject = (
initialRoute: string | undefined,
routeName: string,
params: Record<string, any> | undefined,
isEmpty: boolean
): InitialState => {
if (isEmpty) {
if (initialRoute) {
return {
index: 1,
routes: [{ name: initialRoute }, { name: routeName as string, params }],
};
} else {
return {
routes: [{ name: routeName as string, params }],
};
}
} else {
if (initialRoute) {
return {
index: 1,
routes: [
{ name: initialRoute },
{ name: routeName as string, params, state: { routes: [] } },
],
};
} else {
return {
routes: [{ name: routeName as string, params, state: { routes: [] } }],
};
}
}
};
const createNestedStateObject = (
routes: { name: string; params?: object }[],
initialRoutes: InitialRouteConfig[]
) => {
let state: InitialState;
let route = routes.shift() as { name: string; params?: object };
let initialRoute = findInitialRoute(route.name, initialRoutes);
state = createStateObject(
initialRoute,
route.name,
route.params,
routes.length === 0
);
if (routes.length > 0) {
let nestedState = state;
while ((route = routes.shift() as { name: string; params?: object })) {
initialRoute = findInitialRoute(route.name, initialRoutes);
const nestedStateIndex =
nestedState.index || nestedState.routes.length - 1;
nestedState.routes[nestedStateIndex].state = createStateObject(
initialRoute,
route.name,
route.params,
routes.length === 0
);
if (routes.length > 0) {
nestedState = nestedState.routes[nestedStateIndex]
.state as InitialState;
}
}
}
return state;
};
const createRouteObjects = (
configs: RouteConfig[],
routeNames: string[],
allParams?: Record<string, any>
) =>
routeNames.map((name) => {
const config = configs.find((c) => c.screen === name);
let params: object | undefined;
if (allParams && config?.path) {
const pattern = config.path;
if (pattern) {
const paramPatterns = pattern
.split('/')
.filter((p) => p.startsWith(':'));
if (paramPatterns.length) {
params = paramPatterns.reduce<Record<string, any>>((acc, p) => {
const key = p.replace(/^:/, '').replace(/\?$/, '');
const value = allParams![p];
if (value) {
acc[key] =
config.parse && config.parse[key]
? config.parse[key](value)
: value;
}
return acc;
}, {});
}
}
}
if (params && Object.keys(params).length) {
return { name, params };
}
return { name };
});
const findFocusedRoute = (state: InitialState) => {
let current: InitialState | undefined = state;
if (query) {
while (current?.routes[current.index || 0].state) { while (current?.routes[current.index || 0].state) {
// The query params apply to the deepest route // The query params apply to the deepest route
current = current.routes[current.index || 0].state; current = current.routes[current.index || 0].state;
@@ -523,23 +184,150 @@ const findFocusedRoute = (state: InitialState) => {
current?.index || 0 current?.index || 0
]; ];
return route;
};
const parseQueryParams = (
path: string,
parseConfig?: Record<string, (value: string) => any>
) => {
const query = path.split('?')[1];
const params = queryString.parse(query); const params = queryString.parse(query);
const parseFunction = findParseConfigForRoute(route.name, configs);
if (parseConfig) { if (parseFunction) {
Object.keys(params).forEach((name) => { Object.keys(params).forEach((name) => {
if (parseConfig[name] && typeof params[name] === 'string') { if (parseFunction[name] && typeof params[name] === 'string') {
params[name] = parseConfig[name](params[name] as string); params[name] = parseFunction[name](params[name] as string);
} }
}); });
} }
return Object.keys(params).length ? params : undefined; route.params = { ...route.params, ...params };
}; }
return result;
}
function createNormalizedConfigs(
key: string,
routeConfig: Options,
routeNames: string[] = [],
initials: InitialRouteConfig[]
): RouteConfig[] {
const configs: RouteConfig[] = [];
routeNames.push(key);
const value = routeConfig[key];
if (typeof value === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
if (value !== '') {
configs.push(createConfigItem(routeNames, value));
}
} else if (typeof value === 'object') {
// if an object is specified as the value (e.g. Foo: { ... }),
// it can have `path` property and
// it could have `screens` prop which has nested configs
if (value.path && value.path !== '') {
configs.push(createConfigItem(routeNames, value.path, value.parse));
}
if (value.screens) {
// property `initialRouteName` without `screens` has no purpose
if (value.initialRouteName) {
initials.push({
initialRouteName: value.initialRouteName,
connectedRoutes: Object.keys(value.screens),
});
}
Object.keys(value.screens).forEach((nestedConfig) => {
const result = createNormalizedConfigs(
nestedConfig,
value.screens as Options,
routeNames,
initials
);
configs.push(...result);
});
}
}
routeNames.pop();
return configs;
}
function createConfigItem(
routeNames: string[],
pattern: string,
parse?: ParseConfig
): RouteConfig {
const match = new RegExp(
'^' + escape(pattern).replace(/:[a-z0-9]+/gi, '([^/]+)') + '/?'
);
return {
match,
pattern,
// The routeNames array is mutated, so copy it to keep the current state
routeNames: [...routeNames],
parse,
};
}
function findParseConfigForRoute(
routeName: string,
flatConfig: RouteConfig[]
): ParseConfig | undefined {
for (const config of flatConfig) {
if (routeName === config.routeNames[config.routeNames.length - 1]) {
return config.parse;
}
}
return undefined;
}
// tries to find an initial route connected with the one passed
function findInitialRoute(
routeName: string,
initialRoutes: InitialRouteConfig[]
): string | undefined {
for (const config of initialRoutes) {
if (config.connectedRoutes.includes(routeName)) {
return config.initialRouteName === routeName
? undefined
: config.initialRouteName;
}
}
return undefined;
}
// returns nested state object with values depending on whether
// it is the end of state and if there is initialRoute for this level
function createNestedState(
initialRoute: string | undefined,
routeName: string,
isEmpty: boolean,
params?: Record<string, any> | undefined
): InitialState {
if (isEmpty) {
if (initialRoute) {
return {
index: 1,
routes: [
{ name: initialRoute },
{ name: routeName as string, ...(params && { params }) },
],
};
} else {
return {
routes: [{ name: routeName as string, ...(params && { params }) }],
};
}
} else {
if (initialRoute) {
return {
index: 1,
routes: [
{ name: initialRoute },
{ name: routeName as string, state: { routes: [] } },
],
};
} else {
return { routes: [{ name: routeName as string, state: { routes: [] } }] };
}
}
}

View File

@@ -7,8 +7,6 @@ export { default as NavigationHelpersContext } from './NavigationHelpersContext'
export { default as NavigationContext } from './NavigationContext'; export { default as NavigationContext } from './NavigationContext';
export { default as NavigationRouteContext } from './NavigationRouteContext'; export { default as NavigationRouteContext } from './NavigationRouteContext';
export { default as CurrentRenderContext } from './CurrentRenderContext';
export { default as useNavigationBuilder } from './useNavigationBuilder'; export { default as useNavigationBuilder } from './useNavigationBuilder';
export { default as useNavigation } from './useNavigation'; export { default as useNavigation } from './useNavigation';
export { default as useRoute } from './useRoute'; export { default as useRoute } from './useRoute';

View File

@@ -1,7 +0,0 @@
/**
* Compare two arrays with primitive values as the content.
* We need to make sure that both values and order match.
*/
export default function isArrayEqual(a: any[], b: any[]) {
return a.length === b.length && a.every((it, index) => it === b[index]);
}

View File

@@ -152,7 +152,7 @@ type NavigationHelpersCommon<
* @param [params] Params object for the route. * @param [params] Params object for the route.
*/ */
navigate<RouteName extends keyof ParamList>( navigate<RouteName extends keyof ParamList>(
...args: undefined extends ParamList[RouteName] ...args: ParamList[RouteName] extends undefined | any
? [RouteName] | [RouteName, ParamList[RouteName]] ? [RouteName] | [RouteName, ParamList[RouteName]]
: [RouteName, ParamList[RouteName]] : [RouteName, ParamList[RouteName]]
): void; ): void;
@@ -422,14 +422,6 @@ export type NavigationContainerRef = NavigationHelpers<ParamListBase> &
* Get the rehydrated navigation state of the navigation tree. * Get the rehydrated navigation state of the navigation tree.
*/ */
getRootState(): NavigationState; getRootState(): NavigationState;
/**
* Get the currently focused navigation route.
*/
getCurrentRoute(): Route<string> | undefined;
/**
* Get the currently focused route's options.
*/
getCurrentOptions(): object | undefined;
}; };
export type TypedNavigator< export type TypedNavigator<
@@ -470,16 +462,3 @@ export type TypedNavigator<
_: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap> _: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>
) => null; ) => null;
}; };
export type PathConfig = {
[routeName: string]:
| string
| {
path?: string;
exact?: boolean;
parse?: Record<string, (value: string) => any>;
stringify?: Record<string, (value: any) => string>;
screens?: PathConfig;
initialRouteName?: string;
};
};

View File

@@ -1,28 +0,0 @@
import * as React from 'react';
import { NavigationState, ParamListBase } from '@react-navigation/routers';
import CurrentRenderContext from './CurrentRenderContext';
import { Descriptor, NavigationHelpers } from './types';
type Options = {
state: NavigationState;
navigation: NavigationHelpers<ParamListBase>;
descriptors: {
[key: string]: Descriptor<ParamListBase, string, NavigationState, object>;
};
};
/**
* Write the current options, so that server renderer can get current values
* Mutating values like this is not safe in async mode, but it doesn't apply to SSR
*/
export default function useCurrentRender({
state,
navigation,
descriptors,
}: Options) {
const current = React.useContext(CurrentRenderContext);
if (current && navigation.isFocused()) {
current.options = descriptors[state.routes[state.index].key].options;
}
}

View File

@@ -117,7 +117,22 @@ export default function useDescriptors<
const screen = screens[route.name]; const screen = screens[route.name];
const navigation = navigations[route.key]; const navigation = navigations[route.key];
const routeOptions = { acc[route.key] = {
navigation,
render() {
return (
<NavigationBuilderContext.Provider key={route.key} value={context}>
<SceneView
navigation={navigation}
route={route}
screen={screen}
getState={getState}
setState={setState}
/>
</NavigationBuilderContext.Provider>
);
},
options: {
// The default `screenOptions` passed to the navigator // The default `screenOptions` passed to the navigator
...(typeof screenOptions === 'object' || screenOptions == null ...(typeof screenOptions === 'object' || screenOptions == null
? screenOptions ? screenOptions
@@ -137,25 +152,7 @@ export default function useDescriptors<
})), })),
// The options set via `navigation.setOptions` // The options set via `navigation.setOptions`
...options[route.key], ...options[route.key],
};
acc[route.key] = {
navigation,
render() {
return (
<NavigationBuilderContext.Provider key={route.key} value={context}>
<SceneView
navigation={navigation}
route={route}
screen={screen}
getState={getState}
setState={setState}
options={routeOptions}
/>
</NavigationBuilderContext.Provider>
);
}, },
options: routeOptions,
}; };
return acc; return acc;

View File

@@ -9,13 +9,16 @@ import useNavigation from './useNavigation';
*/ */
export default function useIsFocused(): boolean { export default function useIsFocused(): boolean {
const navigation = useNavigation(); const navigation = useNavigation();
// eslint-disable-next-line react-hooks/exhaustive-deps
const getCurrentValue = React.useCallback(navigation.isFocused, [navigation]); const getCurrentValue = React.useCallback(navigation.isFocused, [navigation]);
const subscribe = React.useCallback( const subscribe = React.useCallback(
(callback: () => void) => { (callback: (value: boolean) => void) => {
const unsubscribeFocus = navigation.addListener('focus', callback); const unsubscribeFocus = navigation.addListener('focus', () =>
callback(true)
);
const unsubscribeBlur = navigation.addListener('blur', callback); const unsubscribeBlur = navigation.addListener('blur', () =>
callback(false)
);
return () => { return () => {
unsubscribeFocus(); unsubscribeFocus();

View File

@@ -11,7 +11,7 @@ import {
NavigationAction, NavigationAction,
Route, Route,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import NavigationStateContext from './NavigationStateContext'; import { NavigationStateContext } from './BaseNavigationContainer';
import NavigationRouteContext from './NavigationRouteContext'; import NavigationRouteContext from './NavigationRouteContext';
import Screen from './Screen'; import Screen from './Screen';
import useEventEmitter from './useEventEmitter'; import useEventEmitter from './useEventEmitter';
@@ -33,8 +33,6 @@ import {
import useStateGetters from './useStateGetters'; import useStateGetters from './useStateGetters';
import useOnGetState from './useOnGetState'; import useOnGetState from './useOnGetState';
import useScheduleUpdate from './useScheduleUpdate'; import useScheduleUpdate from './useScheduleUpdate';
import useCurrentRender from './useCurrentRender';
import isArrayEqual from './isArrayEqual';
// This is to make TypeScript compiler happy // This is to make TypeScript compiler happy
// eslint-disable-next-line babel/no-unused-expressions // eslint-disable-next-line babel/no-unused-expressions
@@ -49,6 +47,13 @@ type NavigatorRoute = {
}; };
}; };
/**
* Compare two arrays with primitive values as the content.
* We need to make sure that both values and order match.
*/
const isArrayEqual = (a: any[], b: any[]) =>
a.length === b.length && a.every((it, index) => it === b[index]);
/** /**
* Extract route config object from React children elements. * Extract route config object from React children elements.
* *
@@ -259,36 +264,41 @@ export default function useNavigationBuilder<
getKey, getKey,
} = React.useContext(NavigationStateContext); } = React.useContext(NavigationStateContext);
const [initializedState, isFirstStateInitialization] = React.useMemo(() => { const previousStateRef = React.useRef<
NavigationState | PartialState<NavigationState> | undefined
>();
const initializedStateRef = React.useRef<State>();
let isFirstStateInitialization = false;
if (
initializedStateRef.current === undefined ||
currentState !== previousStateRef.current
) {
// If the current state isn't initialized on first render, we initialize it // If the current state isn't initialized on first render, we initialize it
// We also need to re-initialize it if the state passed from parent was changed (maybe due to reset) // We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
// Otherwise assume that the state was provided as initial state // Otherwise assume that the state was provided as initial state
// So we need to rehydrate it to make it usable // So we need to rehydrate it to make it usable
if (currentState === undefined || !isStateValid(currentState)) { if (currentState === undefined || !isStateValid(currentState)) {
return [ isFirstStateInitialization = true;
router.getInitialState({ initializedStateRef.current = router.getInitialState({
routeNames, routeNames,
routeParamList, routeParamList,
}), });
true,
];
} else { } else {
return [ initializedStateRef.current = router.getRehydratedState(
router.getRehydratedState(currentState as PartialState<State>, { currentState as PartialState<State>,
{
routeNames, routeNames,
routeParamList, routeParamList,
}),
false,
];
} }
// We explicitly don't include routeNames/routeParamList in the dep list );
// below. We want to avoid forcing a new state to be calculated in cases }
// where routeConfigs change without affecting routeNames/routeParamList. }
// Instead, we handle changes to these in the nextState code below. Note
// that some changes to routeConfigs are explicitly ignored, such as changes React.useEffect(() => {
// to initialParams previousStateRef.current = currentState;
// eslint-disable-next-line react-hooks/exhaustive-deps }, [currentState]);
}, [currentState, router, isStateValid]);
let state = let state =
// If the state isn't initialized, or stale, use the state we initialized instead // If the state isn't initialized, or stale, use the state we initialized instead
@@ -296,7 +306,7 @@ export default function useNavigationBuilder<
// So it'll be `undefined` or stale untill the first navigation event happens // So it'll be `undefined` or stale untill the first navigation event happens
isStateInitialized(currentState) isStateInitialized(currentState)
? (currentState as State) ? (currentState as State)
: (initializedState as State); : (initializedStateRef.current as State);
let nextState: State = state; let nextState: State = state;
@@ -352,24 +362,13 @@ export default function useNavigationBuilder<
return () => { return () => {
// We need to clean up state for this navigator on unmount // We need to clean up state for this navigator on unmount
// We do it in a timeout because we need to detect if another navigator mounted in the meantime
// For example, if another navigator has started rendering, we should skip cleanup
// Otherwise, our cleanup step will cleanup state for the other navigator and re-initialize it
setTimeout(() => {
if (getCurrentState() !== undefined && getKey() === navigatorKey) { if (getCurrentState() !== undefined && getKey() === navigatorKey) {
setState(undefined); setState(undefined);
} }
}, 0);
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
// We initialize this ref here to avoid a new getState getting initialized
// whenever initializedState changes. We want getState to have access to the
// latest initializedState, but don't need it to change when that happens
const initializedStateRef = React.useRef<State>();
initializedStateRef.current = initializedState;
const getState = React.useCallback((): State => { const getState = React.useCallback((): State => {
const currentState = getCurrentState(); const currentState = getCurrentState();
@@ -493,12 +492,6 @@ export default function useNavigationBuilder<
emitter, emitter,
}); });
useCurrentRender({
state,
navigation,
descriptors,
});
return { return {
state, state,
navigation, navigation,

View File

@@ -2,7 +2,6 @@ import * as React from 'react';
import { NavigationState } from '@react-navigation/routers'; import { NavigationState } from '@react-navigation/routers';
import NavigationBuilderContext from './NavigationBuilderContext'; import NavigationBuilderContext from './NavigationBuilderContext';
import NavigationRouteContext from './NavigationRouteContext'; import NavigationRouteContext from './NavigationRouteContext';
import isArrayEqual from './isArrayEqual';
export default function useOnGetState({ export default function useOnGetState({
getStateForRoute, getStateForRoute,
@@ -17,23 +16,13 @@ export default function useOnGetState({
const getRehydratedState = React.useCallback(() => { const getRehydratedState = React.useCallback(() => {
const state = getState(); const state = getState();
return {
// Avoid returning new route objects if we don't need to ...state,
const routes = state.routes.map((route) => { routes: state.routes.map((route) => ({
const childState = getStateForRoute(route.key); ...route,
state: getStateForRoute(route.key),
if (route.state === childState) { })),
return route; };
}
return { ...route, state: childState };
});
if (isArrayEqual(state.routes, routes)) {
return state;
}
return { ...state, routes };
}, [getState, getStateForRoute]); }, [getState, getStateForRoute]);
React.useEffect(() => { React.useEffect(() => {

View File

@@ -1,70 +0,0 @@
import * as React from 'react';
import NavigationStateContext from './NavigationStateContext';
import { NavigationState } from '@react-navigation/routers';
export default function useOptionsGetters({
key,
getOptions,
getState,
}: {
key?: string;
getOptions?: () => object | undefined;
getState?: () => NavigationState;
}) {
const optionsGettersFromChild = React.useRef<
Record<string, (() => object | undefined | null) | undefined>
>({});
const { addOptionsGetter: parentAddOptionsGetter } = React.useContext(
NavigationStateContext
);
const getOptionsFromListener = React.useCallback(() => {
for (let key in optionsGettersFromChild.current) {
if (optionsGettersFromChild.current.hasOwnProperty(key)) {
const result = optionsGettersFromChild.current[key]?.();
// null means unfocused route
if (result !== null) {
return result;
}
}
}
return null;
}, []);
const getCurrentOptions = React.useCallback(() => {
if (getState) {
const state = getState();
if (state.routes[state.index].key !== key) {
// null means unfocused route
return null;
}
}
const optionsFromListener = getOptionsFromListener();
if (optionsFromListener !== null) {
return optionsFromListener;
}
return getOptions?.() ?? undefined;
}, [getState, getOptionsFromListener, getOptions, key]);
React.useEffect(() => {
return parentAddOptionsGetter?.(key!, getCurrentOptions);
}, [getCurrentOptions, parentAddOptionsGetter, key]);
const addOptionsGetter = React.useCallback(
(key: string, getter: () => object | undefined | null) => {
optionsGettersFromChild.current[key] = getter;
return () => {
optionsGettersFromChild.current[key] = undefined;
};
},
[]
);
return {
addOptionsGetter,
getCurrentOptions,
};
}

View File

@@ -8,15 +8,6 @@ const UNINTIALIZED_STATE = {};
export default function useSyncState<T>(initialState?: (() => T) | T) { export default function useSyncState<T>(initialState?: (() => T) | T) {
const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any); const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any);
const isSchedulingRef = React.useRef(false); const isSchedulingRef = React.useRef(false);
const isMountedRef = React.useRef(true);
React.useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
};
}, []);
if (stateRef.current === UNINTIALIZED_STATE) { if (stateRef.current === UNINTIALIZED_STATE) {
stateRef.current = stateRef.current =
@@ -29,7 +20,7 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
const getState = React.useCallback(() => stateRef.current, []); const getState = React.useCallback(() => stateRef.current, []);
const setState = React.useCallback((state: T) => { const setState = React.useCallback((state: T) => {
if (state === stateRef.current || !isMountedRef.current) { if (state === stateRef.current) {
return; return;
} }
@@ -51,10 +42,6 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
}, []); }, []);
const flushUpdates = React.useCallback(() => { const flushUpdates = React.useCallback(() => {
if (!isMountedRef.current) {
return;
}
// Make sure that the tracking state is up-to-date. // Make sure that the tracking state is up-to-date.
// We call it unconditionally, but React should skip the update if state is unchanged. // We call it unconditionally, but React should skip the update if state is unchanged.
setTrackingState(stateRef.current); setTrackingState(stateRef.current);

View File

@@ -3,156 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.8.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.8.1...@react-navigation/drawer@5.8.2) (2020-06-06)
### Bug Fixes
* typo on drawerPosition default props ([#8357](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/8357)) ([762cc44](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/762cc4457842182189eeac84aedbb88169452e1e))
## [5.8.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.8.0...@react-navigation/drawer@5.8.1) (2020-05-27)
**Note:** Version bump only for package @react-navigation/drawer
# [5.8.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.7...@react-navigation/drawer@5.8.0) (2020-05-23)
### Features
* update react-native-safe-area-context to 1.0.0 ([#8182](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/8182)) ([d62fbfe](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/d62fbfe255140f16b182e8b54b276a7c96f2aec6))
## [5.7.7](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.6...@react-navigation/drawer@5.7.7) (2020-05-20)
**Note:** Version bump only for package @react-navigation/drawer
## [5.7.6](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.5...@react-navigation/drawer@5.7.6) (2020-05-20)
**Note:** Version bump only for package @react-navigation/drawer
## [5.7.5](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.4...@react-navigation/drawer@5.7.5) (2020-05-16)
**Note:** Version bump only for package @react-navigation/drawer
## [5.7.4](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.3...@react-navigation/drawer@5.7.4) (2020-05-14)
**Note:** Version bump only for package @react-navigation/drawer
## [5.7.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.2...@react-navigation/drawer@5.7.3) (2020-05-14)
**Note:** Version bump only for package @react-navigation/drawer
## [5.7.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.1...@react-navigation/drawer@5.7.2) (2020-05-10)
**Note:** Version bump only for package @react-navigation/drawer
## [5.7.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.7.0...@react-navigation/drawer@5.7.1) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
# [5.7.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.4...@react-navigation/drawer@5.7.0) (2020-05-08)
### Features
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7971)
## [5.6.4](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.3...@react-navigation/drawer@5.6.4) (2020-05-05)
**Note:** Version bump only for package @react-navigation/drawer
## [5.6.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.2...@react-navigation/drawer@5.6.3) (2020-05-01)
**Note:** Version bump only for package @react-navigation/drawer
## [5.6.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.1...@react-navigation/drawer@5.6.2) (2020-05-01)
**Note:** Version bump only for package @react-navigation/drawer
## [5.6.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.6.0...@react-navigation/drawer@5.6.1) (2020-04-30)
**Note:** Version bump only for package @react-navigation/drawer
# [5.6.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.5.1...@react-navigation/drawer@5.6.0) (2020-04-30)
### Bug Fixes
* fix closing drawer on web with tap on overlay ([70be3f6](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/70be3f6d863c56211e2f90bdf743bd8526338248))
* make sure the address bar hides when scrolling on web ([0a19e94](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/0a19e94b23a4d2b5f22d1d9deb0544f586f475ee))
### Features
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/942d2be2c72720469475ce12ec8df23825994dbf))
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.5.0...@react-navigation/drawer@5.5.1) (2020-04-27) ## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.5.0...@react-navigation/drawer@5.5.1) (2020-04-27)
**Note:** Version bump only for package @react-navigation/drawer **Note:** Version bump only for package @react-navigation/drawer

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/drawer", "name": "@react-navigation/drawer",
"description": "Drawer navigator component with animated transitions and gesturess", "description": "Drawer navigator component with animated transitions and gesturess",
"version": "5.8.2", "version": "5.5.1",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -20,13 +20,11 @@
"homepage": "https://reactnavigation.org/docs/drawer-navigator.html", "homepage": "https://reactnavigation.org/docs/drawer-navigator.html",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
@@ -41,17 +39,17 @@
"react-native-iphone-x-helper": "^1.2.1" "react-native-iphone-x-helper": "^1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.5.1", "@react-navigation/native": "^5.1.7",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"@types/react-native": "^0.62.7", "@types/react-native": "^0.61.22",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-native": "~0.61.5", "react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0", "react-native-gesture-handler": "^1.6.0",
"react-native-reanimated": "^1.8.0", "react-native-reanimated": "^1.7.0",
"react-native-safe-area-context": "^1.0.0", "react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.7.0", "react-native-screens": "^2.3.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -25,7 +25,6 @@ export { default as useIsDrawerOpen } from './utils/useIsDrawerOpen';
export type { export type {
DrawerNavigationOptions, DrawerNavigationOptions,
DrawerNavigationProp, DrawerNavigationProp,
DrawerScreenProps,
DrawerContentOptions, DrawerContentOptions,
DrawerContentComponentProps, DrawerContentComponentProps,
} from './types'; } from './types';

View File

@@ -8,9 +8,8 @@ import {
NavigationHelpers, NavigationHelpers,
DrawerNavigationState, DrawerNavigationState,
DrawerActionHelpers, DrawerActionHelpers,
RouteProp,
} from '@react-navigation/native'; } from '@react-navigation/native';
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler'; import { PanGestureHandler } from 'react-native-gesture-handler';
export type Scene = { export type Scene = {
route: Route<string>; route: Route<string>;
@@ -33,7 +32,6 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
drawerType?: 'front' | 'back' | 'slide' | 'permanent'; drawerType?: 'front' | 'back' | 'slide' | 'permanent';
/** /**
* How far from the edge of the screen the swipe gesture should activate. * How far from the edge of the screen the swipe gesture should activate.
* Not supported on Web.
*/ */
edgeWidth?: number; edgeWidth?: number;
/** /**
@@ -60,9 +58,8 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
statusBarAnimation?: 'slide' | 'none' | 'fade'; statusBarAnimation?: 'slide' | 'none' | 'fade';
/** /**
* Props to pass to the underlying pan gesture handler. * Props to pass to the underlying pan gesture handler.
* Not supported on Web.
*/ */
gestureHandlerProps?: PanGestureHandlerProperties; gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
/** /**
* Whether the screens should render the first time they are accessed. Defaults to `true`. * Whether the screens should render the first time they are accessed. Defaults to `true`.
* Set it to `false` if you want to render all screens on initial render. * Set it to `false` if you want to render all screens on initial render.
@@ -116,15 +113,13 @@ export type DrawerNavigationOptions = {
* Whether you can use gestures to open or close the drawer. * Whether you can use gestures to open or close the drawer.
* Setting this to `false` disables swipe gestures as well as tap on overlay to close. * Setting this to `false` disables swipe gestures as well as tap on overlay to close.
* See `swipeEnabled` to disable only the swipe gesture. * See `swipeEnabled` to disable only the swipe gesture.
* Defaults to `true`. * Defaults to `true`
* Not supported on Web.
*/ */
gestureEnabled?: boolean; gestureEnabled?: boolean;
/** /**
* Whether you can use swipe gestures to open or close the drawer. * Whether you can use swipe gestures to open or close the drawer.
* Defaults to `true`. * Defaults to `true`
* Not supported on Web.
*/ */
swipeEnabled?: boolean; swipeEnabled?: boolean;
@@ -209,14 +204,6 @@ export type DrawerNavigationProp<
> & > &
DrawerActionHelpers<ParamList>; DrawerActionHelpers<ParamList>;
export type DrawerScreenProps<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = {
navigation: DrawerNavigationProp<ParamList, RouteName>;
route: RouteProp<ParamList, RouteName>;
};
export type DrawerDescriptor = Descriptor< export type DrawerDescriptor = Descriptor<
ParamListBase, ParamListBase,
string, string,

View File

@@ -10,14 +10,14 @@ import {
StyleProp, StyleProp,
View, View,
InteractionManager, InteractionManager,
TouchableWithoutFeedback,
} from 'react-native'; } from 'react-native';
import Animated from 'react-native-reanimated';
import { import {
PanGestureHandler, PanGestureHandler,
TapGestureHandler, TapGestureHandler,
GestureState, State as GestureState,
} from './GestureHandler'; TapGestureHandlerStateChangeEvent,
} from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated';
import Overlay from './Overlay'; import Overlay from './Overlay';
const { const {
@@ -79,6 +79,7 @@ type Props = {
open: boolean; open: boolean;
onOpen: () => void; onOpen: () => void;
onClose: () => void; onClose: () => void;
onGestureRef?: (ref: PanGestureHandler | null) => void;
gestureEnabled: boolean; gestureEnabled: boolean;
swipeEnabled: boolean; swipeEnabled: boolean;
drawerPosition: 'left' | 'right'; drawerPosition: 'left' | 'right';
@@ -100,7 +101,7 @@ type Props = {
export default class DrawerView extends React.Component<Props> { export default class DrawerView extends React.Component<Props> {
static defaultProps = { static defaultProps = {
drawerPosition: I18nManager.isRTL ? 'left' : 'right', drawerPostion: I18nManager.isRTL ? 'left' : 'right',
drawerType: 'front', drawerType: 'front',
gestureEnabled: true, gestureEnabled: true,
swipeEnabled: Platform.OS !== 'web', swipeEnabled: Platform.OS !== 'web',
@@ -510,7 +511,18 @@ export default class DrawerView extends React.Component<Props> {
}, },
]); ]);
private handleTapStateChange = event([ private handleTapStateChange =
Platform.OS === 'web'
? // FIXME: Drawer doesn't close on Web with the same code that we use for native
({ nativeEvent }: TapGestureHandlerStateChangeEvent) => {
if (
nativeEvent.state === GestureState.END &&
nativeEvent.oldState === GestureState.ACTIVE
) {
this.toggleDrawer(false);
}
}
: event([
{ {
nativeEvent: { nativeEvent: {
oldState: (s: Animated.Value<number>) => oldState: (s: Animated.Value<number>) =>
@@ -567,6 +579,7 @@ export default class DrawerView extends React.Component<Props> {
sceneContainerStyle, sceneContainerStyle,
drawerStyle, drawerStyle,
overlayStyle, overlayStyle,
onGestureRef,
renderDrawerContent, renderDrawerContent,
renderSceneContent, renderSceneContent,
gestureHandlerProps, gestureHandlerProps,
@@ -611,6 +624,7 @@ export default class DrawerView extends React.Component<Props> {
return ( return (
<PanGestureHandler <PanGestureHandler
ref={onGestureRef}
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]} activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]} failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
onGestureEvent={this.handleGestureEvent} onGestureEvent={this.handleGestureEvent}
@@ -649,20 +663,12 @@ export default class DrawerView extends React.Component<Props> {
</View> </View>
{ {
// Disable overlay if sidebar is permanent // Disable overlay if sidebar is permanent
drawerType === 'permanent' ? null : Platform.OS === 'web' ? ( drawerType === 'permanent' ? null : (
<TouchableWithoutFeedback
onPress={
gestureEnabled ? () => this.toggleDrawer(false) : undefined
}
>
<Overlay progress={progress} style={overlayStyle as any} />
</TouchableWithoutFeedback>
) : (
<TapGestureHandler <TapGestureHandler
enabled={gestureEnabled} enabled={gestureEnabled}
onHandlerStateChange={this.handleTapStateChange} onHandlerStateChange={this.handleTapStateChange}
> >
<Overlay progress={progress} style={overlayStyle as any} /> <Overlay progress={progress} style={overlayStyle} />
</TapGestureHandler> </TapGestureHandler>
) )
} }
@@ -731,11 +737,6 @@ const styles = StyleSheet.create({
}, },
main: { main: {
flex: 1, flex: 1,
...Platform.select({ overflow: 'hidden',
// FIXME: We need to hide `overflowX` on Web so the translated content doesn't show offscreen.
// But adding `overflowX: 'hidden'` prevents content from collapsing the URL bar.
web: null,
default: { overflow: 'hidden' },
}),
}, },
}); });

View File

@@ -11,6 +11,10 @@ import {
} from 'react-native'; } from 'react-native';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { ScreenContainer } from 'react-native-screens'; import { ScreenContainer } from 'react-native-screens';
import {
PanGestureHandler,
GestureHandlerRootView,
} from 'react-native-gesture-handler';
import { import {
NavigationHelpersContext, NavigationHelpersContext,
DrawerNavigationState, DrawerNavigationState,
@@ -18,7 +22,7 @@ import {
useTheme, useTheme,
} from '@react-navigation/native'; } from '@react-navigation/native';
import { GestureHandlerRootView } from './GestureHandler'; import DrawerGestureContext from '../utils/DrawerGestureContext';
import SafeAreaProviderCompat from './SafeAreaProviderCompat'; import SafeAreaProviderCompat from './SafeAreaProviderCompat';
import ResourceSavingScene from './ResourceSavingScene'; import ResourceSavingScene from './ResourceSavingScene';
import DrawerContent from './DrawerContent'; import DrawerContent from './DrawerContent';
@@ -90,6 +94,8 @@ export default function DrawerView({
Dimensions.get('window') Dimensions.get('window')
); );
const drawerGestureRef = React.useRef<PanGestureHandler>(null);
const { colors } = useTheme(); const { colors } = useTheme();
const isDrawerOpen = state.history.some((it) => it.type === 'drawer'); const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
@@ -199,6 +205,7 @@ export default function DrawerView({
<NavigationHelpersContext.Provider value={navigation}> <NavigationHelpersContext.Provider value={navigation}>
<GestureHandlerWrapper style={styles.content}> <GestureHandlerWrapper style={styles.content}>
<SafeAreaProviderCompat> <SafeAreaProviderCompat>
<DrawerGestureContext.Provider value={drawerGestureRef}>
<DrawerOpenContext.Provider value={isDrawerOpen}> <DrawerOpenContext.Provider value={isDrawerOpen}>
<Drawer <Drawer
open={isDrawerOpen} open={isDrawerOpen}
@@ -206,6 +213,10 @@ export default function DrawerView({
swipeEnabled={swipeEnabled} swipeEnabled={swipeEnabled}
onOpen={handleDrawerOpen} onOpen={handleDrawerOpen}
onClose={handleDrawerClose} onClose={handleDrawerClose}
onGestureRef={(ref) => {
// @ts-ignore
drawerGestureRef.current = ref;
}}
gestureHandlerProps={gestureHandlerProps} gestureHandlerProps={gestureHandlerProps}
drawerType={drawerType} drawerType={drawerType}
drawerPosition={drawerPosition} drawerPosition={drawerPosition}
@@ -238,9 +249,11 @@ export default function DrawerView({
renderDrawerContent={renderNavigationView} renderDrawerContent={renderNavigationView}
renderSceneContent={renderContent} renderSceneContent={renderContent}
keyboardDismissMode={keyboardDismissMode} keyboardDismissMode={keyboardDismissMode}
drawerPostion={drawerPosition}
dimensions={dimensions} dimensions={dimensions}
/> />
</DrawerOpenContext.Provider> </DrawerOpenContext.Provider>
</DrawerGestureContext.Provider>
</SafeAreaProviderCompat> </SafeAreaProviderCompat>
</GestureHandlerWrapper> </GestureHandlerWrapper>
</NavigationHelpersContext.Provider> </NavigationHelpersContext.Provider>

View File

@@ -1,23 +0,0 @@
import * as React from 'react';
import {
PanGestureHandler as PanGestureHandlerNative,
PanGestureHandlerProperties,
} from 'react-native-gesture-handler';
import DrawerGestureContext from '../utils/DrawerGestureContext';
export function PanGestureHandler(props: PanGestureHandlerProperties) {
const gestureRef = React.useRef<PanGestureHandlerNative>(null);
return (
<DrawerGestureContext.Provider value={gestureRef}>
<PanGestureHandlerNative {...props} />
</DrawerGestureContext.Provider>
);
}
export {
GestureHandlerRootView,
TapGestureHandler,
State as GestureState,
PanGestureHandlerGestureEvent,
} from 'react-native-gesture-handler';

View File

@@ -1,31 +0,0 @@
import * as React from 'react';
import { View } from 'react-native';
import type {
PanGestureHandlerProperties,
TapGestureHandlerProperties,
} from 'react-native-gesture-handler';
const Dummy: any = ({ children }: { children: React.ReactNode }) => (
<>{children}</>
);
export const PanGestureHandler = Dummy as React.ComponentType<
PanGestureHandlerProperties
>;
export const TapGestureHandler = Dummy as React.ComponentType<
TapGestureHandlerProperties
>;
export const GestureHandlerRootView = View;
export const GestureState = {
UNDETERMINED: 0,
FAILED: 1,
BEGAN: 2,
CANCELLED: 3,
ACTIVE: 4,
END: 5,
};
export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';

View File

@@ -29,7 +29,7 @@ export default class ResourceSavingScene extends React.Component<Props> {
styles.container, styles.container,
Platform.OS === 'web' Platform.OS === 'web'
? { display: isVisible ? 'flex' : 'none' } ? { display: isVisible ? 'flex' : 'none' }
: { overflow: 'hidden' }, : null,
style, style,
]} ]}
collapsable={false} collapsable={false}
@@ -52,6 +52,7 @@ export default class ResourceSavingScene extends React.Component<Props> {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
overflow: 'hidden',
}, },
attached: { attached: {
flex: 1, flex: 1,

View File

@@ -3,144 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.9...@react-navigation/material-bottom-tabs@5.2.10) (2020-06-06)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.8...@react-navigation/material-bottom-tabs@5.2.9) (2020-05-27)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.7...@react-navigation/material-bottom-tabs@5.2.8) (2020-05-23)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.6...@react-navigation/material-bottom-tabs@5.2.7) (2020-05-20)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.5...@react-navigation/material-bottom-tabs@5.2.6) (2020-05-20)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.4...@react-navigation/material-bottom-tabs@5.2.5) (2020-05-16)
### Bug Fixes
* center icons in material tab bar. fixes [#8248](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/8248) ([51b4087](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/51b40879bdb9cea5462a2291955513a88eb87340))
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.3...@react-navigation/material-bottom-tabs@5.2.4) (2020-05-14)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.2...@react-navigation/material-bottom-tabs@5.2.3) (2020-05-14)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.1...@react-navigation/material-bottom-tabs@5.2.2) (2020-05-10)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.2.0...@react-navigation/material-bottom-tabs@5.2.1) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.15...@react-navigation/material-bottom-tabs@5.2.0) (2020-05-08)
### Features
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/issues/7971)
* use links in bottom navigation tabs ([f384706](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/commit/f384706741f7e2422c284b65da10425f7af680c0))
## [5.1.15](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.14...@react-navigation/material-bottom-tabs@5.1.15) (2020-05-05)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.14](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.13...@react-navigation/material-bottom-tabs@5.1.14) (2020-05-01)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.13](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.12...@react-navigation/material-bottom-tabs@5.1.13) (2020-05-01)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.12](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.11...@react-navigation/material-bottom-tabs@5.1.12) (2020-04-30)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.11](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.10...@react-navigation/material-bottom-tabs@5.1.11) (2020-04-30)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.9...@react-navigation/material-bottom-tabs@5.1.10) (2020-04-27) ## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.9...@react-navigation/material-bottom-tabs@5.1.10) (2020-04-27)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs **Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/material-bottom-tabs", "name": "@react-navigation/material-bottom-tabs",
"description": "Integration for bottom navigation component from react-native-paper", "description": "Integration for bottom navigation component from react-native-paper",
"version": "5.2.10", "version": "5.1.10",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -20,13 +20,11 @@
"homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator.html", "homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator.html",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
@@ -37,15 +35,15 @@
"clean": "del lib" "clean": "del lib"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.5.1", "@react-navigation/native": "^5.1.7",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"@types/react-native": "^0.62.7", "@types/react-native": "^0.61.22",
"@types/react-native-vector-icons": "^6.4.5", "@types/react-native-vector-icons": "^6.4.5",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-native": "~0.61.5", "react-native": "~0.61.5",
"react-native-paper": "^3.10.1", "react-native-paper": "^3.7.0",
"react-native-vector-icons": "^6.6.0", "react-native-vector-icons": "^6.6.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },

View File

@@ -14,5 +14,4 @@ export { default as MaterialBottomTabView } from './views/MaterialBottomTabView'
export type { export type {
MaterialBottomTabNavigationOptions, MaterialBottomTabNavigationOptions,
MaterialBottomTabNavigationProp, MaterialBottomTabNavigationProp,
MaterialBottomTabScreenProps,
} from './types'; } from './types';

View File

@@ -6,7 +6,6 @@ import {
NavigationHelpers, NavigationHelpers,
TabNavigationState, TabNavigationState,
TabActionHelpers, TabActionHelpers,
RouteProp,
} from '@react-navigation/native'; } from '@react-navigation/native';
export type MaterialBottomTabNavigationEventMap = { export type MaterialBottomTabNavigationEventMap = {
@@ -33,14 +32,6 @@ export type MaterialBottomTabNavigationProp<
> & > &
TabActionHelpers<ParamList>; TabActionHelpers<ParamList>;
export type MaterialBottomTabScreenProps<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = {
navigation: MaterialBottomTabNavigationProp<ParamList, RouteName>;
route: RouteProp<ParamList, RouteName>;
};
export type MaterialBottomTabNavigationOptions = { export type MaterialBottomTabNavigationOptions = {
/** /**
* Title text for the screen. * Title text for the screen.

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { StyleSheet, Platform } from 'react-native'; import { StyleSheet } from 'react-native';
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper'; import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { import {
@@ -8,8 +8,6 @@ import {
TabNavigationState, TabNavigationState,
TabActions, TabActions,
useTheme, useTheme,
useLinkBuilder,
Link,
} from '@react-navigation/native'; } from '@react-navigation/native';
import { import {
@@ -26,14 +24,13 @@ type Props = MaterialBottomTabNavigationConfig & {
type Scene = { route: { key: string } }; type Scene = { route: { key: string } };
function MaterialBottomTabViewInner({ export default function MaterialBottomTabView({
state, state,
navigation, navigation,
descriptors, descriptors,
...rest ...rest
}: Props) { }: Props) {
const { dark, colors } = useTheme(); const { dark, colors } = useTheme();
const buildLink = useLinkBuilder();
const theme = React.useMemo(() => { const theme = React.useMemo(() => {
const t = dark ? DarkTheme : DefaultTheme; const t = dark ? DarkTheme : DefaultTheme;
@@ -49,6 +46,7 @@ function MaterialBottomTabViewInner({
}, [colors, dark]); }, [colors, dark]);
return ( return (
<NavigationHelpersContext.Provider value={navigation}>
<BottomNavigation <BottomNavigation
{...rest} {...rest}
theme={theme} theme={theme}
@@ -60,39 +58,6 @@ function MaterialBottomTabViewInner({
}) })
} }
renderScene={({ route }) => descriptors[route.key].render()} renderScene={({ route }) => descriptors[route.key].render()}
renderTouchable={
Platform.OS === 'web'
? ({
onPress,
route,
accessibilityRole: _0,
borderless: _1,
centered: _2,
rippleColor: _3,
style,
...rest
}) => {
return (
<Link
{...rest}
// @ts-ignore
to={buildLink(route.name, route.params)}
accessibilityRole="link"
onPress={(e: any) => {
if (
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
(e.button == null || e.button === 0) // ignore everything but left clicks
) {
e.preventDefault();
onPress?.(e);
}
}}
style={[styles.touchable, style]}
/>
);
}
: undefined
}
renderIcon={({ route, focused, color }) => { renderIcon={({ route, focused, color }) => {
const { options } = descriptors[route.key]; const { options } = descriptors[route.key];
@@ -103,6 +68,8 @@ function MaterialBottomTabViewInner({
color={color} color={color}
size={24} size={24}
style={styles.icon} style={styles.icon}
importantForAccessibility="no-hide-descendants"
accessibilityElementsHidden
/> />
); );
} }
@@ -140,13 +107,6 @@ function MaterialBottomTabViewInner({
} }
}} }}
/> />
);
}
export default function MaterialBottomTabView(props: Props) {
return (
<NavigationHelpersContext.Provider value={props.navigation}>
<MaterialBottomTabViewInner {...props} />
</NavigationHelpersContext.Provider> </NavigationHelpersContext.Provider>
); );
} }
@@ -155,8 +115,4 @@ const styles = StyleSheet.create({
icon: { icon: {
backgroundColor: 'transparent', backgroundColor: 'transparent',
}, },
touchable: {
display: 'flex',
justifyContent: 'center',
},
}); });

View File

@@ -3,140 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.9...@react-navigation/material-top-tabs@5.2.10) (2020-06-06)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.9](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.8...@react-navigation/material-top-tabs@5.2.9) (2020-05-27)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.8](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.7...@react-navigation/material-top-tabs@5.2.8) (2020-05-23)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.7](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.6...@react-navigation/material-top-tabs@5.2.7) (2020-05-20)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.5...@react-navigation/material-top-tabs@5.2.6) (2020-05-20)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.4...@react-navigation/material-top-tabs@5.2.5) (2020-05-16)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.3...@react-navigation/material-top-tabs@5.2.4) (2020-05-14)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.2...@react-navigation/material-top-tabs@5.2.3) (2020-05-14)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.1...@react-navigation/material-top-tabs@5.2.2) (2020-05-10)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.2.0...@react-navigation/material-top-tabs@5.2.1) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.15...@react-navigation/material-top-tabs@5.2.0) (2020-05-08)
### Features
* add generic type aliases for screen props ([bea14aa](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/commit/bea14aa26fd5cbfebc7973733c5cf1f44fd323aa)), closes [#7971](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/issues/7971)
## [5.1.15](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.14...@react-navigation/material-top-tabs@5.1.15) (2020-05-05)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.14](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.13...@react-navigation/material-top-tabs@5.1.14) (2020-05-01)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.13](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.12...@react-navigation/material-top-tabs@5.1.13) (2020-05-01)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.12](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.11...@react-navigation/material-top-tabs@5.1.12) (2020-04-30)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.11](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.10...@react-navigation/material-top-tabs@5.1.11) (2020-04-30)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.9...@react-navigation/material-top-tabs@5.1.10) (2020-04-27) ## [5.1.10](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.9...@react-navigation/material-top-tabs@5.1.10) (2020-04-27)
**Note:** Version bump only for package @react-navigation/material-top-tabs **Note:** Version bump only for package @react-navigation/material-top-tabs

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/material-top-tabs", "name": "@react-navigation/material-top-tabs",
"description": "Integration for the animated tab view component from react-native-tab-view", "description": "Integration for the animated tab view component from react-native-tab-view",
"version": "5.2.10", "version": "5.1.10",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -20,13 +20,11 @@
"homepage": "https://reactnavigation.org/docs/material-top-tab-navigator.html", "homepage": "https://reactnavigation.org/docs/material-top-tab-navigator.html",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
@@ -40,15 +38,15 @@
"color": "^3.1.2" "color": "^3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.5.1", "@react-navigation/native": "^5.1.7",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"@types/react-native": "^0.62.7", "@types/react-native": "^0.61.22",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-native": "~0.61.5", "react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0", "react-native-gesture-handler": "^1.6.0",
"react-native-reanimated": "^1.8.0", "react-native-reanimated": "^1.7.0",
"react-native-tab-view": "^2.14.0", "react-native-tab-view": "^2.14.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },

View File

@@ -15,7 +15,6 @@ export { default as MaterialTopTabBar } from './views/MaterialTopTabBar';
export type { export type {
MaterialTopTabNavigationOptions, MaterialTopTabNavigationOptions,
MaterialTopTabNavigationProp, MaterialTopTabNavigationProp,
MaterialTopTabScreenProps,
MaterialTopTabBarProps, MaterialTopTabBarProps,
MaterialTopTabBarOptions, MaterialTopTabBarOptions,
} from './types'; } from './types';

View File

@@ -8,7 +8,6 @@ import {
NavigationProp, NavigationProp,
TabNavigationState, TabNavigationState,
TabActionHelpers, TabActionHelpers,
RouteProp,
} from '@react-navigation/native'; } from '@react-navigation/native';
export type MaterialTopTabNavigationEventMap = { export type MaterialTopTabNavigationEventMap = {
@@ -47,14 +46,6 @@ export type MaterialTopTabNavigationProp<
> & > &
TabActionHelpers<ParamList>; TabActionHelpers<ParamList>;
export type MaterialTopTabScreenProps<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = {
navigation: MaterialTopTabNavigationProp<ParamList, RouteName>;
route: RouteProp<ParamList, RouteName>;
};
export type MaterialTopTabNavigationOptions = { export type MaterialTopTabNavigationOptions = {
/** /**
* Title text for the screen. * Title text for the screen.

View File

@@ -3,184 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.5.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.5.0...@react-navigation/native@5.5.1) (2020-06-06)
**Note:** Version bump only for package @react-navigation/native
# [5.5.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.3...@react-navigation/native@5.5.0) (2020-05-27)
### Bug Fixes
* export types from /native ([af1722d](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/af1722d1e915f3ec234df202f74c4b4c631472c7))
### Features
* add a `ServerContainer` component for SSR ([#8297](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8297)) ([68e750d](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/68e750d5a6d198a2f5bdb86ba631de0a27732943))
* add ref to get current options in `ServerContainer` ([#8333](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8333)) ([0b1a718](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/0b1a718756e208d84b20e45ca56004332308ad54))
## [5.4.3](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.2...@react-navigation/native@5.4.3) (2020-05-23)
**Note:** Version bump only for package @react-navigation/native
## [5.4.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.1...@react-navigation/native@5.4.2) (2020-05-20)
**Note:** Version bump only for package @react-navigation/native
## [5.4.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.4.0...@react-navigation/native@5.4.1) (2020-05-20)
**Note:** Version bump only for package @react-navigation/native
# [5.4.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.3.2...@react-navigation/native@5.4.0) (2020-05-16)
### Bug Fixes
* fix types for linking options ([d14f38b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/d14f38b80ad569d5828c1919cea426c659173924))
### Features
* add a PathConfig type ([60cb3c9](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/60cb3c9ba76d7ef166c9fe8b55f23728975b5b6e))
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.3.1...@react-navigation/native@5.3.2) (2020-05-14)
**Note:** Version bump only for package @react-navigation/native
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.3.0...@react-navigation/native@5.3.1) (2020-05-14)
**Note:** Version bump only for package @react-navigation/native
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.6...@react-navigation/native@5.3.0) (2020-05-10)
### Features
* initialState should take priority over deep link ([039017b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/039017bc2af69120d2d10e8f2c8a62919c37eb65))
## [5.2.6](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.5...@react-navigation/native@5.2.6) (2020-05-08)
### Bug Fixes
* fix building typescript definitions. closes [#8216](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8216) ([47a1229](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/47a12298378747edd2d22e54dc1c8677f98c49b4))
## [5.2.5](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.4...@react-navigation/native@5.2.5) (2020-05-08)
### Bug Fixes
* return a promise-like from getInitialState ([#8210](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8210)) ([85ae378](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/85ae378d8cb1073895b281e13ebccee881d4c062))
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.3...@react-navigation/native@5.2.4) (2020-05-05)
### Bug Fixes
* return undefined for buildLink if linking is not enabled ([9fd2635](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/9fd2635756362c8da79656b4d9b101bebaaf7003))
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.2...@react-navigation/native@5.2.3) (2020-05-01)
### Bug Fixes
* default linking enabled to true ([c7b8e2e](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/c7b8e2e9666733143eef156b27f3e4995c36b856))
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.1...@react-navigation/native@5.2.2) (2020-05-01)
### Bug Fixes
* don't throw when using 'useLinking'. fixes [#8171](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8171) ([10eca8b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/10eca8b92edbce6dbef8abaf189e4b59a29b3748))
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.2.0...@react-navigation/native@5.2.1) (2020-04-30)
### Bug Fixes
* render fallback only if linking is enabled. closes [#8161](https://github.com/react-navigation/react-navigation/tree/master/packages/native/issues/8161) ([1c075ff](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/1c075ffb169d233ed0515efea264a5a69b4de52e))
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.7...@react-navigation/native@5.2.0) (2020-04-30)
### Bug Fixes
* add catch to thenable returned by getInitialState ([d6fa279](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/d6fa279d9371c7a6403d10d209a2a64147891c63))
* return onPress instead of onClick for useLinkProps ([ae5442e](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/ae5442ebe812b91fa1f12164f27d1aeed918ab0e))
### Features
* add `useLinkBuilder` hook to build links ([2792f43](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/2792f438fe45428fe193e3708fee7ad61966cbf4))
* add a useLinkProps hook ([f2291d1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/f2291d110faa2aa8e10c9133c1c0c28d54af7917))
* add action prop to Link ([942d2be](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/942d2be2c72720469475ce12ec8df23825994dbf))
* add Link component as useLinkTo hook for navigating to links ([2573b5b](https://github.com/react-navigation/react-navigation/tree/master/packages/native/commit/2573b5beaac1240434e52f3f57bb29da2f541c88))
## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.6...@react-navigation/native@5.1.7) (2020-04-27) ## [5.1.7](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.6...@react-navigation/native@5.1.7) (2020-04-27)
**Note:** Version bump only for package @react-navigation/native **Note:** Version bump only for package @react-navigation/native

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/native", "name": "@react-navigation/native",
"description": "React Native integration for React Navigation", "description": "React Native integration for React Navigation",
"version": "5.5.1", "version": "5.1.7",
"keywords": [ "keywords": [
"react-native", "react-native",
"react-navigation", "react-navigation",
@@ -16,13 +16,11 @@
"homepage": "https://reactnavigation.org", "homepage": "https://reactnavigation.org",
"main": "lib/commonjs/index.js", "main": "lib/commonjs/index.js",
"react-native": "src/index.tsx", "react-native": "src/index.tsx",
"source": "src/index.tsx",
"module": "lib/module/index.js", "module": "lib/module/index.js",
"types": "lib/typescript/src/index.d.ts", "types": "lib/typescript/src/index.d.ts",
"files": [ "files": [
"src", "src",
"lib", "lib"
"!**/__tests__"
], ],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
@@ -33,19 +31,16 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/core": "^5.10.0", "@react-navigation/core": "^5.3.5"
"nanoid": "^3.1.9"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.14.3", "@react-native-community/bob": "^0.10.0",
"@types/react": "^16.9.34", "@types/react": "^16.9.23",
"@types/react-dom": "^16.9.8", "@types/react-native": "^0.61.22",
"@types/react-native": "^0.62.7",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "~16.9.0", "react": "~16.9.0",
"react-dom": "^16.13.1",
"react-native": "~0.61.5", "react-native": "~0.61.5",
"react-native-testing-library": "^1.13.2", "react-native-testing-library": "^1.12.0",
"typescript": "^3.8.3" "typescript": "^3.8.3"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Text, TextProps, GestureResponderEvent, Platform } from 'react-native'; import { Text, TextProps, GestureResponderEvent } from 'react-native';
import { NavigationAction } from '@react-navigation/core'; import { NavigationAction } from '@react-navigation/core';
import useLinkProps from './useLinkProps'; import useLinkProps from './useLinkProps';
@@ -28,15 +28,16 @@ export default function Link({ to, action, ...rest }: Props) {
rest.onPress?.(e); rest.onPress?.(e);
} }
if (props.onClick) {
props.onClick(e);
} else {
props.onPress(e); props.onPress(e);
}
}; };
return React.createElement(Text, { return React.createElement(Text, {
...props, ...props,
...rest, ...rest,
...Platform.select({ ...(props.onClick ? { onClick: onPress } : { onPress }),
web: { onClick: onPress } as any,
default: { onPress },
}),
}); });
} }

View File

@@ -22,7 +22,7 @@ type Props = NavigationContainerProps & {
* Container component which holds the navigation state designed for React Native apps. * Container component which holds the navigation state designed for React Native apps.
* This should be rendered at the root wrapping the whole app. * This should be rendered at the root wrapping the whole app.
* *
* @param props.initialState Initial state object for the navigation tree. When deep link handling is enabled, this will override deep links when specified. Make sure that you don't specify an `initialState` when there's a deep link (`Linking.getInitialURL()`). * @param props.initialState Initial state object for the navigation tree. When deep link handling is enabled, this will be ignored if there's an incoming link.
* @param props.onStateChange Callback which is called with the latest navigation state when it changes. * @param props.onStateChange Callback which is called with the latest navigation state when it changes.
* @param props.theme Theme object for the navigators. * @param props.theme Theme object for the navigators.
* @param props.linking Options for deep linking. Deep link handling is enabled when this prop is provided, unless `linking.enabled` is `false`. * @param props.linking Options for deep linking. Deep link handling is enabled when this prop is provided, unless `linking.enabled` is `false`.
@@ -46,13 +46,15 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
...linking, ...linking,
}); });
const [isReady, initialState] = useThenable(getInitialState); const [isReady, initialState = rest.initialState] = useThenable(
getInitialState
);
React.useImperativeHandle(ref, () => refContainer.current); React.useImperativeHandle(ref, () => refContainer.current);
const linkingContext = React.useMemo(() => ({ options: linking }), [linking]); const linkingContext = React.useMemo(() => ({ options: linking }), [linking]);
if (rest.initialState == null && isLinkingEnabled && !isReady) { if (!isReady) {
// This is temporary until we have Suspense for data-fetching // This is temporary until we have Suspense for data-fetching
// Then the fallback will be handled by a parent `Suspense` component // Then the fallback will be handled by a parent `Suspense` component
return fallback as React.ReactElement; return fallback as React.ReactElement;
@@ -63,9 +65,7 @@ const NavigationContainer = React.forwardRef(function NavigationContainer(
<ThemeProvider value={theme}> <ThemeProvider value={theme}>
<BaseNavigationContainer <BaseNavigationContainer
{...rest} {...rest}
initialState={ initialState={initialState}
rest.initialState == null ? initialState : rest.initialState
}
ref={refContainer} ref={refContainer}
/> />
</ThemeProvider> </ThemeProvider>

View File

@@ -1,55 +0,0 @@
import * as React from 'react';
import { CurrentRenderContext } from '@react-navigation/core';
import ServerContext, { ServerContextType } from './ServerContext';
import { ServerContainerRef } from './types';
type Props = ServerContextType & {
children: React.ReactNode;
};
/**
* Container component for server rendering.
*
* @param props.location Location object to base the initial URL for SSR.
* @param props.children Child elements to render the content.
* @param props.ref Ref object which contains helper methods.
*/
export default React.forwardRef(function ServerContainer(
{ children, location }: Props,
ref: React.Ref<ServerContainerRef>
) {
React.useEffect(() => {
console.error(
"'ServerContainer' should only be used on the server with 'react-dom/server' for SSR."
);
}, []);
const current: { options?: object } = {};
if (ref) {
const value = {
getCurrentOptions() {
return current.options;
},
};
// We write to the `ref` during render instead of `React.useImperativeHandle`
// This is because `useImperativeHandle` will update the ref after 'commit',
// and there's no 'commit' phase during SSR.
// Mutating ref during render is unsafe in concurrent mode, but we don't care about it for SSR.
if (typeof ref === 'function') {
ref(value);
} else {
// @ts-ignore: the TS types are incorrect and say that ref.current is readonly
ref.current = value;
}
}
return (
<ServerContext.Provider value={{ location }}>
<CurrentRenderContext.Provider value={current}>
{children}
</CurrentRenderContext.Provider>
</ServerContext.Provider>
);
});

View File

@@ -1,14 +0,0 @@
import * as React from 'react';
export type ServerContextType = {
location?: {
pathname: string;
search: string;
};
};
const ServerContext = React.createContext<ServerContextType | undefined>(
undefined
);
export default ServerContext;

View File

@@ -1,69 +0,0 @@
const location = new URL('', 'http://example.com');
let listeners: (() => void)[] = [];
let entries = [{ state: null, href: location.href }];
let index = 0;
let currentState: any = null;
const history = {
get state() {
return currentState;
},
pushState(state: any, _: string, path: string) {
Object.assign(location, new URL(path, location.origin));
currentState = state;
entries = entries.slice(0, index + 1);
entries.push({ state, href: location.href });
index = entries.length - 1;
},
replaceState(state: any, _: string, path: string) {
Object.assign(location, new URL(path, location.origin));
currentState = state;
entries[index] = { state, href: location.href };
},
go(n: number) {
setTimeout(() => {
if (
(n > 0 && n < entries.length - index) ||
(n < 0 && Math.abs(n) <= index)
) {
index += n;
Object.assign(location, new URL(entries[index].href));
listeners.forEach((cb) => cb);
}
}, 0);
},
back() {
this.go(-1);
},
forward() {
this.go(1);
},
};
const addEventListener = (type: 'popstate', listener: () => void) => {
if (type === 'popstate') {
listeners.push(listener);
}
};
const removeEventListener = (type: 'popstate', listener: () => void) => {
if (type === 'popstate') {
listeners = listeners.filter((cb) => cb !== listener);
}
};
export default {
location,
history,
addEventListener,
removeEventListener,
};

View File

@@ -1,149 +0,0 @@
import * as React from 'react';
import {
useNavigationBuilder,
createNavigatorFactory,
StackRouter,
TabRouter,
NavigationHelpersContext,
NavigationContainerRef,
} from '@react-navigation/core';
import { act, render } from 'react-native-testing-library';
import NavigationContainer from '../NavigationContainer';
import window from '../__mocks__/window';
// @ts-ignore
global.window = window;
// We want to use the web version of useLinking
jest.mock('../useLinking', () => require('../useLinking.tsx').default);
it('integrates with the history API', () => {
jest.useFakeTimers();
const createStackNavigator = createNavigatorFactory((props: any) => {
const { navigation, state, descriptors } = useNavigationBuilder(
StackRouter,
props
);
return (
<NavigationHelpersContext.Provider value={navigation}>
{state.routes.map((route, i) => (
<div key={route.key} aria-current={state.index === i || undefined}>
{descriptors[route.key].render()}
</div>
))}
</NavigationHelpersContext.Provider>
);
});
const createTabNavigator = createNavigatorFactory((props: any) => {
const { navigation, state, descriptors } = useNavigationBuilder(
TabRouter,
props
);
return (
<NavigationHelpersContext.Provider value={navigation}>
{state.routes.map((route, i) => (
<div key={route.key} aria-current={state.index === i || undefined}>
{descriptors[route.key].render()}
</div>
))}
</NavigationHelpersContext.Provider>
);
});
const Stack = createStackNavigator();
const Tab = createTabNavigator();
const TestScreen = ({ route }: any): any =>
`${route.name} ${JSON.stringify(route.params)}`;
const linking = {
prefixes: [],
config: {
Home: {
path: '',
initialRouteName: 'Feed',
screens: {
Profile: ':user',
Settings: 'edit',
Updates: 'updates',
Feed: 'feed',
},
},
Chat: 'chat',
},
};
const navigation = React.createRef<NavigationContainerRef>();
render(
<NavigationContainer ref={navigation} linking={linking}>
<Tab.Navigator>
<Tab.Screen name="Home">
{() => (
<Stack.Navigator initialRouteName="Feed">
<Stack.Screen name="Profile" component={TestScreen} />
<Stack.Screen name="Settings" component={TestScreen} />
<Stack.Screen name="Feed" component={TestScreen} />
<Stack.Screen name="Updates" component={TestScreen} />
</Stack.Navigator>
)}
</Tab.Screen>
<Tab.Screen name="Chat" component={TestScreen} />
</Tab.Navigator>
</NavigationContainer>
);
expect(window.location.pathname).toBe('/feed');
act(() => navigation.current?.navigate('Profile', { user: 'jane' }));
expect(window.location.pathname).toBe('/jane');
act(() => navigation.current?.navigate('Updates'));
expect(window.location.pathname).toBe('/updates');
act(() => navigation.current?.goBack());
jest.runAllTimers();
expect(window.location.pathname).toBe('/jane');
act(() => {
window.history.back();
jest.runAllTimers();
});
expect(window.location.pathname).toBe('/feed');
act(() => {
window.history.forward();
jest.runAllTimers();
});
expect(window.location.pathname).toBe('/jane');
act(() => navigation.current?.navigate('Settings'));
expect(window.location.pathname).toBe('/edit');
act(() => {
window.history.go(-2);
jest.runAllTimers();
});
expect(window.location.pathname).toBe('/feed');
act(() => navigation.current?.navigate('Settings'));
act(() => navigation.current?.navigate('Chat'));
expect(window.location.pathname).toBe('/chat');
act(() => navigation.current?.navigate('Home'));
expect(window.location.pathname).toBe('/edit');
});

View File

@@ -1,185 +0,0 @@
import * as React from 'react';
import {
useNavigationBuilder,
createNavigatorFactory,
StackRouter,
TabRouter,
NavigationHelpersContext,
} from '@react-navigation/core';
import { renderToString } from 'react-dom/server';
import NavigationContainer from '../NavigationContainer';
import ServerContainer from '../ServerContainer';
import { ServerContainerRef } from '../types';
// @ts-ignore
global.window = global;
window.addEventListener = () => {};
window.removeEventListener = () => {};
// We want to use the web version of useLinking
jest.mock('../useLinking', () => require('../useLinking.tsx').default);
it('renders correct state with location', () => {
const createStackNavigator = createNavigatorFactory((props: any) => {
const { navigation, state, descriptors } = useNavigationBuilder(
StackRouter,
props
);
return (
<NavigationHelpersContext.Provider value={navigation}>
{state.routes.map((route) => (
<div key={route.key}>{descriptors[route.key].render()}</div>
))}
</NavigationHelpersContext.Provider>
);
});
const Stack = createStackNavigator();
const TestScreen = ({ route }: any): any =>
`${route.name} ${JSON.stringify(route.params)}`;
const NestedStack = () => {
return (
<Stack.Navigator initialRouteName="Feed">
<Stack.Screen name="Profile" component={TestScreen} />
<Stack.Screen name="Settings" component={TestScreen} />
<Stack.Screen name="Feed" component={TestScreen} />
<Stack.Screen name="Updates" component={TestScreen} />
</Stack.Navigator>
);
};
const element = (
<NavigationContainer
linking={{
prefixes: [],
config: {
Home: {
initialRouteName: 'Profile',
screens: {
Settings: {
path: ':user/edit',
},
Updates: {
path: ':user/updates',
},
},
},
},
}}
>
<Stack.Navigator>
<Stack.Screen name="Home" component={NestedStack} />
<Stack.Screen name="Chat" component={TestScreen} />
</Stack.Navigator>
</NavigationContainer>
);
// @ts-ignore
window.location = { pathname: '/jane/edit', search: '' };
const client = renderToString(element);
expect(client).toMatchInlineSnapshot(
`"<div><div>Profile undefined</div><div>Settings {&quot;user&quot;:&quot;jane&quot;}</div></div>"`
);
const server = renderToString(
<ServerContainer location={{ pathname: '/john/updates', search: '' }}>
{element}
</ServerContainer>
);
expect(server).toMatchInlineSnapshot(
`"<div><div>Profile undefined</div><div>Updates {&quot;user&quot;:&quot;john&quot;}</div></div>"`
);
});
it('gets the current options', () => {
const createTabNavigator = createNavigatorFactory((props: any) => {
const { navigation, state, descriptors } = useNavigationBuilder(
TabRouter,
props
);
return (
<NavigationHelpersContext.Provider value={navigation}>
{state.routes.map((route) => (
<div key={route.key}>{descriptors[route.key].render()}</div>
))}
</NavigationHelpersContext.Provider>
);
});
const Tab = createTabNavigator();
const TestScreen = ({ route }: any): any =>
`${route.name} ${JSON.stringify(route.params)}`;
const NestedStack = () => {
return (
<Tab.Navigator initialRouteName="Feed">
<Tab.Screen
name="Profile"
component={TestScreen}
options={{ title: 'My profile' }}
/>
<Tab.Screen
name="Settings"
component={TestScreen}
options={{ title: 'Configure' }}
/>
<Tab.Screen
name="Feed"
component={TestScreen}
options={{ title: 'News feed' }}
/>
<Tab.Screen
name="Updates"
component={TestScreen}
options={{ title: 'Updates from cloud', description: 'Woah' }}
/>
</Tab.Navigator>
);
};
const ref = React.createRef<ServerContainerRef>();
renderToString(
<ServerContainer ref={ref}>
<NavigationContainer
initialState={{
routes: [
{
name: 'Others',
state: {
routes: [{ name: 'Updates' }],
},
},
],
}}
>
<Tab.Navigator>
<Tab.Screen
name="Home"
component={TestScreen}
options={{ title: 'My app' }}
/>
<Tab.Screen
name="Others"
component={NestedStack}
options={{ title: 'Other stuff' }}
/>
</Tab.Navigator>
</NavigationContainer>
</ServerContainer>
);
expect(ref.current?.getCurrentOptions()).toEqual({
title: 'Updates from cloud',
description: 'Woah',
});
});

View File

@@ -17,7 +17,7 @@ it('throws if multiple instances of useLinking are used', () => {
let element; let element;
expect(() => (element = render(<Sample />))).toThrowError( expect(() => (element = render(<Sample />))).toThrowError(
'Looks like you have configured linking in multiple places.' "Looks like you are using 'useLinking' in multiple components."
); );
// @ts-ignore // @ts-ignore
@@ -41,7 +41,9 @@ it('throws if multiple instances of useLinking are used', () => {
<B /> <B />
</> </>
)) ))
).toThrowError('Looks like you have configured linking in multiple places.'); ).toThrowError(
"Looks like you are using 'useLinking' in multiple components."
);
// @ts-ignore // @ts-ignore
element?.unmount(); element?.unmount();
@@ -55,19 +57,5 @@ it('throws if multiple instances of useLinking are used', () => {
render(wrapper2).unmount(); render(wrapper2).unmount();
expect(() => (element = render(wrapper2))).not.toThrow(); expect(() => render(wrapper2)).not.toThrow();
// @ts-ignore
element?.unmount();
function Sample3() {
useLinking(ref, options);
useLinking(ref, { ...options, enabled: false });
return null;
}
expect(() => (element = render(<Sample3 />))).not.toThrowError();
// @ts-ignore
element?.unmount();
}); });

View File

@@ -15,7 +15,3 @@ export { default as useLinking } from './useLinking';
export { default as useLinkTo } from './useLinkTo'; export { default as useLinkTo } from './useLinkTo';
export { default as useLinkProps } from './useLinkProps'; export { default as useLinkProps } from './useLinkProps';
export { default as useLinkBuilder } from './useLinkBuilder'; export { default as useLinkBuilder } from './useLinkBuilder';
export { default as ServerContainer } from './ServerContainer';
export * from './types';

Some files were not shown because too many files have changed in this diff Show More