diff --git a/.eslintignore b/.eslintignore index 34541f1a98..ec5a73f56f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ index.d.ts scripts/ +/plugin/build \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index c86edfece7..620ff4b205 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { root: true, parser: 'babel-eslint', - plugins: ['react', 'react-native', 'prettier', 'fp', 'import'], + plugins: ['react', 'react-native', 'fp', 'import', 'prettier'], env: { jest: true, }, @@ -86,4 +86,73 @@ module.exports = { ], 'fp/no-mutating-methods': 'warn', }, + overrides: [ + // Match TypeScript Files + // ================================= + { + files: ['**/*.{ts,tsx}'], + + // Global ESLint Settings + // ================================= + env: { + jest: true, + es6: true, + browser: true, + node: true, + }, + globals: { + __DEV__: true, + element: true, + by: true, + waitFor: true, // detox e2e + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + }, + react: { + version: 'detect', // React version. "detect" automatically picks the version you have installed. + // You can also use `16.0`, `16.3`, etc, if you want to override the detected value. + // default to latest and warns if missing + // It will default to "detect" in the future + }, + }, + + // Parser Settings + parser: '@typescript-eslint/parser', + parserOptions: { + // Lint with Type Information + // https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TYPED_LINTING.md + tsconfigRootDir: __dirname, + project: './tsconfig.json', + ecmaFeatures: { + experimentalObjectRestSpread: true, + jsx: true, + }, + sourceType: 'module', + }, + + // Extend Other Configs + // ================================= + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:react-native/all', + 'eslint:recommended', + 'plugin:react/recommended', + 'prettier', + ], + plugins: ['react', 'react-hooks', '@typescript-eslint', 'prettier'], + rules: { + // turn these one to check where all the return types are missing + // and where arguments of functions are not typed + '@typescript-eslint/explicit-function-return-type': ['error'], + '@typescript-eslint/explicit-module-boundary-types': ['error'], + 'no-use-before-define': 'off', + '@typescript-eslint/no-use-before-define': ['warn'], + 'react/prop-types': 'off', + }, + }, + ], }; diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 519f9eec53..d7c8610391 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,30 +7,38 @@ assignees: '' --- + +--------- + 🚨🚨🚨 * Please respect and fill out the issue template * Before you report, please make sure you tested on a physical device * For build issues: Can you reproduce it on a clean install of the example app? Please include full steps to reproduce from `react-native init` * Please include standalone code sample - a single component with one MapView in it. Use [one of our example](https://github.com/react-native-mapbox-gl/maps/blob/master/example/src/examples/PointInMapView.js) screens as a starging point. -* Use gitter and/or stack overflow for questions. +* Use [discussions](https://github.com/react-native-mapbox-gl/maps/discussions) or gitter and/or stack overflow for questions. If you want others to spend time on your issue, please make sure to first spend some time on the ticket. Not following the above will lead to the ticket being closed. -Thanks for understanding. Please understand that the project is run by volunteers on their own free time. +Thanks for understanding. +Please understand that the project is run by volunteers on their own free time. -🚨🚨🚨 +🚨🚨🚨 +--------- -**Describe the bug** +**Describe the bug** A clear and concise description of what the bug is. -**To Reproduce** +**To Reproduce** Steps to reproduce the behavior. -Please include a single standalone React Native component. Use [one of our example](https://github.com/react-native-mapbox-gl/maps/blob/master/example/src/examples/BugReportTemplate.js) screens as a starting point. -Please simplify the example as much as possible. +Please include a single standalone React Native component. +Use [our BugReportTemplate](https://github.com/react-native-mapbox-gl/maps/blob/master/example/src/examples/BugReportTemplate.js) screens as a starting point. +Please simplify the example as much as possible! + +Chances that a bug report will be investiagete and worked on are exponetially higher with a complete and _working_ repro BugTemplate! Example: ```js @@ -76,19 +84,24 @@ npm install react-native-mapbox-gl/maps#master --save react-native run-android ``` -**Expected behavior** +**Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** +**Actual behavior** +A clear and concise description of what is currently happening. + +**Screenshots** If applicable, add screenshots to help explain your problem. -**Versions (please complete the following information):** +**Versions (please complete the following information):** - Platform: [e.g. Android, iOS] + - Platform OS: [e.g. Android 9, iOS 10] - Device: [e.g. iPhone6] - Emulator/ Simulator: [yes/ no] - - OS: [e.g. iOS8.1] + - Dev OS: [e.g. OSX 11.0.1, Win10] - react-native-mapbox-gl Version [e.g. 7.0.9] + - Mapbox GL version [e.g. 6.3.0] - React Native Version [e.g. 0.59] -**Additional context** +**Additional context** Add any other context about the problem here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..6b67aca195 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,42 @@ +version: 2 +updates: + # root + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "monthly" + labels: + - "πŸ€– dependabot πŸ€–" + open-pull-requests-limit: 99 + pull-request-branch-name: + separator: "-" + ignore: # ignore updates for react-native deps, think about updating, once react-native is updated + - dependency-name: "@babel/core" + - dependency-name: "@babel/runtime" + - dependency-name: "@react-native-community/eslint-config" + - dependency-name: "eslint" + - dependency-name: "jest" + - dependency-name: "metro-react-native-babel-preset" + - dependency-name: "react-test-renderer" + - dependency-name: "react" + - dependency-name: "react-native" + - dependency-name: "jest-cli" + # example + - package-ecosystem: "npm" + directory: "/example" + schedule: + interval: "monthly" + labels: + - "πŸ€– dependabot πŸ€–" + open-pull-requests-limit: 99 + pull-request-branch-name: + separator: "-" + # gh-actions workflow files + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + pull-request-branch-name: + separator: "-" + labels: + - "πŸ€– dependabot πŸ€–" diff --git a/.github/label-actions.yml b/.github/label-actions.yml index 6d30136cf4..f765d9007d 100644 --- a/.github/label-actions.yml +++ b/.github/label-actions.yml @@ -1,15 +1,17 @@ # Configuration for Label Actions - https://github.com/dessant/label-actions +"Needs: Issue Template": + comment: > + :wave: @{issue-author}, please respect our issue template
required fields are missing. + # Close the issue + close: true +"question": + comment: > + :thinking: @{issue-author}, this is rather a question than an issue
please use our [discussions](https://github.com/react-native-mapbox-gl/maps/discussions) or [gitter](https://gitter.im/react-native-mapbox-gl/Lobby) or stackoverflow for this. + # Close the issue + close: true +"stale": + comment: > + :thinking: @{issue-author}, closing the issue for lack of activity, if the issue still persist, pls open a new one with steps to reproduce on recent versions + # Close the issue + close: true -issues: - actions: - # our issue template is not respected - required fields are missing - "Needs: Issue Template": - comment: > - :wave: @{issue-author}, please respect our issue template
required fields are missing. - # Close the issue - close: true - "question": - comment: > - :thinking: @{issue-author}, this is rather a question than an issue
please use our [gitter](https://gitter.im/react-native-mapbox-gl/Lobby) or stackoverflow for this. - # Close the issue - close: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..a6a125d536 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ + + +## Description + +Fixes # + + + +Added `your feature` that allows ... + +## Checklist + + + +- [ ] I have tested this on a device/simulator for each compatible OS +- [ ] I formatted JS and TS files with running `yarn lint:fix` in the root folder +- [ ] I updated the documentation with running `yarn generate` in the root folder +- [ ] I mentioned this change in `CHANGELOG.md` +- [ ] I updated the typings files (`index.d.ts`) +- [ ] I added/ updated a sample (`/example`) + +## Screenshot OR Video + + diff --git a/.github/workflows/android-actions.yml b/.github/workflows/android-actions.yml new file mode 100644 index 0000000000..40baae0f7d --- /dev/null +++ b/.github/workflows/android-actions.yml @@ -0,0 +1,40 @@ +name: Android Build + +on: + workflow_call: + inputs: + NVMRC: + required: true + type: string + secrets: + MAPBOX_ACCESS_TOKEN: + required: true + +jobs: + build_example: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup node ${{ inputs.NVMRC }} + uses: actions/setup-node@v2.5.0 + with: + node-version: ${{ inputs.NVMRC }} + + - name: Setup JDK zulu 11 + uses: actions/setup-java@v2.4.0 + with: + distribution: 'zulu' + java-version: '11' + + - run: echo $MAPBOX_ACCESS_TOKEN > ./accesstoken + working-directory: example + env: + MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} + + - run: yarn install --network-timeout 1000000 + working-directory: example + + - run: ./gradlew assemble + working-directory: example/android diff --git a/.github/workflows/ios-actions.yml b/.github/workflows/ios-actions.yml new file mode 100644 index 0000000000..9b8109f51f --- /dev/null +++ b/.github/workflows/ios-actions.yml @@ -0,0 +1,51 @@ +name: iOS Build & Detox + +on: + workflow_call: + inputs: + NVMRC: + required: true + type: string + secrets: + MAPBOX_ACCESS_TOKEN: + required: true + +jobs: + build: + runs-on: macos-latest + timeout-minutes: 25 + + defaults: + run: + working-directory: ./example + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Access Token + run: echo $MAPBOX_ACCESS_TOKEN > ./accesstoken + env: + MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} + + - name: Setup node ${{ inputs.NVMRC }} + uses: actions/setup-node@v2.5.0 + with: + node-version: ${{ inputs.NVMRC }} + + - name: Install Yarn Dependencies + run: yarn install --network-timeout 1000000 + + - name: Install Pod Dependencies + run: cd ios && pod install + + - name: Install Detox Dependencies + run: | + brew tap wix/brew + brew install applesimutils + + - name: Build for detox + run: yarn detox build + + - name: Test with detox + run: yarn detox test --debug-synchronization 200 diff --git a/.github/workflows/label-actions.yml b/.github/workflows/label-actions.yml new file mode 100644 index 0000000000..fea7e4670a --- /dev/null +++ b/.github/workflows/label-actions.yml @@ -0,0 +1,17 @@ +# Configuration for Label Actions - https://github.com/dessant/label-actions + +name: "Label Actions" + +on: + issues: + types: labeled + +permissions: + contents: read + issues: write + +jobs: + action: + runs-on: ubuntu-latest + steps: + - uses: dessant/label-actions@v3 diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml new file mode 100644 index 0000000000..b014baaecc --- /dev/null +++ b/.github/workflows/on-push.yml @@ -0,0 +1,71 @@ +name: On Push +on: [push] + +jobs: + lint_test_generate: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Read .nvmrc + run: echo ::set-output name=NVMRC::$(cat .nvmrc) + id: nvm + + - name: Setup node ${{ steps.nvm.outputs.NVMRC }} + uses: actions/setup-node@v2.5.0 + with: + node-version: ${{ steps.nvm.outputs.NVMRC }} + + - name: Install + run: yarn install --network-timeout 1000000 + + - name: Lint + run: yarn lint + + - name: Test + run: yarn unittest + + - name: Generate + run: yarn generate + + outputs: + NVMRC: ${{ steps.nvm.outputs.NVMRC }} + + call_android_workflow: + needs: lint_test_generate + uses: react-native-mapbox-gl/maps/.github/workflows/android-actions.yml@master + with: + NVMRC: ${{ needs.lint_test_generate.outputs.NVMRC }} + secrets: + MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} + + + call_ios_workflow: + needs: lint_test_generate + uses: react-native-mapbox-gl/maps/.github/workflows/ios-actions.yml@master + with: + NVMRC: ${{ needs.lint_test_generate.outputs.NVMRC }} + secrets: + MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} + + publish: + if: startsWith(github.ref, 'refs/tags/') + needs: [lint_test_generate, call_android_workflow, call_ios_workflow] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup node ${{ steps.nvm.outputs.NVMRC }} + uses: actions/setup-node@v2.5.0 + with: + node-version: ${{ needs.lint_test_generate.outputs.NVMRC }} + registry-url: https://registry.npmjs.org/ + + - name: Install and Publish + run: npm install --force && npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} diff --git a/.gitignore b/.gitignore index 20dc4030cb..811366a864 100644 --- a/.gitignore +++ b/.gitignore @@ -121,3 +121,6 @@ coverage *.iml react-native-mapbox-gl-maps.tgz + +# Config plugin +/plugin/build \ No newline at end of file diff --git a/.npmignore b/.npmignore index eb03bb362b..8b93d2edfb 100644 --- a/.npmignore +++ b/.npmignore @@ -42,3 +42,7 @@ android/local.properties example __tests__ coverage + +plugin/src +plugin/jest.config.js +plugin/tsconfig.json diff --git a/.nvmrc b/.nvmrc index ebf08b0645..ad122c64e7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1,2 +1,2 @@ -v10.18.1 +v14.17.0 diff --git a/example/.prettierrc.js b/.prettierrc.js similarity index 83% rename from example/.prettierrc.js rename to .prettierrc.js index 5c4de1a4f6..84196d95f4 100644 --- a/example/.prettierrc.js +++ b/.prettierrc.js @@ -3,4 +3,5 @@ module.exports = { jsxBracketSameLine: true, singleQuote: true, trailingComma: 'all', + arrowParens: 'avoid', }; diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c47ef4b030..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,88 +0,0 @@ -matrix: - include: - - language: android - jdk: oraclejdk8 - before_install: - - nvm install 10.22.1 - - echo yes | sdkmanager "platforms;android-28" - - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 - - export PATH=$HOME/.yarn/bin:$PATH - android: - components: - - tools - - platform-tools - - build-tools-28.0.3 - - tools - cache: - bundler: true - yarn: true - directories: - - example/node_modules - - "$HOME/.gradle/caches/" - - "$HOME/.gradle/wrapper/" - before_cache: - - rm -f example/node_modules/\@react-native-mapbox-gl/ - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - - os: osx - osx_image: xcode12.2 - node_js: 10.22.1 - cache: - bundler: true - yarn: true - cocoapods: true - directories: - - example/node_modules - - "$HOME/Library/Caches/Homebrew" - before_cache: - - rm -f example/node_modules/\@react-native-mapbox-gl/ - - brew cleanup - podfile: example/ios/Podfile -before_install: -- nvm use 10.22.1 -- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 -- export PATH=$HOME/.yarn/bin:$PATH -install: -- cd $TRAVIS_BUILD_DIR/example -- echo $MAPBOX_ACCESS_TOKEN > ./accesstoken -- find node_modules -name ".git" -exec rm -r "{}" \; || true -- rm -rf example/node_modules/\@react-native-mapbox-gl/ -- yarn install --ignore-engines -- echo $TRAVIS_OS_NAME -- | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - cd $TRAVIS_BUILD_DIR/example/ios - pod install - pod update - gem install xcpretty - fi -script: -- | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - cd $TRAVIS_BUILD_DIR/example/ios - set -o pipefail - rm -f build - xcodebuild -arch x86_64 -sdk iphonesimulator14.2 -workspace ./RNMapboxGLExample.xcworkspace -scheme RNMapboxGLExample | xcpretty -c - fi -- | - if [ "$TRAVIS_OS_NAME" == "linux" ]; then - cd $TRAVIS_BUILD_DIR/example/android - TERM=dumb ./gradlew assemble - fi -- cd $TRAVIS_BUILD_DIR -- yarn global add -g 'jest@24.8.0' -- yarn install --ignore-engines -- yarn run unittest -- yarn run generate -deploy: - provider: npm - edge: true - email: kristfallro@gmail.com - api_key: - secure: XP74gf8AAWkjMRBYzOQcSbU5uLSQA5Y7UYHjTPd+At/nBuf/38hkiZ/oOkJUNSVtc76zfCdnLRjXRFgGNYYH4XPe9vviaENWs0ykrT8H0OBhkKARF7w/R55uZF9IByOyOqnl2VviGgXHIJNCjZCr+r1Nm3nenfNdyEFfR6RT/5ZilsoZbvfZ/fs74HH3NE27q4T+mdgF9hgDOVO+p1tfGn0P7yGUbRhytQhMWkyeFnaYnu4kNv/aWWIkbzwEdOg1fYcjYv7LZVxaQFmrKpMDlyoBn9K3RwWgKX0mVR/IHfM0U3Vz0C/s9Zym8i9nBiJuBR0dH6tBwCgu/ORL+bX5PmuHYIk/vTcvU3dh0sFmdE8z6dFAThlHvNQMy9jW7QaYeBrnzDITyQMghI9+cBgnOtL0tlMU2TeAsbyfxjfr6HvOrKJiCQCv3kL7FoXCCtdlWjHnYHBzazj35bZYzyOEFFcyNe9Qe6pVwdz4AbAF1Tw0KmpZtfJTqNl512hmEFV9J2/0qIYvX7K+dlacRLKMImAwaU3pnkS40qXCAVnmSBHd3lq3QmIXp0t+QSuqRr88FOMAV/ShJDwbT2juCs13iLHHX4Jr1KCTowGatjoKbV/3OyuB9qpdsz62a7HAw3YulU1cnHRt8cTADb/7S0UZCy0TBzPKitg8MHSyQqEyvEs= - on: - tags: true - repo: react-native-mapbox-gl/maps - branch: master - condition: $TRAVIS_OS_NAME = linux - diff --git a/CHANGELOG.md b/CHANGELOG.md index f36b61d596..a0c198925d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,219 @@ +## UNRELEASED + +``` +Please add unreleased changes in the following style: +PR Title ([#123](link to my pr)) +``` + +fix: add TypeScript type for MapViews's preferredFramesPerSecond prop ([#1717](https://github.com/react-native-mapbox-gl/maps/pull/1717)) +fix(example): update `/example` project (iOS only) to work with ARM-based Macs ([#1703](https://github.com/react-native-mapbox-gl/maps/pull/1703)) + +fix(iOS): correct import of UIView+React.h header ([#1672](https://github.com/react-native-mapbox-gl/maps/pull/1672)) +--- + +## 8.5.0 + +build: update install guide and `/example` project for android dependencies ([#1640](https://github.com/react-native-mapbox-gl/maps/pull/1640)) +build(turf): update to version 6.5.0 ([#1638](https://github.com/react-native-mapbox-gl/maps/pull/1638)) +fix(Camera) fix `zoomTo` method and expand Fit example ([#1631](https://github.com/react-native-mapbox-gl/maps/pull/1631)) +ci: two scripts for linting with and without fix ([#1630](https://github.com/react-native-mapbox-gl/maps/pull/1630)) +feat(Camera) add an optional `allowUpdates` boolean prop ([#1619](https://github.com/react-native-mapbox-gl/maps/pull/1619)) +refactor(example): remove unused modules and scripts ([#1618](https://github.com/react-native-mapbox-gl/maps/pull/1618)) +fix(react-native): update api to get rid of EventEmitter warnings ([#1615](https://github.com/react-native-mapbox-gl/maps/pull/1615)) +fix(Camera) persist zoom when changing from `bounds` to `centerCoordinate`, fix zero padding not causing map to update, create unified example showcasing bounds/centerCoordinate/zoom/padding ([#1614](https://github.com/react-native-mapbox-gl/maps/pull/1614)) +Update MapLibre to 5.12.1 on iOS ([#1596](https://github.com/react-native-mapbox-gl/maps/pull/1596)) +Update ShapeSource methods to make it usable with any cluster ( Use cluster itself instead of cluster_id as first argument for getClusterExpansionZoom/getClusterLeaves/getClusterChildren methods. Version < 9 methods still supports passing cluster_id as a first argument but a deprecation warning will be shown. ) ([#1499](https://github.com/react-native-mapbox-gl/maps/pull/1499)) + +--- + +## 8.4.0 + +fix(iOS): pin mapLibre back to `5.12.0` ([#1589](https://github.com/react-native-mapbox-gl/maps/pull/1589)) +chore: improve GH workflows ([#1588](https://github.com/react-native-mapbox-gl/maps/pull/1588)) +build(deps): bump @expo/config-plugins from 3.1.0 to 4.0.3 ([#1585](https://github.com/react-native-mapbox-gl/maps/pull/1585)) +chore(pre-commit): run lint on TS files, change PR template ([#1584](https://github.com/react-native-mapbox-gl/maps/pull/1584)) +feat(example): update vertical alignment example ([#1579](https://github.com/react-native-mapbox-gl/maps/pull/1579)) +fix incorrect anchor calculation for PointAnnotation on iOS ([#1576](https://github.com/react-native-mapbox-gl/maps/pull/1576)) +style(eslint): align root and example with the same configuration ([#1575](https://github.com/react-native-mapbox-gl/maps/pull/1575)) +fix(mapLibre): support version `5.12.0` upwards ([#1571](https://github.com/react-native-mapbox-gl/maps/pull/1571)) +build: upgrade to RN `0.66` ([#1570](https://github.com/react-native-mapbox-gl/maps/pull/1570)) +build(android): add telemetry dependency to default build setup ([#1550](https://github.com/react-native-mapbox-gl/maps/pull/1550)) +feat(camera): Enable `padding` as a root-level prop on the camera, with `bounds.padding*` as fallbacks ([#1538](https://github.com/react-native-mapbox-gl/maps/pull/1538/files)) +fix: revert pinned mapLibre version to `5.11.0` ([8a2b00e67ba6398f3f6e6f52e98b0f0cea437e4d](https://github.com/react-native-mapbox-gl/maps/commit/8a2b00e67ba6398f3f6e6f52e98b0f0cea437e4d)) + +--- + +## 8.3.0 + +Fix TypeScript type for Callout's textStyle prop ([#1450](https://github.com/react-native-mapbox-gl/maps/pull/1450)) +Build(ios): pin maplibre version to 5.12.0 ([#1454](https://github.com/react-native-mapbox-gl/maps/pull/1454)) +Update geoUtils helpers types to correspond with `turf/helpers` ([#1455](https://github.com/react-native-mapbox-gl/maps/pull/1455)) +Fix crash with missing okhttp dependency ([#1452](https://github.com/react-native-mapbox-gl/maps/pull/1452)) +Move from react-native-testing-library => @testing-library/react-native ([#1453](https://github.com/react-native-mapbox-gl/maps/pull/1453)) +Feat(camera): maxBounds/(min|max)ZoomLevel can be updated dynamically ([#1462](https://github.com/react-native-mapbox-gl/maps/pull/1462)) +Refactor(example): clean up folder structure ([#1464](https://github.com/react-native-mapbox-gl/maps/pull/1464)) +Fix lineGradient showing wrong colors ([#1471](https://github.com/react-native-mapbox-gl/maps/pull/1471)) +Support tintColor on Android ([#1465](https://github.com/react-native-mapbox-gl/maps/pull/1465)) +Feat(android): dynamically update tintColor & add example ([#1469](https://github.com/react-native-mapbox-gl/maps/pull/1469) +Examples: align install steps with yarn, ignore created env files ([#1484](https://github.com/react-native-mapbox-gl/maps/pull/1484) +Fix(plugin): Exclude arm64 architectures for simulator builds ([#1490](https://github.com/react-native-mapbox-gl/maps/pull/1490) +Feat(android): dynamically update tintColor & add example ([#1469](https://github.com/react-native-mapbox-gl/maps/pull/1469)) +Docs: make background in example pngs transparent ([#1483](https://github.com/react-native-mapbox-gl/maps/pull/1483)) +Style: run yarn lint ([#1486](https://github.com/react-native-mapbox-gl/maps/pull/1486)) +Test: add unit tests for component light ([#1489](https://github.com/react-native-mapbox-gl/maps/pull/1489)) +Feat: add Adds getClusterChildren method to ShapeSource ([#1495](https://github.com/react-native-mapbox-gl/maps/pull/1495)) + +## 8.2.1 + +fix issue when publishing to npm with `prepare` script + +## 8.2.0 + +getClusterLeaves method for ShapeSource ([#1411](https://github.com/react-native-mapbox-gl/maps/pull/1411)) +Add logoPosition props to `MapView` to position the mapbox logo ([#1396](https://github.com/react-native-mapbox-gl/maps/pull/1396)) +Add compatibility with React 17/ npm7 ([#1387](https://github.com/react-native-mapbox-gl/maps/pull/1387)) +Add Expo config plugin ([#1388](https://github.com/react-native-mapbox-gl/maps/pull/1388)) +Android: Bump `okhttp` to `4.9.0` ([#1390](https://github.com/react-native-mapbox-gl/maps/pull/1390)) +Support dynamically changing local JSON in styleURL ([#1399](https://github.com/react-native-mapbox-gl/maps/pull/1399)) +Add missing types to `SymbolLayerStyle` & `ImagesProps` ([#1360](https://github.com/react-native-mapbox-gl/maps/pull/1360)) +Fix error while updating coordinates of RCTMGLImageSource ([#1310](https://github.com/react-native-mapbox-gl/maps/pull/1310)) + +## 8.2.0-beta2 + +Add types for `Logger` class ([#1316](https://github.com/react-native-mapbox-gl/maps/pull/1316)) +Enable linear easing on map camera ([#1281](https://github.com/react-native-mapbox-gl/maps/pull/1281)) +Allow MapLibre as an option ([#1311](https://github.com/react-native-mapbox-gl/maps/pull/1311)) +Fix native UserLocation on Android ([#1284](https://github.com/react-native-mapbox-gl/maps/pull/1284)) +Add getClusterExpansionZoom to ShapeSource ([#1279](https://github.com/react-native-mapbox-gl/maps/pull/1279)) +Add type definition for AnimatedPoint ([#1280](https://github.com/react-native-mapbox-gl/maps/pull/1280)) + +## 8.2.0-beta1 + +### Breaking changes: + +Use `pre_install` hook to support non `use_frameworks!` usage #1262. Please add the following to your `Podfile`: + +```ruby +pre_install do |installer| + $RNMBGL.pre_install(installer) + ... +end +``` + +and + +```ruby +post_install do |installer| + $RNMBGL.post_install(installer) + ... +end +``` + +### Other changes: + +- Add course to the location events #1209 +- Fix heading indicator alignment #1215 +- App crash when ProGuard is set to true #1184 +- [iOS] Implemented ShapeSource.features(...) method #1140 +- style json support on styleURL #1102 +- Fix: onUpdate not called when renderMode is native #1135 + +## 8.1.0 + +- By default [use 5.9.0 Mapbox on iOS as 8.1.0rc8 and before](https://github.com/react-native-mapbox-gl/maps/pull/1120) +- Fix [crash during styleURL change on adroid](https://github.com/react-native-mapbox-gl/maps/pull/1119) +- Fix [warning Sending LogEvent with no listeners registered.](https://github.com/react-native-mapbox-gl/maps/pull/1108) +- Fix [race in close map and icon image download](https://github.com/react-native-mapbox-gl/maps/pull/1089) +- Fix [android padding](https://github.com/react-native-mapbox-gl/maps/pull/1087) +- Android [custom mapboxgl version](https://github.com/react-native-mapbox-gl/maps/pull/1088) +- Fix [support 6.\* of MapboxGL IOS by setting `$ReactNativeMapboxGLIOSVersion = "6.2.1"` in Podfile](https://github.com/react-native-mapbox-gl/maps/pull/1044) +- Fix [map rendered at (0,0,0,0) on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1084) +- Fix [edge Padding + auto limit padding on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1057) +- Fix [coordinate 0,0 was considered invalid on IOS](https://github.com/react-native-mapbox-gl/maps/pull/1076) +- Fix [refresh on PointAnnotation on Android](https://github.com/react-native-mapbox-gl/maps/pull/1062) +- Fix [Image source coordinates update on the fly](https://github.com/react-native-mapbox-gl/maps/pull/1036/files) +- Upgrade to [ios 5.9.0](https://github.com/mapbox/mapbox-gl-native-ios/releases/tag/ios-v5.9.0) +- Upgrade to [android 9.1.0](https://github.com/mapbox/mapbox-gl-native-android/releases/tag/android-v9.1.0) +- Set default Mapbox logging verbosity to warning. (Change it using Logger.setLogLevel('verbose')) +- Error/Warn mapbox log messages are treated as redbox/yellowbox errors/warnings. (Override it using Logger.setLoggerCallback(log => { return true }) +- Native user location [#825](https://github.com/react-native-mapbox-gl/maps/pull/825) + +## 8.1.0-rc11 + +- By default [use 5.9.0 Mapbox on iOS as 8.1.0rc8 and before](https://github.com/react-native-mapbox-gl/maps/pull/1120) +- Fix [crash during styleURL change on adroid](https://github.com/react-native-mapbox-gl/maps/pull/1119) +- Fix [warning Sending LogEvent with no listeners registered.](https://github.com/react-native-mapbox-gl/maps/pull/1108) +- Fix [race in close map and icon image download](https://github.com/react-native-mapbox-gl/maps/pull/1089) +- Fix [android padding](https://github.com/react-native-mapbox-gl/maps/pull/1087) +- Android [custom mapboxgl version](https://github.com/react-native-mapbox-gl/maps/pull/1088) +- Fix [support 6.\* of MapboxGL IOS by setting `$ReactNativeMapboxGLIOSVersion = "6.2.1"` in Podfile](https://github.com/react-native-mapbox-gl/maps/pull/1044) +- Fix [map rendered at (0,0,0,0) on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1084) +- Fix [edge Padding + auto limit padding on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1057) +- Fix [coordinate 0,0 was considered invalid on IOS](https://github.com/react-native-mapbox-gl/maps/pull/1076) +- Fix [refresh on PointAnnotation on Android](https://github.com/react-native-mapbox-gl/maps/pull/1062) +- Fix [Image source coordinates update on the fly](https://github.com/react-native-mapbox-gl/maps/pull/1036/files) +- Upgrade to [ios 5.9.0](https://github.com/mapbox/mapbox-gl-native-ios/releases/tag/ios-v5.9.0) +- Upgrade to [android 9.1.0](https://github.com/mapbox/mapbox-gl-native-android/releases/tag/android-v9.1.0) +- Set default Mapbox logging verbosity to warning. (Change it using Logger.setLogLevel('verbose')) +- Error/Warn mapbox log messages are treated as redbox/yellowbox errors/warnings. (Override it using Logger.setLoggerCallback(log => { return true }) +- Native user location [#825](https://github.com/react-native-mapbox-gl/maps/pull/825) + +## 8.1.0-rc10 + +- By default [use 5.9.0 Mapbox on iOS as 8.1.0rc8 and before](https://github.com/react-native-mapbox-gl/maps/pull/1120) +- Fix [crash during styleURL change on adroid](https://github.com/react-native-mapbox-gl/maps/pull/1119) +- Fix [warning Sending LogEvent with no listeners registered.](https://github.com/react-native-mapbox-gl/maps/pull/1108) +- Fix [race in close map and icon image download](https://github.com/react-native-mapbox-gl/maps/pull/1089) +- Fix [android padding](https://github.com/react-native-mapbox-gl/maps/pull/1087) +- Android [custom mapboxgl version](https://github.com/react-native-mapbox-gl/maps/pull/1088) +- Fix [support 6.\* of MapboxGL IOS by setting `$ReactNativeMapboxGLIOSVersion = "6.2.1"` in Podfile](https://github.com/react-native-mapbox-gl/maps/pull/1044) +- Fix [map rendered at (0,0,0,0) on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1084) +- Fix [edge Padding + auto limit padding on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1057) +- Fix [coordinate 0,0 was considered invalid on IOS](https://github.com/react-native-mapbox-gl/maps/pull/1076) +- Fix [refresh on PointAnnotation on Android](https://github.com/react-native-mapbox-gl/maps/pull/1062) +- Fix [Image source coordinates update on the fly](https://github.com/react-native-mapbox-gl/maps/pull/1036/files) +- Upgrade to [ios 5.9.0](https://github.com/mapbox/mapbox-gl-native-ios/releases/tag/ios-v5.9.0) +- Upgrade to [android 9.1.0](https://github.com/mapbox/mapbox-gl-native-android/releases/tag/android-v9.1.0) +- Set default Mapbox logging verbosity to warning. (Change it using Logger.setLogLevel('verbose')) +- Error/Warn mapbox log messages are treated as redbox/yellowbox errors/warnings. (Override it using Logger.setLoggerCallback(log => { return true }) +- Native user location [#825](https://github.com/react-native-mapbox-gl/maps/pull/825) + +## 8.1.0.rc10 + +- By default [use 5.9.0 Mapbox on iOS as 8.1.0rc8 and before](https://github.com/react-native-mapbox-gl/maps/pull/1120) +- Fix [crash during styleURL change on adroid](https://github.com/react-native-mapbox-gl/maps/pull/1119) +- Fix [warning Sending LogEvent with no listeners registered.](https://github.com/react-native-mapbox-gl/maps/pull/1108) + +## 8.1.0.rc9 + +- Fix [race in close map and icon image download](https://github.com/react-native-mapbox-gl/maps/pull/1089) + +## 8.1.0.rc8 + +- Fix [android padding](https://github.com/react-native-mapbox-gl/maps/pull/1087) +- Android [custome mapboxgl version](https://github.com/react-native-mapbox-gl/maps/pull/1088) + +## 8.1.0.rc7 + +- Fix [map rendered at (0,0,0,0) on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1084) + +## 8.1.0.rc6 + +- Fix [edge Padding + auto limit padding on iOS](https://github.com/react-native-mapbox-gl/maps/pull/1057) +- Fix [coordinate 0,0 was considered invalid on IOS](https://github.com/react-native-mapbox-gl/maps/pull/1076) +- Fix [refresh on PointAnnotation on Android](https://github.com/react-native-mapbox-gl/maps/pull/1062) + +## 8.1.0.rc5 + +- Fix [support 6.\* of MapboxGL IOS by setting `$ReactNativeMapboxGLIOSVersion = "6.2.1"` in Podfile](https://github.com/react-native-mapbox-gl/maps/pull/1044) +- Fix [Image source coordinates update on the fly](https://github.com/react-native-mapbox-gl/maps/pull/1036/files) + +## 8.1.0.rc4 + ## 8.1.0.rc3 +- Fix [android crashes](https://github.com/react-native-mapbox-gl/maps/pull/963) +- Fix [android padding addition](https://github.com/react-native-mapbox-gl/maps/pull/973) - Fix [iOS interface for getAccessToken() on Android](https://github.com/react-native-mapbox-gl/maps/pull/954) ## 8.1.0.rc2 @@ -27,7 +241,7 @@ ### Breaking changes - [#610](https://github.com/react-native-mapbox-gl/maps/issues/610) - iOS mapbox libraries updated to [5.7.0](https://github.com/mapbox/mapbox-gl-native-ios/releases/tag/ios-v5.7.0) android libraries updated to [9.0.0](https://github.com/mapbox/mapbox-gl-native-android/releases/tag/android-v9.0.0) -- ShapeSource#images is now removed (deprecated in 7.*), use Images#images instead. Also special `assets` inside `images` is now deprecated, use `nativeAssetImages` istead. +- ShapeSource#images is now removed (deprecated in 7.\*), use Images#images instead. Also special `assets` inside `images` is now deprecated, use `nativeAssetImages` istead. - iOS now defaults to non `use_frameworks!`, if you want to continue to use `use_frameworks!` please see our iOS installation guidelines - [Images#onImagesMissing](docs/Images.md) - Android code migrated to AndroidX, RN 60.0+ is recommended. diff --git a/LICENSE.md b/LICENSE.md index a0b06297d2..0e77b64a4b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,23 +1,5 @@ -react-native-mapbox-gl copyright (c) 2017, Mapbox. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the β€œSoftware”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Mapbox GL uses portions of the Mapbox Maps SDK for iOS, which was derived from the Route-Me open source project, including the Alpstein fork of it. - -The Route-Me license appears below. - -Copyright (c) 2008-2013, Route-Me Contributors All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 1edc97563b..f13e283de9 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,157 @@ +
+ +### πŸŸ₯ Future of this repo: participate in the [discussion thread](https://github.com/react-native-mapbox-gl/maps/discussions/1680) πŸŸ₯ + + +## Call for additional maintainers +Hey you, yes _you_! +Do you like this repo, are you using it (are you using it for production apps?! It's cool, we do too!)? + +If you have some time to spare, we'd love to get your help! +I hear you ask "**Yes, _YES_, but How?!**" (in deafening silence), +well...here are some examples: +* check our docs (still up to date? need some edits?) +* review issue tickets and reply with helpful answers +* join the Gitter chat and engage with other users +* review PRs and comment on things you notice +* actively help move the project forward by submitting PRs that introduce fixes and features + +You don't need to be a full-fledged maintainer to do those things, however, +if you are interested in becoming one, don't hesitate to reply in [this discussion](https://github.com/react-native-mapbox-gl/maps/discussions/1551). + +Thanks πŸ™‡ + +
+ +--- + +
+ # Mapbox Maps SDK for React Native -_An unofficial React Native library for building maps with the [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/) and [Mapbox Maps SDK for Android](https://www.mapbox.com/android-sdk/)_ +_An unofficial React Native library for building maps with +the [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/) and [Mapbox Maps SDK for Android](https://www.mapbox.com/android-sdk/)_ -[![npm version](https://badge.fury.io/js/%40react-native-mapbox-gl%2Fmaps.svg)](https://badge.fury.io/js/%40react-native-mapbox-gl%2Fmaps) -![build_status](https://travis-ci.org/react-native-mapbox-gl/maps.svg?branch=master) -[![Depfu](https://badges.depfu.com/badges/2eac6b62372619718b7f55ebbf8e9d8f/overview.svg)](https://depfu.com/github/react-native-mapbox-gl/maps?project_id=8248) -## Installation -### Prerequisit -On Android we support from version 6 (API 23) upwards +We also support [MapLibre](https://github.com/maplibre/maplibre-gl-native) flavors of Mapbox SDKs now πŸŽ‰ + + +--- + + +[![npm version](https://badge.fury.io/js/%40react-native-mapbox-gl%2Fmaps.svg)](https://badge.fury.io/js/%40react-native-mapbox-gl%2Fmaps) +[![Android Build](https://github.com/react-native-mapbox-gl/maps/actions/workflows/android-actions.yml/badge.svg)](https://github.com/react-native-mapbox-gl/maps/actions/workflows/android-actions.yml) +[![iOS Build](https://github.com/react-native-mapbox-gl/maps/actions/workflows/ios-actions.yml/badge.svg)](https://github.com/react-native-mapbox-gl/maps/actions/workflows/ios-actions.yml) + +--- + +
+ +Indoor Building Map Android +Indoor Building Map iOS + +## Prerequisite +1. On Android we support from version 6 (API 23) upwards +2. Please [Sign Up to Mapbox](https://account.mapbox.com/auth/signup/) to get the Mapbox Access Token. -### Dependencies + +## Dependencies - [node](https://nodejs.org) - [npm](https://www.npmjs.com/) - [React Native](https://facebook.github.io/react-native/) (0.60+) -### Git -``` -git clone git@github.com:react-native-mapbox-gl/maps.git -cd maps -``` +## Installation -### Yarn +### Step 1 - Install Package: -``` +```sh +# install with Yarn yarn add @react-native-mapbox-gl/maps -``` -### Npm -``` +# or install with NPM npm install @react-native-mapbox-gl/maps --save ``` -## Installation Guides +### Step 2 - Installation Guides: - [Android](/android/install.md) - [iOS](/ios/install.md) +- [Expo](/plugin/install.md) - [Example](/example) -## [Getting Started](/docs/GettingStarted.md) + +### Getting Started +For more information, check out our [Getting Started](/docs/GettingStarted.md) section + +## Run Project +Before you run your project be sure you have completeded the Installation Guides for Android or iOS. + +### Run iOS Simulator +```sh +# Run with yarn +yarn run ios + +# or Run with NPM +npm run ios +``` + +### Run Android Emulator +```sh +# Run with yarn +yarn run android + +# or Run with NPM +npm run android +``` + +## Adding a map + +```js +import React, { Component } from 'react'; +import { StyleSheet, View } from 'react-native'; +import MapboxGL from '@react-native-mapbox-gl/maps'; + +MapboxGL.setAccessToken(''); + +const styles = StyleSheet.create({ + page: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#F5FCFF' + }, + container: { + height: 300, + width: 300, + backgroundColor: 'tomato' + }, + map: { + flex: 1 + } +}); + +export default class App extends Component { + render() { + return ( + + + + + + ); + } +} +``` ## Documentation @@ -84,10 +193,11 @@ npm install @react-native-mapbox-gl/maps --save - [MapboxGL](/docs/MapboxGL.md) - [CustomHttpHeaders](/docs/CustomHttpHeaders.md) +- [Logger](/docs/Logger.md) ## Expo Support -We have a feature request open with Expo if you want to see it get in show your support https://expo.canny.io/feature-requests/p/add-mapbox-gl-support +This package is not available in the [Expo Go](https://expo.io/client) app. Learn how you can use it with [custom dev clients](/plugin/install.md). ## Testing with Jest diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..344699dddb --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,28 @@ +# How to create a release for this repo + +## Make sure `master` builds correctly + +Are all our [actions](https://github.com/react-native-mapbox-gl/maps/actions) passing successfully? +If not, make sure to investigate the issue and fix it prior to a release. + +## Bump the version in our package.json + +Once you verified, that `master` isn't broken, go on and increase the `version` within our `package.json`. + +## Update the CHANGELOG accordingly + +Our [`CHANGELOG.md`](https://github.com/react-native-mapbox-gl/maps/blob/master/CHANGELOG.md) should be updated whenever a PR is merged/ noteworthy changes are commited to `master`. +Prior to a release, the changes should be documented under the `UNRELEASED` section. +Once it's clear, that a release is about to be published, move the items under `UNRELEASED` to _this_ releases sections. +Let your actions be guided by the previous release entries. + +## Draft a new release on Github + +Within the [releases](https://github.com/react-native-mapbox-gl/maps/releases) section of the repo you can [`Draft a new release`](https://github.com/react-native-mapbox-gl/maps/releases/new). + +`Tag version` & `Release title` should be the same. +As redundant as it might sound, please add the changes from the `CHANGELOG.md` into the body of the release. + +## Monitor the repos issues for updates + +Once the release is out the door (on [npm](https://www.npmjs.com/package/@react-native-mapbox-gl/maps)), make sure to monitor the [issues](https://github.com/react-native-mapbox-gl/maps/issues) closely for problems the community might encounter diff --git a/__tests__/__mocks__/react-native.mock.js b/__tests__/__mocks__/react-native.mock.js index 8a2bd31460..05d7c64714 100644 --- a/__tests__/__mocks__/react-native.mock.js +++ b/__tests__/__mocks__/react-native.mock.js @@ -5,6 +5,5 @@ jest.mock('react-native/Libraries/Image/resolveAssetSource', () => { jest.mock('NativeEventEmitter', () => { function MockEventEmitter() {} MockEventEmitter.prototype.addListener = jest.fn(() => ({remove: jest.fn()})); - MockEventEmitter.prototype.removeListener = jest.fn(); return MockEventEmitter; }); diff --git a/__tests__/components/BackgroundLayer.test.js b/__tests__/components/BackgroundLayer.test.js index 0c9c7f41ac..4dddc1f6d1 100644 --- a/__tests__/components/BackgroundLayer.test.js +++ b/__tests__/components/BackgroundLayer.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import BackgroundLayer from '../../javascript/components/BackgroundLayer'; diff --git a/__tests__/components/Callout.test.js b/__tests__/components/Callout.test.js index 8d112bf0dc..864a49797e 100644 --- a/__tests__/components/Callout.test.js +++ b/__tests__/components/Callout.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import {Text, View} from 'react-native'; import Callout from '../../javascript/components/Callout'; diff --git a/__tests__/components/Camera.test.js b/__tests__/components/Camera.test.js index dd8c70cfe8..162c58c95d 100644 --- a/__tests__/components/Camera.test.js +++ b/__tests__/components/Camera.test.js @@ -1,9 +1,10 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import Camera from '../../javascript/components/Camera'; const cameraWithoutFollowDefault = { + ...Camera.defaultProps, animationDuration: 2000, animationMode: 'easeTo', centerCoordinate: [-111.8678, 40.2866], @@ -14,6 +15,7 @@ const cameraWithoutFollowDefault = { }; const cameraWithoutFollowChanged = { + ...Camera.defaultProps, animationDuration: 1000, animationMode: 'easeTo', centerCoordinate: [-110.8678, 37.2866], @@ -24,6 +26,7 @@ const cameraWithoutFollowChanged = { }; const cameraWithFollowCourse = { + ...Camera.defaultProps, animationDuration: 2000, animationMode: 'easeTo', defaultSettings: { @@ -36,6 +39,7 @@ const cameraWithFollowCourse = { }; const cameraWithBounds = { + ...Camera.defaultProps, animationDuration: 2000, animationMode: 'easeTo', bounds: { @@ -71,6 +75,10 @@ describe('Camera', () => { heading: undefined, duration: 2000, zoom: undefined, + paddingBottom: 0, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, }, maxZoomLevel: undefined, minZoomLevel: undefined, @@ -170,7 +178,7 @@ describe('Camera', () => { ); expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._hasBoundsChanged).not.toHaveBeenCalled(); + expect(camera._hasBoundsChanged).toHaveBeenCalled(); expect(camera._setCamera).toHaveBeenCalledWith({ animationDuration: 1000, animationMode: 'easeTo', @@ -188,7 +196,7 @@ describe('Camera', () => { ); expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._hasBoundsChanged).toHaveBeenCalledTimes(1); + expect(camera._hasBoundsChanged).toHaveBeenCalledTimes(2); expect(camera._setCamera).toHaveBeenCalledWith({ animationDuration: 2000, animationMode: 'easeTo', @@ -273,7 +281,7 @@ describe('Camera', () => { ], ]; - testCases.forEach((c) => { + testCases.forEach(c => { expect(camera._hasCameraChanged(c[0], c[1])).toBe(true); }); }); @@ -287,7 +295,7 @@ describe('Camera', () => { [{followPitch: 40}, {followPitch: 49}], ]; - testCases.forEach((c) => { + testCases.forEach(c => { expect(camera._hasCameraChanged(c[0], c[1])).toBe(true); }); }); @@ -298,7 +306,7 @@ describe('Camera', () => { [{animationMode: 'flyTo'}, {animationMode: 'easeTo'}], ]; - testCases.forEach((c) => { + testCases.forEach(c => { expect(camera._hasCameraChanged(c[0], c[1])).toBe(true); }); }); @@ -314,40 +322,34 @@ describe('Camera', () => { test('returns false when centerCoordinates have not changed', () => { expect( camera._hasCenterCoordinateChanged( - {centerCoordinate: [-111.8678, 40.2866]}, - {centerCoordinate: [-111.8678, 40.2866]}, + [-111.8678, 40.2866], + [-111.8678, 40.2866], ), ).toBe(false); }); test('returns true when centerCoordinates have changed', () => { expect( - camera._hasCenterCoordinateChanged( - {centerCoordinate: [-111.8678, 40.2866]}, - {}, - ), + camera._hasCenterCoordinateChanged([-111.8678, 40.2866], undefined), ).toBe(true); expect( - camera._hasCenterCoordinateChanged( - {}, - {centerCoordinate: [-111.8678, 40.2866]}, - ), + camera._hasCenterCoordinateChanged(undefined, [-111.8678, 40.2866]), ).toBe(true); // isLngDiff expect( camera._hasCenterCoordinateChanged( - {centerCoordinate: [-111.2678, 40.2866]}, - {centerCoordinate: [-111.8678, 40.2866]}, + [-111.2678, 40.2866], + [-111.8678, 40.2866], ), ).toBe(true); // isLatDiff expect( camera._hasCenterCoordinateChanged( - {centerCoordinate: [-111.2678, 40.2866]}, - {centerCoordinate: [-111.8678, 33.2866]}, + [-111.2678, 40.2866], + [-111.8678, 33.2866], ), ).toBe(true); }); @@ -356,18 +358,16 @@ describe('Camera', () => { describe('#_hasBoundsChanged', () => { const camera = new Camera(); const bounds = { - bounds: { - ne: [-74.12641, 40.797968], - sw: [-74.143727, 40.772177], - paddingTop: 5, - paddingLeft: 5, - paddingRight: 5, - paddingBottom: 5, - }, + ne: [-74.12641, 40.797968], + sw: [-74.143727, 40.772177], + paddingTop: 5, + paddingLeft: 5, + paddingRight: 5, + paddingBottom: 5, }; test('returns false when bounds are missing', () => { - expect(camera._hasBoundsChanged({}, {})).toBe(false); + expect(camera._hasBoundsChanged(undefined, undefined)).toBe(false); }); test('returns false when bounds have not changed', () => { @@ -378,83 +378,105 @@ describe('Camera', () => { // ne[0] expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - ne: [-34.12641, 40.797968], - }, + ...bounds, + ne: [-34.12641, 40.797968], }), ).toBe(true); // ne[1] expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - ne: [-74.12641, 30.797968], - }, + ...bounds, + ne: [-74.12641, 30.797968], }), ).toBe(true); // sw[0] expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - sw: [-74.143723, 40.772177], - }, + ...bounds, + sw: [-74.143723, 40.772177], }), ).toBe(true); // sw[1] expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - sw: [-74.143727, 40.772137], - }, + ...bounds, + sw: [-74.143727, 40.772137], }), ).toBe(true); // paddingTop expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - paddingTop: 3, - }, + ...bounds, + paddingTop: 3, }), ).toBe(true); // paddingLeft expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - paddingLeft: 3, - }, + ...bounds, + paddingLeft: 3, }), ).toBe(true); // paddingRight expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - paddingRight: 3, - }, + ...bounds, + paddingRight: 3, }), ).toBe(true); // paddingBottom expect( camera._hasBoundsChanged(bounds, { - bounds: { - ...bounds.bounds, - paddingBottom: 3, - }, + ...bounds, + paddingBottom: 3, }), ).toBe(true); }); + + describe('does work with maxBounds', () => { + const currentMaxBounds = { + ne: [-74.12641, 40.797968], + sw: [-74.143727, 40.772177], + }; + + const nextMaxBounds = { + ne: [-83.12641, 42.797968], + sw: [-64.143727, 35.772177], + }; + + test('returns true if changed', () => { + expect( + camera._hasBoundsChanged(currentMaxBounds, nextMaxBounds), + ).toBe(true); + }); + + test('returns false if unchanged', () => { + expect( + camera._hasBoundsChanged(currentMaxBounds, currentMaxBounds), + ).toBe(false); + }); + + test('returns false if both undefined', () => { + expect(camera._hasBoundsChanged(undefined, undefined)).toBe(false); + }); + + test('does work with currentBounds being undefined', () => { + expect(camera._hasBoundsChanged(undefined, nextMaxBounds)).toBe(true); + }); + + test('does work with nextBounds being undefined', () => { + expect(camera._hasBoundsChanged(currentMaxBounds, undefined)).toBe( + true, + ); + }); + }); }); describe('#fitBounds', () => { @@ -475,11 +497,13 @@ describe('Camera', () => { animationMode: 'easeTo', bounds: { ne: [-63.12641, 39.797968], + sw: [-74.143727, 40.772177], + }, + padding: { paddingBottom: null, paddingLeft: null, paddingRight: null, paddingTop: null, - sw: [-74.143727, 40.772177], }, }, { @@ -487,11 +511,13 @@ describe('Camera', () => { animationMode: 'easeTo', bounds: { ne: [-63.12641, 39.797968], + sw: [-74.143727, 40.772177], + }, + padding: { paddingBottom: null, paddingLeft: null, paddingRight: null, paddingTop: null, - sw: [-74.143727, 40.772177], }, }, { @@ -499,11 +525,13 @@ describe('Camera', () => { animationMode: 'easeTo', bounds: { ne: [-63.12641, 39.797968], + sw: [-74.143727, 40.772177], + }, + padding: { paddingBottom: 0, paddingLeft: 0, paddingRight: 0, paddingTop: 0, - sw: [-74.143727, 40.772177], }, }, ]; @@ -527,11 +555,13 @@ describe('Camera', () => { animationMode: 'easeTo', bounds: { ne: [-63.12641, 39.797968], + sw: [-74.143727, 40.772177], + }, + padding: { paddingBottom: 3, paddingLeft: 3, paddingRight: 3, paddingTop: 3, - sw: [-74.143727, 40.772177], }, }; @@ -545,11 +575,13 @@ describe('Camera', () => { animationMode: 'easeTo', bounds: { ne: [-63.12641, 39.797968], + sw: [-74.143727, 40.772177], + }, + padding: { paddingBottom: 3, paddingLeft: 5, paddingRight: 5, paddingTop: 3, - sw: [-74.143727, 40.772177], }, }; @@ -563,11 +595,13 @@ describe('Camera', () => { animationMode: 'easeTo', bounds: { ne: [-63.12641, 39.797968], + sw: [-74.143727, 40.772177], + }, + padding: { paddingBottom: 8, paddingLeft: 10, paddingRight: 5, paddingTop: 3, - sw: [-74.143727, 40.772177], }, }; @@ -761,10 +795,10 @@ describe('Camera', () => { stop: { bounds: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - boundsPaddingBottom: 8, - boundsPaddingLeft: 10, - boundsPaddingRight: 5, - boundsPaddingTop: 3, + paddingBottom: 8, + paddingLeft: 10, + paddingRight: 5, + paddingTop: 3, duration: 500, heading: 100, mode: 'Ease', @@ -851,10 +885,10 @@ describe('Camera', () => { { bounds: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - boundsPaddingBottom: 2, - boundsPaddingLeft: 2, - boundsPaddingRight: 2, - boundsPaddingTop: 2, + paddingBottom: 2, + paddingLeft: 2, + paddingRight: 2, + paddingTop: 2, duration: 50, heading: 20, mode: 'Ease', @@ -864,10 +898,10 @@ describe('Camera', () => { { bounds: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,59.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-71.143727,40.772177]}}]}', - boundsPaddingBottom: 8, - boundsPaddingLeft: 10, - boundsPaddingRight: 5, - boundsPaddingTop: 3, + paddingBottom: 8, + paddingLeft: 10, + paddingRight: 5, + paddingTop: 3, duration: 3000, heading: 40, mode: 'Flight', @@ -877,10 +911,10 @@ describe('Camera', () => { { bounds: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - boundsPaddingBottom: 8, - boundsPaddingLeft: 10, - boundsPaddingRight: 5, - boundsPaddingTop: 3, + paddingBottom: 8, + paddingLeft: 10, + paddingRight: 5, + paddingTop: 3, duration: 500, heading: 100, mode: 'Ease', @@ -919,6 +953,10 @@ describe('Camera', () => { mode: 'None', pitch: undefined, zoom: 16, + paddingBottom: 0, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, }; expect(camera.defaultCamera).toStrictEqual(undefined); @@ -978,6 +1016,10 @@ describe('Camera', () => { mode: 'Ease', pitch: 45, zoom: 9, + paddingBottom: 0, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, }); // with centerCoordinate @@ -994,6 +1036,10 @@ describe('Camera', () => { mode: 'Ease', pitch: 45, zoom: 9, + paddingBottom: 0, + paddingLeft: 0, + paddingRight: 0, + paddingTop: 0, }); }); @@ -1005,10 +1051,10 @@ describe('Camera', () => { expect(camera._createStopConfig(configWithBounds, true)).toStrictEqual({ bounds: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - boundsPaddingBottom: 8, - boundsPaddingLeft: 10, - boundsPaddingRight: 5, - boundsPaddingTop: 3, + paddingBottom: 8, + paddingLeft: 10, + paddingRight: 5, + paddingTop: 3, duration: 500, heading: 100, mode: 'Ease', @@ -1025,10 +1071,10 @@ describe('Camera', () => { ).toStrictEqual({ bounds: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - boundsPaddingBottom: 8, - boundsPaddingLeft: 10, - boundsPaddingRight: 5, - boundsPaddingTop: 3, + paddingBottom: 8, + paddingLeft: 10, + paddingRight: 5, + paddingTop: 3, centerCoordinate: '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-111.8678,40.2866]}}', duration: 500, diff --git a/__tests__/components/CircleLayer.test.js b/__tests__/components/CircleLayer.test.js index 1efda4eba5..5525269655 100644 --- a/__tests__/components/CircleLayer.test.js +++ b/__tests__/components/CircleLayer.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import CircleLayer from '../../javascript/components/CircleLayer'; diff --git a/__tests__/components/HeatmapLayer.test.js b/__tests__/components/HeatmapLayer.test.js index 2c5e8e0345..ab517b5770 100644 --- a/__tests__/components/HeatmapLayer.test.js +++ b/__tests__/components/HeatmapLayer.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import HeatmapLayer from '../../javascript/components/HeatmapLayer'; diff --git a/__tests__/components/Light.test.js b/__tests__/components/Light.test.js new file mode 100644 index 0000000000..96105c0cca --- /dev/null +++ b/__tests__/components/Light.test.js @@ -0,0 +1,39 @@ +import React from 'react'; +import {render} from '@testing-library/react-native'; + +import Light from '../../javascript/components/Light'; + +export const NATIVE_MODULE_NAME = 'RCTMGLLight'; + +describe('Light', () => { + test('renders correctly', () => { + const {queryByTestId} = render(); + const light = queryByTestId('rctmglLight'); + expect(light).toBeDefined(); + }); + + test('renders correctly with custom styles', () => { + const testStyles = { + position: [1234, 1234, 1234], + color: '#FA0000', // === ProcessedTestColor + anchor: 'map', + intensity: 1, + }; + const processedTestColor = 4294574080; + + const {queryByTestId} = render(); + + const customStyles = queryByTestId('rctmglLight').props.reactStyle; + const {anchor} = customStyles; + const {color} = customStyles; + const {position} = customStyles; + const {intensity} = customStyles; + + expect(anchor.stylevalue.value).toStrictEqual(testStyles.anchor); + expect(color.stylevalue.value).toStrictEqual(processedTestColor); + expect(intensity.stylevalue.value).toStrictEqual(testStyles.intensity); + expect(position.stylevalue.value[0].value).toStrictEqual( + testStyles.position[0], + ); + }); +}); diff --git a/__tests__/components/MapView.test.js b/__tests__/components/MapView.test.js index 46eb2cac53..99cc164f55 100644 --- a/__tests__/components/MapView.test.js +++ b/__tests__/components/MapView.test.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import MapView from '../../javascript/components/MapView'; diff --git a/__tests__/components/Style.test.js b/__tests__/components/Style.test.js index 1156415886..31c771f64d 100644 --- a/__tests__/components/Style.test.js +++ b/__tests__/components/Style.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; +import {render} from '@testing-library/react-native'; import VectorSource from '../../javascript/components/VectorSource'; import RasterSource from '../../javascript/components/RasterSource'; diff --git a/__tests__/components/SymbolLayer.test.js b/__tests__/components/SymbolLayer.test.js index 9e33bfc34e..c3238cf373 100644 --- a/__tests__/components/SymbolLayer.test.js +++ b/__tests__/components/SymbolLayer.test.js @@ -1,6 +1,5 @@ import React from 'react'; -import {render} from 'react-native-testing-library'; -import PropTypes from 'prop-types'; +import {render} from '@testing-library/react-native'; import SymbolLayer, { NATIVE_MODULE_NAME, diff --git a/__tests__/components/UserLocation.test.js b/__tests__/components/UserLocation.test.js index 8525da5e4f..24558a5aa3 100644 --- a/__tests__/components/UserLocation.test.js +++ b/__tests__/components/UserLocation.test.js @@ -1,9 +1,7 @@ import React from 'react'; -import {render, fireEvent} from 'react-native-testing-library'; +import {render, fireEvent} from '@testing-library/react-native'; -import UserLocation, { - normalIcon, -} from '../../javascript/components/UserLocation'; +import UserLocation from '../../javascript/components/UserLocation'; import ShapeSource from '../../javascript/components/ShapeSource'; import CircleLayer from '../../javascript/components/CircleLayer'; import locationManager from '../../javascript/modules/location/locationManager'; @@ -16,6 +14,7 @@ const position = { latitude: 51.5462244, longitude: 4.1036916, speed: 0.08543474227190018, + course: 251.5358428955078, }, timestamp: 1573730357879, }; @@ -35,7 +34,7 @@ describe('UserLocation', () => { jest.clearAllMocks(); }); - test('renders with CircleLayers by default', (done) => { + test('renders with CircleLayers by default', done => { const {UNSAFE_getAllByType} = render(); setTimeout(() => { @@ -48,7 +47,7 @@ describe('UserLocation', () => { }); }); - test('does not render with visible set to false', (done) => { + test('does not render with visible set to false', done => { const {UNSAFE_queryByType} = render(); setTimeout(() => { @@ -61,7 +60,7 @@ describe('UserLocation', () => { }); }); - test('renders with CustomChild when provided', (done) => { + test('renders with CustomChild when provided', done => { const circleLayerProps = { key: 'testUserLocationCircle', id: 'testUserLocationCircle', @@ -105,6 +104,7 @@ describe('UserLocation', () => { latitude: 51.5462244, longitude: 4.1036916, speed: 0.08543474227190018, + course: 251.5358428955078, }, timestamp: 1573730357879, }); @@ -142,6 +142,7 @@ describe('UserLocation', () => { beforeEach(() => { ul = new UserLocation(); + jest.spyOn(locationManager, 'start').mockImplementation(jest.fn()); jest.spyOn(locationManager, 'stop').mockImplementation(jest.fn()); jest @@ -150,11 +151,7 @@ describe('UserLocation', () => { ul.setState = jest.fn(); - ul.props = { - animated: true, - visible: true, - minDisplacement: 0, - }; + ul.props = UserLocation.defaultProps; ul._isMounted = true; }); diff --git a/__tests__/interface.test.js b/__tests__/interface.test.js index eed8c1dc8d..a062eb8e14 100644 --- a/__tests__/interface.test.js +++ b/__tests__/interface.test.js @@ -87,6 +87,6 @@ describe('Public Interface', () => { 'Logger', 'Style', ]; - actualKeys.forEach((key) => expect(expectedKeys).toContain(key)); + actualKeys.forEach(key => expect(expectedKeys).toContain(key)); }); }); diff --git a/__tests__/modules/location/locationManager.test.js b/__tests__/modules/location/locationManager.test.js index b4ba3e3ad8..a6a1260329 100644 --- a/__tests__/modules/location/locationManager.test.js +++ b/__tests__/modules/location/locationManager.test.js @@ -198,8 +198,6 @@ describe('LocationManager', () => { // native location manager has no #stop exposed in tests? MapboxGLLocationManager.stop = jest.fn(); - jest.spyOn(LocationModuleEventEmitter, 'removeListener'); - MapboxGL.LocationCallbackName = {Update: 'MapboxUserLocationUpdate'}; expect(locationManager._isListening).toStrictEqual(true); @@ -207,10 +205,7 @@ describe('LocationManager', () => { locationManager.stop(); expect(MapboxGLLocationManager.stop).toHaveBeenCalledTimes(1); - expect(LocationModuleEventEmitter.removeListener).toHaveBeenCalledWith( - MapboxGL.LocationCallbackName.Update, - locationManager.onUpdate, - ); + expect(locationManager.subscription.remove).toHaveBeenCalled(); expect(locationManager._isListening).toStrictEqual(false); }); @@ -221,8 +216,6 @@ describe('LocationManager', () => { // native location manager has no #stop exposed in tests? MapboxGLLocationManager.stop = jest.fn(); - jest.spyOn(LocationModuleEventEmitter, 'removeListener'); - MapboxGL.LocationCallbackName = {Update: 'MapboxUserLocationUpdate'}; expect(locationManager._isListening).toStrictEqual(false); @@ -230,9 +223,7 @@ describe('LocationManager', () => { locationManager.stop(); expect(MapboxGLLocationManager.stop).toHaveBeenCalledTimes(1); - expect( - LocationModuleEventEmitter.removeListener, - ).not.toHaveBeenCalled(); + expect(locationManager.subscription.remove).not.toHaveBeenCalled(); }); }); @@ -260,13 +251,13 @@ describe('LocationManager', () => { test('calls listeners with location', () => { const listeners = [jest.fn(), jest.fn(), jest.fn()]; - listeners.forEach((listener) => { + listeners.forEach(listener => { locationManager.addListener(listener); }); locationManager.onUpdate(location); - listeners.forEach((listener) => { + listeners.forEach(listener => { expect(listener).toHaveBeenCalledTimes(1); expect(listener).toHaveBeenCalledWith(location); }); diff --git a/__tests__/modules/offline/offlineManager.test.js b/__tests__/modules/offline/offlineManager.test.js index 53d22b5098..ec3b1d322a 100644 --- a/__tests__/modules/offline/offlineManager.test.js +++ b/__tests__/modules/offline/offlineManager.test.js @@ -94,7 +94,7 @@ describe('offlineManager', () => { const noop = () => {}; await MapboxGL.offlineManager.createPack(packOptions, noop, noop); expect(spy).toHaveBeenCalledTimes(2); - spy.mockRestore(); + spy.mockClear(); }); it('should call progress listener', async () => { @@ -133,12 +133,17 @@ describe('offlineManager', () => { }); it('should unsubscribe from native events', async () => { - const spy = jest.spyOn(OfflineModuleEventEmitter, 'removeListener'); const noop = () => {}; + await MapboxGL.offlineManager.createPack(packOptions, noop, noop); MapboxGL.offlineManager.unsubscribe(packOptions.name); - expect(spy).toHaveBeenCalledTimes(2); - spy.mockRestore(); + + expect( + MapboxGL.offlineManager.subscriptionProgress.remove, + ).toHaveBeenCalledTimes(1); + expect( + MapboxGL.offlineManager.subscriptionError.remove, + ).toHaveBeenCalledTimes(1); }); it('should unsubscribe event listeners once a pack download has completed', async () => { diff --git a/__tests__/modules/snapshot/SnapshotOptions.test.js b/__tests__/modules/snapshot/SnapshotOptions.test.js index 739a6f6a54..fa222777f3 100644 --- a/__tests__/modules/snapshot/SnapshotOptions.test.js +++ b/__tests__/modules/snapshot/SnapshotOptions.test.js @@ -65,7 +65,7 @@ describe('SnapshotOptions', () => { }; const geoJSONBounds = JSON.stringify( - makeFeatureCollection(expectedOptions.bounds.map((c) => makePoint(c))), + makeFeatureCollection(expectedOptions.bounds.map(c => makePoint(c))), ); const options = new SnapshotOptions(expectedOptions); diff --git a/__tests__/utils/animated/AnimatedCoordinatesArray.test.js b/__tests__/utils/animated/AnimatedCoordinatesArray.test.js index c9ac54da3f..805f520010 100644 --- a/__tests__/utils/animated/AnimatedCoordinatesArray.test.js +++ b/__tests__/utils/animated/AnimatedCoordinatesArray.test.js @@ -13,13 +13,13 @@ let clock = null; beforeAll(() => { clock = FakeTimers.install(); clock._requestedAnimationFrames = []; - clock.requestAnimationFrame = (callback) => { + clock.requestAnimationFrame = callback => { clock._requestedAnimationFrames.push(callback); }; clock.fireRequestAnimationFrames = () => { const oldRAF = clock._requestedAnimationFrames; clock._requestedAnimationFrames = []; - oldRAF.forEach((cb) => cb(Date.now())); + oldRAF.forEach(cb => cb(Date.now())); }; }); @@ -42,7 +42,7 @@ describe('AnimatedShapeSource', () => { const testRenderer = TestRenderer.create( (shapeSourceRef = ref)} + ref={ref => (shapeSourceRef = ref)} />, ); const setNativeProps = jest.fn(); @@ -91,7 +91,7 @@ describe('AnimatedShapeSource', () => { const testRenderer = TestRenderer.create( (shapeSourceRef = ref)} + ref={ref => (shapeSourceRef = ref)} />, ); const setNativeProps = jest.fn(); @@ -143,7 +143,7 @@ describe('AnimatedShapeSource', () => { const testRenderer = TestRenderer.create( (shapeSourceRef = ref)} + ref={ref => (shapeSourceRef = ref)} />, ); const setNativeProps = jest.fn(); diff --git a/android/install.md b/android/install.md index 77b4d5e8e1..6af8eb82ba 100644 --- a/android/install.md +++ b/android/install.md @@ -1,4 +1,125 @@ # Android Installation -## React-Native > `0.60.0` +## React-Native > `0.60.0` + If you are using autolinking feature introduced in React-Native `0.60.0` you do not need any additional steps. + +
+ +--- + +
+ +## Mapbox Maps SDK (pre v10) + +We've set up default Mapbox dependencies for you. +Feel free to check em out [here](https://github.com/react-native-mapbox-gl/maps/blob/eca4858744cab134b06ae455bcdacc63233318a5/android/rctmgl/build.gradle#L55-L76) + +However, it is also possible to set a custom version of the [Mapbox SDK](https://github.com/mapbox/mapbox-gl-native-android) +Which will overwrite our defaults. + +Add something like the following to your `android/build.gradle > buildscript > ext` section: + +```groovy +// android/build.gradle + +buildscript { + // ... stuff + ext { + // ... stuff + rnmbglMapboxLibs = { + implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.7.1' + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:5.8.0' + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-turf:5.8.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.7.0' + } + + rnmbglMapboxPlugins = { + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.8.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.14.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-markerview-v9:0.4.0' + } + // ... more stuff? + } +} +``` + +NOTICE, If you are using newer versions of the SDK, you will need to authorize your download of the Maps SDK via a secret access token with the `DOWNLOADS:READ` scope. +This [guide](https://docs.mapbox.com/android/maps/guides/install/#configure-credentials) explains how to `Configure credentials` and `Configure your secret token`. + +Then under section `allprojects/repositories` add your data: + +```groovy +// android/build.gradle + +allprojects { + repositories { + // ...other repos + maven { + url 'https://api.mapbox.com/downloads/v2/releases/maven' + authentication { + basic(BasicAuthentication) + } + credentials { + // Do not change the username below. + // This should always be `mapbox` (not your username). + username = 'mapbox' + // Use the secret token you stored in gradle.properties as the password + password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: "" + } + } + // ...even more repos? + } +} +``` + +Feel free to check out the `/example` projects [`android/build.gradle`](https://github.com/react-native-mapbox-gl/maps/blob/master/example/android/build.gradle) for inspiration! + +
+ +--- + +
+ +## Using MapLibre + +[MapLibre](https://github.com/maplibre/maplibre-gl-native) is an OSS fork of MapboxGL + +Overwrite mapbox dependecies within your `android/build.gradle > buildscript > ext` section + +```groovy +buildscript { + ext { + // ... + + rnmbglMapboxLibs = { + implementation ("org.maplibre.gl:android-sdk:9.2.1") + implementation ("com.mapbox.mapboxsdk:mapbox-sdk-turf:5.3.0") + } + + rnmbglMapboxPlugins = { + implementation ("com.mapbox.mapboxsdk:mapbox-android-gestures:0.7.0") + implementation ("com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.12.0") { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + implementation ("com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.8.0") { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + implementation ("com.mapbox.mapboxsdk:mapbox-android-plugin-markerview-v9:0.4.0") { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + } + } +} + +allprojects { + repositories { + // ... + maven { + url = "https://dl.bintray.com/maplibre/maplibre-gl-native" + } + } +} +``` + +Feel free to check out the `/example` projects [`android/build.gradle`](https://github.com/react-native-mapbox-gl/maps/blob/master/example/android/build.gradle) for inspiration! diff --git a/android/rctmgl/build.gradle b/android/rctmgl/build.gradle index 14fe2c8a33..362db0b568 100644 --- a/android/rctmgl/build.gradle +++ b/android/rctmgl/build.gradle @@ -28,6 +28,23 @@ android { } } +def customizableDependencies(name, defaultDependencies) { + if (rootProject.ext.has(name)) { + def libs = rootProject.ext.get(name) + if (libs instanceof CharSequence) { + libs.split(';').each { + implementation it + } + } else { + libs.delegate = defaultDependencies.owner.delegate + libs.call() + } + } else { + defaultDependencies.delegate = defaultDependencies.owner.delegate + defaultDependencies.call() + } +} + dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) @@ -35,13 +52,10 @@ dependencies { implementation "com.facebook.react:react-native:+" // Mapbox SDK - if (rootProject.ext.has('rnmbglMapboxLibs')) { - rootProject.ext.get('rnmbglMapboxLibs').split(';').each { - implementation it - } - } else { + customizableDependencies('rnmbglMapboxLibs') { implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:5.1.0' implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-telemetry:6.1.0' implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.8.0' } @@ -49,14 +63,12 @@ dependencies { implementation "com.android.support:support-vector-drawable:${safeExtGet('supportLibVersion', '28.0.0')}" implementation "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '28.0.0')}" implementation "com.android.support:appcompat-v7:${safeExtGet('supportLibVersion', '28.0.0')}" - implementation "com.squareup.okhttp3:okhttp:${safeExtGet('okhttpVersion', '3.12.1')}" + implementation "com.squareup.okhttp3:okhttp:${safeExtGet('okhttpVersion', '4.9.0')}" + implementation "com.squareup.okhttp3:okhttp-urlconnection:${safeExtGet('okhttpVersion', '4.9.0')}" + // Mapbox plugins - if (rootProject.ext.has('rnmbglMapboxPlugins')) { - rootProject.ext.get('rnmbglMapboxPlugins').split(';').each { - implementation it - } - } else { + customizableDependencies('rnmbglMapboxPlugins') { implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.6.0' implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.12.0' implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-markerview-v9:0.4.0' diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/MarkerViewManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/MarkerViewManager.java index 900083a0e3..ed806704c5 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/MarkerViewManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/MarkerViewManager.java @@ -8,17 +8,26 @@ import java.util.ArrayList; import java.util.List; +import java.lang.reflect.InvocationTargetException; + /** * Subclass of MarkerViewManager implementing removeViews and restoreViews */ public class MarkerViewManager extends com.mapbox.mapboxsdk.plugins.markerview.MarkerViewManager { private final List markers = new ArrayList<>(); private MapView mapView; + private java.lang.reflect.Method markerUpdate; public MarkerViewManager(MapView mapView, MapboxMap mapboxMap) { super(mapView, mapboxMap); this.mapView = mapView; // this.mapboxMap = mapboxMap; + + try { + markerUpdate = MarkerView.class.getSuperclass().getDeclaredMethod("update"); + markerUpdate.setAccessible(true); + } + catch (NoSuchMethodException e) {System.out.println(e.toString());} } public void addMarker(@NonNull MarkerView markerView) { @@ -42,4 +51,17 @@ public void restoreViews() { mapView.addView(marker.getView()); } } + + public void updateMarkers(){ + + try { + for( int i = 0; i < markers.size(); i++ ){ + markerUpdate.invoke(markers.get(i)); + } + } + catch (IllegalArgumentException e) { System.out.println(e.toString()); } + catch (IllegalAccessException e) { System.out.println(e.toString()); } + catch (InvocationTargetException e) { System.out.println(e.toString()); } + } + } \ No newline at end of file diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java index f6623b56c1..490122ae27 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java @@ -29,10 +29,10 @@ public class CameraStop { private LatLng mLatLng; private LatLngBounds mBounds; - private int mBoundsPaddingLeft = 0; - private int mBoundsPaddingRight = 0; - private int mBoundsPaddingBottom = 0; - private int mBoundsPaddingTop = 0; + private int mPaddingLeft = 0; + private int mPaddingRight = 0; + private int mPaddingBottom = 0; + private int mPaddingTop = 0; private int mMode = CameraMode.EASE; private int mDuration = 2000; @@ -65,12 +65,15 @@ public void setCallback(MapboxMap.CancelableCallback callback) { mCallback = callback; } - public void setBounds(LatLngBounds bounds, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) { + public void setBounds(LatLngBounds bounds) { mBounds = bounds; - mBoundsPaddingLeft = paddingLeft; - mBoundsPaddingRight = paddingRight; - mBoundsPaddingTop = paddingTop; - mBoundsPaddingBottom = paddingBottom; + } + + public void setPadding(int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) { + mPaddingLeft = paddingLeft; + mPaddingRight = paddingRight; + mPaddingTop = paddingTop; + mPaddingBottom = paddingBottom; } public void setMode(@CameraMode.Mode int mode) { @@ -82,32 +85,32 @@ public CameraUpdateItem toCameraUpdate(RCTMGLMapView mapView) { CameraPosition currentCamera = map.getCameraPosition(); CameraPosition.Builder builder = new CameraPosition.Builder(currentCamera); - if (mBearing != null) { - builder.bearing(mBearing); - } + // Adding map padding to the camera padding which is the same behavior as + // mapbox native does on iOS + double[] contentInset = mapView.getContentInset(); - if (mTilt != null) { - builder.tilt(mTilt); - } + int paddingLeft = Double.valueOf(contentInset[0] + mPaddingLeft).intValue(); + int paddingTop = Double.valueOf(contentInset[1] + mPaddingTop).intValue(); + int paddingRight = Double.valueOf(contentInset[2] + mPaddingRight).intValue(); + int paddingBottom = Double.valueOf(contentInset[3] + mPaddingBottom).intValue(); + + int[] cameraPadding = {paddingLeft, paddingTop, paddingRight, paddingBottom}; + int[] cameraPaddingClipped = clippedPadding(cameraPadding, mapView); + + boolean hasSetZoom = false; if (mLatLng != null) { builder.target(mLatLng); + builder.padding( + cameraPaddingClipped[0], + cameraPaddingClipped[1], + cameraPaddingClipped[2], + cameraPaddingClipped[3] + ); } else if (mBounds != null) { double tilt = mTilt != null ? mTilt : currentCamera.tilt; double bearing = mBearing != null ? mBearing : currentCamera.bearing; - // Adding map padding to the camera padding which is the same behavior as - // mapbox native does on iOS - double[] contentInset = mapView.getContentInset(); - - int paddingLeft = Double.valueOf(contentInset[0] + mBoundsPaddingLeft).intValue(); - int paddingTop = Double.valueOf(contentInset[1] + mBoundsPaddingTop).intValue(); - int paddingRight = Double.valueOf(contentInset[2] + mBoundsPaddingRight).intValue(); - int paddingBottom = Double.valueOf(contentInset[3] + mBoundsPaddingBottom).intValue(); - - int[] cameraPadding = {paddingLeft, paddingTop, paddingRight, paddingBottom}; - int[] cameraPaddingClipped = clippedPadding(cameraPadding, mapView); - CameraPosition boundsCamera = map.getCameraForLatLngBounds(mBounds, cameraPaddingClipped, bearing, tilt); if (boundsCamera != null) { builder.target(boundsCamera.target); @@ -115,17 +118,26 @@ public CameraUpdateItem toCameraUpdate(RCTMGLMapView mapView) { builder.padding(boundsCamera.padding); } else { CameraUpdate update = CameraUpdateFactory.newLatLngBounds( - mBounds, - cameraPaddingClipped[0], - cameraPaddingClipped[1], - cameraPaddingClipped[2], - cameraPaddingClipped[3] + mBounds, + cameraPaddingClipped[0], + cameraPaddingClipped[1], + cameraPaddingClipped[2], + cameraPaddingClipped[3] ); return new CameraUpdateItem(map, update, mDuration, mCallback, mMode); } + hasSetZoom = true; + } + + if (mBearing != null) { + builder.bearing(mBearing); + } + + if (mTilt != null) { + builder.tilt(mTilt); } - if (mZoom != null) { + if (mZoom != null && !hasSetZoom) { builder.zoom(mZoom); } @@ -143,6 +155,25 @@ public static CameraStop fromReadableMap(Context context, @NonNull ReadableMap r stop.setBearing(readableMap.getDouble("heading")); } + int paddingTop = getPaddingByKey(readableMap, "paddingTop"); + int paddingRight = getPaddingByKey(readableMap, "paddingRight"); + int paddingBottom = getPaddingByKey(readableMap, "paddingBottom"); + int paddingLeft = getPaddingByKey(readableMap, "paddingLeft"); + + // scale padding by pixel ratio + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + paddingTop = Float.valueOf(paddingTop * metrics.scaledDensity).intValue(); + paddingRight = Float.valueOf(paddingRight * metrics.scaledDensity).intValue(); + paddingBottom = Float.valueOf(paddingBottom * metrics.scaledDensity).intValue(); + paddingLeft = Float.valueOf(paddingLeft * metrics.scaledDensity).intValue(); + + stop.setPadding( + paddingLeft, + paddingRight, + paddingTop, + paddingBottom + ); + if (readableMap.hasKey("centerCoordinate")) { Point target = GeoJSONUtils.toPointGeometry(readableMap.getString("centerCoordinate")); stop.setLatLng(GeoJSONUtils.toLatLng(target)); @@ -157,21 +188,8 @@ public static CameraStop fromReadableMap(Context context, @NonNull ReadableMap r } if (readableMap.hasKey("bounds")) { - int paddingTop = getBoundsPaddingByKey(readableMap, "boundsPaddingTop"); - int paddingRight = getBoundsPaddingByKey(readableMap, "boundsPaddingRight"); - int paddingBottom = getBoundsPaddingByKey(readableMap, "boundsPaddingBottom"); - int paddingLeft = getBoundsPaddingByKey(readableMap, "boundsPaddingLeft"); - - // scale padding by pixel ratio - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - paddingTop = Float.valueOf(paddingTop * metrics.scaledDensity).intValue(); - paddingRight = Float.valueOf(paddingRight * metrics.scaledDensity).intValue(); - paddingBottom = Float.valueOf(paddingBottom * metrics.scaledDensity).intValue(); - paddingLeft = Float.valueOf(paddingLeft * metrics.scaledDensity).intValue(); - FeatureCollection collection = FeatureCollection.fromJson(readableMap.getString("bounds")); - stop.setBounds(GeoJSONUtils.toLatLngBounds(collection), paddingLeft, paddingRight, - paddingTop, paddingBottom); + stop.setBounds(GeoJSONUtils.toLatLngBounds(collection)); } if (readableMap.hasKey("mode")) { @@ -179,6 +197,9 @@ public static CameraStop fromReadableMap(Context context, @NonNull ReadableMap r case CameraMode.FLIGHT: stop.setMode(CameraMode.FLIGHT); break; + case CameraMode.LINEAR: + stop.setMode(CameraMode.LINEAR); + break; case CameraMode.NONE: stop.setMode(CameraMode.NONE); break; @@ -222,7 +243,7 @@ private static int[] clippedPadding(int[] padding, RCTMGLMapView mapView) { return new int[] {resultLeft, resultTop, resultRight, resultBottom}; } - private static int getBoundsPaddingByKey(ReadableMap map, String key) { + private static int getPaddingByKey(ReadableMap map, String key) { return map.hasKey(key) ? map.getInt(key) : 0; } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java index 11053f4630..4bfb472bd4 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java @@ -76,8 +76,10 @@ public void onFinish() { if (mCameraMode == CameraMode.FLIGHT) { map.animateCamera(mCameraUpdate, duration, callback); + } else if (mCameraMode == CameraMode.LINEAR) { + map.easeCamera(mCameraUpdate, duration, false, callback); } else if (mCameraMode == CameraMode.EASE) { - map.easeCamera(mCameraUpdate, duration, callback); + map.easeCamera(mCameraUpdate, duration, true, callback); } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java index daa51cb2ef..7bf17454e7 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java @@ -327,7 +327,7 @@ public void onFinish() { }; if (isAnimated) { - mMapView.easeCamera(cameraUpdate, USER_LOCATION_CAMERA_MOVE_DURATION, callback); + mMapView.easeCamera(cameraUpdate, USER_LOCATION_CAMERA_MOVE_DURATION, true, callback); } else { mMapView.moveCamera(cameraUpdate, callback); } @@ -517,7 +517,10 @@ private WritableMap makeLocationChangePayload(Location location) { coords.putDouble("latitude", location.getLatitude()); coords.putDouble("altitude", location.getAltitude()); coords.putDouble("accuracy", location.getAccuracy()); + // A better solution will be to pull the heading from the compass engine, + // unfortunately the api is not publicly available in the mapbox sdk coords.putDouble("heading", location.getBearing()); + coords.putDouble("course", location.getBearing()); coords.putDouble("speed", location.getSpeed()); positionProperties.putMap("coords", coords); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/constants/CameraMode.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/constants/CameraMode.java index 016fa7096d..4d0bc1bcc4 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/constants/CameraMode.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/constants/CameraMode.java @@ -11,11 +11,12 @@ public class CameraMode { - @IntDef({ FLIGHT, EASE, NONE }) + @IntDef({ FLIGHT, EASE, LINEAR, NONE }) @Retention(RetentionPolicy.SOURCE) public @interface Mode {} public static final int FLIGHT = 1; public static final int EASE = 2; - public static final int NONE = 3; + public static final int LINEAR = 3; + public static final int NONE = 4; } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/LocationComponentManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/LocationComponentManager.java index 69fe1d50fc..2cf8f0c410 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/LocationComponentManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/LocationComponentManager.java @@ -1,11 +1,8 @@ package com.mapbox.rctmgl.components.location; +import android.annotation.SuppressLint; import android.content.Context; -import android.location.Location; -import androidx.annotation.NonNull; - -import com.mapbox.android.core.permissions.PermissionsManager; import com.mapbox.mapboxsdk.location.LocationComponent; import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions; import com.mapbox.mapboxsdk.location.LocationComponentOptions; @@ -17,7 +14,8 @@ import com.mapbox.rctmgl.R; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.location.LocationManager; -import com.mapbox.rctmgl.location.UserTrackingMode; + +import androidx.annotation.NonNull; /** * The LocationComponent on android implements both location tracking and display of user's current location. @@ -32,8 +30,6 @@ public class LocationComponentManager { private LocationComponent mLocationComponent = null; private Context mContext = null; - // state - private @CameraMode.Mode int mCameraMode = CameraMode.NONE; private @RenderMode.Mode int mRenderMode = RenderMode.COMPASS; public LocationComponentManager(RCTMGLMapView rctmglMapView, Context context) { @@ -75,6 +71,7 @@ public void addOnCameraTrackingChangedListener(OnCameraTrackingChangedListener o mLocationComponent.addOnCameraTrackingChangedListener(onCameraTrackingChangedListener); } + @SuppressLint("MissingPermission") private void stateChanged() { mLocationComponent.setLocationComponentEnabled((mFollowUserLocation || mShowUserLocation)); @@ -98,20 +95,14 @@ public boolean hasLocationComponent() { return (mLocationComponent != null); } - public void forceLocationUpdate(Location location) { - mLocationComponent.forceLocationUpdate(location); - } - public void update(@NonNull Style style) { - if (mLocationComponent == null) { - update(mShowUserLocation, style); - } else { - update(mShowUserLocation, style); - } + update(mShowUserLocation, style); } public void update(boolean displayUserLocation, @NonNull Style style) { - if (mLocationComponent == null) { + Integer tintColor = mMapView.getTintColor(); + + if (mLocationComponent == null || tintColor != null ) { mLocationComponent = mMap.getLocationComponent(); LocationComponentActivationOptions locationComponentActivationOptions = LocationComponentActivationOptions @@ -135,6 +126,7 @@ private void updateShowUserLocation(boolean displayUserLocation) { LocationComponentOptions options(boolean displayUserLocation) { LocationComponentOptions.Builder builder = LocationComponentOptions.builder(mContext); + Integer tintColor = mMapView.getTintColor(); if (!displayUserLocation) { builder = builder .padding(mMap.getPadding()) @@ -145,6 +137,12 @@ LocationComponentOptions options(boolean displayUserLocation) { .foregroundDrawableStale(R.drawable.empty) .gpsDrawable(R.drawable.empty) .accuracyAlpha(0.0f); + } else if (tintColor != null) { + builder = builder + .enableStaleState(false) + .bearingTintColor(tintColor) + .foregroundTintColor(tintColor) + .accuracyColor(tintColor); } return builder.build(); } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocation.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocation.java index 559c4823e4..19700fe3f5 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocation.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocation.java @@ -5,8 +5,6 @@ import androidx.annotation.NonNull; import com.mapbox.android.core.permissions.PermissionsManager; -import com.mapbox.mapboxsdk.location.LocationComponent; -import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions; import com.mapbox.mapboxsdk.location.modes.RenderMode; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -54,6 +52,7 @@ public void onStyleLoaded(@NonNull Style style) { } LocationComponentManager locationComponent = mMapView.getLocationComponentManager(); + locationComponent.update(style); locationComponent.showUserLocation(mEnabled); } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocationManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocationManager.java index 074cd70c67..834246537c 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocationManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/location/RCTMGLNativeUserLocationManager.java @@ -8,7 +8,7 @@ import javax.annotation.Nonnull; public class RCTMGLNativeUserLocationManager extends ViewGroupManager { - public static final String REACT_CLASS = RCTMGLNativeUserLocation.class.getSimpleName(); + public static final String REACT_CLASS = "RCTMGLNativeUserLocation"; @Nonnull @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapView.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapView.java index 08a1ec8efa..df81804e44 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapView.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapView.java @@ -8,7 +8,7 @@ @SuppressWarnings({"MissingPermission"}) public class RCTMGLAndroidTextureMapView extends RCTMGLMapView { - public static final String LOG_TAG = RCTMGLAndroidTextureMapView.class.getSimpleName(); + public static final String LOG_TAG = "RCTMGLAndroidTextureMapView"; public RCTMGLAndroidTextureMapView(Context context, RCTMGLAndroidTextureMapViewManager manager, MapboxMapOptions options) { super(context, manager, options); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapViewManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapViewManager.java index f96af25b7f..39964b87ad 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapViewManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLAndroidTextureMapViewManager.java @@ -10,7 +10,7 @@ */ public class RCTMGLAndroidTextureMapViewManager extends RCTMGLMapViewManager { - public static final String LOG_TAG = RCTMGLAndroidTextureMapViewManager.class.getSimpleName(); + public static final String LOG_TAG = "RCTMGLAndroidTextureMapViewManager"; public static final String REACT_CLASS = "RCTMGLAndroidTextureMapView"; public RCTMGLAndroidTextureMapViewManager(ReactApplicationContext context) { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java index c896fad4ab..ef3548938b 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java @@ -80,6 +80,7 @@ import java.util.List; import java.util.Map; import java.util.Locale; +import org.json.*; import javax.annotation.Nullable; @@ -96,7 +97,7 @@ public class RCTMGLMapView extends MapView implements OnMapReadyCallback, Mapbox MapView.OnWillStartRenderingFrameListener, MapView.OnDidFinishRenderingFrameListener, MapView.OnWillStartRenderingMapListener, MapView.OnDidFinishRenderingMapListener, MapView.OnDidFinishLoadingStyleListener, MapView.OnStyleImageMissingListener { - public static final String LOG_TAG = RCTMGLMapView.class.getSimpleName(); + public static final String LOG_TAG = "RCTMGLMapView"; private RCTMGLMapViewManager mManager; private Context mContext; @@ -130,6 +131,8 @@ public class RCTMGLMapView extends MapView implements OnMapReadyCallback, Mapbox private Integer mAttributionGravity; private int[] mAttributionMargin; private Boolean mLogoEnabled; + private Integer mLogoGravity; + private int[] mLogoMargins; private Boolean mCompassEnabled; private ReadableMap mCompassViewMargins; private int mCompassViewPosition = -1; @@ -150,6 +153,8 @@ public class RCTMGLMapView extends MapView implements OnMapReadyCallback, Mapbox private LocationComponentManager mLocationComponentManager = null; + private @Nullable Integer mTintColor = null; + public RCTMGLMapView(Context context, RCTMGLMapViewManager manager, MapboxMapOptions options) { super(context, options); @@ -342,8 +347,8 @@ public void moveCamera(CameraUpdate cameraUpdate) { mMap.moveCamera(cameraUpdate); } - public void easeCamera(CameraUpdate cameraUpdate, int duration, MapboxMap.CancelableCallback callback) { - mMap.easeCamera(cameraUpdate, duration, callback); + public void easeCamera(CameraUpdate cameraUpdate, int duration, boolean easingInterpolator, MapboxMap.CancelableCallback callback) { + mMap.easeCamera(cameraUpdate, duration, easingInterpolator, callback); } public void easeCamera(CameraUpdate cameraUpdate) { @@ -418,16 +423,27 @@ public void waitForLayer(String layerID, FoundLayerCallback callback) { } } + public boolean isJSONValid(String test) { + try { + new JSONObject(test); + } catch (JSONException ex) { + return false; + } + return true; + } + + @Override public void onMapReady(final MapboxMap mapboxMap) { mMap = mapboxMap; - //if the myStyleURL contains "mapbox" the library will get the style from internet, else myStyleURL is a json and the library will load it - if(mStyleURL.substring(0,6).contains("mapbox")) - mMap.setStyle(new Style.Builder().fromUrl(mStyleURL)); - else + if (isJSONValid(mStyleURL)) { mMap.setStyle(new Style.Builder().fromJson(mStyleURL)); - + } else { + mMap.setStyle(new Style.Builder().fromUri(mStyleURL)); + } + + reflow(); mMap.getStyle(new Style.OnStyleLoaded() { @@ -459,6 +475,15 @@ public void onCameraMoveStarted(int reason) { } }); + mMap.addOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() { + @Override + public void onCameraMove() { + if (markerViewManager != null) { + markerViewManager.updateMarkers(); + } + } + }); + mMap.addOnMoveListener(new MapboxMap.OnMoveListener() { @Override public void onMoveBegin(MoveGestureDetector detector) { @@ -758,12 +783,21 @@ public void setReactStyleURL(String styleURL) { if (mMap != null) { removeAllSourcesFromMap(); - mMap.setStyle(styleURL, new Style.OnStyleLoaded() { - @Override - public void onStyleLoaded(@NonNull Style style) { - addAllSourcesToMap(); - } - }); + if (isJSONValid(mStyleURL)) { + mMap.setStyle(new Style.Builder().fromJson(mStyleURL), new Style.OnStyleLoaded() { + @Override + public void onStyleLoaded(@NonNull Style style) { + addAllSourcesToMap(); + } + }); + } else { + mMap.setStyle(styleURL, new Style.OnStyleLoaded() { + @Override + public void onStyleLoaded(@NonNull Style style) { + addAllSourcesToMap(); + } + }); + } } } @@ -806,6 +840,41 @@ public void setReactLogoEnabled(boolean logoEnabled) { updateUISettings(); } + public void setReactLogoPosition(ReadableMap position) { + if (position == null) { + // reset from explicit to default + if (mLogoGravity != null) { + MapboxMapOptions defaultOptions = MapboxMapOptions.createFromAttributes(mContext); + mLogoGravity = defaultOptions.getLogoGravity(); + mLogoMargins = Arrays.copyOf(defaultOptions.getLogoMargins(), 4); + updateUISettings(); + } + return; + } + + mLogoGravity = Gravity.NO_GRAVITY; + if (position.hasKey("left")) { + mLogoGravity |= Gravity.START; + } + if (position.hasKey("right")) { + mLogoGravity |= Gravity.END; + } + if (position.hasKey("top")) { + mLogoGravity |= Gravity.TOP; + } + if (position.hasKey("bottom")) { + mLogoGravity |= Gravity.BOTTOM; + } + float density = getDisplayDensity(); + mLogoMargins = new int[]{ + position.hasKey("left") ? (int) density * position.getInt("left") : 0, + position.hasKey("top") ? (int) density * position.getInt("top") : 0, + position.hasKey("right") ? (int) density * position.getInt("right") : 0, + position.hasKey("bottom") ? (int) density * position.getInt("bottom") : 0 + }; + updateUISettings(); + } + public void setReactCompassEnabled(boolean compassEnabled) { mCompassEnabled = compassEnabled; updateUISettings(); @@ -1059,10 +1128,33 @@ private void updateUISettings() { ); } + if (mTintColor != null) { + uiSettings.setAttributionTintColor(mTintColor); + } + if (mLogoEnabled != null && uiSettings.isLogoEnabled() != mLogoEnabled) { uiSettings.setLogoEnabled(mLogoEnabled); } + if (mLogoGravity != null && uiSettings.getLogoGravity() != mLogoGravity) { + uiSettings.setLogoGravity(mLogoGravity); + } + + if (mLogoMargins != null && + (uiSettings.getLogoMarginLeft() != mLogoMargins[0] || + uiSettings.getLogoMarginTop() != mLogoMargins[1] || + uiSettings.getLogoMarginRight() != mLogoMargins[2] || + uiSettings.getLogoMarginBottom() != mLogoMargins[3] + ) + ) { + uiSettings.setLogoMargins( + mLogoMargins[0], + mLogoMargins[1], + mLogoMargins[2], + mLogoMargins[3] + ); + } + if (mCompassEnabled != null && uiSettings.isCompassEnabled() != mCompassEnabled) { uiSettings.setCompassEnabled(mCompassEnabled); } @@ -1076,7 +1168,7 @@ private void updateUISettings() { uiSettings.setCompassGravity(Gravity.TOP | Gravity.END); break; case 2: - uiSettings.setCompassGravity(Gravity.BOTTOM | Gravity.END); + uiSettings.setCompassGravity(Gravity.BOTTOM | Gravity.START); break; case 3: uiSettings.setCompassGravity(Gravity.BOTTOM | Gravity.END); @@ -1125,7 +1217,7 @@ private void updatePreferredFramesPerSecond() { public double[] getContentInset() { if (mInsets == null) { double[] result = {0,0,0,0}; - + return result; } double top = 0, right = 0, bottom = 0, left = 0; @@ -1148,7 +1240,7 @@ public double[] getContentInset() { } final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); - + double[] result = {left * metrics.scaledDensity, top * metrics.scaledDensity, right * metrics.scaledDensity, bottom * metrics.scaledDensity}; return result; } @@ -1363,7 +1455,10 @@ private WritableMap makeLocationChangePayload(Location location) { coords.putDouble("latitude", location.getLatitude()); coords.putDouble("altitude", location.getAltitude()); coords.putDouble("accuracy", location.getAccuracy()); + // A better solution will be to pull the heading from the compass engine, + // unfortunately the api is not publicly available in the mapbox sdk coords.putDouble("heading", location.getBearing()); + coords.putDouble("course", location.getBearing()); coords.putDouble("speed", location.getSpeed()); positionProperties.putMap("coords", coords); @@ -1413,4 +1508,16 @@ public LocationComponentManager getLocationComponentManager() { } return mLocationComponentManager; } + + public @Nullable Integer getTintColor() { + return mTintColor; + } + + public void setTintColor(@Nullable Integer tintColor) { + if (mTintColor == tintColor) return; + mTintColor = tintColor; + updateUISettings(); + if (mLocationComponentManager == null) return; + mLocationComponentManager.update(getMapboxMap().getStyle()); + } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java index c6cd92c035..3e90d253ee 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java @@ -38,7 +38,7 @@ */ public class RCTMGLMapViewManager extends AbstractEventEmitter { - public static final String LOG_TAG = RCTMGLMapViewManager.class.getSimpleName(); + public static final String LOG_TAG = "RCTMGLMapViewManager"; public static final String REACT_CLASS = "RCTMGLMapView"; private Map mViews; @@ -165,6 +165,11 @@ public void setLogoEnabled(RCTMGLMapView mapView, boolean logoEnabled) { mapView.setReactLogoEnabled(logoEnabled); } + @ReactProp(name="logoPosition") + public void setLogoPosition(RCTMGLMapView mapView, ReadableMap logoPosition) { + mapView.setReactLogoPosition(logoPosition); + } + @ReactProp(name="compassEnabled") public void setCompassEnabled(RCTMGLMapView mapView, boolean compassEnabled) { mapView.setReactCompassEnabled(compassEnabled); @@ -185,6 +190,11 @@ public void setContentInset(RCTMGLMapView mapView, ReadableArray array) { mapView.setReactContentInset(array); } + @ReactProp(name = "tintColor", customType = "Color") + public void setTintColor(RCTMGLMapView mapView, @Nullable Integer tintColor) { + mapView.setTintColor(tintColor); + } + //endregion //region Custom Events @@ -332,7 +342,7 @@ public void run() } catch (Exception ex) { - Log.e(getClass().getSimpleName() , " disposeNativeMapView() exception destroying map view", ex); + Log.e(LOG_TAG , " disposeNativeMapView() exception destroying map view", ex); } } }); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java index f3560322a1..37a166272a 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java @@ -204,7 +204,7 @@ public TransitionOptions getTransition() { duration = config.getMap("duration").getInt("value"); } if (config.hasKey("delay") && ReadableType.Map.equals(config.getType("delay"))) { - duration = config.getMap("delay").getInt("value"); + delay = config.getMap("delay").getInt("value"); } return new TransitionOptions(duration, delay, enablePlacementTransitions); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java index 249e89402b..477197525f 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java @@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.common.logging.FLog; +import com.mapbox.mapboxsdk.location.LocationComponentConstants; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.style.expressions.Expression; @@ -13,7 +14,6 @@ import com.mapbox.mapboxsdk.style.layers.PropertyFactory; import com.mapbox.rctmgl.components.AbstractMapFeature; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; -import com.mapbox.rctmgl.location.UserLocationLayerConstants; import com.mapbox.rctmgl.utils.ExpressionParser; import java.util.Arrays; @@ -25,7 +25,7 @@ */ public abstract class RCTLayer extends AbstractMapFeature { - public static final String LOG_TAG = RCTLayer.class.getSimpleName(); + public static final String LOG_TAG = "RCTLayer"; protected String mID; protected String mSourceID; @@ -155,7 +155,7 @@ public void add() { } if (getStyle() == null) return; - String userBackgroundID = UserLocationLayerConstants.BACKGROUND_LAYER_ID; + String userBackgroundID = LocationComponentConstants.BACKGROUND_LAYER; Layer userLocationBackgroundLayer = getStyle().getLayer(userBackgroundID); // place below user location layer diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java index 2d97fdb611..d39c22ef30 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java @@ -19,7 +19,7 @@ */ public class RCTMGLImageSource extends RCTSource { - public static final String LOG_TAG = RCTMGLImageSource.class.getSimpleName(); + public static final String LOG_TAG = "RCTMGLImageSource"; private URL mURL; private int mResourceId; @@ -70,8 +70,12 @@ public void setURL(String url) { public void setCoordinates(LatLngQuad coordQuad) { mCoordQuad = coordQuad; - if (mSource != null) { - mSource.setCoordinates(this.mCoordQuad); + try { + if (mSource != null) { + mSource.setCoordinates(this.mCoordQuad); + } + } catch (Exception e) { + Log.w(LOG_TAG, e.getLocalizedMessage()); } } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java index c5467dd0a2..e705ac8180 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java @@ -48,6 +48,7 @@ public class RCTMGLShapeSource extends RCTSource { private Integer mMaxZoom; private Integer mBuffer; private Double mTolerance; + private Boolean mLineMetrics; private static Bitmap mImagePlaceholder; private List> mImages; @@ -121,6 +122,10 @@ public void setTolerance(double tolerance) { mTolerance = tolerance; } + public void setLineMetrics(boolean lineMetrics) { + mLineMetrics = lineMetrics; + } + public void onPress(OnPressEvent event) { mManager.handleEvent(FeatureClickEvent.makeShapeSourceEvent(this, event)); } @@ -152,6 +157,10 @@ private GeoJsonOptions getOptions() { options.withTolerance(mTolerance.floatValue()); } + if (mLineMetrics != null) { + options.withLineMetrics(mLineMetrics); + } + return options; } @@ -171,4 +180,123 @@ public void querySourceFeatures(String callbackID, AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); mManager.handleEvent(event); } + + public void getClusterExpansionZoom(String callbackID, String featureJSON) { + if (mSource == null) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "source is not yet loaded"); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + Feature feature = Feature.fromJson(featureJSON); + + int zoom = mSource.getClusterExpansionZoom(feature); + + WritableMap payload = new WritableNativeMap(); + payload.putInt("data", zoom); + + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + } + + public void getClusterLeaves(String callbackID, String featureJSON, int number, int offset) { + if (mSource == null) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "source is not yet loaded"); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + Feature clusterFeature = Feature.fromJson(featureJSON); + FeatureCollection leaves = mSource.getClusterLeaves(clusterFeature, number, offset); + WritableMap payload = new WritableNativeMap(); + payload.putString("data", leaves.toJson()); + + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + } + + public void getClusterChildren(String callbackID, String featureJSON) { + if (mSource == null) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "source is not yet loaded"); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + Feature clusterFeature = Feature.fromJson(featureJSON); + FeatureCollection leaves = mSource.getClusterChildren(clusterFeature); + WritableMap payload = new WritableNativeMap(); + payload.putString("data", leaves.toJson()); + + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + } + + // Deprecated. Will be removed in 9+ ver. + public void getClusterExpansionZoomById(String callbackID, int clusterId) { + if (mSource == null) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "source is not yet loaded"); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + List features = mSource.querySourceFeatures(Expression.eq(Expression.id(), clusterId)); + int zoom = -1; + if (features.size() > 0) { + zoom = mSource.getClusterExpansionZoom(features.get(0)); + } + + if (zoom == -1) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "Could not get zoom for cluster id " + clusterId); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + + WritableMap payload = new WritableNativeMap(); + payload.putInt("data", zoom); + + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + } + + // Deprecated. Will be removed in 9+ ver. + public void getClusterLeavesById(String callbackID, int clusterId, int number, int offset) { + if (mSource == null) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "source is not yet loaded"); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + Feature clusterFeature = mSource.querySourceFeatures(Expression.eq(Expression.get("cluster_id"), clusterId)).get(0); + FeatureCollection leaves = mSource.getClusterLeaves(clusterFeature, number, offset); + WritableMap payload = new WritableNativeMap(); + payload.putString("data", leaves.toJson()); + + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + } + + // Deprecated. Will be removed in 9+ ver. + public void getClusterChildrenById(String callbackID, int clusterId) { + if (mSource == null) { + WritableMap payload = new WritableNativeMap(); + payload.putString("error", "source is not yet loaded"); + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + return; + } + Feature clusterFeature = mSource.querySourceFeatures(Expression.eq(Expression.get("cluster_id"), clusterId)).get(0); + FeatureCollection leaves = mSource.getClusterChildren(clusterFeature); + WritableMap payload = new WritableNativeMap(); + payload.putString("data", leaves.toJson()); + + AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, payload); + mManager.handleEvent(event); + } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSourceManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSourceManager.java index 75669814a6..1b6525b0d2 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSourceManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSourceManager.java @@ -39,7 +39,7 @@ */ public class RCTMGLShapeSourceManager extends AbstractEventEmitter { - public static final String LOG_TAG = RCTMGLShapeSourceManager.class.getSimpleName(); + public static final String LOG_TAG = "RCTMGLShapeSourceManager"; public static final String REACT_CLASS = "RCTMGLShapeSource"; private ReactApplicationContext mContext; @@ -128,6 +128,11 @@ public void setTolerance(RCTMGLShapeSource source, double tolerance) { source.setTolerance(tolerance); } + @ReactProp(name = "lineMetrics") + public void setLineMetrics(RCTMGLShapeSource source, boolean lineMetrics) { + source.setLineMetrics(lineMetrics); + } + @ReactProp(name = "hasPressListener") public void setHasPressListener(RCTMGLShapeSource source, boolean hasPressListener) { source.setHasPressListener(hasPressListener); @@ -148,12 +153,29 @@ public Map customEvents() { //region React Methods public static final int METHOD_FEATURES = 103; + public static final int METHOD_GET_CLUSTER_EXPANSION_ZOOM = 104; + public static final int METHOD_GET_CLUSTER_LEAVES = 105; + public static final int METHOD_GET_CLUSTER_CHILDREN = 106; + + // Deprecated. Will be removed in 9+ ver. + public static final int METHOD_GET_CLUSTER_EXPANSION_ZOOM_BY_ID = 107; + public static final int METHOD_GET_CLUSTER_LEAVES_BY_ID = 108; + public static final int METHOD_GET_CLUSTER_CHILDREN_BY_ID = 109; @Nullable @Override public Map getCommandsMap() { return MapBuilder.builder() .put("features", METHOD_FEATURES) + .put("getClusterExpansionZoom", METHOD_GET_CLUSTER_EXPANSION_ZOOM) + .put("getClusterLeaves", METHOD_GET_CLUSTER_LEAVES) + .put("getClusterChildren", METHOD_GET_CLUSTER_CHILDREN) + + // Deprecated. Will be removed in 9+ ver. + .put("getClusterExpansionZoomById", METHOD_GET_CLUSTER_EXPANSION_ZOOM_BY_ID) + .put("getClusterLeavesById", METHOD_GET_CLUSTER_LEAVES_BY_ID) + .put("getClusterChildrenById", METHOD_GET_CLUSTER_CHILDREN_BY_ID) + .build(); } @@ -164,7 +186,41 @@ public void receiveCommand(RCTMGLShapeSource source, int commandID, @Nullable Re source.querySourceFeatures( args.getString(0), ExpressionParser.from(args.getArray(1)) - ); + ); + break; + case METHOD_GET_CLUSTER_EXPANSION_ZOOM: + source.getClusterExpansionZoom(args.getString(0), args.getString(1)); + break; + case METHOD_GET_CLUSTER_LEAVES: + source.getClusterLeaves( + args.getString(0), + args.getString(1), + args.getInt(2), + args.getInt((3)) + ); + break; + case METHOD_GET_CLUSTER_CHILDREN: + source.getClusterChildren( + args.getString(0), + args.getString(1) + ); + break; + case METHOD_GET_CLUSTER_EXPANSION_ZOOM_BY_ID: + source.getClusterExpansionZoomById(args.getString(0), args.getInt(1)); + break; + case METHOD_GET_CLUSTER_LEAVES_BY_ID: + source.getClusterLeavesById( + args.getString(0), + args.getInt(1), + args.getInt(2), + args.getInt((3)) + ); + break; + case METHOD_GET_CLUSTER_CHILDREN_BY_ID: + source.getClusterChildrenById( + args.getString(0), + args.getInt(1) + ); break; } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java index 92ce50dd07..6b8d8c197a 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java @@ -10,6 +10,7 @@ import com.facebook.react.common.MapBuilder; import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.log.Logger; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.style.sources.Source; @@ -28,6 +29,7 @@ public abstract class RCTSource extends AbstractMapFeature { public static final String DEFAULT_ID = "composite"; + public static final String LOG_TAG = "RCTSource"; public static final double DEFAULT_HITBOX_WIDTH = 44.0; public static final double DEFAULT_HITBOX_HEIGHT = 44.0; @@ -155,7 +157,11 @@ public void removeFromMap(RCTMGLMapView mapView) { mQueuedLayers.clear(); } if (mMap != null && mSource != null && mMap.getStyle() != null) { - mMap.getStyle().removeSource(mSource); + try { + mMap.getStyle().removeSource(mSource); + } catch (Throwable ex) { + Logger.w(LOG_TAG, String.format("RCTSource.removeFromMap: %s - %s", mSource, ex.getMessage()), ex); + } } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java index f33f61b51c..da009739ea 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java @@ -11,7 +11,7 @@ public class EventEmitter { - public static final String LOG_TAG = EventEmitter.class.getSimpleName(); + public static final String LOG_TAG = "EventEmitter"; private static ReactContext getCurrentReactContext(ReactApplicationContext reactApplicationContext) { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java index c6f1d601d1..91718dc3c4 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java @@ -73,7 +73,10 @@ public WritableMap getPayload() { coords.putDouble("latitude", location.getLatitude()); coords.putDouble("altitude", location.getAltitude()); coords.putDouble("accuracy", location.getAccuracy()); + // A better solution will be to pull the heading from the compass engine, + // unfortunately the api is not publicly available in the mapbox sdk coords.putDouble("heading", location.getBearing()); + coords.putDouble("course", location.getBearing()); coords.putDouble("speed", location.getSpeed()); positionProperties.putMap("coords", coords); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java index 1489a50d93..e06b531459 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java @@ -32,7 +32,7 @@ public class LocationManager implements LocationEngineCallback getConstants() { Map cameraModes = new HashMap<>(); cameraModes.put("Flight", CameraMode.FLIGHT); cameraModes.put("Ease", CameraMode.EASE); + cameraModes.put("Linear", CameraMode.LINEAR); cameraModes.put("None", CameraMode.NONE); // style source constants diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java index 6d9d2b7ece..7e1be28402 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java @@ -71,6 +71,16 @@ public String getName () { return REACT_CLASS; } + @ReactMethod + public void addListener(String eventName) { + // Set up any upstream listeners or background tasks as necessary + } + + @ReactMethod + public void removeListeners(Integer count) { + // Remove upstream listeners, stop unnecessary background tasks + } + @ReactMethod public void createPack(ReadableMap options, final Promise promise) { final String name = ConvertUtils.getString("name", options, ""); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java index ef2e77212c..5551bd1e67 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java @@ -25,7 +25,7 @@ */ public class BitmapUtils { - public static final String LOG_TAG = BitmapUtils.class.getSimpleName(); + public static final String LOG_TAG = "BitmapUtils"; private static int CACHE_SIZE = 1024 * 1024; private static LruCache mCache = new LruCache(CACHE_SIZE) { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java index 4014f5d1a2..3c739dd5c6 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java @@ -28,7 +28,7 @@ */ public class ConvertUtils { - public static final String LOG_TAG = ConvertUtils.class.getSimpleName(); + public static final String LOG_TAG = "ConvertUtils"; public static JsonObject toJsonObject(ReadableMap map) { if (map == null) return null; diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java index 097d45e32c..c13fae5bc8 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java @@ -38,7 +38,7 @@ */ public class DownloadMapImageTask extends AsyncTask, Void, List>> { - public static final String LOG_TAG = DownloadMapImageTask.class.getSimpleName(); + public static final String LOG_TAG = "DownloadMapImageTask"; private WeakReference mContext; private WeakReference mMap; diff --git a/app.plugin.js b/app.plugin.js new file mode 100644 index 0000000000..d1e04f17b1 --- /dev/null +++ b/app.plugin.js @@ -0,0 +1 @@ +module.exports = require('./plugin/build/withMapbox'); diff --git a/assets/indoor_building_map_android.png b/assets/indoor_building_map_android.png new file mode 100644 index 0000000000..8ad8d94af8 Binary files /dev/null and b/assets/indoor_building_map_android.png differ diff --git a/assets/indoor_building_map_ios.png b/assets/indoor_building_map_ios.png new file mode 100644 index 0000000000..966c6d402f Binary files /dev/null and b/assets/indoor_building_map_ios.png differ diff --git a/babel.config.js b/babel.config.js index 2df2b18b90..95dd5a64e5 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,12 +1,6 @@ module.exports = { presets: ['module:metro-react-native-babel-preset'], - plugins: [ - // Use @babel/preset-flow when - // https://github.com/babel/babel/issues/7233 is fixed - '@babel/plugin-transform-flow-strip-types', - ['@babel/plugin-proposal-class-properties', {loose: true}], - '@babel/plugin-transform-exponentiation-operator', - ], + plugins: [['@babel/plugin-proposal-class-properties', {loose: true}]], env: { production: { plugins: ['transform-remove-console'], diff --git a/circle.yml b/circle.yml deleted file mode 100644 index fa2af1f0e5..0000000000 --- a/circle.yml +++ /dev/null @@ -1,5 +0,0 @@ -machine: - node: - version: 8 - environment: - PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" diff --git a/docs/BackgroundLayer.md b/docs/BackgroundLayer.md index 500950ff7a..d87c29e0d2 100644 --- a/docs/BackgroundLayer.md +++ b/docs/BackgroundLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/Camera.md b/docs/Camera.md index 2819b010d0..5a2e245f65 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -5,32 +5,43 @@ ### props | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | +| allowUpdates | `bool` | `true` | `false` | If false, the camera will not send any props to the native module. Intended to be used to prevent unnecessary tile fetching and improve performance when the map is not visible. Defaults to true. | | animationDuration | `number` | `2000` | `false` | The duration a camera update takes (in ms) | -| animationMode | `enum` | `'easeTo'` | `false` | The animationstyle when the camara updates. One of; `flyTo`, `easeTo`, `moveTo` | +| animationMode | `enum` | `'easeTo'` | `false` | The animationstyle when the camara updates. One of: `flyTo`, `easeTo`, `linearTo`, `moveTo` | | defaultSettings | `shape` | `none` | `false` | Default view settings applied on camera | |   centerCoordinate | `array` | `none` | `false` | Center coordinate on map [lng, lat] | +|   padding | `shape` | `none` | `false` | Padding around edges of map in points | +|     paddingLeft | `number` | `none` | `false` | Left padding in points | +|     paddingRight | `number` | `none` | `false` | Right padding in points | +|     paddingTop | `number` | `none` | `false` | Top padding in points | +|     paddingBottom | `number` | `none` | `false` | Bottom padding in points | |   heading | `number` | `none` | `false` | Heading on map | |   pitch | `number` | `none` | `false` | Pitch on map | -|   bounds | `shape` | `none` | `false` | Represents a rectangle in geographical coordinates marking the visible area of the map. | +|   bounds | `shape` | `none` | `false` | Represents a rectangle in geographical coordinates marking the visible area of the map.
The `bounds.padding*` properties are deprecated; use root `padding` property instead. | |     ne | `array` | `none` | `true` | North east coordinate of bound | |     sw | `array` | `none` | `true` | South west coordinate of bound | -|     paddingLeft | `number` | `none` | `false` | Left camera padding for bounds | -|     paddingRight | `number` | `none` | `false` | Right camera padding for bounds | -|     paddingTop | `number` | `none` | `false` | Top camera padding for bounds | -|     paddingBottom | `number` | `none` | `false` | Bottom camera padding for bounds | -|     onUserTrackingModeChange | `func` | `none` | `false` | Callback that is triggered on user tracking mode changes | +|     paddingLeft | `number` | `none` | `false` | Left padding in points (deprecated; use root `padding` property instead) | +|     paddingRight | `number` | `none` | `false` | Right padding in points (deprecated; use root `padding` property instead) | +|     paddingTop | `number` | `none` | `false` | Top padding in points (deprecated; use root `padding` property instead) | +|     paddingBottom | `number` | `none` | `false` | Bottom padding in points (deprecated; use root `padding` property instead) | +|   onUserTrackingModeChange | `func` | `none` | `false` | Callback that is triggered on user tracking mode changes | |   zoomLevel | `number` | `none` | `false` | Zoom level of the map | | centerCoordinate | `array` | `none` | `false` | Center coordinate on map [lng, lat] | +| padding | `shape` | `none` | `false` | Padding around edges of map in points | +|   paddingLeft | `number` | `none` | `false` | Left padding in points | +|   paddingRight | `number` | `none` | `false` | Right padding in points | +|   paddingTop | `number` | `none` | `false` | Top padding in points | +|   paddingBottom | `number` | `none` | `false` | Bottom padding in points | | heading | `number` | `none` | `false` | Heading on map | | pitch | `number` | `none` | `false` | Pitch on map | -| bounds | `shape` | `none` | `false` | Represents a rectangle in geographical coordinates marking the visible area of the map. | +| bounds | `shape` | `none` | `false` | Represents a rectangle in geographical coordinates marking the visible area of the map.
The `bounds.padding*` properties are deprecated; use root `padding` property instead. | |   ne | `array` | `none` | `true` | North east coordinate of bound | |   sw | `array` | `none` | `true` | South west coordinate of bound | -|   paddingLeft | `number` | `none` | `false` | Left camera padding for bounds | -|   paddingRight | `number` | `none` | `false` | Right camera padding for bounds | -|   paddingTop | `number` | `none` | `false` | Top camera padding for bounds | -|   paddingBottom | `number` | `none` | `false` | Bottom camera padding for bounds | -|   onUserTrackingModeChange | `func` | `none` | `false` | Callback that is triggered on user tracking mode changes | +|   paddingLeft | `number` | `none` | `false` | Left padding in points (deprecated; use root `padding` property instead) | +|   paddingRight | `number` | `none` | `false` | Right padding in points (deprecated; use root `padding` property instead) | +|   paddingTop | `number` | `none` | `false` | Top padding in points (deprecated; use root `padding` property instead) | +|   paddingBottom | `number` | `none` | `false` | Bottom padding in points (deprecated; use root `padding` property instead) | +| onUserTrackingModeChange | `func` | `none` | `false` | Callback that is triggered on user tracking mode changes | | zoomLevel | `number` | `none` | `false` | Zoom level of the map | | minZoomLevel | `number` | `none` | `false` | The minimun zoom level of the map | | maxZoomLevel | `number` | `none` | `false` | The maximun zoom level of the map | @@ -43,7 +54,6 @@ | followPitch | `number` | `none` | `false` | The pitch on map while followUserLocation is set to `true` | | followHeading | `number` | `none` | `false` | The heading on map while followUserLocation is set to `true` | | triggerKey | `any` | `none` | `false` | Manually update the camera - helpful for when props did not update, however you still want the camera to move | -| onUserTrackingModeChange | `func` | `none` | `false` | FIX ME NO DESCRIPTION | ### methods #### fitBounds(northEastCoordinates, southWestCoordinates[, padding][, animationDuration]) diff --git a/docs/CircleLayer.md b/docs/CircleLayer.md index 94dec76b4c..872e9f18c3 100644 --- a/docs/CircleLayer.md +++ b/docs/CircleLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property
from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/CustomHttpHeaders.md b/docs/CustomHttpHeaders.md index bc9487c1e1..dda8f7affd 100644 --- a/docs/CustomHttpHeaders.md +++ b/docs/CustomHttpHeaders.md @@ -20,11 +20,10 @@ Suggested location is `[AppDelegate application: didFinishLaunchingWithOptions:] #### Working example (AppDelegate.m) ```obj-c -@implementation AppDelegate - // (1) Include the header file #import "MGLCustomHeaders.h" +@implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { diff --git a/docs/FillExtrusionLayer.md b/docs/FillExtrusionLayer.md index 5b22ffe569..f743d27421 100644 --- a/docs/FillExtrusionLayer.md +++ b/docs/FillExtrusionLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/FillLayer.md b/docs/FillLayer.md index a2b30376e7..bb188d32ee 100644 --- a/docs/FillLayer.md +++ b/docs/FillLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/HeatmapLayer.md b/docs/HeatmapLayer.md index 04c8c1dfd8..7a628d01d2 100644 --- a/docs/HeatmapLayer.md +++ b/docs/HeatmapLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property
from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/LineLayer.md b/docs/LineLayer.md index 206f25b086..c204239c97 100644 --- a/docs/LineLayer.md +++ b/docs/LineLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/Logger.md b/docs/Logger.md new file mode 100644 index 0000000000..c7c5f2fca4 --- /dev/null +++ b/docs/Logger.md @@ -0,0 +1,23 @@ +## Logger +### + +### methods +#### setLogLevel(LogLevel) + +##### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | +| `LogLevel` | `String` | `Yes` | required level of logging, can be `"error" | "warning" | "info" | "debug" | "verbose"` | + +##### Description +sets the amount of logging + +#### setLogCallback(LogCallback) + +##### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | +| `LogCallback` | `function` | `Yes` | callback taking a log object `{ message: String, level: LogLevel, tag: String }` as param. If callback return falsy value then default logging will take place. | + +##### Description +overwrite logging - good to mute specific errors/ warnings \ No newline at end of file diff --git a/docs/MapView.md b/docs/MapView.md index 429e7fa564..dabefbb1bb 100644 --- a/docs/MapView.md +++ b/docs/MapView.md @@ -7,7 +7,8 @@ | ---- | :--: | :-----: | :------: | :----------: | | contentInset | `union` | `none` | `false` | The distance from the edges of the map view’s frame to the edges of the map view’s logical viewport. | | style | `any` | `none` | `false` | Style for wrapping React Native View | -| styleURL | `string` | `MapboxGL.StyleURL.Street` | `false` | Style URL for map | +| styleURL | `string` | `none` | `false` | Style URL for map - notice, if non is set it _will_ default to `MapboxGL.StyleURL.Street` | +| styleJSON | `string` | `none` | `false` | StyleJSON for map - according to TileJSON specs: https://github.com/mapbox/tilejson-spec | | preferredFramesPerSecond | `number` | `none` | `false` | iOS: The preferred frame rate at which the map view is rendered.
The default value for this property is MGLMapViewPreferredFramesPerSecondDefault,
which will adaptively set the preferred frame rate based on the capability of
the user’s device to maintain a smooth experience. This property can be set to arbitrary integer values.

Android: The maximum frame rate at which the map view is rendered, but it can't excess the ability of device hardware.
This property can be set to arbitrary integer values. | | localizeLabels | `bool` | `false` | `false` | Automatically change the language of the map labels to the system’s preferred language,
this is not something that can be toggled on/off | | zoomEnabled | `bool` | `none` | `false` | Enable/Disable zoom on the map | @@ -16,8 +17,9 @@ | rotateEnabled | `bool` | `true` | `false` | Enable/Disable rotation on map | | attributionEnabled | `bool` | `true` | `false` | The Mapbox terms of service, which governs the use of Mapbox-hosted vector tiles and styles,
[requires](https://www.mapbox.com/help/how-attribution-works/) these copyright notices to accompany any map that features Mapbox-designed styles, OpenStreetMap data, or other Mapbox data such as satellite or terrain data.
If that applies to this map view, do not hide this view or remove any notices from it.

You are additionally [required](https://www.mapbox.com/help/how-mobile-apps-work/#telemetry) to provide users with the option to disable anonymous usage and location sharing (telemetry).
If this view is hidden, you must implement this setting elsewhere in your app. See our website for [Android](https://www.mapbox.com/android-docs/map-sdk/overview/#telemetry-opt-out) and [iOS](https://www.mapbox.com/ios-sdk/#telemetry_opt_out) for implementation details.

Enable/Disable attribution on map. For iOS you need to add MGLMapboxMetricsEnabledSettingShownInApp=YES
to your Info.plist | | attributionPosition | `union` | `none` | `false` | Adds attribution offset, e.g. `{top: 8, left: 8}` will put attribution button in top-left corner of the map | -| tintColor | `union` | `none` | `false` | MapView's tintColor - ios only
@platform ios | +| tintColor | `union` | `none` | `false` | MapView's tintColor | | logoEnabled | `bool` | `true` | `false` | Enable/Disable the logo on the map. | +| logoPosition | `union` | `none` | `false` | Adds logo offset, e.g. `{top: 8, left: 8}` will put the logo in top-left corner of the map | | compassEnabled | `bool` | `none` | `false` | Enable/Disable the compass from appearing on the map | | compassViewPosition | `number` | `none` | `false` | Change corner of map the compass starts at. 0: TopLeft, 1: TopRight, 2: BottomLeft, 3: BottomRight | | compassViewMargins | `object` | `none` | `false` | Add margins to the compass with x and y values | diff --git a/docs/OfflineManager.md b/docs/OfflineManager.md index 72bdd91bab..539f7dcb3e 100644 --- a/docs/OfflineManager.md +++ b/docs/OfflineManager.md @@ -177,7 +177,7 @@ await MapboxGL.offlineManager.mergeOfflineRegions(path); #### setTileCountLimit(limit) -Sets the maximum number of Mapbox-hosted tiles that may be downloaded and stored on the current device.
The Mapbox Terms of Service prohibits changing or bypassing this limit without permission from Mapbox. +Sets the maximum number of Mapbox-hosted tiles that may be downloaded and stored on the current device.
The Mapbox Terms of Service prohibit changing or bypassing this limit without permission from Mapbox. ##### arguments | Name | Type | Required | Description | @@ -193,7 +193,7 @@ MapboxGL.offlineManager.setTileCountLimit(1000); #### setProgressEventThrottle(throttleValue) -Sets the value at which download status events will be sent over the React Native bridge.
These events happening very very fast default is 500ms. +Sets the period at which download status events will be sent over the React Native bridge.
The default is 500ms. ##### arguments | Name | Type | Required | Description | @@ -203,7 +203,7 @@ Sets the value at which download status events will be sent over the React Nativ ```javascript -MapboxGL.setProgressEventThrottle(500); +MapboxGL.offlineManager.setProgressEventThrottle(500); ``` diff --git a/docs/RasterLayer.md b/docs/RasterLayer.md index 3b47172155..321ccea183 100644 --- a/docs/RasterLayer.md +++ b/docs/RasterLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/ShapeSource.md b/docs/ShapeSource.md index 348e595c38..f6e7dbc795 100644 --- a/docs/ShapeSource.md +++ b/docs/ShapeSource.md @@ -14,6 +14,7 @@ | maxZoomLevel | `number` | `none` | `false` | Specifies the maximum zoom level at which to create vector tiles.
A greater value produces greater detail at high zoom levels.
The default value is 18. | | buffer | `number` | `none` | `false` | Specifies the size of the tile buffer on each side.
A value of 0 produces no buffer. A value of 512 produces a buffer as wide as the tile itself.
Larger values produce fewer rendering artifacts near tile edges and slower performance.
The default value is 128. | | tolerance | `number` | `none` | `false` | Specifies the Douglas-Peucker simplification tolerance.
A greater value produces simpler geometries and improves performance.
The default value is 0.375. | +| lineMetrics | `bool` | `none` | `false` | Whether to calculate line distance metrics.
This is required for line layers that specify lineGradient values.
The default value is false. | | onPress | `func` | `none` | `false` | Source press listener, gets called when a user presses one of the children layers only
if that layer has a higher z-index than another source layers | | hitbox | `shape` | `none` | `false` | Overrides the default touch hitbox(44x44 pixels) for the source layers | |   width | `number` | `none` | `true` | `width` of hitbox | @@ -36,6 +37,56 @@ shapeSource.features() ``` +#### getClusterExpansionZoom(feature) + +Returns the zoom needed to expand the cluster. + +##### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | +| `feature` | `Feature` | `Yes` | The feature cluster to expand. | + + + +```javascript +const zoom = await shapeSource.getClusterExpansionZoom(clusterId); +``` + + +#### getClusterLeaves(feature, limit, offset) + +Returns the FeatureCollection from the cluster. + +##### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | +| `feature` | `Feature` | `Yes` | The feature cluster to expand. | +| `limit` | `number` | `Yes` | The number of points to return. | +| `offset` | `number` | `Yes` | The amount of points to skip (for pagination). | + + + +```javascript +const collection = await shapeSource.getClusterLeaves(clusterId, limit, offset); +``` + + +#### getClusterChildren(feature) + +Returns the FeatureCollection from the cluster (on the next zoom level). + +##### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | +| `feature` | `Feature` | `Yes` | The feature cluster to expand. | + + + +```javascript +const collection = await shapeSource.getClusterChildren(clusterId); +``` + + #### onPress(event) diff --git a/docs/StyleSheet.md b/docs/StyleSheet.md index 7ce2fe0587..4d54c76094 100644 --- a/docs/StyleSheet.md +++ b/docs/StyleSheet.md @@ -228,7 +228,7 @@ const layerStyles = { circleColor: [ 'interpolate', ['exponential', 1.5], - ['get' 'point_count'], + ['get','point_count'], 25, 'yellow', 50, 'red', 75, 'blue', @@ -240,7 +240,7 @@ const layerStyles = { circleRadius: [ 'interpolate', ['exponential', 1.5], - ['get' 'point_count'], + ['get','point_count'], [0, 15], [100, 20], [750, 30], diff --git a/docs/SymbolLayer.md b/docs/SymbolLayer.md index 238884bf25..1453c3dd2e 100644 --- a/docs/SymbolLayer.md +++ b/docs/SymbolLayer.md @@ -6,7 +6,7 @@ | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | | id | `string` | `none` | `true` | A string that uniquely identifies the source in the style to which it is added. | -| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. | +| sourceID | `string` | `MapboxGL.StyleSource.DefaultSourceID` | `false` | The source from which to obtain the data to style.
If the source has not yet been added to the current style, the behavior is undefined.
Inferred from parent source only if the layer is a direct child to it. | | sourceLayerID | `string` | `none` | `false` | Identifier of the layer within the source identified by the sourceID property from which the receiver obtains the data to style. | | aboveLayerID | `string` | `none` | `false` | Inserts a layer above aboveLayerID. | | belowLayerID | `string` | `none` | `false` | Inserts a layer below belowLayerID | diff --git a/docs/docs.json b/docs/docs.json index d999b6413b..5cd835867b 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -16,7 +16,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -402,6 +402,13 @@ } ], "props": [ + { + "name": "allowUpdates", + "required": false, + "type": "bool", + "default": "true", + "description": "If false, the camera will not send any props to the native module. Intended to be used to prevent unnecessary tile fetching and improve performance when the map is not visible. Defaults to true." + }, { "name": "animationDuration", "required": false, @@ -414,7 +421,7 @@ "required": false, "type": "enum", "default": "'easeTo'", - "description": "The animationstyle when the camara updates. One of; `flyTo`, `easeTo`, `moveTo`" + "description": "The animationstyle when the camara updates. One of: `flyTo`, `easeTo`, `linearTo`, `moveTo`" }, { "name": "defaultSettings", @@ -434,6 +441,45 @@ "default": "none", "description": "Center coordinate on map [lng, lat]" }, + { + "name": "padding", + "required": false, + "type": { + "name": "shape", + "value": [ + { + "name": "paddingLeft", + "required": false, + "type": "number", + "default": "none", + "description": "Left padding in points" + }, + { + "name": "paddingRight", + "required": false, + "type": "number", + "default": "none", + "description": "Right padding in points" + }, + { + "name": "paddingTop", + "required": false, + "type": "number", + "default": "none", + "description": "Top padding in points" + }, + { + "name": "paddingBottom", + "required": false, + "type": "number", + "default": "none", + "description": "Bottom padding in points" + } + ] + }, + "default": "none", + "description": "Padding around edges of map in points" + }, { "name": "heading", "required": false, @@ -483,40 +529,40 @@ "required": false, "type": "number", "default": "none", - "description": "Left camera padding for bounds" + "description": "Left padding in points (deprecated; use root `padding` property instead)" }, { "name": "paddingRight", "required": false, "type": "number", "default": "none", - "description": "Right camera padding for bounds" + "description": "Right padding in points (deprecated; use root `padding` property instead)" }, { "name": "paddingTop", "required": false, "type": "number", "default": "none", - "description": "Top camera padding for bounds" + "description": "Top padding in points (deprecated; use root `padding` property instead)" }, { "name": "paddingBottom", "required": false, "type": "number", "default": "none", - "description": "Bottom camera padding for bounds" - }, - { - "name": "onUserTrackingModeChange", - "required": false, - "type": "func", - "default": "none", - "description": "Callback that is triggered on user tracking mode changes" + "description": "Bottom padding in points (deprecated; use root `padding` property instead)" } ] }, "default": "none", - "description": "Represents a rectangle in geographical coordinates marking the visible area of the map." + "description": "Represents a rectangle in geographical coordinates marking the visible area of the map.\nThe `bounds.padding*` properties are deprecated; use root `padding` property instead." + }, + { + "name": "onUserTrackingModeChange", + "required": false, + "type": "func", + "default": "none", + "description": "Callback that is triggered on user tracking mode changes" }, { "name": "zoomLevel", @@ -542,6 +588,45 @@ "default": "none", "description": "Center coordinate on map [lng, lat]" }, + { + "name": "padding", + "required": false, + "type": { + "name": "shape", + "value": [ + { + "name": "paddingLeft", + "required": false, + "type": "number", + "default": "none", + "description": "Left padding in points" + }, + { + "name": "paddingRight", + "required": false, + "type": "number", + "default": "none", + "description": "Right padding in points" + }, + { + "name": "paddingTop", + "required": false, + "type": "number", + "default": "none", + "description": "Top padding in points" + }, + { + "name": "paddingBottom", + "required": false, + "type": "number", + "default": "none", + "description": "Bottom padding in points" + } + ] + }, + "default": "none", + "description": "Padding around edges of map in points" + }, { "name": "heading", "required": false, @@ -591,40 +676,40 @@ "required": false, "type": "number", "default": "none", - "description": "Left camera padding for bounds" + "description": "Left padding in points (deprecated; use root `padding` property instead)" }, { "name": "paddingRight", "required": false, "type": "number", "default": "none", - "description": "Right camera padding for bounds" + "description": "Right padding in points (deprecated; use root `padding` property instead)" }, { "name": "paddingTop", "required": false, "type": "number", "default": "none", - "description": "Top camera padding for bounds" + "description": "Top padding in points (deprecated; use root `padding` property instead)" }, { "name": "paddingBottom", "required": false, "type": "number", "default": "none", - "description": "Bottom camera padding for bounds" - }, - { - "name": "onUserTrackingModeChange", - "required": false, - "type": "func", - "default": "none", - "description": "Callback that is triggered on user tracking mode changes" + "description": "Bottom padding in points (deprecated; use root `padding` property instead)" } ] }, "default": "none", - "description": "Represents a rectangle in geographical coordinates marking the visible area of the map." + "description": "Represents a rectangle in geographical coordinates marking the visible area of the map.\nThe `bounds.padding*` properties are deprecated; use root `padding` property instead." + }, + { + "name": "onUserTrackingModeChange", + "required": false, + "type": "func", + "default": "none", + "description": "Callback that is triggered on user tracking mode changes" }, { "name": "zoomLevel", @@ -723,13 +808,6 @@ "type": "any", "default": "none", "description": "Manually update the camera - helpful for when props did not update, however you still want the camera to move" - }, - { - "name": "onUserTrackingModeChange", - "required": false, - "type": "func", - "default": "none", - "description": "FIX ME NO DESCRIPTION" } ], "composes": [ @@ -754,7 +832,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -1095,7 +1173,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -1346,7 +1424,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -1606,7 +1684,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -2005,7 +2083,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -2736,8 +2814,15 @@ "name": "styleURL", "required": false, "type": "string", - "default": "MapboxGL.StyleURL.Street", - "description": "Style URL for map" + "default": "none", + "description": "Style URL for map - notice, if non is set it _will_ default to `MapboxGL.StyleURL.Street`" + }, + { + "name": "styleJSON", + "required": false, + "type": "string", + "default": "none", + "description": "StyleJSON for map - according to TileJSON specs: https://github.com/mapbox/tilejson-spec" }, { "name": "preferredFramesPerSecond", @@ -2800,7 +2885,7 @@ "required": false, "type": "union", "default": "none", - "description": "MapView's tintColor - ios only\n@platform ios" + "description": "MapView's tintColor" }, { "name": "logoEnabled", @@ -2809,6 +2894,13 @@ "default": "true", "description": "Enable/Disable the logo on the map." }, + { + "name": "logoPosition", + "required": false, + "type": "union", + "default": "none", + "description": "Adds logo offset, e.g. `{top: 8, left: 8}` will put the logo in top-left corner of the map" + }, { "name": "compassEnabled", "required": false, @@ -3221,7 +3313,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -3566,6 +3658,103 @@ "\nshapeSource.features()\n\n" ] }, + { + "name": "getClusterExpansionZoom", + "docblock": "Returns the zoom needed to expand the cluster.\n\n@example\nconst zoom = await shapeSource.getClusterExpansionZoom(clusterId);\n\n@param {Feature} feature - The feature cluster to expand.\n@return {number}", + "modifiers": [ + "async" + ], + "params": [ + { + "name": "feature", + "description": "The feature cluster to expand.", + "type": { + "name": "Feature" + }, + "optional": false + } + ], + "returns": { + "description": null, + "type": { + "name": "number" + } + }, + "description": "Returns the zoom needed to expand the cluster.", + "examples": [ + "\nconst zoom = await shapeSource.getClusterExpansionZoom(clusterId);\n\n" + ] + }, + { + "name": "getClusterLeaves", + "docblock": "Returns the FeatureCollection from the cluster.\n\n@example\nconst collection = await shapeSource.getClusterLeaves(clusterId, limit, offset);\n\n@param {Feature} feature - The feature cluster to expand.\n@param {number} limit - The number of points to return.\n@param {number} offset - The amount of points to skip (for pagination).\n@return {FeatureCollection}", + "modifiers": [ + "async" + ], + "params": [ + { + "name": "feature", + "description": "The feature cluster to expand.", + "type": { + "name": "Feature" + }, + "optional": false + }, + { + "name": "limit", + "description": "The number of points to return.", + "type": { + "name": "number" + }, + "optional": false + }, + { + "name": "offset", + "description": "The amount of points to skip (for pagination).", + "type": { + "name": "number" + }, + "optional": false + } + ], + "returns": { + "description": null, + "type": { + "name": "FeatureCollection" + } + }, + "description": "Returns the FeatureCollection from the cluster.", + "examples": [ + "\nconst collection = await shapeSource.getClusterLeaves(clusterId, limit, offset);\n\n" + ] + }, + { + "name": "getClusterChildren", + "docblock": "Returns the FeatureCollection from the cluster (on the next zoom level).\n\n@example\nconst collection = await shapeSource.getClusterChildren(clusterId);\n\n@param {Feature} feature - The feature cluster to expand.\n@return {FeatureCollection}", + "modifiers": [ + "async" + ], + "params": [ + { + "name": "feature", + "description": "The feature cluster to expand.", + "type": { + "name": "Feature" + }, + "optional": false + } + ], + "returns": { + "description": null, + "type": { + "name": "FeatureCollection" + } + }, + "description": "Returns the FeatureCollection from the cluster (on the next zoom level).", + "examples": [ + "\nconst collection = await shapeSource.getClusterChildren(clusterId);\n\n" + ] + }, { "name": "onPress", "docblock": null, @@ -3643,6 +3832,13 @@ "default": "none", "description": "Specifies the Douglas-Peucker simplification tolerance.\nA greater value produces simpler geometries and improves performance.\nThe default value is 0.375." }, + { + "name": "lineMetrics", + "required": false, + "type": "bool", + "default": "none", + "description": "Whether to calculate line distance metrics.\nThis is required for line layers that specify lineGradient values.\nThe default value is false." + }, { "name": "onPress", "required": false, @@ -3756,7 +3952,7 @@ "required": false, "type": "string", "default": "MapboxGL.StyleSource.DefaultSourceID", - "description": "The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined." + "description": "The source from which to obtain the data to style.\nIf the source has not yet been added to the current style, the behavior is undefined.\nInferred from parent source only if the layer is a direct child to it." }, { "name": "sourceLayerID", @@ -5759,7 +5955,7 @@ }, { "name": "setTileCountLimit", - "description": "Sets the maximum number of Mapbox-hosted tiles that may be downloaded and stored on the current device.\nThe Mapbox Terms of Service prohibits changing or bypassing this limit without permission from Mapbox.", + "description": "Sets the maximum number of Mapbox-hosted tiles that may be downloaded and stored on the current device.\nThe Mapbox Terms of Service prohibit changing or bypassing this limit without permission from Mapbox.", "params": [ { "name": "limit", @@ -5782,7 +5978,7 @@ }, { "name": "setProgressEventThrottle", - "description": "Sets the value at which download status events will be sent over the React Native bridge.\nThese events happening very very fast default is 500ms.", + "description": "Sets the period at which download status events will be sent over the React Native bridge.\nThe default is 500ms.", "params": [ { "name": "throttleValue", @@ -5794,7 +5990,7 @@ } ], "examples": [ - "MapboxGL.setProgressEventThrottle(500);" + "MapboxGL.offlineManager.setProgressEventThrottle(500);" ], "returns": { "description": "", diff --git a/example/.eslintrc.js b/example/.eslintrc.js deleted file mode 100644 index ceb62ef512..0000000000 --- a/example/.eslintrc.js +++ /dev/null @@ -1,138 +0,0 @@ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - env: { - jest: true, - es6: true, - browser: true, - node: true, - }, - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:react-native/all', - 'prettier', - 'prettier/@typescript-eslint', - "eslint:recommended", - "plugin:react/recommended" - ], - parserOptions: { - ecmaFeatures: { - experimentalObjectRestSpread: true, - jsx: true - }, - project: './tsconfig.json', - sourceType: 'module' - }, - plugins: [ - '@typescript-eslint', - 'react', - 'react-native', - 'import', - 'prettier', - ], - settings: { - "import/resolver": { - "node": { - "extensions": [ - ".js", - ".jsx", - ".ts", - ".tsx" - ] - } - }, - "react": { - "version": "detect", // React version. "detect" automatically picks the version you have installed. - // You can also use `16.0`, `16.3`, etc, if you want to override the detected value. - // default to latest and warns if missing - // It will default to "detect" in the future - }, - }, - rules: { - camelcase: 0, - 'linebreak-style': ['error', 'unix'], - quotes: ['error', 'single'], - semi: ['error', 'always'], - 'global-require': 0, - 'no-underscore-dangle': 0, - 'consistent-return': 0, - 'jsx-quotes': ['error', 'prefer-double'], - 'import/no-extraneous-dependencies': ['error', { packageDir: './' }], - 'import/prefer-default-export': 'off', - 'import/first': 1, - 'import/order': ['error', { groups: ['external', 'internal'] }], - 'import/extensions': [ - 'error', - 'ignorePackages', - { - 'js': 'never', - 'jsx': 'never', - 'ts': 'never', - 'tsx': 'never' - } - ], - 'arrow-body-style': 0, - 'no-plusplus': 0, - 'function-paren-newline': 0, - 'no-unused-expressions': 0, - 'import/no-cycle': 0, - 'no-use-before-define': 0, - 'prefer-promise-reject-errors': 0, - 'comma-dangle': 0, - 'class-methods-use-this': 0, - 'react/display-name': 0, - 'react/prefer-stateless-function': 0, - 'react/destructuring-assignment': 0, - 'react/no-unescaped-entities': 0, - 'react-native/split-platform-components': 0, - 'react-native/no-color-literals': 0, - 'react/jsx-filename-extension': [ - 1, - { extensions: ['.js', '.jsx', '.tsx'] } - ], - 'react/jsx-boolean-value': 0, - 'react/jsx-tag-spacing': [ - 'error', - { - closingSlash: 'never', - beforeSelfClosing: 'always', - afterOpening: 'never' - } - ], - 'react/jsx-props-no-spreading': 0, // we like our speads just fine, thank you very much - 'react/static-property-placement': 0, // we can ignore this - soon to be removed anyways - 'react/jsx-curly-newline': 0, - 'react/state-in-constructor': 0, // it is ok to initialize state as a class prop - 'react/prop-types': 0, - 'react/sort-comp': 0, - 'prettier/prettier': 'error', - 'react/jsx-one-expression-per-line': 0, - '@typescript-eslint/camelcase': 'off', - '@typescript-eslint/explicit-member-accessibility': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', // donΒ΄t want to type every argument again like getState or dispatch (and many more) - }, - 'overrides': [ - { - // enable the rule specifically for TypeScript files - 'files': ['*.ts', '*.tsx'], - 'rules': { - // turn these one to check where all the return types are missing - // and where arguments of functions are not typed - '@typescript-eslint/explicit-function-return-type': ['error'], - '@typescript-eslint/explicit-module-boundary-types': ['error'] - } - } - ], - globals: { - __DEV__: true, - element: true, - by: true, - waitFor: true // detox e2e - } -}; diff --git a/example/.gitattributes b/example/.gitattributes index d42ff18354..45a3dcb2a2 100644 --- a/example/.gitattributes +++ b/example/.gitattributes @@ -1 +1,3 @@ -*.pbxproj -text +# Windows files should use crlf line endings +# https://help.github.com/articles/dealing-with-line-endings/ +*.bat text eol=crlf diff --git a/example/.gitignore b/example/.gitignore index ad6db8935e..8a4c58b264 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -2,6 +2,12 @@ # .DS_Store +# MAPBOX token ENVs +# generated on set-up +# +accesstoken +env.json + # Xcode # build/ @@ -29,6 +35,7 @@ build/ .gradle local.properties *.iml +*.hprof # node.js # @@ -59,3 +66,4 @@ buck-out/ # CocoaPods /ios/Pods/ +/ios/Podfile.lock diff --git a/example/.nvmrc b/example/.nvmrc new file mode 100644 index 0000000000..4fe96938e5 --- /dev/null +++ b/example/.nvmrc @@ -0,0 +1,2 @@ +v14.15.0 + diff --git a/example/README.md b/example/README.md index 14feb2b378..ca50e54a41 100644 --- a/example/README.md +++ b/example/README.md @@ -1,5 +1,27 @@ + +

+ + + + + + + + + + + + + + + +

+ +
+ + - + # React Native Mapbox GL Demo @@ -8,38 +30,47 @@ Demo Application for [React Native Mapbox GL](../README.md) *Note:* this app is using [non trivial babel/metro configs](https://github.com/react-native-mapbox-gl/maps/pull/778), so we can consume the `maps` library from parent directory directly. Regular apps don't need this complicated setup. +
+ ## What is Mapbox? -Mapbox is the location data platform for mobile and web applications. We provide [building blocks](https://www.mapbox.com/products/) to add location features like maps, search, and navigation into any experience you create. Use our simple and powerful APIs & SDKs and our open source libraries for interactivity and control. +[Mapbox](https://www.mapbox.com/) is the location data platform for mobile and web applications. + +
## Sign up for Mapbox Not a Mapbox user yet? [Sign up for an account here](https://www.mapbox.com/signup/). Once you’re signed in, all you need to start building is a Mapbox access token. Use this same short code with all of our interactive mapping libraries, Python and JavaScript SDKs, and directly against our REST APIs. You can create and manage your access tokens on your [Mapbox Account page](https://www.mapbox.com/account/). +
+ ## Installation * Make sure you are in the example directory ``` cd example ``` -* Create a file called `accesstoken` in the root of the example project and just paste in your [Mapbox access token](https://www.mapbox.com/studio/account/tokens/). (The `accesstoken` file is processed in postinstall, so you need to run `npm install` after adding/changing accesstoken.) +* Create a file called `accesstoken` in the root of the example project and just paste in your [Mapbox access token](https://www.mapbox.com/studio/account/tokens/). (The `accesstoken` file is processed in postinstall, so you need to run `yarn install` after adding/changing accesstoken.) + +* Install our dependencies using `yarn install`. -* Install our dependencies using `npm install`. +
-## Start React Native Packager +## Start React Native Packager (or not, it starts automatically πŸ€·β€β™€οΈ) Open up another tab in your Terminal and run ``` -npm start +yarn start ``` -*Note*: if modules were added to base lib you might need to run `npm start --reset-cache` because we're using `babel` to [rewrite imports](https://github.com/react-native-mapbox-gl/maps/pull/778) +*Note*: if modules were added to base lib you might need to run `yarn start --reset-cache` because we're using `babel` to [rewrite imports](https://github.com/react-native-mapbox-gl/maps/pull/778) -## Run Android Simulator +
+ +## Run Android Emulator * Start Android emulator -* Run `adb reverse tcp:8081 tcp:8081` to forward port to packager(needed for hot reloading, if you're not developing you can skip this step). -* Run `react-native run-android` from `example` directory +* Run `yarn android` from `example` directory **NOTE** @@ -49,12 +80,13 @@ cd android chmod +x gradlew ``` +
+ ## Run iOS Simulator You can run this with the react-native cli or Xcode -* Run `cd ios && pod install && cd ..` from `example` directory to install cocoapods pods -* Run `react-native run-ios` from `example` directory +* Run `yarn ios` from `example` directory **NOTE** diff --git a/example/__tests__/App-test.js b/example/__tests__/App-test.js deleted file mode 100644 index 3d690bae56..0000000000 --- a/example/__tests__/App-test.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @format - */ - -import 'react-native'; -import React from 'react'; -import renderer from 'react-test-renderer'; - -import App from '../App'; - -// Note: test renderer must be required after react-native. - -it('renders correctly', () => { - renderer.create(); -}); diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 8b924f5585..7afa99c742 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -95,7 +95,7 @@ def enableSeparateBuildPerCPUArchitecture = false /** * Run Proguard to shrink the Java bytecode in release builds. */ -def enableProguardInReleaseBuilds = false +def enableProguardInReleaseBuilds = true /** * The preferred build flavor of JavaScriptCore. @@ -119,7 +119,15 @@ def jscFlavor = 'org.webkit:android-jsc:+' */ def enableHermes = project.ext.react.get("enableHermes", false); +/** + * Architectures to build native code for in debug. + */ +def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures") + + android { + ndkVersion rootProject.ext.ndkVersion + compileSdkVersion rootProject.ext.compileSdkVersion compileOptions { @@ -153,6 +161,11 @@ android { buildTypes { debug { signingConfig signingConfigs.debug + if (nativeArchitectures) { + ndk { + abiFilters nativeArchitectures.split(',') + } + } } release { // Caution! In production, you need to generate your own keystore file. @@ -167,11 +180,12 @@ android { variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html + // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = - versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + defaultConfig.versionCode * 1000 + versionCodes.get(abi) } } @@ -211,7 +225,7 @@ dependencies { // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { - from configurations.compile + from configurations.implementation into 'libs' } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index fa26aa56e1..b2f3ad9fce 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -4,5 +4,10 @@ - + + + diff --git a/example/android/app/src/debug/java/com/rnmapboxglexample/ReactNativeFlipper.java b/example/android/app/src/debug/java/com/rnmapboxglexample/ReactNativeFlipper.java index fd84f4b9c3..4a1334c2f7 100644 --- a/example/android/app/src/debug/java/com/rnmapboxglexample/ReactNativeFlipper.java +++ b/example/android/app/src/debug/java/com/rnmapboxglexample/ReactNativeFlipper.java @@ -40,7 +40,8 @@ public static void initializeFlipper(Context context, ReactInstanceManager react new NetworkingModule.CustomClientBuilder() { @Override public void apply(OkHttpClient.Builder builder) { - builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + // FIXME: had to disable for RN 0.66 upgrade + // builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); } }); client.addPlugin(networkFlipperPlugin); @@ -54,7 +55,8 @@ public void apply(OkHttpClient.Builder builder) { new ReactInstanceManager.ReactInstanceEventListener() { @Override public void onReactContextInitialized(ReactContext reactContext) { - reactInstanceManager.removeReactInstanceEventListener(this); + // FIXME: had to disable for RN 0.66 upgrade + // reactInstanceManager.removeReactInstanceEventListener(this); reactContext.runOnNativeModulesQueueThread( new Runnable() { @Override diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index d7e96d673e..5e807e17d0 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -25,7 +25,5 @@ - - diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 62fe59fa48..24bc061368 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,9 +1,8 @@ - diff --git a/example/android/build.gradle b/example/android/build.gradle index e958706c1f..b9b563910d 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -2,18 +2,61 @@ buildscript { ext { - buildToolsVersion = "29.0.2" - minSdkVersion = 16 - compileSdkVersion = 29 - targetSdkVersion = 29 + buildToolsVersion = "30.0.2" + minSdkVersion = 21 + compileSdkVersion = 30 + targetSdkVersion = 30 + ndkVersion = "21.4.7075529" + + useMapLibre = false + useCustomMapbox = false + + // Mapbox deps + if (useCustomMapbox) { + rnmbglMapboxLibs = { + implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.7.1' + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:5.8.0' + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-turf:5.8.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.7.0' + } + + rnmbglMapboxPlugins = { + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.8.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.14.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-markerview-v9:0.4.0' + } + } + + + // MapLibre deps + if (useMapLibre) { + rnmbglMapboxLibs = { + implementation ("org.maplibre.gl:android-sdk:9.2.1") + implementation ("com.mapbox.mapboxsdk:mapbox-sdk-turf:5.8.0") + implementation 'com.mapbox.mapboxsdk:mapbox-android-telemetry:6.1.0' + } + + rnmbglMapboxPlugins = { + implementation ("com.mapbox.mapboxsdk:mapbox-android-gestures:0.7.0") + implementation ("com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.14.0") { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + implementation ("com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.8.0") { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + implementation ("com.mapbox.mapboxsdk:mapbox-android-plugin-markerview-v9:0.4.0") { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + } + } } repositories { google() + mavenCentral() jcenter() } dependencies { - classpath("com.android.tools.build:gradle:3.5.3") - + classpath("com.android.tools.build:gradle:4.1.0") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -21,6 +64,7 @@ buildscript { allprojects { repositories { + mavenCentral() mavenLocal() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm @@ -31,6 +75,27 @@ allprojects { url("$rootDir/../node_modules/jsc-android/dist") } + if (rootProject.ext.get('useCustomMapbox')) { + maven { + url 'https://api.mapbox.com/downloads/v2/releases/maven' + authentication { + basic(BasicAuthentication) + } + credentials { + username = "mapbox" + password = "REPLACE WITH YOUR MAPBOX DOWNLOAD TOKEN" + + } + } + } + + + if (rootProject.ext.get('useMapLibre')) { + maven { + url = "https://dl.bintray.com/maplibre/maplibre-gl-native" + } + } + google() jcenter() maven { url 'https://jitpack.io' } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index bcf12cdd97..6bae022b95 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -20,5 +20,5 @@ android.useAndroidX=true android.enableJetifier=true -FLIPPER_VERSION=0.54.0 +FLIPPER_VERSION=0.99.0 diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 8422670206..7665b0fa93 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat index c86d88fbc2..279743597e 100644 --- a/example/android/gradlew.bat +++ b/example/android/gradlew.bat @@ -1,103 +1,88 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem http://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/example/e2e/config.json b/example/e2e/config.json new file mode 100644 index 0000000000..5467a93030 --- /dev/null +++ b/example/e2e/config.json @@ -0,0 +1,8 @@ +{ + "testEnvironment": "./environment", + "testRunner": "jest-circus/runner", + "testTimeout": 120000, + "testRegex": "\\.e2e\\.js$", + "reporters": ["detox/runners/jest/streamlineReporter"], + "verbose": true +} diff --git a/example/e2e/environment.js b/example/e2e/environment.js new file mode 100644 index 0000000000..7f4fc942fc --- /dev/null +++ b/example/e2e/environment.js @@ -0,0 +1,23 @@ +const { + DetoxCircusEnvironment, + SpecReporter, + WorkerAssignReporter, +} = require('detox/runners/jest-circus'); + +class CustomDetoxEnvironment extends DetoxCircusEnvironment { + constructor(config, context) { + super(config, context); + + // Can be safely removed, if you are content with the default value (=300000ms) + this.initTimeout = 300000; + + // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level. + // This is strictly optional. + this.registerListeners({ + SpecReporter, + WorkerAssignReporter, + }); + } +} + +module.exports = CustomDetoxEnvironment; diff --git a/example/e2e/firstTest.e2e.js b/example/e2e/firstTest.e2e.js new file mode 100644 index 0000000000..31022533cc --- /dev/null +++ b/example/e2e/firstTest.e2e.js @@ -0,0 +1,17 @@ +/* eslint-disable */ + +describe('Maps Example App', () => { + beforeAll(async () => { + await device.launchApp(); + }); + + afterEach(async () => { + await device.reloadReactNative(); + }); + + it('should show initial screen', async () => { + await expect(element(by.text('Map'))).toBeVisible(); + await expect(element(by.text('Camera'))).toBeVisible(); + await expect(element(by.text('User Location'))).toBeVisible(); + }); +}); diff --git a/example/ios/Podfile b/example/ios/Podfile index 55ec7d62ad..6b093c400d 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,7 +1,21 @@ require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' -platform :ios, '10.0' +platform :ios, '11.0' + +useMapLibre = false + +if useMapLibre + $RNMBGL_Use_SPM = { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { + kind: "upToNextMajorVersion", + minimumVersion: "5.11.0" + }, + product_name: "Mapbox" + } + $RNMGL_USE_MAPLIBRE = true +end # We ingore warning except for RNMBGL INHIBIT_WARNING_BY_DEFAULT = true @@ -9,7 +23,7 @@ INHIBIT_WARNING_BY_DEFAULT = true if INHIBIT_WARNING_BY_DEFAULT ORIG_POD = method(:pod) - # Override pods so we default to disbling all warnings + # Override pods so we default to disabling all warnings def pod(name, *requirements) options = requirements.last if options.is_a?(Hash) @@ -23,34 +37,44 @@ end target 'RNMapboxGLExample' do config = use_native_modules! - use_react_native!(:path => config["reactNativePath"]) + use_react_native!( + :path => config[:reactNativePath], + # to enable hermes on iOS, change `false` to `true` and then install pods + :hermes_enabled => false + ) # default version pod 'react-native-mapbox-gl', :path => '../../', :inhibit_warnings => false - target 'RNMapboxGLExampleTests' do - inherit! :search_paths - # Pods for testing - end - use_native_modules! + pre_install do |installer| + $RNMBGL.pre_install(installer) + end + # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable these next few lines. - use_flipper! - post_install do |installer| - flipper_post_install(installer) - end -end - -target 'RNMapboxGLExample-tvOS' do - # Pods for RNMapboxGLExample-tvOS - - target 'RNMapboxGLExample-tvOSTests' do - inherit! :search_paths - # Pods for testing + if !ENV['CI'] + # local configuration + use_flipper!() + post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings["ONLY_ACTIVE_ARCH"] = "NO" + end + end + react_native_post_install(installer) + __apply_Xcode_12_5_M1_post_install_workaround(installer) + $RNMBGL.post_install(installer) + end + else + # CI configuration + post_install do |installer| + react_native_post_install(installer) + __apply_Xcode_12_5_M1_post_install_workaround(installer) + $RNMBGL.post_install(installer) + end end - end diff --git a/example/ios/RNMapboxGLExample-tvOS/Info.plist b/example/ios/RNMapboxGLExample-tvOS/Info.plist deleted file mode 100644 index ecbd496be7..0000000000 --- a/example/ios/RNMapboxGLExample-tvOS/Info.plist +++ /dev/null @@ -1,53 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSExceptionDomains - - localhost - - NSExceptionAllowsInsecureHTTPLoads - - - - - NSLocationWhenInUseUsageDescription - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/example/ios/RNMapboxGLExample-tvOSTests/Info.plist b/example/ios/RNMapboxGLExample-tvOSTests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/example/ios/RNMapboxGLExample-tvOSTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj b/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj index 3c024455f4..45a9045033 100644 --- a/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj +++ b/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj @@ -7,39 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - 044BEA9D22C011DB0466B5BC /* libPods-RNMapboxGLExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8710EEDC913062333D22662 /* libPods-RNMapboxGLExampleTests.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 4B80F360E704445017B77C68 /* libPods-RNMapboxGLExample-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5FD19BC3E96CC127B3C6F2B /* libPods-RNMapboxGLExample-tvOS.a */; }; 51C0260825301F99008C5283 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51C0260725301F99008C5283 /* LaunchScreen.storyboard */; }; - 5B3D5813F07ECD76D1CC6849 /* libPods-RNMapboxGLExample-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5B72910482F547500EC4E1D /* libPods-RNMapboxGLExample-tvOSTests.a */; }; AD8CDE4F410D2E699BE7E99D /* libPods-RNMapboxGLExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A98F897FA1ECD18EB0F1C4DC /* libPods-RNMapboxGLExample.a */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = RNMapboxGLExample; - }; - 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7; - remoteInfo = "RNMapboxGLExample-tvOS"; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; - 00E356EE1AD99517003FC87E /* RNMapboxGLExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNMapboxGLExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* RNMapboxGLExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNMapboxGLExampleTests.m; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* RNMapboxGLExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RNMapboxGLExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -49,8 +25,6 @@ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RNMapboxGLExample/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = RNMapboxGLExample/main.m; sourceTree = ""; }; 1DDF0404A5884CA6A9492067 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; }; - 2D02E47B1E0B4A5D006451C7 /* RNMapboxGLExample-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RNMapboxGLExample-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 2D02E4901E0B4A5D006451C7 /* RNMapboxGLExample-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RNMapboxGLExample-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 307368D9D36BA4F44073E949 /* Pods-RNMapboxGLExample-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNMapboxGLExample-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-RNMapboxGLExample-tvOS/Pods-RNMapboxGLExample-tvOS.debug.xcconfig"; sourceTree = ""; }; 364D3AF88B4E4C1AA568F0FA /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; }; 3734D1612BE792B8299729E1 /* Pods-RNMapboxGLExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNMapboxGLExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-RNMapboxGLExampleTests/Pods-RNMapboxGLExampleTests.debug.xcconfig"; sourceTree = ""; }; @@ -84,14 +58,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 00E356EB1AD99517003FC87E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 044BEA9D22C011DB0466B5BC /* libPods-RNMapboxGLExampleTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -100,22 +66,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2D02E4781E0B4A5D006451C7 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4B80F360E704445017B77C68 /* libPods-RNMapboxGLExample-tvOS.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 5B3D5813F07ECD76D1CC6849 /* libPods-RNMapboxGLExample-tvOSTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -213,9 +163,6 @@ isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* RNMapboxGLExample.app */, - 00E356EE1AD99517003FC87E /* RNMapboxGLExampleTests.xctest */, - 2D02E47B1E0B4A5D006451C7 /* RNMapboxGLExample-tvOS.app */, - 2D02E4901E0B4A5D006451C7 /* RNMapboxGLExample-tvOSTests.xctest */, ); name = Products; sourceTree = ""; @@ -238,25 +185,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* RNMapboxGLExampleTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "RNMapboxGLExampleTests" */; - buildPhases = ( - FCA9706C7D689F287412B48F /* [CP] Check Pods Manifest.lock */, - 00E356EA1AD99517003FC87E /* Sources */, - 00E356EB1AD99517003FC87E /* Frameworks */, - 00E356EC1AD99517003FC87E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 00E356F51AD99517003FC87E /* PBXTargetDependency */, - ); - name = RNMapboxGLExampleTests; - productName = RNMapboxGLExampleTests; - productReference = 00E356EE1AD99517003FC87E /* RNMapboxGLExampleTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 13B07F861A680F5B00A75B9A /* RNMapboxGLExample */ = { isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RNMapboxGLExample" */; @@ -279,45 +207,6 @@ productReference = 13B07F961A680F5B00A75B9A /* RNMapboxGLExample.app */; productType = "com.apple.product-type.application"; }; - 2D02E47A1E0B4A5D006451C7 /* RNMapboxGLExample-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RNMapboxGLExample-tvOS" */; - buildPhases = ( - 4E6A88DBBBFF8B0CDECFEA4D /* [CP] Check Pods Manifest.lock */, - FD10A7F122414F3F0027D42C /* Start Packager */, - 2D02E4771E0B4A5D006451C7 /* Sources */, - 2D02E4781E0B4A5D006451C7 /* Frameworks */, - 2D02E4791E0B4A5D006451C7 /* Resources */, - 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "RNMapboxGLExample-tvOS"; - productName = "RNMapboxGLExample-tvOS"; - productReference = 2D02E47B1E0B4A5D006451C7 /* RNMapboxGLExample-tvOS.app */; - productType = "com.apple.product-type.application"; - }; - 2D02E48F1E0B4A5D006451C7 /* RNMapboxGLExample-tvOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RNMapboxGLExample-tvOSTests" */; - buildPhases = ( - 589FF0A4FB2D1C23FEBE261A /* [CP] Check Pods Manifest.lock */, - 2D02E48C1E0B4A5D006451C7 /* Sources */, - 2D02E48D1E0B4A5D006451C7 /* Frameworks */, - 2D02E48E1E0B4A5D006451C7 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */, - ); - name = "RNMapboxGLExample-tvOSTests"; - productName = "RNMapboxGLExample-tvOSTests"; - productReference = 2D02E4901E0B4A5D006451C7 /* RNMapboxGLExample-tvOSTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -326,21 +215,6 @@ attributes = { LastUpgradeCheck = 940; ORGANIZATIONNAME = Facebook; - TargetAttributes = { - 00E356ED1AD99517003FC87E = { - CreatedOnToolsVersion = 6.2; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; - 2D02E47A1E0B4A5D006451C7 = { - CreatedOnToolsVersion = 8.2.1; - ProvisioningStyle = Automatic; - }; - 2D02E48F1E0B4A5D006451C7 = { - CreatedOnToolsVersion = 8.2.1; - ProvisioningStyle = Automatic; - TestTargetID = 2D02E47A1E0B4A5D006451C7; - }; - }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RNMapboxGLExample" */; compatibilityVersion = "Xcode 3.2"; @@ -357,21 +231,11 @@ projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* RNMapboxGLExample */, - 00E356ED1AD99517003FC87E /* RNMapboxGLExampleTests */, - 2D02E47A1E0B4A5D006451C7 /* RNMapboxGLExample-tvOS */, - 2D02E48F1E0B4A5D006451C7 /* RNMapboxGLExample-tvOSTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 00E356EC1AD99517003FC87E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 13B07F8E1A680F5B00A75B9A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -381,21 +245,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2D02E4791E0B4A5D006451C7 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2D02E48E1E0B4A5D006451C7 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -413,64 +262,6 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Bundle React Native Code And Images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; - }; - 4E6A88DBBBFF8B0CDECFEA4D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNMapboxGLExample-tvOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 589FF0A4FB2D1C23FEBE261A /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNMapboxGLExample-tvOSTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 7BC27FFD4688D6914F2322C7 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -478,7 +269,6 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNMapboxGLExample/Pods-RNMapboxGLExample-resources.sh", - "${PODS_ROOT}/MapboxMobileEvents/MapboxMobileEvents/Resources/logger.html", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", @@ -499,7 +289,6 @@ ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/logger.html", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", @@ -552,57 +341,23 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNMapboxGLExample/Pods-RNMapboxGLExample-frameworks.sh", - "${PODS_ROOT}/@react-native-mapbox-gl-mapbox-static/dynamic/MapboxMobileEvents.framework", - "${PODS_ROOT}/@react-native-mapbox-gl-mapbox-static/dynamic/MapboxMobileEvents.framework.dSYM", - "${PODS_ROOT}/@react-native-mapbox-gl-mapbox-static/dynamic/BADC3E19-B154-39DA-BE9A-E7C52B45BD0C.bcsymbolmap", - "${PODS_ROOT}/@react-native-mapbox-gl-mapbox-static/dynamic/F86F5A50-B0A0-3D96-8330-20AFDAC47DCC.bcsymbolmap", - "${PODS_ROOT}/@react-native-mapbox-gl-mapbox-static/dynamic/877E1F78-505A-34B9-A9A0-42F8BFA435B9.bcsymbolmap", - "${PODS_ROOT}/@react-native-mapbox-gl-mapbox-static/dynamic/7C38D00F-328F-3E0C-B30E-E47F366F0F3D.bcsymbolmap", "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework.dSYM", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/BADC3E19-B154-39DA-BE9A-E7C52B45BD0C.bcsymbolmap", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/F86F5A50-B0A0-3D96-8330-20AFDAC47DCC.bcsymbolmap", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/877E1F78-505A-34B9-A9A0-42F8BFA435B9.bcsymbolmap", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/7C38D00F-328F-3E0C-B30E-E47F366F0F3D.bcsymbolmap", + "${BUILT_PRODUCTS_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", - "${DWARF_DSYM_FOLDER_PATH}/MapboxMobileEvents.framework.dSYM", - "${BUILT_PRODUCTS_DIR}/BADC3E19-B154-39DA-BE9A-E7C52B45BD0C.bcsymbolmap", - "${BUILT_PRODUCTS_DIR}/F86F5A50-B0A0-3D96-8330-20AFDAC47DCC.bcsymbolmap", - "${BUILT_PRODUCTS_DIR}/877E1F78-505A-34B9-A9A0-42F8BFA435B9.bcsymbolmap", - "${BUILT_PRODUCTS_DIR}/7C38D00F-328F-3E0C-B30E-E47F366F0F3D.bcsymbolmap", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework", - "${DWARF_DSYM_FOLDER_PATH}/Mapbox.framework.dSYM", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNMapboxGLExample/Pods-RNMapboxGLExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - FCA9706C7D689F287412B48F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RNMapboxGLExampleTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -622,35 +377,9 @@ shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; - FD10A7F122414F3F0027D42C /* Start Packager */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Start Packager"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 00E356EA1AD99517003FC87E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 13B07F871A680F5B00A75B9A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -660,81 +389,9 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2D02E4771E0B4A5D006451C7 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, - 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2D02E48C1E0B4A5D006451C7 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* RNMapboxGLExample */; - targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; - }; - 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2D02E47A1E0B4A5D006451C7 /* RNMapboxGLExample-tvOS */; - targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ - 00E356F61AD99517003FC87E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3734D1612BE792B8299729E1 /* Pods-RNMapboxGLExampleTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = RNMapboxGLExampleTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RNMapboxGLExample.app/RNMapboxGLExample"; - }; - name = Debug; - }; - 00E356F71AD99517003FC87E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5B5EC0CFE257E5F2D7A2020B /* Pods-RNMapboxGLExampleTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - COPY_PHASE_STRIP = NO; - INFOPLIST_FILE = RNMapboxGLExampleTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RNMapboxGLExample.app/RNMapboxGLExample"; - }; - name = Release; - }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = A32049E2AE532B3366AA9311 /* Pods-RNMapboxGLExample.debug.xcconfig */; @@ -742,12 +399,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = RNMapboxGLExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", @@ -767,8 +425,9 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = RNMapboxGLExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", @@ -781,116 +440,6 @@ }; name = Release; }; - 2D02E4971E0B4A5E006451C7 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 307368D9D36BA4F44073E949 /* Pods-RNMapboxGLExample-tvOS.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - CLANG_ANALYZER_NONNULL = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_TESTABILITY = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "RNMapboxGLExample-tvOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.RNMapboxGLExample-tvOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.2; - }; - name = Debug; - }; - 2D02E4981E0B4A5E006451C7 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C65B11B3DF018E09B71139E5 /* Pods-RNMapboxGLExample-tvOS.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - CLANG_ANALYZER_NONNULL = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "RNMapboxGLExample-tvOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.RNMapboxGLExample-tvOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.2; - }; - name = Release; - }; - 2D02E4991E0B4A5E006451C7 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9C1D64476C1CA1BE3A3FD524 /* Pods-RNMapboxGLExample-tvOSTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_TESTABILITY = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "RNMapboxGLExample-tvOSTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.RNMapboxGLExample-tvOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RNMapboxGLExample-tvOS.app/RNMapboxGLExample-tvOS"; - TVOS_DEPLOYMENT_TARGET = 10.1; - }; - name = Debug; - }; - 2D02E49A1E0B4A5E006451C7 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A388EB87A8261F3D1674F825 /* Pods-RNMapboxGLExample-tvOSTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "RNMapboxGLExample-tvOSTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.RNMapboxGLExample-tvOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RNMapboxGLExample-tvOS.app/RNMapboxGLExample-tvOS"; - TVOS_DEPLOYMENT_TARGET = 10.1; - }; - name = Release; - }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -923,6 +472,7 @@ COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 "; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -938,7 +488,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = ( "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", @@ -983,6 +533,7 @@ COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 "; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -991,7 +542,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = ( "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", @@ -1007,15 +558,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "RNMapboxGLExampleTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 00E356F61AD99517003FC87E /* Debug */, - 00E356F71AD99517003FC87E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RNMapboxGLExample" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1025,24 +567,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RNMapboxGLExample-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2D02E4971E0B4A5E006451C7 /* Debug */, - 2D02E4981E0B4A5E006451C7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RNMapboxGLExample-tvOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2D02E4991E0B4A5E006451C7 /* Debug */, - 2D02E49A1E0B4A5E006451C7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RNMapboxGLExample" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample-tvOS.xcscheme b/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample-tvOS.xcscheme deleted file mode 100644 index 38655953d0..0000000000 --- a/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample-tvOS.xcscheme +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample.xcscheme b/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample.xcscheme index 26ccf9ef00..e59cf2cc79 100644 --- a/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample.xcscheme +++ b/example/ios/RNMapboxGLExample.xcodeproj/xcshareddata/xcschemes/RNMapboxGLExample.xcscheme @@ -1,6 +1,6 @@ + buildForAnalyzing = "NO"> - - - - NSAppTransportSecurity - NSAllowsArbitraryLoads - NSExceptionDomains localhost diff --git a/example/ios/RNMapboxGLExample/LaunchScreen.storyboard b/example/ios/RNMapboxGLExample/LaunchScreen.storyboard index e7277bc19c..f57ed75f00 100644 --- a/example/ios/RNMapboxGLExample/LaunchScreen.storyboard +++ b/example/ios/RNMapboxGLExample/LaunchScreen.storyboard @@ -16,32 +16,21 @@ - - + - - - diff --git a/example/ios/RNMapboxGLExampleTests/Info.plist b/example/ios/RNMapboxGLExampleTests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/example/ios/RNMapboxGLExampleTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/example/ios/RNMapboxGLExampleTests/RNMapboxGLExampleTests.m b/example/ios/RNMapboxGLExampleTests/RNMapboxGLExampleTests.m deleted file mode 100644 index 14e58028f2..0000000000 --- a/example/ios/RNMapboxGLExampleTests/RNMapboxGLExampleTests.m +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import - -#import -#import - -#define TIMEOUT_SECONDS 600 -#define TEXT_TO_LOOK_FOR @"Welcome to React" - -@interface RNMapboxGLExampleTests : XCTestCase - -@end - -@implementation RNMapboxGLExampleTests - -- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test -{ - if (test(view)) { - return YES; - } - for (UIView *subview in [view subviews]) { - if ([self findSubviewInView:subview matching:test]) { - return YES; - } - } - return NO; -} - -- (void)testRendersWelcomeScreen -{ - UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; - BOOL foundElement = NO; - - __block NSString *redboxError = nil; -#ifdef DEBUG - RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { - if (level >= RCTLogLevelError) { - redboxError = message; - } - }); -#endif - - while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - - foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { - if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { - return YES; - } - return NO; - }]; - } - -#ifdef DEBUG - RCTSetLogFunction(RCTDefaultLogFunction); -#endif - - XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); - XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); -} - - -@end diff --git a/example/metro.config.js b/example/metro.config.js index cdade59095..672ca3a278 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -12,7 +12,9 @@ const path = require('path'); https://medium.com/@dushyant_db/how-to-import-files-from-outside-of-root-directory-with-react-native-metro-bundler-18207a348427 */ -const blacklist = require('metro-config/src/defaults/blacklist'); +// exclusionList is a function that takes an array of regexes and combines +// them with the default exclusions to return a single regex. +const exclusionList = require('metro-config/src/defaults/exclusionList'); const glob = require('glob-to-regexp'); const extraNodeModules = { @@ -36,7 +38,7 @@ function getBlacklist() { )}/node_modules/react-native/node_modules/@babel/*`, ), ]; - return blacklist(nodeModuleDirs); + return exclusionList(nodeModuleDirs); } module.exports = { @@ -55,7 +57,7 @@ module.exports = { getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, - inlineRequires: false, + inlineRequires: true, }, }), }, diff --git a/example/package.json b/example/package.json index 48b37a0eb3..739ffde61b 100644 --- a/example/package.json +++ b/example/package.json @@ -4,70 +4,79 @@ "private": true, "scripts": { "android": "react-native run-android", - "ios": "react-native run-ios", + "ios": "react-native run-ios --simulator=\"iPhone SE (2nd generation)\"", + "pod:install": "cd ios && pod install", + "preios": "yarn pod:install && yarn fix-nvm-ios", "start": "react-native start", - "test": "jest", - "lint": "eslint ./src", - "postinstall": "node ./scripts/set_access_token.js && jetifier" + "postinstall": "node ./scripts/set_access_token.js && jetifier", + "purge:android": "(cd android && ./gradlew --stop) && rm -rf ~/.gradle/caches/ android/app/build", + "purge:ios": "rm -rf ios/Pods/* ios/build ~/Library/Caches/CocoaPods ~/Library/Developer/Xcode/DerivedData && pod cache clean --all", + "purge:js": "rm -rf node_modules && yarn cache clean && watchman watch-del-all", + "purge": "yarn purge:js && yarn purge:android && yarn purge:ios", + "fix-nvm-ios": "node ./scripts/fix_nvm_issue.js" }, "dependencies": { - "@mapbox/geo-viewport": "^0.4.0", - "@mapbox/mapbox-sdk": "^0.6.0", + "@mapbox/geo-viewport": "^0.5.0", + "@mapbox/mapbox-sdk": "^0.13.0", "@react-native-community/masked-view": "^0.1.7", - "@turf/along": "^5.1.5", - "@turf/bbox-polygon": "^6.0.1", - "@turf/bearing": "^5.1.5", - "@turf/distance": "^5.1.5", - "@turf/helpers": "^6.1.4", - "@turf/length": "^6.0.2", - "@turf/line-distance": "^4.7.3", - "@turf/nearest": "^4.7.3", - "@turf/nearest-point-on-line": ">= 4.0.0 <7.0.0", - "buffer": "^5.1.0", + "@turf/along": "^6.5.0", + "@turf/bbox-polygon": "^6.5.0", + "@turf/distance": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/length": "^6.5.0", + "@turf/nearest-point-on-line": "6.5.0", "debounce": "^1.2.0", - "install": "^0.12.2", + "fbjs": "^3.0.0", "moment": "^2.24.0", - "npm": "^6.13.4", "prop-types": "^15.7.2", - "react": "16.13.1", - "react-native": "0.63.3", - "react-native-elements": "^2.2.1", + "react": "17.0.2", + "react-native": "0.66.0", + "react-native-elements": "^3.4.2", "react-native-gesture-handler": "^1.6.1", - "react-native-safe-area-context": "^0.7.3", - "react-native-safe-area-view": "^0.13.1", - "react-native-screens": "^2.4.0", + "react-native-safe-area-context": "^3.1.9", + "react-native-screens": "^3.0.0", "react-native-svg": "^12.1.0", - "react-native-vector-icons": "6.6.0", + "react-native-vector-icons": "9.0.0", "react-navigation": "^4.3.7", - "react-navigation-stack": "^2.3.11", - "url": "^0.11.0" + "react-navigation-stack": "^2.3.11" }, "devDependencies": { - "@babel/core": "^7.8.4", - "@babel/runtime": "^7.8.4", - "@react-native-community/eslint-config": "^1.1.0", - "@types/jest": "^26.0.0", - "@types/react": "^16.9.36", - "@types/react-native": "^0.62.13", - "@types/react-test-renderer": "^16.9.2", - "@typescript-eslint/eslint-plugin": "^3.0.0", - "@typescript-eslint/parser": "^3.0.0", - "babel-jest": "^25.1.0", - "babel-plugin-module-resolver": "^3.2.0", - "eslint": "^7.3.0", - "eslint-config-prettier": "^6.11.0", - "eslint-plugin-import": "^2.22.0", + "@babel/core": "^7.12.9", + "@babel/runtime": "^7.12.5", + "@types/react": "^17.0.0", + "@types/react-native": "^0.66.1", + "babel-plugin-module-resolver": "^4.1.0", + "detox": "^19.1.0", "glob-to-regexp": "^0.4.0", - "jest": "^25.1.0", - "jetifier": "^1.6.4", - "metro-react-native-babel-preset": "^0.59.0", - "react-test-renderer": "16.13.1", - "typescript": "^4.0.3" + "jest": "^27.1.0", + "jest-circus": "^27.1.1", + "jetifier": "^2.0.0", + "metro-react-native-babel-preset": "^0.64.0", + "typescript": "^4.4.3" }, - "jest": { - "preset": "react-native", - "setupFilesAfterEnv": [ - "../setup-jest" - ] + "detox": { + "testRunner": "jest", + "runnerConfig": "e2e/config.json", + "apps": { + "ios": { + "type": "ios.app", + "build": "xcodebuild -quiet -workspace ios/RNMapboxGLExample.xcworkspace -configuration Release -scheme RNMapboxGLExample -sdk iphonesimulator -derivedDataPath ios/build -destination 'platform=iOS Simulator,name=iPhone SE (2nd generation)'", + "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/RNMapboxGLExample.app" + } + }, + "devices": { + "simulator": { + "type": "ios.simulator", + "device": { + "type": "iPhone 11" + } + } + }, + "configurations": { + "ios": { + "device": "simulator", + "app": "ios" + } + } } } diff --git a/example/readme_assets/example_choropleth_layer.png b/example/readme_assets/example_choropleth_layer.png new file mode 100644 index 0000000000..89ca5eca5b Binary files /dev/null and b/example/readme_assets/example_choropleth_layer.png differ diff --git a/example/readme_assets/example_clustering_earthquakes.png b/example/readme_assets/example_clustering_earthquakes.png new file mode 100644 index 0000000000..cd05bb5196 Binary files /dev/null and b/example/readme_assets/example_clustering_earthquakes.png differ diff --git a/example/readme_assets/example_custom_callout.png b/example/readme_assets/example_custom_callout.png new file mode 100644 index 0000000000..48089c7637 Binary files /dev/null and b/example/readme_assets/example_custom_callout.png differ diff --git a/example/readme_assets/example_data_driven_circle_colors.png b/example/readme_assets/example_data_driven_circle_colors.png new file mode 100644 index 0000000000..ba0f1e202e Binary files /dev/null and b/example/readme_assets/example_data_driven_circle_colors.png differ diff --git a/example/readme_assets/example_home.png b/example/readme_assets/example_home.png new file mode 100644 index 0000000000..c87cbe98f8 Binary files /dev/null and b/example/readme_assets/example_home.png differ diff --git a/example/readme_assets/example_image_overlay.png b/example/readme_assets/example_image_overlay.png new file mode 100644 index 0000000000..716e54765a Binary files /dev/null and b/example/readme_assets/example_image_overlay.png differ diff --git a/example/scripts/fix_nvm_issue.js b/example/scripts/fix_nvm_issue.js new file mode 100644 index 0000000000..539094aa72 --- /dev/null +++ b/example/scripts/fix_nvm_issue.js @@ -0,0 +1,19 @@ +// fix ios build issue related to nvm +// error message: `nvm is not compatible with the "PREFIX" environment variable: currently set to "/usr/local"` +// see detailed discussion here => https://github.com/facebook/react-native/issues/31181 +// see detailed discussion here => https://github.com/facebook/react-native/issues/31259 + +const fs = require('fs'); + +// solution as described here: https://github.com/facebook/react-native/issues/31181#issuecomment-815913541 +const anchorLine = /set -e/; +const replacementContent = 'unset npm_config_prefix\nunset PREFIX\nset -e\n'; +const problemFilePath = './node_modules/react-native/scripts/find-node.sh'; +const problemFileContent = fs.readFileSync(problemFilePath, 'utf8'); +fs.writeFileSync( + problemFilePath, + problemFileContent.replace(anchorLine, replacementContent), + 'utf8' +); + +console.log('πŸ™πŸ» nvm with iOS should work πŸ™πŸ»'); diff --git a/example/src/assets/map-styleURL-style.json b/example/src/assets/map-styleURL-style.json new file mode 100644 index 0000000000..40469cc0eb --- /dev/null +++ b/example/src/assets/map-styleURL-style.json @@ -0,0 +1,28 @@ +{ + "version": 8, + "name": "Basic", + "constants": {}, + "sources": { + "mapbox": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-streets-v6" + } + }, + "sprite": "", + "glyphs": "", + "layers": [{ + "id": "background", + "type": "background", + "paint": { + "background-color": "rgba(135, 149, 154, 1)" + } + }, { + "id": "water", + "type": "fill", + "source": "mapbox", + "source-layer": "water", + "paint": { + "fill-color": "rgba(108, 148, 120, 1)" + } + }] +} diff --git a/example/src/examples/AnimatedLine.js b/example/src/examples/Animations/AnimatedLine.js similarity index 96% rename from example/src/examples/AnimatedLine.js rename to example/src/examples/Animations/AnimatedLine.js index 24a1f0251f..5ea6a7e0e4 100755 --- a/example/src/examples/AnimatedLine.js +++ b/example/src/examples/Animations/AnimatedLine.js @@ -5,11 +5,10 @@ import along from '@turf/along'; import length from '@turf/length'; import {point, lineString} from '@turf/helpers'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const blon = -73.99155; const blat = 40.73481; @@ -210,7 +209,7 @@ class AnimatedLine extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} onDidFinishLoadingMap={this.onDidFinishLoadingMap} style={sheet.matchParent}> diff --git a/example/src/examples/DriveTheLine.js b/example/src/examples/Animations/DriveTheLine.js similarity index 90% rename from example/src/examples/DriveTheLine.js rename to example/src/examples/Animations/DriveTheLine.js index 14d67ef18d..95b591bf29 100755 --- a/example/src/examples/DriveTheLine.js +++ b/example/src/examples/Animations/DriveTheLine.js @@ -5,14 +5,13 @@ import {Button} from 'react-native-elements'; import {lineString as makeLineString} from '@turf/helpers'; import {point} from '@turf/helpers'; -import RouteSimulator from '../utils/RouteSimulator'; -import {directionsClient} from '../MapboxClient'; -import sheet from '../styles/sheet'; -import {SF_OFFICE_COORDINATE} from '../utils'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import PulseCircleLayer from './common/PulseCircleLayer'; +import RouteSimulator from '../../utils/RouteSimulator'; +import {directionsClient} from '../../MapboxClient'; +import sheet from '../../styles/sheet'; +import {SF_OFFICE_COORDINATE} from '../../utils'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import PulseCircleLayer from '../common/PulseCircleLayer'; const SF_ZOO_COORDINATE = [-122.505412, 37.737463]; @@ -72,7 +71,7 @@ class DriveTheLine extends React.Component { onStart() { const routeSimulator = new RouteSimulator(this.state.route); - routeSimulator.addListener((currentPoint) => this.setState({currentPoint})); + routeSimulator.addListener(currentPoint => this.setState({currentPoint})); routeSimulator.start(); this.setState({routeSimulator}); } @@ -192,7 +191,7 @@ class DriveTheLine extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark}> = ({ message }) => { - return - - {message} - - ; -} +const CustomCalloutView: FC = ({message}) => { + return ( + + {message} + + ); +}; type CustomCalloutProps = { - label: String - onDismissExample: ()=>any + label: string; + onDismissExample: () => any; }; -const CustomCallout: FC = (props) => { - const [selectedFeature, setSelectedFeature] = useState>(); - const onPinPress = (e:any): void=>{ - if (e?.features?.length > 0) { - const feature = e?.features[0]; - setSelectedFeature(feature); +const CustomCallout: FC = props => { + const [selectedFeature, setSelectedFeature] = + useState>(); + + const onPinPress = (e: any): void => { + if (selectedFeature) { + setSelectedFeature(undefined); + return; } + + const feature = e?.features[0]; + setSelectedFeature(feature); }; return ( - + - + onPress={onPinPress}> + - {selectedFeature && - - } + {selectedFeature && ( + + + + )} ); @@ -92,11 +94,11 @@ const styles: CustomCalloutStyles = { iconAllowOverlap: true, iconAnchor: 'bottom', iconSize: 1.0, - iconImage: exampleIcon + iconImage: exampleIcon, }, customCalloutText: { color: 'black', - fontSize: 16 + fontSize: 16, }, calloutContainerStyle: { backgroundColor: 'white', @@ -104,8 +106,8 @@ const styles: CustomCalloutStyles = { height: 40, display: 'flex', justifyContent: 'center', - alignItems: 'center' - } + alignItems: 'center', + }, }; export default CustomCallout; diff --git a/example/src/examples/Annotations/Heatmap.js b/example/src/examples/Annotations/Heatmap.js index 340e96bd04..bd4e7b191a 100644 --- a/example/src/examples/Annotations/Heatmap.js +++ b/example/src/examples/Annotations/Heatmap.js @@ -3,7 +3,6 @@ import MapboxGL from '@react-native-mapbox-gl/maps'; import sheet from '../../styles/sheet'; import {SF_OFFICE_COORDINATE} from '../../utils'; - import Page from '../common/Page'; import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; diff --git a/example/src/examples/Annotations/MarkerView.js b/example/src/examples/Annotations/MarkerView.js index 264bf3824a..7fd4e9731e 100644 --- a/example/src/examples/Annotations/MarkerView.js +++ b/example/src/examples/Annotations/MarkerView.js @@ -1,9 +1,9 @@ import React from 'react'; import {View, Text, TouchableOpacity} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; +import PropTypes from 'prop-types'; import sheet from '../../styles/sheet'; - import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; import Page from '../common/Page'; import Bubble from '../common/Bubble'; @@ -32,6 +32,9 @@ const AnnotationContent = ({title}) => ( ); +AnnotationContent.propTypes = { + title: PropTypes.string, +}; class ShowMarkerView extends React.Component { static propTypes = { @@ -54,7 +57,7 @@ class ShowMarkerView extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} onDidFinishLoadingMap={this.onDidFinishLoadingMap} style={sheet.matchParent}> diff --git a/example/src/examples/Annotations/PointAnnotationAnchors.js b/example/src/examples/Annotations/PointAnnotationAnchors.js new file mode 100644 index 0000000000..429b17ccad --- /dev/null +++ b/example/src/examples/Annotations/PointAnnotationAnchors.js @@ -0,0 +1,132 @@ +import React from 'react'; +import MapboxGL from '@react-native-mapbox-gl/maps'; +import {StyleSheet, Text, View} from 'react-native'; + +import sheet from '../../styles/sheet'; +import Page from '../common/Page'; + +const ANNOTATION_SIZE = 50; + +const defaultCamera = { + centerCoordinate: [-73.98004319979121, 40.75272669831773], + zoomLevel: 17, +}; + +const corners = [ + { + coordinate: [-73.980313714175, 40.75279456928388], + anchor: {x: 0, y: 1}, + }, + { + coordinate: [-73.9803415496257, 40.75275624885313], + anchor: {x: 0, y: 0}, + }, + { + coordinate: [-73.98048535932631, 40.752816154647235], + anchor: {x: 1, y: 0}, + }, + { + coordinate: [-73.98045541426053, 40.75285444197175], + anchor: {x: 1, y: 1}, + }, +]; + +const sides = [ + { + coordinate: [-73.97952569308393, 40.75274356459241], + anchor: {x: 1 / 3, y: 0}, + }, + { + coordinate: [-73.98082017858928, 40.75329086324669], + anchor: {x: 1 / 3, y: 1}, + }, + { + coordinate: [-73.97985980165191, 40.752286242917535], + anchor: {x: 0, y: 1 / 3}, + containerStyle: {flexDirection: 'row'}, + }, +]; + +const styles = StyleSheet.create({ + small: { + backgroundColor: 'blue', + height: ANNOTATION_SIZE, + justifyContent: 'center', + width: ANNOTATION_SIZE, + flex: 1, + }, + large: { + borderColor: 'blue', + backgroundColor: 'transparent', + borderWidth: StyleSheet.hairlineWidth, + height: ANNOTATION_SIZE * 2, + justifyContent: 'center', + width: ANNOTATION_SIZE * 2, + flex: 1, + }, + text: { + position: 'absolute', + fontSize: 10, + }, +}); + +const PointAnnotationAnchors = props => { + return ( + + + + {corners.map((p, i) => ( + + + + x={p.anchor.x.toPrecision(2)}, y={p.anchor.y.toPrecision(2)} + + + + ))} + {sides.map((p, i) => { + let {x, y} = p.anchor; + if (x === 1) { + x = 0; + } + if (y === 1) { + y = 0; + } + return ( + + + + + + x={p.anchor.x.toPrecision(2)}, y={p.anchor.y.toPrecision(2)} + + + + ); + })} + + + ); +}; + +export default PointAnnotationAnchors; diff --git a/example/src/examples/Annotations/ShowPointAnnotation.js b/example/src/examples/Annotations/ShowPointAnnotation.js index 8d69f118a7..3598365751 100755 --- a/example/src/examples/Annotations/ShowPointAnnotation.js +++ b/example/src/examples/Annotations/ShowPointAnnotation.js @@ -1,9 +1,9 @@ import React from 'react'; import {Animated, View, Text, StyleSheet, Image} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; +import PropTypes from 'prop-types'; import sheet from '../../styles/sheet'; - import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; import Page from '../common/Page'; import Bubble from '../common/Bubble'; @@ -35,16 +35,16 @@ class AnnotationWithRemoteImage extends React.Component { coordinate={coordinate} title={title} draggable - onDrag={(e) => + onDrag={e => console.log('onDrag:', e.properties.id, e.geometry.coordinates) } - onDragStart={(e) => + onDragStart={e => console.log('onDragStart:', e.properties.id, e.geometry.coordinates) } - onDragEnd={(e) => + onDragEnd={e => console.log('onDragEnd:', e.properties.id, e.geometry.coordinates) } - ref={(ref) => (this.annotationRef = ref)}> + ref={ref => (this.annotationRef = ref)}> (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} onDidFinishLoadingMap={this.onDidFinishLoadingMap} style={sheet.matchParent}> diff --git a/example/src/examples/BugReportExample.js b/example/src/examples/BugReportExample.js index 3f994fec9d..e760cf5216 100644 --- a/example/src/examples/BugReportExample.js +++ b/example/src/examples/BugReportExample.js @@ -11,7 +11,7 @@ import { const styles = { mapView: {flex: 1}, circleLayer: { - circleRadiusTransition: {duration: 5000}, + circleRadiusTransition: {duration: 5000, delay: 0}, circleColor: '#ff0000', }, }; diff --git a/example/src/examples/CacheManagement.js b/example/src/examples/CacheManagement.js index ef7922ee1e..232df0c8c8 100755 --- a/example/src/examples/CacheManagement.js +++ b/example/src/examples/CacheManagement.js @@ -78,9 +78,9 @@ class CacheManagement extends React.Component { Alert.alert(`Max cache size successfully set to ${newMaxSize} bytes`); }; - validateCacheInputValue = (value) => !isNaN(parseInt(value, 10)); + validateCacheInputValue = value => !isNaN(parseInt(value, 10)); - onChangeCacheSize = (cacheSize) => this.setState({cacheSize}); + onChangeCacheSize = cacheSize => this.setState({cacheSize}); render() { const validSizeValue = this.validateCacheInputValue(this.state.cacheSize); diff --git a/example/src/examples/CompassView.js b/example/src/examples/Camera/CompassView.js similarity index 77% rename from example/src/examples/CompassView.js rename to example/src/examples/Camera/CompassView.js index 138f9c96ac..5c8d9b6d26 100644 --- a/example/src/examples/CompassView.js +++ b/example/src/examples/Camera/CompassView.js @@ -1,10 +1,9 @@ import React from 'react'; import {MapView, Camera} from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; class CompassView extends React.Component { static propTypes = { diff --git a/example/src/examples/Camera/Fit.js b/example/src/examples/Camera/Fit.js new file mode 100755 index 0000000000..034354f972 --- /dev/null +++ b/example/src/examples/Camera/Fit.js @@ -0,0 +1,296 @@ +import React from 'react'; +import {View, Text} from 'react-native'; +import {isEqual} from 'lodash'; +import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler'; +import MapboxGL from '@react-native-mapbox-gl/maps'; + +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; + +const buildPadding = ([top, right, bottom, left] = [0, 0, 0, 0]) => { + return { + paddingLeft: left, + paddingRight: right, + paddingTop: top, + paddingBottom: bottom, + }; +}; + +const houseBounds = { + ne: [-74.135379, 40.795909], + sw: [-74.135449, 40.795578], +}; + +const townBounds = { + ne: [-74.12641, 40.797968], + sw: [-74.143727, 40.772177], +}; + +const houseCenter = [ + (houseBounds.ne[0] + houseBounds.sw[0]) / 2, + (houseBounds.ne[1] + houseBounds.sw[1]) / 2, +]; +const townCenter = [ + (townBounds.ne[0] + townBounds.sw[0]) / 2, + (townBounds.ne[1] + townBounds.sw[1]) / 2, +]; + +const paddingZero = buildPadding(); +const paddingTop = buildPadding([200, 40, 40, 40]); +const paddingBottom = buildPadding([40, 40, 200, 40]); + +class Fit extends React.Component { + static propTypes = {...BaseExamplePropTypes}; + + constructor(props) { + super(props); + + this.state = { + locationType: 'houseCenter', // houseCenter | houseBounds | townCenter | townBounds + zoomLevel: 16, // number + followUserLocation: false, + padding: paddingZero, + animationDuration: 500, + + // For updating the UI in this example. + cachedFlyTo: undefined, // house | town + cachedZoomLevel: undefined, // number + }; + + this.camera = null; + } + + componentDidUpdate(prevProps, prevState) { + const changed = stateKey => { + // Checking if final state is `undefined` prevents another round of zeroing out in + // second `componentDidUpdate` call. + return ( + !isEqual(prevState[stateKey], this.state[stateKey]) && + this.state[stateKey] !== undefined + ); + }; + + if (changed('followUserLocation') && this.state.followUserLocation) { + this.setState({ + locationType: undefined, + zoomLevel: undefined, + cachedFlyTo: undefined, + cachedZoomLevel: undefined, + }); + return; + } + + if (changed('locationType') || changed('zoomLevel') || changed('padding')) { + this.setState({ + cachedFlyTo: undefined, + cachedZoomLevel: undefined, + }); + } else if (changed('cachedFlyTo') || changed('cachedZoomLevel')) { + this.setState({ + locationType: undefined, + zoomLevel: undefined, + padding: paddingZero, + }); + } + } + + renderSection = (title, buttons, fade = false) => { + return ( + + {title} + + {buttons.map(button => ( + + {button.title} + + ))} + + + ); + }; + + cameraProps = () => { + const { + locationType, + zoomLevel, + followUserLocation, + padding, + animationDuration, + } = this.state; + + let p = { + bounds: undefined, + centerCoordinate: undefined, + zoomLevel: undefined, + followUserLocation, + padding, + animationDuration, + }; + + if (locationType === 'houseCenter') { + p.centerCoordinate = houseCenter; + } else if (locationType === 'houseBounds') { + p.bounds = houseBounds; + } else if (locationType === 'townCenter') { + p.centerCoordinate = townCenter; + } else if (locationType === 'townBounds') { + p.bounds = townBounds; + } + + if (zoomLevel !== undefined) { + p.zoomLevel = zoomLevel; + } + + return p; + }; + + render() { + const { + locationType, + zoomLevel, + followUserLocation, + padding, + cachedFlyTo, + cachedZoomLevel, + } = this.state; + + const centerIsSet = locationType?.toLowerCase().includes('center'); + + const locationTypeButtons = [ + ['House (center)', 'houseCenter'], + ['House (bounds)', 'houseBounds'], + ['Town (center)', 'townCenter'], + ['Town (bounds)', 'townBounds'], + ['undef', undefined], + ].map(o => { + return { + title: `${o[0]}`, + selected: locationType === o[1], + onPress: () => this.setState({locationType: o[1]}), + }; + }); + + const zoomConfigButtons = [14, 15, 16, 17, 18, 19, 20, undefined].map(n => { + return { + title: n ? `${n}` : 'undef', + selected: zoomLevel === n, + onPress: () => this.setState({zoomLevel: n}), + }; + }); + + const zoomToButtons = [14, 15, 16, 17, 18, 19, 20].map(n => { + return { + title: `${n}`, + selected: cachedZoomLevel === n, + onPress: () => { + this.camera.zoomTo(n, 1000); + this.setState({cachedZoomLevel: n}); + }, + }; + }); + + return ( + + + (this.camera = ref)} + {...this.cameraProps()} + /> + + + + + + + {this.renderSection('Location type', locationTypeButtons)} + + {this.renderSection( + 'Zoom' + + (centerIsSet ? '' : ' (only used if center coordinate is set)'), + zoomConfigButtons, + !centerIsSet, + )} + + {this.renderSection('Follow user location', [ + { + title: followUserLocation ? 'Enabled' : 'Disabled', + selected: followUserLocation, + onPress: () => + this.setState({followUserLocation: !followUserLocation}), + }, + ])} + + {this.renderSection('Fly to (imperative)', [ + { + title: 'House', + selected: cachedFlyTo === 'house', + onPress: () => { + this.camera.flyTo(houseCenter); + this.setState({cachedFlyTo: 'house'}); + }, + }, + { + title: 'Town', + selected: cachedFlyTo === 'town', + onPress: () => { + this.camera.flyTo(townCenter); + this.setState({cachedFlyTo: 'town'}); + }, + }, + ])} + + {this.renderSection('Zoom to (imperative)', zoomToButtons)} + + {this.renderSection('Padding', [ + { + title: 'None', + selected: isEqual(padding, paddingZero), + onPress: () => this.setState({padding: paddingZero}), + }, + { + title: 'Top', + selected: isEqual(padding, paddingTop), + onPress: () => this.setState({padding: paddingTop}), + }, + { + title: 'Bottom', + selected: isEqual(padding, paddingBottom), + onPress: () => this.setState({padding: paddingBottom}), + }, + ])} + + + ); + } +} + +export default Fit; diff --git a/example/src/examples/FlyTo.js b/example/src/examples/Camera/FlyTo.js similarity index 93% rename from example/src/examples/FlyTo.js rename to example/src/examples/Camera/FlyTo.js index c0da01936b..a95bfe21f7 100755 --- a/example/src/examples/FlyTo.js +++ b/example/src/examples/Camera/FlyTo.js @@ -2,10 +2,9 @@ import React from 'react'; import {Alert} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; const layerStyles = { building: { diff --git a/example/src/examples/GetCenter.js b/example/src/examples/Camera/GetCenter.js similarity index 88% rename from example/src/examples/GetCenter.js rename to example/src/examples/Camera/GetCenter.js index 32653942cd..3a30c14ddd 100755 --- a/example/src/examples/GetCenter.js +++ b/example/src/examples/Camera/GetCenter.js @@ -2,9 +2,9 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const styles = { mapView: {flex: 1}, @@ -47,7 +47,7 @@ class GetCenter extends React.Component { (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={styles.mapView}> (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={styles.mapView}> ( +const RestrictMapBounds = props => ( diff --git a/example/src/examples/SetHeading.js b/example/src/examples/Camera/SetHeading.js similarity index 86% rename from example/src/examples/SetHeading.js rename to example/src/examples/Camera/SetHeading.js index 23ce5751f5..9ceb8a1eac 100755 --- a/example/src/examples/SetHeading.js +++ b/example/src/examples/Camera/SetHeading.js @@ -1,10 +1,9 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; class SetHeading extends React.Component { static propTypes = { @@ -55,7 +54,7 @@ class SetHeading extends React.Component { options={this._bearingOptions} onOptionPress={this.onHeadingChange}> (this.map = ref)} + ref={ref => (this.map = ref)} style={sheet.matchParent}> diff --git a/example/src/examples/SetPitch.js b/example/src/examples/Camera/SetPitch.js similarity index 87% rename from example/src/examples/SetPitch.js rename to example/src/examples/Camera/SetPitch.js index dde2c2f4fb..d871b1db33 100755 --- a/example/src/examples/SetPitch.js +++ b/example/src/examples/Camera/SetPitch.js @@ -1,10 +1,9 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; class SetPitch extends React.Component { static propTypes = { diff --git a/example/src/examples/SetUserTrackingModes.js b/example/src/examples/Camera/SetUserTrackingModes.js similarity index 93% rename from example/src/examples/SetUserTrackingModes.js rename to example/src/examples/Camera/SetUserTrackingModes.js index 1fb04c922e..116bc074f6 100755 --- a/example/src/examples/SetUserTrackingModes.js +++ b/example/src/examples/Camera/SetUserTrackingModes.js @@ -2,12 +2,11 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import {onSortOptions} from '../utils'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import {onSortOptions} from '../../utils'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; +import Bubble from '../common/Bubble'; const styles = { bubbleOne: {bottom: 80}, @@ -24,7 +23,7 @@ class SetUserTrackingModes extends React.Component { super(props); this._trackingOptions = Object.keys(MapboxGL.UserTrackingModes) - .map((key) => { + .map(key => { return { label: key, data: MapboxGL.UserTrackingModes[key], diff --git a/example/src/examples/TakeSnapshot.js b/example/src/examples/Camera/TakeSnapshot.js similarity index 94% rename from example/src/examples/TakeSnapshot.js rename to example/src/examples/Camera/TakeSnapshot.js index 9eb02c8746..86f30333a7 100755 --- a/example/src/examples/TakeSnapshot.js +++ b/example/src/examples/Camera/TakeSnapshot.js @@ -9,8 +9,8 @@ import { ActivityIndicator, } from 'react-native'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; const styles = StyleSheet.create({ container: { diff --git a/example/src/examples/TakeSnapshotWithMap.js b/example/src/examples/Camera/TakeSnapshotWithMap.js similarity index 88% rename from example/src/examples/TakeSnapshotWithMap.js rename to example/src/examples/Camera/TakeSnapshotWithMap.js index 04fd2f07f0..1b0129ad97 100755 --- a/example/src/examples/TakeSnapshotWithMap.js +++ b/example/src/examples/Camera/TakeSnapshotWithMap.js @@ -2,11 +2,10 @@ import React from 'react'; import {StyleSheet, View, Text, TouchableOpacity, Image} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import colors from '../styles/colors'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; +import sheet from '../../styles/sheet'; +import colors from '../../styles/colors'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; const styles = StyleSheet.create({ button: { @@ -53,7 +52,7 @@ class TakeSnapshotWithMap extends React.Component { return ( - (this.map = ref)} style={styles.map}> + (this.map = ref)} style={styles.map}> { const nextZoomLevel = this.state.zoomLevel === 12 ? 2 : 12; this.setState({zoomLevel: nextZoomLevel}); - setTimeout(() => this.cameraLoop(), 2000); + this.timeout = setTimeout(() => this.cameraLoop(), 2000); }); } @@ -58,7 +62,7 @@ class YoYo extends React.Component { return ( (this.map = ref)} + ref={ref => (this.map = ref)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark}> - diff --git a/example/src/examples/EarthQuakes.js b/example/src/examples/EarthQuakes.js deleted file mode 100755 index de8b58291b..0000000000 --- a/example/src/examples/EarthQuakes.js +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react'; -import MapboxGL from '@react-native-mapbox-gl/maps'; - -import sheet from '../styles/sheet'; -import {SF_OFFICE_COORDINATE} from '../utils'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; - -const layerStyles = { - singlePoint: { - circleColor: 'green', - circleOpacity: 0.84, - circleStrokeWidth: 2, - circleStrokeColor: 'white', - circleRadius: 5, - circlePitchAlignment: 'map', - }, - - clusteredPoints: { - circlePitchAlignment: 'map', - - circleColor: [ - 'step', - ['get', 'point_count'], - '#51bbd6', - 100, - '#f1f075', - 750, - '#f28cb1', - ], - - circleRadius: ['step', ['get', 'point_count'], 20, 100, 30, 750, 40], - - circleOpacity: 0.84, - circleStrokeWidth: 2, - circleStrokeColor: 'white', - }, - - clusterCount: { - textField: '{point_count}', - textSize: 12, - textPitchAlignment: 'map', - }, -}; - -class EarthQuakes extends React.Component { - static propTypes = { - ...BaseExamplePropTypes, - }; - - render() { - return ( - - - - - - - - - - - - - - ); - } -} - -export default EarthQuakes; diff --git a/example/src/examples/ChoroplethLayerByZoomLevel.js b/example/src/examples/FillRasterLayer/ChoroplethLayerByZoomLevel.js similarity index 93% rename from example/src/examples/ChoroplethLayerByZoomLevel.js rename to example/src/examples/FillRasterLayer/ChoroplethLayerByZoomLevel.js index 37f4388321..2308e1a1d8 100755 --- a/example/src/examples/ChoroplethLayerByZoomLevel.js +++ b/example/src/examples/FillRasterLayer/ChoroplethLayerByZoomLevel.js @@ -1,10 +1,9 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; const styles = { statePopulation: { diff --git a/example/src/examples/CustomVectorSource.js b/example/src/examples/FillRasterLayer/CustomVectorSource.js similarity index 87% rename from example/src/examples/CustomVectorSource.js rename to example/src/examples/FillRasterLayer/CustomVectorSource.js index 39ec6b23e0..f12acef228 100755 --- a/example/src/examples/CustomVectorSource.js +++ b/example/src/examples/FillRasterLayer/CustomVectorSource.js @@ -2,11 +2,10 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; import {Text} from 'react-native'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const styles = { boxFill: { @@ -56,10 +55,10 @@ class CustomVectorSource extends React.PureComponent { { + ref={source => { this._vectorSource = source; }} - onPress={(e) => { + onPress={e => { console.log(`VectorSource onPress: ${e.features}`, e.features); }}> (this.map = ref)} + ref={ref => (this.map = ref)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark}> (this.map = ref)} + ref={ref => (this.map = ref)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Satellite}> diff --git a/example/src/examples/IndoorBuilding.js b/example/src/examples/FillRasterLayer/IndoorBuilding.js similarity index 88% rename from example/src/examples/IndoorBuilding.js rename to example/src/examples/FillRasterLayer/IndoorBuilding.js index d159d6e532..365acc06a5 100755 --- a/example/src/examples/IndoorBuilding.js +++ b/example/src/examples/FillRasterLayer/IndoorBuilding.js @@ -3,12 +3,11 @@ import {View, StyleSheet} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; import {Slider} from 'react-native-elements'; -import sheet from '../styles/sheet'; -import colors from '../styles/colors'; -import indoorMapGeoJSON from '../assets/indoor_3d_map.json'; - -import Page from './common/Page'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; +import sheet from '../../styles/sheet'; +import colors from '../../styles/colors'; +import indoorMapGeoJSON from '../../assets/indoor_3d_map.json'; +import Page from '../common/Page'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; const styles = StyleSheet.create({ slider: { @@ -53,7 +52,7 @@ class IndoorBuilding extends React.Component { return ( (this.map = ref)} + ref={ref => (this.map = ref)} style={sheet.matchParent}> (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Light}> diff --git a/example/src/examples/QueryWithRect.js b/example/src/examples/FillRasterLayer/QueryWithRect.js similarity index 91% rename from example/src/examples/QueryWithRect.js rename to example/src/examples/FillRasterLayer/QueryWithRect.js index f4b53a214a..d41f36b7bc 100755 --- a/example/src/examples/QueryWithRect.js +++ b/example/src/examples/FillRasterLayer/QueryWithRect.js @@ -2,12 +2,11 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import nycJSON from '../assets/nyc_geojson.json'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import nycJSON from '../../assets/nyc_geojson.json'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const styles = { neighborhoods: { @@ -83,7 +82,7 @@ class QueryWithRect extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Light}> diff --git a/example/src/examples/WatercolorRasterTiles.js b/example/src/examples/FillRasterLayer/WatercolorRasterTiles.js similarity index 89% rename from example/src/examples/WatercolorRasterTiles.js rename to example/src/examples/FillRasterLayer/WatercolorRasterTiles.js index 4fd8cab29b..a8ad2c9940 100755 --- a/example/src/examples/WatercolorRasterTiles.js +++ b/example/src/examples/FillRasterLayer/WatercolorRasterTiles.js @@ -3,12 +3,11 @@ import {View, StyleSheet} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; import {Slider} from 'react-native-elements'; -import sheet from '../styles/sheet'; -import colors from '../styles/colors'; -import {SF_OFFICE_COORDINATE} from '../utils'; - -import Page from './common/Page'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; +import sheet from '../../styles/sheet'; +import colors from '../../styles/colors'; +import {SF_OFFICE_COORDINATE} from '../../utils'; +import Page from '../common/Page'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; const styles = StyleSheet.create({ slider: { diff --git a/example/src/examples/FitBounds.js b/example/src/examples/FitBounds.js deleted file mode 100755 index 0750303e60..0000000000 --- a/example/src/examples/FitBounds.js +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import MapboxGL from '@react-native-mapbox-gl/maps'; - -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; - -class FitBounds extends React.Component { - static propTypes = {...BaseExamplePropTypes}; - - houseBounds = [ - [-74.135379, 40.795909], - [-74.135449, 40.795578], - ]; - - townBounds = [ - [-74.12641, 40.797968], - [-74.143727, 40.772177], - ]; - - constructor(props) { - super(props); - - this._bounds = [ - {label: 'Fit House', data: this.houseBounds}, - {label: 'Fit Town', data: this.townBounds}, - ]; - - this.onFitBounds = this.onFitBounds.bind(this); - - this.state = { - bounds: { - ne: this.houseBounds[0], - sw: this.houseBounds[1], - }, - animationDuration: 0, - }; - } - - onFitBounds(i, bounds) { - this.setState({ - bounds: { - ne: bounds[0], - sw: bounds[1], - }, - animationDuration: 2000, - }); - } - - render() { - return ( - - - - - - ); - } -} - -export default FitBounds; diff --git a/example/src/examples/LineLayer/GradientLine.js b/example/src/examples/LineLayer/GradientLine.js new file mode 100644 index 0000000000..3f1141f445 --- /dev/null +++ b/example/src/examples/LineLayer/GradientLine.js @@ -0,0 +1,83 @@ +import React from 'react'; +import MapboxGL from '@react-native-mapbox-gl/maps'; + +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; + +const styles = { + lineLayer: { + lineColor: 'red', + lineCap: 'round', + lineJoin: 'round', + lineWidth: 14, + lineGradient: [ + 'interpolate', + ['linear'], + ['line-progress'], + 0, + 'blue', + 0.1, + 'royalblue', + 0.3, + 'cyan', + 0.5, + 'lime', + 0.7, + 'yellow', + 1, + 'red', + ], + }, +}; + +class GradientLine extends React.Component { + static propTypes = { + ...BaseExamplePropTypes, + }; + + render() { + return ( + + + + + + + + + ); + } +} + +export default GradientLine; diff --git a/example/src/examples/ChangeLayerColor.js b/example/src/examples/Map/ChangeLayerColor.js similarity index 85% rename from example/src/examples/ChangeLayerColor.js rename to example/src/examples/Map/ChangeLayerColor.js index c5e032b23e..177c01b188 100644 --- a/example/src/examples/ChangeLayerColor.js +++ b/example/src/examples/Map/ChangeLayerColor.js @@ -2,9 +2,9 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const defaultCamera = { centerCoordinate: [12.338, 45.4385], @@ -34,7 +34,7 @@ class ChangeLayerColor extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={styles.mapView}> diff --git a/example/src/examples/CreateOfflineRegion.js b/example/src/examples/Map/CreateOfflineRegion.js similarity index 96% rename from example/src/examples/CreateOfflineRegion.js rename to example/src/examples/Map/CreateOfflineRegion.js index 9babe8702e..c9be0dfe3f 100755 --- a/example/src/examples/CreateOfflineRegion.js +++ b/example/src/examples/Map/CreateOfflineRegion.js @@ -10,11 +10,10 @@ import { import MapboxGL from '@react-native-mapbox-gl/maps'; import geoViewport from '@mapbox/geo-viewport'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const CENTER_COORD = [-73.970895, 40.723279]; const MAPBOX_VECTOR_TILE_SIZE = 512; @@ -142,7 +141,7 @@ class CreateOfflineRegion extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} onDidFinishLoadingMap={this.onDidFinishLoadingStyle} style={sheet.matchParent}> diff --git a/example/src/examples/PointInMapView.js b/example/src/examples/Map/PointInMapView.js similarity index 87% rename from example/src/examples/PointInMapView.js rename to example/src/examples/Map/PointInMapView.js index 2941f691f3..644161e9dd 100755 --- a/example/src/examples/PointInMapView.js +++ b/example/src/examples/Map/PointInMapView.js @@ -2,9 +2,9 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const styles = { mapView: {flex: 1}, @@ -45,7 +45,7 @@ class PointInMapView extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={styles.mapView}> (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={styles.mapView}> diff --git a/example/src/examples/ShowClick.js b/example/src/examples/Map/ShowClick.js similarity index 87% rename from example/src/examples/ShowClick.js rename to example/src/examples/Map/ShowClick.js index d9448134ac..68b7c4da67 100755 --- a/example/src/examples/ShowClick.js +++ b/example/src/examples/Map/ShowClick.js @@ -2,12 +2,11 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import {DEFAULT_CENTER_COORDINATE} from '../utils'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import {DEFAULT_CENTER_COORDINATE} from '../../utils'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; class ShowClick extends React.Component { static propTypes = { diff --git a/example/src/examples/Map/ShowMap.tsx b/example/src/examples/Map/ShowMap.tsx index 8afebcbc77..cc5a537220 100755 --- a/example/src/examples/Map/ShowMap.tsx +++ b/example/src/examples/Map/ShowMap.tsx @@ -6,9 +6,9 @@ import sheet from '../../styles/sheet'; import {onSortOptions} from '../../utils'; import TabBarPage from '../common/TabBarPage'; -const ShowMap: FC = (props) => { +const ShowMap: FC = props => { const _mapOptions = Object.keys(MapboxGL.StyleURL) - .map((key) => { + .map(key => { return { label: key, data: (MapboxGL.StyleURL as any)[key], // bad any, because enums diff --git a/example/src/examples/Map/ShowMapLocalStyle.tsx b/example/src/examples/Map/ShowMapLocalStyle.tsx new file mode 100644 index 0000000000..82bfa7020f --- /dev/null +++ b/example/src/examples/Map/ShowMapLocalStyle.tsx @@ -0,0 +1,35 @@ +import React, {FC, useEffect} from 'react'; +import {Alert} from 'react-native'; +import MapboxGL from '@react-native-mapbox-gl/maps'; + +import sheet from '../../styles/sheet'; +import Page from '../common/Page'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const style = JSON.stringify(require('../../assets/map-styleURL-style.json')); + +const ShowMap: FC = props => { + useEffect(() => { + MapboxGL.locationManager.start(); + + return (): void => { + MapboxGL.locationManager.stop(); + }; + }, []); + + const onUserMarkerPress = (): void => { + Alert.alert('You pressed on the user location annotation'); + }; + + return ( + + + + + + + + ); +}; + +export default ShowMap; diff --git a/example/src/examples/ShowRegionDidChange.js b/example/src/examples/Map/ShowRegionDidChange.js similarity index 91% rename from example/src/examples/ShowRegionDidChange.js rename to example/src/examples/Map/ShowRegionDidChange.js index 95a6faa944..df614b69eb 100644 --- a/example/src/examples/ShowRegionDidChange.js +++ b/example/src/examples/Map/ShowRegionDidChange.js @@ -2,18 +2,17 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import {DEFAULT_CENTER_COORDINATE, SF_OFFICE_COORDINATE} from '../utils'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import {DEFAULT_CENTER_COORDINATE, SF_OFFICE_COORDINATE} from '../../utils'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; +import Bubble from '../common/Bubble'; const styles = { bubble: {marginBottom: 100}, }; -const isValidCoordinate = (geometry) => { +const isValidCoordinate = geometry => { if (!geometry) { return false; } @@ -106,10 +105,10 @@ class ShowRegionDidChange extends React.Component { const {geometry, properties} = this.state.regionFeature; const neCoord = properties.visibleBounds[0] - .map((n) => n.toPrecision(6)) + .map(n => n.toPrecision(6)) .join(', '); const swCoord = properties.visibleBounds[1] - .map((n) => n.toPrecision(6)) + .map(n => n.toPrecision(6)) .join(', '); return ( @@ -137,7 +136,7 @@ class ShowRegionDidChange extends React.Component { options={this._tabOptions} onOptionPress={this.onOptionPress}> (this.map = c)} + ref={c => (this.map = c)} style={sheet.matchParent} onRegionWillChange={this.onRegionWillChange} onRegionIsChanging={this.onRegionIsChanging} diff --git a/example/src/examples/SourceLayerVisibility.js b/example/src/examples/Map/SourceLayerVisibility.js similarity index 85% rename from example/src/examples/SourceLayerVisibility.js rename to example/src/examples/Map/SourceLayerVisibility.js index a804f63c79..3c52d445ec 100755 --- a/example/src/examples/SourceLayerVisibility.js +++ b/example/src/examples/Map/SourceLayerVisibility.js @@ -2,9 +2,9 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const defaultCamera = { centerCoordinate: [-74.005974, 40.712776], @@ -39,7 +39,7 @@ class SourceLayerVisibility extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={styles.mapView}> diff --git a/example/src/examples/StyleJson.js b/example/src/examples/Map/StyleJson.js similarity index 79% rename from example/src/examples/StyleJson.js rename to example/src/examples/Map/StyleJson.js index 832acd7547..e140be7125 100755 --- a/example/src/examples/StyleJson.js +++ b/example/src/examples/Map/StyleJson.js @@ -2,12 +2,11 @@ import React from 'react'; import {Text, StyleSheet} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import StyleJsonExample from '../assets/style-json-example.json'; -import StyleJsonExample2 from '../assets/style-json-example2.json'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import StyleJsonExample from '../../assets/style-json-example.json'; +import StyleJsonExample2 from '../../assets/style-json-example2.json'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const styles = StyleSheet.create({ map: { diff --git a/example/src/examples/TwoByTwo.js b/example/src/examples/Map/TwoByTwo.js similarity index 83% rename from example/src/examples/TwoByTwo.js rename to example/src/examples/Map/TwoByTwo.js index b9bfc2faed..22e3eea66c 100755 --- a/example/src/examples/TwoByTwo.js +++ b/example/src/examples/Map/TwoByTwo.js @@ -1,11 +1,10 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import smileyFaceGeoJSON from '../assets/smiley_face.json'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; +import sheet from '../../styles/sheet'; +import smileyFaceGeoJSON from '../../assets/smiley_face.json'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; const layerStyles = { smileyFaceLight: { @@ -31,7 +30,7 @@ class TwoByTwo extends React.Component { zoomLevel={2} centerCoordinate={[-35.15165038, 40.6235728]} onSetCameraComplete={this.onUpdateZoomLevel} - ref={(ref) => (this.map = ref)} + ref={ref => (this.map = ref)} style={sheet.matchParent} styleURL={styleURL}> diff --git a/example/src/examples/CustomIcon.js b/example/src/examples/SymbolCircleLayer/CustomIcon.js similarity index 87% rename from example/src/examples/CustomIcon.js rename to example/src/examples/SymbolCircleLayer/CustomIcon.js index cc32dffb29..138732fbc1 100755 --- a/example/src/examples/CustomIcon.js +++ b/example/src/examples/SymbolCircleLayer/CustomIcon.js @@ -3,12 +3,11 @@ import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; import {featureCollection, feature} from '@turf/helpers'; -import sheet from '../styles/sheet'; -import exampleIcon from '../assets/example.png'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import exampleIcon from '../../assets/example.png'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; const styles = { icon: { @@ -58,7 +57,7 @@ class CustomIcon extends React.Component { return ( (this._map = c)} + ref={c => (this._map = c)} onPress={this.onPress} style={sheet.matchParent}> + + { + this.setState({selectedCluster: null}); + }} + icon={} + size="large" + style={styles.fab} + /> + {this.state.selectedCluster && ( + { + return earthquakeInfo.code; + }} + data={this.state.selectedCluster.features} + renderItem={({item: {properties: earthquakeInfo}}) => { + const magnitude = `Magnitude: ${earthquakeInfo.mag}`; + const place = `Place: ${earthquakeInfo.place}`; + const code = `Code: ${earthquakeInfo.code}`; + const time = `Time: ${moment(earthquakeInfo.time).format( + 'MMMM Do YYYY, h:mm:ss a', + )}`; + + return ( + + + {earthquakeInfo.title} + {magnitude} + {place} + {code} + {time} + + + ); + }} + /> + )} + + + + + + { + const cluster = shape.features[0]; + const collection = await this.shape.getClusterLeaves( + cluster, + 999, + 0, + ); + + this.setState({selectedCluster: collection}); + }} + ref={shape => (this.shape = shape)} + cluster + clusterRadius={50} + clusterMaxZoom={14} + url="https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson"> + + + + + + + + + + ); + } +} + +export default EarthQuakes; diff --git a/example/src/examples/SymbolCircleLayer/ShapeSource.tsx b/example/src/examples/SymbolCircleLayer/ShapeSource.tsx deleted file mode 100644 index c0b8fed5e5..0000000000 --- a/example/src/examples/SymbolCircleLayer/ShapeSource.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; -import MapboxGL, {SymbolLayerStyle} from '@react-native-mapbox-gl/maps'; -import {View} from 'react-native'; - -const {MapView, Camera, Images, ShapeSource, SymbolLayer} = MapboxGL; - -const styles = { - icon: { - iconImage: ['get', 'icon'], - iconSize: [ - 'match', - ['get', 'icon'], - 'example', - 1.2, - 'airport-15', - 1.2, - /* default */ 1, - ], - }, - mapView: {flex: 1}, - mapContainer: {flex: 1}, -}; - -const exampleIcon = require('../../assets/example.png'); - -const featureCollection: GeoJSON.FeatureCollection = { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - id: '9d10456e-bdda-4aa9-9269-04c1667d4552', - properties: { - icon: 'example', - }, - geometry: { - type: 'Point', - coordinates: [-117.20611157485, 52.180961084261], - }, - }, - { - type: 'Feature', - id: '9d10456e-bdda-4aa9-9269-04c1667d4552', - properties: { - icon: 'airport-15', - }, - geometry: { - type: 'Point', - coordinates: [-117.205908, 52.180843], - }, - }, - { - type: 'Feature', - id: '9d10456e-bdda-4aa9-9269-04c1667d4552', - properties: { - icon: 'pin', - }, - geometry: { - type: 'Point', - coordinates: [-117.206562, 52.180797], - }, - }, - { - type: 'Feature', - id: '9d10456e-bdda-4aa9-9269-04c1667d4553', - properties: { - icon: 'pin3', - }, - geometry: { - type: 'Point', - coordinates: [-117.206862, 52.180897], - }, - }, - ], -}; - -class ShapeSourceIcon extends React.Component { - state = { - images: { - example: exampleIcon, - }, - }; - - render(): JSX.Element { - const {images} = this.state; - - return ( - - - - - - - - - - ); - } -} - -export default ShapeSourceIcon; diff --git a/example/src/examples/ShapeSourceIcon.js b/example/src/examples/SymbolCircleLayer/ShapeSourceIcon.js similarity index 89% rename from example/src/examples/ShapeSourceIcon.js rename to example/src/examples/SymbolCircleLayer/ShapeSourceIcon.js index 3932001711..34333a18de 100755 --- a/example/src/examples/ShapeSourceIcon.js +++ b/example/src/examples/SymbolCircleLayer/ShapeSourceIcon.js @@ -1,12 +1,11 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; -import exampleIcon from '../assets/example.png'; -import pinIcon from '../assets/pin.png'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; +import sheet from '../../styles/sheet'; +import exampleIcon from '../../assets/example.png'; +import pinIcon from '../../assets/pin.png'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; const styles = { icon: { @@ -98,7 +97,7 @@ class ShapeSourceIcon extends React.Component { + onImageMissing={imageKey => this.setState({ images: {...this.state.images, [imageKey]: pinIcon}, }) diff --git a/example/src/examples/SetDisplacement.js b/example/src/examples/UserLocation/SetDisplacement.js similarity index 84% rename from example/src/examples/SetDisplacement.js rename to example/src/examples/UserLocation/SetDisplacement.js index c01a0be939..b9f3ea6ec8 100644 --- a/example/src/examples/SetDisplacement.js +++ b/example/src/examples/UserLocation/SetDisplacement.js @@ -1,10 +1,9 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; const DISPLACEMENT = [0, 5, 10]; const OPTIONS = [{label: '0 meter'}, {label: '5 meter'}, {label: '10 meter'}]; @@ -24,7 +23,7 @@ class SetDisplacement extends React.Component { MapboxGL.locationManager.stop(); } - onDisplacementChange = (index) => { + onDisplacementChange = index => { this.setState({minDisplacement: DISPLACEMENT[index]}); }; diff --git a/example/src/examples/UserLocation/SetTintColor.js b/example/src/examples/UserLocation/SetTintColor.js new file mode 100644 index 0000000000..9ac6d28dc4 --- /dev/null +++ b/example/src/examples/UserLocation/SetTintColor.js @@ -0,0 +1,47 @@ +import React from 'react'; +import MapboxGL from '@react-native-mapbox-gl/maps'; + +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; + +const COLOR = ['red', 'yellow', 'green']; +const OPTIONS = [{label: 'red'}, {label: 'yellow'}, {label: 'green'}]; + +class SetTintColor extends React.Component { + static propTypes = { + ...BaseExamplePropTypes, + }; + + state = {tintColor: COLOR[0]}; + + onTintColorChange = index => { + this.setState({tintColor: COLOR[index]}); + }; + + render() { + return ( + + + + + + + + ); + } +} + +export default SetTintColor; diff --git a/example/src/examples/SetUserLocationRenderMode.js b/example/src/examples/UserLocation/SetUserLocationRenderMode.js similarity index 81% rename from example/src/examples/SetUserLocationRenderMode.js rename to example/src/examples/UserLocation/SetUserLocationRenderMode.js index daece02023..9adb18b026 100755 --- a/example/src/examples/SetUserLocationRenderMode.js +++ b/example/src/examples/UserLocation/SetUserLocationRenderMode.js @@ -2,11 +2,11 @@ import React from 'react'; import MapboxGL from '@react-native-mapbox-gl/maps'; import {Button, View} from 'react-native'; import {ButtonGroup} from 'react-native-elements'; +import PropTypes from 'prop-types'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import TabBarPage from './common/TabBarPage'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import TabBarPage from '../common/TabBarPage'; class SettingsPane extends React.Component { render() { @@ -19,12 +19,8 @@ class SettingsPane extends React.Component { followUserMode = 'normal', androidRenderMode = 'normal', } = settings; - const selectedModeIndex = followModes.findIndex( - (i) => i === followUserMode, - ); - const renderModeIndex = renderModes.findIndex( - (i) => i === androidRenderMode, - ); + const selectedModeIndex = followModes.findIndex(i => i === followUserMode); + const renderModeIndex = renderModes.findIndex(i => i === androidRenderMode); return ( @@ -53,7 +49,7 @@ class SettingsPane extends React.Component { + onPress={i => onUpdateSettings({ followUserMode: followModes[i], }) @@ -62,7 +58,7 @@ class SettingsPane extends React.Component { + onPress={i => onUpdateSettings({ androidRenderMode: renderModes[i], }) @@ -72,6 +68,15 @@ class SettingsPane extends React.Component { ); } } +SettingsPane.propTypes = { + settings: PropTypes.shape({ + followUserLocation: PropTypes.bool, + showsUserHeadingIndicator: PropTypes.bool, + followUserMode: PropTypes.string, + androidRenderMode: PropTypes.string, + }), + onUpdateSettings: PropTypes.func, +}; class SetUserLocationRenderMode extends React.Component { static propTypes = { @@ -123,7 +128,7 @@ class SetUserLocationRenderMode extends React.Component { onOptionPress={this.onRenderModeChange}> this.setState(settings)} + onUpdateSettings={settings => this.setState(settings)} /> { - return { - label: key, - data: MapboxGL.UserLocationVerticalAlignment[key], - }; - }) - .sort(onSortOptions); + this._alignmentOptions = Object.keys(Alignments).map(key => { + console.log('key: ', key); + + return { + label: key, + data: key, + }; + }); this.state = { - currentAlignmentMode: this._alignmentOptions[0].data, + currentAlignmentMode: Alignments.Center, }; this.onAlignmentChange = this.onAlignmentChange.bind(this); } onAlignmentChange(index, userLocationVerticalAlignment) { - this.setState({currentAlignmentMode: userLocationVerticalAlignment}); + this.setState({ + currentAlignmentMode: Alignments[userLocationVerticalAlignment], + }); } render() { @@ -41,7 +48,9 @@ class SetUserLocationVerticalAlignment extends React.Component { {...this.props} options={this._alignmentOptions} onOptionPress={this.onAlignmentChange}> - + diff --git a/example/src/examples/UserLocationChange.js b/example/src/examples/UserLocation/UserLocationChange.js similarity index 91% rename from example/src/examples/UserLocationChange.js rename to example/src/examples/UserLocation/UserLocationChange.js index 1197bc9fad..6a68bf9f4d 100755 --- a/example/src/examples/UserLocationChange.js +++ b/example/src/examples/UserLocation/UserLocationChange.js @@ -2,11 +2,10 @@ import React from 'react'; import {Text} from 'react-native'; import MapboxGL from '@react-native-mapbox-gl/maps'; -import sheet from '../styles/sheet'; - -import BaseExamplePropTypes from './common/BaseExamplePropTypes'; -import Page from './common/Page'; -import Bubble from './common/Bubble'; +import sheet from '../../styles/sheet'; +import BaseExamplePropTypes from '../common/BaseExamplePropTypes'; +import Page from '../common/Page'; +import Bubble from '../common/Bubble'; class UserLocationChange extends React.Component { static propTypes = { diff --git a/example/src/examples/common/TabBarPage.js b/example/src/examples/common/TabBarPage.js index a27a66029f..e57bcde5ab 100755 --- a/example/src/examples/common/TabBarPage.js +++ b/example/src/examples/common/TabBarPage.js @@ -68,7 +68,7 @@ class TabBarPage extends React.Component { containerBorderRadius: 0, onPress: this.onOptionPress, selectedIndex: this.state.currentIndex, - buttons: this.props.options.map((o) => o.label), + buttons: this.props.options.map(o => o.label), containerStyle: styles.buttonGroup, }; diff --git a/example/src/scenes/Home.js b/example/src/scenes/Home.js index 401bdf3c1b..76296b464e 100644 --- a/example/src/scenes/Home.js +++ b/example/src/scenes/Home.js @@ -6,53 +6,65 @@ import PropTypes from 'prop-types'; import Page from '../examples/common/Page'; import MapHeader from '../examples/common/MapHeader'; import sheet from '../styles/sheet'; -import ShowMap from '../examples/Map/ShowMap'; +// ANIMATIONS +import AnimatedLine from '../examples/Animations/AnimatedLine'; +import DriveTheLine from '../examples/Animations/DriveTheLine'; +// ANNOTATIONS +import CustomCallout from '../examples/Annotations/CustomCallout'; +import Heatmap from '../examples/Annotations/Heatmap'; import MarkerView from '../examples/Annotations/MarkerView'; -import SetPitch from '../examples/SetPitch'; -import SetHeading from '../examples/SetHeading'; -import ShowClick from '../examples/ShowClick'; -import FlyTo from '../examples/FlyTo'; -import FitBounds from '../examples/FitBounds'; -import SetUserTrackingModes from '../examples/SetUserTrackingModes'; -import SetUserLocationVerticalAlignment from '../examples/SetUserLocationVerticalAlignment'; -import SetUserLocationRenderMode from '../examples/SetUserLocationRenderMode'; -import ShowRegionDidChange from '../examples/ShowRegionDidChange'; -import CustomIcon from '../examples/CustomIcon'; -import YoYo from '../examples/YoYo'; -import EarthQuakes from '../examples/EarthQuakes'; -import GeoJSONSource from '../examples/GeoJSONSource'; -import WatercolorRasterTiles from '../examples/WatercolorRasterTiles'; -import TwoByTwo from '../examples/TwoByTwo'; -import IndoorBuilding from '../examples/IndoorBuilding'; -import QueryAtPoint from '../examples/QueryAtPoint'; -import QueryWithRect from '../examples/QueryWithRect'; -import ShapeSourceIcon from '../examples/ShapeSourceIcon'; -import CustomVectorSource from '../examples/CustomVectorSource'; import ShowPointAnnotation from '../examples/Annotations/ShowPointAnnotation'; -import AnimatedLine from '../examples/AnimatedLine'; -import CreateOfflineRegion from '../examples/CreateOfflineRegion'; -import DriveTheLine from '../examples/DriveTheLine'; -import ImageOverlay from '../examples/ImageOverlay'; -import DataDrivenCircleColors from '../examples/DataDrivenCircleColors'; -import ChoroplethLayerByZoomLevel from '../examples/ChoroplethLayerByZoomLevel'; -import PointInMapView from '../examples/PointInMapView'; -import TakeSnapshot from '../examples/TakeSnapshot'; -import TakeSnapshotWithMap from '../examples/TakeSnapshotWithMap'; -import GetZoom from '../examples/GetZoom'; -import GetCenter from '../examples/GetCenter'; -import UserLocationChange from '../examples/UserLocationChange'; -import Heatmap from '../examples/Annotations/Heatmap'; -import RestrictMapBounds from '../examples/RestrictMapBounds'; -import ShowAndHideLayer from '../examples/ShowAndHideLayer'; -import ChangeLayerColor from '../examples/ChangeLayerColor'; -import SourceLayerVisibility from '../examples/SourceLayerVisibility'; -import SetDisplacement from '../examples/SetDisplacement'; -import CompassView from '../examples/CompassView'; +import PointAnnotationAnchors from '../examples/Annotations/PointAnnotationAnchors'; +// CAMERA +import CompassView from '../examples/Camera/CompassView'; +import Fit from '../examples/Camera/Fit'; +import FlyTo from '../examples/Camera/FlyTo'; +import GetCenter from '../examples/Camera/GetCenter'; +import GetZoom from '../examples/Camera/GetZoom'; +import RestrictMapBounds from '../examples/Camera/RestrictMapBounds'; +import SetHeading from '../examples/Camera/SetHeading'; +import SetPitch from '../examples/Camera/SetPitch'; +import SetUserTrackingModes from '../examples/Camera/SetUserTrackingModes'; +import TakeSnapshot from '../examples/Camera/TakeSnapshot'; +import TakeSnapshotWithMap from '../examples/Camera/TakeSnapshotWithMap'; +import YoYo from '../examples/Camera/YoYo'; +// FILLRASTERLAYER +import ChoroplethLayerByZoomLevel from '../examples/FillRasterLayer/ChoroplethLayerByZoomLevel'; +import CustomVectorSource from '../examples/FillRasterLayer/CustomVectorSource'; +import GeoJSONSource from '../examples/FillRasterLayer/GeoJSONSource'; +import ImageOverlay from '../examples/FillRasterLayer/ImageOverlay'; +import IndoorBuilding from '../examples/FillRasterLayer/IndoorBuilding'; +import QueryAtPoint from '../examples/FillRasterLayer/QueryAtPoint'; +import QueryWithRect from '../examples/FillRasterLayer/QueryWithRect'; +import WatercolorRasterTiles from '../examples/FillRasterLayer/WatercolorRasterTiles'; +// LINE LAYER +import GradientLine from '../examples/LineLayer/GradientLine'; +// MAP +import ChangeLayerColor from '../examples/Map/ChangeLayerColor'; +import CreateOfflineRegion from '../examples/Map/CreateOfflineRegion'; +import PointInMapView from '../examples/Map/PointInMapView'; +import ShowAndHideLayer from '../examples/Map/ShowAndHideLayer'; +import ShowClick from '../examples/Map/ShowClick'; +import ShowMap from '../examples/Map/ShowMap'; +import ShowMapLocalStyle from '../examples/Map/ShowMapLocalStyle'; +import ShowRegionDidChange from '../examples/Map/ShowRegionDidChange'; +import SourceLayerVisibility from '../examples/Map/SourceLayerVisibility'; +import StyleJson from '../examples/Map/StyleJson'; +import TwoByTwo from '../examples/Map/TwoByTwo'; +// SYMBOLCIRCLELAYER +import CustomIcon from '../examples/SymbolCircleLayer/CustomIcon'; +import DataDrivenCircleColors from '../examples/SymbolCircleLayer/DataDrivenCircleColors'; +import EarthQuakes from '../examples/SymbolCircleLayer/EarthQuakes'; +import ShapeSourceIcon from '../examples/SymbolCircleLayer/ShapeSourceIcon'; +// USERLOCATION +import SetDisplacement from '../examples/UserLocation/SetDisplacement'; +import SetTintColor from '../examples/UserLocation/SetTintColor'; +import SetUserLocationRenderMode from '../examples/UserLocation/SetUserLocationRenderMode'; +import SetUserLocationVerticalAlignment from '../examples/UserLocation/SetUserLocationVerticalAlignment'; +import UserLocationChange from '../examples/UserLocation/UserLocationChange'; +// MISC import BugReportTemplate from '../examples/BugReportExample'; -import StyleJson from '../examples/StyleJson'; -import ShapeSourceTS from '../examples/SymbolCircleLayer/ShapeSource'; import CacheManagement from '../examples/CacheManagement'; -import CustomCallout from '../examples/Annotations/CustomCallout'; const styles = StyleSheet.create({ exampleList: { @@ -86,6 +98,7 @@ class ExampleGroup { this.label = label; this.items = items; this.navigationType = 'Group'; + // eslint-disable-next-line react/prop-types this.Component = ({navigation}) => ( ); @@ -99,8 +112,10 @@ const BugReportPage = ({...props}) => ( ); const Examples = [ + new ExampleItem('Bug Report Template', BugReportPage), new ExampleGroup('Map', [ new ExampleItem('Show Map', ShowMap), + new ExampleItem('Show Map With Local Style.JSON', ShowMapLocalStyle), new ExampleItem('Show Click', ShowClick), new ExampleItem('Show Region Did Change', ShowRegionDidChange), new ExampleItem('Two Map Views', TwoByTwo), @@ -110,12 +125,13 @@ const Examples = [ new ExampleItem('Change Layer Color', ChangeLayerColor), new ExampleItem('Source Layer Visiblity', SourceLayerVisibility), new ExampleItem('Style JSON', StyleJson), + new ExampleItem('Set Tint Color', SetTintColor), ]), new ExampleGroup('Camera', [ + new ExampleItem('Fit (Bounds, Center/Zoom, Padding)', Fit), new ExampleItem('Set Pitch', SetPitch), new ExampleItem('Set Heading', SetHeading), new ExampleItem('Fly To', FlyTo), - new ExampleItem('Fit Bounds', FitBounds), new ExampleItem('Restrict Bounds', RestrictMapBounds), new ExampleItem('Set User Tracking Modes', SetUserTrackingModes), new ExampleItem('Yo Yo Camera', YoYo), @@ -139,7 +155,6 @@ const Examples = [ new ExampleItem('Clustering Earthquakes', EarthQuakes), new ExampleItem('Shape Source From Icon', ShapeSourceIcon), new ExampleItem('Data Driven Circle Colors', DataDrivenCircleColors), - new ExampleItem('Shape Source From Icon.TS', ShapeSourceTS), ]), new ExampleGroup('Fill/RasterLayer', [ new ExampleItem('GeoJSON Source', GeoJSONSource), @@ -154,18 +169,20 @@ const Examples = [ ChoroplethLayerByZoomLevel, ), ]), + new ExampleGroup('LineLayer', [ + new ExampleItem('GradientLine', GradientLine), + ]), new ExampleGroup('Annotations', [ new ExampleItem('Show Point Annotation', ShowPointAnnotation), + new ExampleItem('Point Annotation Anchors', PointAnnotationAnchors), new ExampleItem('Marker View', MarkerView), new ExampleItem('Heatmap', Heatmap), - new ExampleItem('Custom Callout', CustomCallout) + new ExampleItem('Custom Callout', CustomCallout), ]), new ExampleGroup('Animations', [ new ExampleItem('Animated Line', AnimatedLine), new ExampleItem('Animation Along a Line', DriveTheLine), - new ExampleItem('Yo Yo Camera', YoYo), ]), - new ExampleItem('Bug Report Template', BugReportPage), new ExampleItem('Cache management', CacheManagement), ]; @@ -207,17 +224,29 @@ function ExampleGroupComponent({items, navigation, showBack}) { item.label} + keyExtractor={item => item.label} renderItem={renderItem} /> ); } +ExampleGroupComponent.propTypes = { + navigation: PropTypes.shape({ + navigate: PropTypes.func, + getParam: PropTypes.func, + goBack: PropTypes.func, + }), + showBack: PropTypes.bool, + items: PropTypes.any, +}; class Home extends React.Component { static propTypes = { - navigation: PropTypes.shape({navigate: PropTypes.func}), + navigation: PropTypes.shape({ + navigate: PropTypes.func, + getParam: PropTypes.func, + }), }; render() { diff --git a/example/src/utils/RouteSimulator.js b/example/src/utils/RouteSimulator.js index 13896deb9a..d357411a7d 100755 --- a/example/src/utils/RouteSimulator.js +++ b/example/src/utils/RouteSimulator.js @@ -78,7 +78,7 @@ class RouteSimulator { this._currentDistance += this._speed; // interpolate between previous to current distance - const listener = (step) => { + const listener = step => { const currentPosition = this._polyline.coordinateFromStart(step.value); this.emit(currentPosition); }; diff --git a/index.d.ts b/index.d.ts index cb8819f635..ab303bb588 100644 --- a/index.d.ts +++ b/index.d.ts @@ -10,8 +10,10 @@ import { ViewProps, ViewStyle, StyleProp, + TextStyle, ImageSourcePropType, } from 'react-native'; +import ReactNative from 'react-native'; import { Geometry, @@ -28,39 +30,36 @@ import { // prettier-ignore type ExpressionName = - // Types - | 'array' | 'boolean' | 'collator' | 'format' | 'literal' | 'number' | 'object' | 'string' - | 'to-boolean' | 'to-color' | 'to-number' | 'to-string' | 'typeof' - // Feature data - | 'feature-state' | 'geometry-type' | 'id' | 'line-progress' | 'properties' - // Lookup - | 'at' | 'get' | 'has' | 'length' - // Decision - | '!' | '!=' | '<' | '<=' | '==' | '>' | '>=' | 'all' | 'any' | 'case' | 'match' | 'coalesce' - // Ramps, scales, curves - | 'interpolate' | 'interpolate-hcl' | 'interpolate-lab' | 'step' - // Variable binding - | 'let' | 'var' - // String - | 'concat' | 'downcase' | 'is-supported-script' | 'resolved-locale' | 'upcase' - // Color - | 'rgb' | 'rgba' - // Math - | '-' | '*' | '/' | '%' | '^' | '+' | 'abs' | 'acos' | 'asin' | 'atan' | 'ceil' | 'cos' | 'e' - | 'floor' | 'ln' | 'ln2' | 'log10' | 'log2' | 'max' | 'min' | 'pi' | 'round' | 'sin' | 'sqrt' | 'tan' - // Zoom, Heatmap - | 'zoom' | 'heatmap-density'; - -type ExpressionField = any; - -// After TS 3.7 this can be typed as: -// string -// | number -// | boolean -// | Expression -// | ExpressionField[] -// | {[key: string]: ExpressionField}; -// See https://github.com/microsoft/TypeScript/pull/33050 + // Types + | 'array' | 'boolean' | 'collator' | 'format' | 'literal' | 'number' | 'object' | 'string' + | 'to-boolean' | 'to-color' | 'to-number' | 'to-string' | 'typeof' + // Feature data + | 'feature-state' | 'geometry-type' | 'id' | 'line-progress' | 'properties' + // Lookup + | 'at' | 'get' | 'has' | 'length' + // Decision + | '!' | '!=' | '<' | '<=' | '==' | '>' | '>=' | 'all' | 'any' | 'case' | 'match' | 'coalesce' + // Ramps, scales, curves + | 'interpolate' | 'interpolate-hcl' | 'interpolate-lab' | 'step' + // Variable binding + | 'let' | 'var' + // String + | 'concat' | 'downcase' | 'is-supported-script' | 'resolved-locale' | 'upcase' + // Color + | 'rgb' | 'rgba' + // Math + | '-' | '*' | '/' | '%' | '^' | '+' | 'abs' | 'acos' | 'asin' | 'atan' | 'ceil' | 'cos' | 'e' + | 'floor' | 'ln' | 'ln2' | 'log10' | 'log2' | 'max' | 'min' | 'pi' | 'round' | 'sin' | 'sqrt' | 'tan' + // Zoom, Heatmap + | 'zoom' | 'heatmap-density'; + +type ExpressionField = + | string + | number + | boolean + | Expression + | ExpressionField[] + | {[key: string]: ExpressionField}; export type Expression = [ExpressionName, ...ExpressionField[]]; @@ -80,20 +79,20 @@ type AutoAlignment = Alignment | 'auto'; type NamedStyles = { [P in keyof T]: - | SymbolLayerStyle - | RasterLayerStyle - | LineLayerStyle - | FillLayerStyle - | FillExtrusionLayerStyle - | CircleLayerStyle - | BackgroundLayerStyle; + | SymbolLayerStyle + | RasterLayerStyle + | LineLayerStyle + | FillLayerStyle + | FillExtrusionLayerStyle + | CircleLayerStyle + | BackgroundLayerStyle; }; export type MapboxGLEvent< T extends string, P = GeoJSON.Feature, V = Element -> = SyntheticEvent; + > = SyntheticEvent; export type OnPressEvent = { features: Array; @@ -133,30 +132,48 @@ declare namespace MapboxGL { } namespace geoUtils { - function makePoint

(coordinates: Position, properties?: P, options?: PositionsOptions): Feature; + function makePoint

(coordinates: Position, properties?: P, options?: PositionsOptions): Feature; function makeLineString

(coordinates: Position[], properties?: P, options?: PositionsOptions): Feature; function makeLatLngBounds(northEastCoordinates: Position[], southWestCoordinates: Position[]): FeatureCollection; function makeFeature(geometry: G, properties?: P): Feature; - function makeFeatureCollection(features: Array>, options?: PositionsOptions): FeatureCollection; + function makeFeatureCollection(features: Array>, options?: PositionsOptions): FeatureCollection; function addToFeatureCollection(newFeatureCollection: Array>, newFeature: Feature): FeatureCollection; function calculateDistance(origin: Coord, dest: Coord, options?: UnitsOptions): number; - function pointAlongLine(newLineString: Feature | LineString, distAlong: number, options?: UnitsOptions): Feature; + function pointAlongLine(newLineString: Feature | LineString, distAlong: number, options?: UnitsOptions): Feature; function getOrCalculateVisibleRegion(coord: { lon: number; lat: number }, zoomLevel: number, width: number, height: number, nativeRegion: { properties: { visibleBounds: number[] }; visibleBounds: number[] }): void; } namespace Animated { // sources - class ShapeSource extends Component {} - class ImageSource extends Component {} + class ShapeSource extends Component { } + class ImageSource extends Component { } // layers - class FillLayer extends Component {} - class FillExtrusionLayer extends Component {} - class LineLayer extends Component {} - class CircleLayer extends Component {} - class SymbolLayer extends Component {} - class RasterLayer extends Component {} - class BackgroundLayer extends Component {} + class FillLayer extends Component { } + class FillExtrusionLayer extends Component { } + class LineLayer extends Component { } + class CircleLayer extends Component { } + class SymbolLayer extends Component { } + class RasterLayer extends Component { } + class BackgroundLayer extends Component { } + } + + /** + * Classes + */ + + class AnimatedPoint { + constructor(point?: GeoJSON.Point); + longitude: ReactNative.Animated.Value; + latitude: ReactNative.Animated.Value; + setValue: (point: GeoJSON.Point) => void; + setOffset: (point: GeoJSON.Point) => void; + flattenOffset: () => void; + stopAnimation: (cb?: () => GeoJSON.Point) => void; + addListener: (cb?: () => GeoJSON.Point) => void; + removeListener: (id: string) => void; + spring: (config: Record) => ReactNative.Animated.CompositeAnimation; + timing: (config: Record) => ReactNative.Animated.CompositeAnimation; } /** @@ -197,7 +214,7 @@ declare namespace MapboxGL { setCamera(config: CameraSettings): void; } - class UserLocation extends Component {} + class UserLocation extends Component { } interface Location { coords: Coordinates; @@ -205,29 +222,62 @@ declare namespace MapboxGL { } interface Coordinates { + + /** + * The heading (measured in degrees) relative to true north. + * Heading is used to describe the direction the device is pointing to (the value of the compass). + * Note that on Android this is incorrectly reporting the course value as mentioned in issue https://github.com/react-native-mapbox-gl/maps/issues/1213 + * and will be corrected in a future update. + */ heading?: number; + + /** + * The direction in which the device is traveling, measured in degrees and relative to due north. + * The course refers to the direction the device is actually moving (not the same as heading). + */ + course?: number; + + /** + * The instantaneous speed of the device, measured in meters per second. + */ speed?: number; + + /** + * The latitude in degrees. + */ latitude: number; + + /** + * The longitude in degrees. + */ longitude: number; + + /** + * The radius of uncertainty for the location, measured in meters. + */ accuracy?: number; + + /** + * The altitude, measured in meters. + */ altitude?: number; } - class Light extends Component {} + class Light extends Component { } class StyleSheet extends Component { - static create | NamedStyles>(styles: T): void; + static create | NamedStyles>(styles: T): T; camera( - stops: {[key: number]: string}, + stops: { [key: number]: string }, interpolationMode?: InterpolationMode, ): void; source( - stops: {[key: number]: string}, + stops: { [key: number]: string }, attributeName: string, interpolationMode?: InterpolationMode, ): void; composite( - stops: {[key: number]: string}, + stops: { [key: number]: string }, attributeName: string, interpolationMode?: InterpolationMode, ): void; @@ -238,30 +288,46 @@ declare namespace MapboxGL { class PointAnnotation extends Component { refresh(): void; } - class MarkerView extends Component {} - class Callout extends Component {} - interface Style extends React.FC {} + class MarkerView extends Component { } + class Callout extends Component { } + interface Style extends React.FC { } /** * Sources */ - class VectorSource extends Component {} - class ShapeSource extends Component {} - class RasterSource extends Component {} + class VectorSource extends Component { } + class ShapeSource extends Component { + features(filter?: Expression): Promise> + + getClusterExpansionZoom(feature: Feature | number): Promise + /** + * Returns all the leaves of a cluster with pagination support. + * @param cluster feature cluster + * @param limit the number of leaves to return + * @param offset the amount of points to skip (for pagination) + */ + getClusterLeaves: (feature: Feature | number, limit: number, offset: number ) => Promise> + /** + * Returns the children of a cluster (on the next zoom level). + * @param cluster feature cluster + */ + getClusterChildren: (feature: Feature | number) => Promise> + } + class RasterSource extends Component { } /** * Layers */ - class BackgroundLayer extends Component {} - class CircleLayer extends Component {} - class FillExtrusionLayer extends Component {} - class FillLayer extends Component {} - class LineLayer extends Component {} - class RasterLayer extends Component {} - class SymbolLayer extends Component {} - class HeatmapLayer extends Component {} - class Images extends Component {} - class ImageSource extends Component {} + class BackgroundLayer extends Component { } + class CircleLayer extends Component { } + class FillExtrusionLayer extends Component { } + class FillLayer extends Component { } + class LineLayer extends Component { } + class RasterLayer extends Component { } + class SymbolLayer extends Component { } + class HeatmapLayer extends Component { } + class Images extends Component { } + class ImageSource extends Component { } class LocationManager extends Component { start(displacement?: number): void; @@ -278,6 +344,7 @@ declare namespace MapboxGL { errorListener?: (pack: OfflinePack, err: OfflineProgressError) => void ): Promise; deletePack(name: string): Promise; + invalidatePack(name: string): Promise; getPacks(): Promise>; getPack(name: string): Promise; invalidateAmbientCache(): Promise; @@ -360,17 +427,19 @@ declare namespace MapboxGL { TrafficDay = 'mapbox://styles/mapbox/navigation-preview-day-v4', TrafficNight = 'mapbox://styles/mapbox/navigation-preview-night-v4', } - - enum StyleSource { - DefaultSourceID = 0, - } } export type AttributionPosition = - | {top: number; left: number} - | {top: number; right: number} - | {bottom: number; left: number} - | {bottom: number; right: number}; + | { top: number; left: number } + | { top: number; right: number } + | { bottom: number; left: number } + | { bottom: number; right: number }; + +export type LogoPosition = + | { top: number; left: number } + | { top: number; right: number } + | { bottom: number; left: number } + | { bottom: number; right: number }; export interface RegionPayload { zoomLevel: number; @@ -388,6 +457,8 @@ export interface MapViewProps extends ViewProps { contentInset?: Array; style?: StyleProp; styleURL?: string; + styleJSON?: string; + preferredFramesPerSecond?: number; localizeLabels?: boolean; zoomEnabled?: boolean; scrollEnabled?: boolean; @@ -396,12 +467,14 @@ export interface MapViewProps extends ViewProps { attributionEnabled?: boolean; attributionPosition?: AttributionPosition; logoEnabled?: boolean; + logoPosition?: LogoPosition; compassEnabled?: boolean; compassViewPosition?: number; compassViewMargins?: Point; surfaceView?: boolean; regionWillChangeDebounceTime?: number; regionDidChangeDebounceTime?: number; + tintColor?: string; onPress?: (feature: GeoJSON.Feature) => void; onLongPress?: (feature: GeoJSON.Feature) => void; @@ -429,12 +502,13 @@ export interface MapViewProps extends ViewProps { } export interface CameraProps extends CameraSettings, ViewProps { + allowUpdates?: boolean; animationDuration?: number; - animationMode?: 'flyTo' | 'easeTo' | 'moveTo'; + animationMode?: 'flyTo' | 'easeTo' | 'linearTo' | 'moveTo'; defaultSettings?: CameraSettings; minZoomLevel?: number; maxZoomLevel?: number; - maxBounds?: {ne: [number, number]; sw: [number, number]}; + maxBounds?: { ne: [number, number]; sw: [number, number] }; followUserLocation?: boolean; followUserMode?: 'normal' | 'compass' | 'course'; followZoomLevel?: number; @@ -453,21 +527,25 @@ export interface CameraProps extends CameraSettings, ViewProps { ) => void; } +export interface CameraPadding { + paddingLeft?: number; + paddingRight?: number; + paddingTop?: number; + paddingBottom?: number; +} + export interface CameraSettings { centerCoordinate?: GeoJSON.Position; heading?: number; pitch?: number; - bounds?: { + padding?: CameraPadding; + bounds?: CameraPadding & { ne: GeoJSON.Position; sw: GeoJSON.Position; - paddingLeft?: number; - paddingRight?: number; - paddingTop?: number; - paddingBottom?: number; }; zoomLevel?: number; animationDuration?: number; - animationMode?: 'flyTo' | 'easeTo' | 'moveTo'; + animationMode?: 'flyTo' | 'easeTo' | 'linearTo' | 'moveTo'; stops?: CameraSettings[]; } @@ -612,12 +690,13 @@ export interface RasterLayerStyle { rasterFadeDuration?: number | Expression; } -export type TextVariableAnchorValues = "center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right"; +export type TextVariableAnchorValues = "center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right"; export interface SymbolLayerStyle { symbolPlacement?: 'point' | 'line' | Expression; symbolSpacing?: number | Expression; symbolAvoidEdges?: boolean | Expression; + symbolSortKey?: number | Expression; symbolZOrder?: 'auto' | 'viewport-y' | 'source' | Expression; iconAllowOverlap?: boolean | Expression; iconIgnorePlacement?: boolean | Expression; @@ -732,7 +811,7 @@ export interface CalloutProps extends Omit { containerStyle?: StyleProp>; contentStyle?: StyleProp>; tipStyle?: StyleProp>; - textStyle?: StyleProp>; + textStyle?: StyleProp>; } export interface TileSourceProps extends ViewProps { @@ -761,7 +840,8 @@ export interface ShapeSourceProps extends ViewProps { maxZoomLevel?: number; buffer?: number; tolerance?: number; - images?: {assets?: string[]} & {[key: string]: ImageSourcePropType}; + lineMetrics?: boolean; + images?: { assets?: string[] } & { [key: string]: ImageSourcePropType }; onPress?: (event: OnPressEvent) => void; hitbox?: { width: number; @@ -775,7 +855,7 @@ export interface RasterSourceProps extends TileSourceProps { export interface LayerBaseProps extends Omit { id: string; - sourceID?: MapboxGL.StyleSource; + sourceID?: string; sourceLayerID?: string; aboveLayerID?: string; belowLayerID?: string; @@ -793,7 +873,7 @@ export interface CircleLayerProps extends LayerBaseProps { style?: StyleProp; } -export interface FillExtrusionLayerProps extends Omit { +export interface FillExtrusionLayerProps extends Omit { id: string; style?: StyleProp; } @@ -819,7 +899,9 @@ export interface HeatmapLayerProps extends LayerBaseProps { } export interface ImagesProps extends ViewProps { - images?: {assets?: string[]} & {[key: string]: ImageSourcePropType}; + images?: { assets?: string[] } & { [key: string]: ImageSourcePropType }; + nativeAssetImages?: string[] + onImageMissing?: (imageKey: string) => void } export interface ImageSourceProps extends ViewProps { @@ -853,4 +935,20 @@ export interface SnapshotOptions { writeToDisk?: boolean; } +// Logger class +type LogLevel = "error" | "warning" | "info" | "debug" | "verbose"; + +interface LogObject { + level: LogLevel; + message: string; + tag: string; +} + +type LogCallback = (object: LogObject) => void; + +export class Logger { + public static setLogCallback: (cb: LogCallback) => boolean; + public static setLogLevel: (level: LogLevel) => void; +} + export default MapboxGL; diff --git a/ios/RCTMGL/CameraMode.h b/ios/RCTMGL/CameraMode.h index 46c034af24..b467506987 100644 --- a/ios/RCTMGL/CameraMode.h +++ b/ios/RCTMGL/CameraMode.h @@ -12,6 +12,7 @@ extern int const RCT_MAPBOX_CAMERA_MODE_FLIGHT; extern int const RCT_MAPBOX_CAMERA_MODE_EASE; +extern int const RCT_MAPBOX_CAMERA_MODE_LINEAR; extern int const RCT_MAPBOX_CAMERA_MODE_NONE; @end diff --git a/ios/RCTMGL/CameraMode.m b/ios/RCTMGL/CameraMode.m index 8bd67224de..b27df6cafa 100644 --- a/ios/RCTMGL/CameraMode.m +++ b/ios/RCTMGL/CameraMode.m @@ -12,6 +12,7 @@ @implementation CameraMode int const RCT_MAPBOX_CAMERA_MODE_FLIGHT = 1; int const RCT_MAPBOX_CAMERA_MODE_EASE = 2; -int const RCT_MAPBOX_CAMERA_MODE_NONE = 3; +int const RCT_MAPBOX_CAMERA_MODE_LINEAR = 3; +int const RCT_MAPBOX_CAMERA_MODE_NONE = 4; @end diff --git a/ios/RCTMGL/CameraStop.h b/ios/RCTMGL/CameraStop.h index 18386180ea..3986435420 100644 --- a/ios/RCTMGL/CameraStop.h +++ b/ios/RCTMGL/CameraStop.h @@ -19,7 +19,7 @@ @property (nonatomic, assign) CLLocationCoordinate2D coordinate; @property (nonatomic, assign) MGLCoordinateBounds bounds; -@property (nonatomic, assign) UIEdgeInsets boundsPadding; +@property (nonatomic, assign) UIEdgeInsets padding; + (CameraStop*)fromDictionary:(NSDictionary*)args; diff --git a/ios/RCTMGL/CameraStop.m b/ios/RCTMGL/CameraStop.m index 1d40af9495..0aaf23c8df 100644 --- a/ios/RCTMGL/CameraStop.m +++ b/ios/RCTMGL/CameraStop.m @@ -21,6 +21,8 @@ - (void)setMode:(NSNumber *)mode _mode = [NSNumber numberWithInt:modeInt]; } else if (modeInt == RCT_MAPBOX_CAMERA_MODE_NONE) { _mode = [NSNumber numberWithInt:modeInt]; + } else if (modeInt == RCT_MAPBOX_CAMERA_MODE_LINEAR) { + _mode = [NSNumber numberWithInt:modeInt]; } else { _mode = [NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_EASE]; } @@ -46,7 +48,7 @@ + (CameraStop*)fromDictionary:(NSDictionary *)args if (args[@"heading"]) { stop.heading = args[@"heading"]; } - + if (args[@"centerCoordinate"]) { stop.coordinate = [RCTMGLUtils fromFeature:args[@"centerCoordinate"]]; } @@ -61,14 +63,14 @@ + (CameraStop*)fromDictionary:(NSDictionary *)args if (args[@"bounds"]) { stop.bounds = [RCTMGLUtils fromFeatureCollection:args[@"bounds"]]; - - CGFloat paddingTop = args[@"boundsPaddingTop"] ? [args[@"boundsPaddingTop"] floatValue] : 0.0; - CGFloat paddingRight = args[@"boundsPaddingRight"] ? [args[@"boundsPaddingRight"] floatValue] : 0.0; - CGFloat paddingBottom = args[@"boundsPaddingBottom"] ? [args[@"boundsPaddingBottom"] floatValue] : 0.0; - CGFloat paddingLeft = args[@"boundsPaddingLeft"] ? [args[@"boundsPaddingLeft"] floatValue] : 0.0; - stop.boundsPadding = UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight); } + CGFloat paddingTop = args[@"paddingTop"] ? [args[@"paddingTop"] floatValue] : 0.0; + CGFloat paddingRight = args[@"paddingRight"] ? [args[@"paddingRight"] floatValue] : 0.0; + CGFloat paddingBottom = args[@"paddingBottom"] ? [args[@"paddingBottom"] floatValue] : 0.0; + CGFloat paddingLeft = args[@"paddingLeft"] ? [args[@"paddingLeft"] floatValue] : 0.0; + stop.padding = UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight); + NSTimeInterval duration = 2.0; if (args[@"duration"]) { duration = [RCTMGLUtils fromMS:args[@"duration"]]; diff --git a/ios/RCTMGL/CameraUpdateItem.m b/ios/RCTMGL/CameraUpdateItem.m index 515ee0a7bb..64018a59bc 100644 --- a/ios/RCTMGL/CameraUpdateItem.m +++ b/ios/RCTMGL/CameraUpdateItem.m @@ -32,11 +32,11 @@ - (void)execute:(RCTMGLMapView *)mapView withCompletionHandler:(void (^)(void))c if (_cameraStop.mode == [NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_FLIGHT]) { [self _flyToCamera:mapView withCompletionHandler:completionHandler]; } else if (_cameraStop.mode == [NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_EASE]) { - [self _moveCamera:mapView animated:YES withCompletionHandler:completionHandler]; - } else if ([self _areBoundsValid:_cameraStop.bounds]) { - [self _fitBoundsCamera:mapView withCompletionHandler:completionHandler]; + [self _moveCamera:mapView animated:YES ease:YES withCompletionHandler:completionHandler]; + } else if (_cameraStop.mode == [NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_LINEAR]) { + [self _moveCamera:mapView animated:YES ease:NO withCompletionHandler:completionHandler]; } else { - [self _moveCamera:mapView animated:NO withCompletionHandler:completionHandler]; + [self _moveCamera:mapView animated:NO ease:NO withCompletionHandler:completionHandler]; } } @@ -45,25 +45,82 @@ - (void)_flyToCamera:(RCTMGLMapView*)mapView withCompletionHandler:(void (^)(voi RCTMGLCameraWithPadding *nextCamera = [self _makeCamera:mapView]; if ([mapView respondsToSelector:@selector(_flyToCamera:edgePadding:withDuration:peakAltitude:completionHandler:)]) { - [mapView _flyToCamera:nextCamera.camera edgePadding:nextCamera.boundsPadding withDuration:_cameraStop.duration peakAltitude:-1 completionHandler:completionHandler]; + [mapView + _flyToCamera:nextCamera.camera + edgePadding:nextCamera.boundsPadding + withDuration:_cameraStop.duration + peakAltitude:-1 + completionHandler:completionHandler]; } else { - [mapView flyToCamera:nextCamera.camera withDuration:_cameraStop.duration completionHandler:completionHandler]; + [mapView + flyToCamera:nextCamera.camera + withDuration:_cameraStop.duration + completionHandler:completionHandler]; } } -- (void)_moveCamera:(RCTMGLMapView*)mapView animated:(BOOL)animated withCompletionHandler:(void (^)(void))completionHandler +- (void)_moveCamera:(RCTMGLMapView*)mapView animated:(BOOL)animated ease:(BOOL)ease withCompletionHandler:(void (^)(void))completionHandler { - if ([self _hasCenterCoordAndZoom]) { - [self _centerCoordWithZoomCamera:mapView animated:animated withCompletionHandler:completionHandler]; - } else { RCTMGLCameraWithPadding *nextCamera = [self _makeCamera:mapView]; + NSString *easeFunctionName = ease ? kCAMediaTimingFunctionEaseInEaseOut : kCAMediaTimingFunctionLinear; [mapView setCamera:nextCamera.camera withDuration:animated ? _cameraStop.duration : 0 - animationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] + animationTimingFunction:[CAMediaTimingFunction functionWithName:easeFunctionName] edgePadding:nextCamera.boundsPadding completionHandler:completionHandler]; +} + +- (RCTMGLCameraWithPadding*)_makeCamera:(RCTMGLMapView*)mapView +{ + MGLMapCamera *nextCamera = [mapView.camera copy]; + + UIEdgeInsets padding = [self _clippedPadding:_cameraStop.padding forView:mapView]; + if (padding.top <= 0 && padding.bottom <= 0) { + // If all padding properties are 0 in the update, and the bounds and centerCoordinate do not + // change, the padding doesn't change either. This seems to be a bug in the iOS SDK. + padding.top = 1.0; + padding.bottom = 1.0; } + + bool hasSetAltitude = false; + + if ([self _isCoordValid:_cameraStop.coordinate]) { + MGLCoordinateBounds boundsFromCoord = { .sw = _cameraStop.coordinate, .ne = _cameraStop.coordinate }; + MGLMapCamera *boundsCamera = [mapView + camera:nextCamera + fittingCoordinateBounds:boundsFromCoord + edgePadding: padding]; + nextCamera.centerCoordinate = boundsCamera.centerCoordinate; + } else if ([self _areBoundsValid:_cameraStop.bounds]) { + MGLMapCamera *boundsCamera = [mapView + camera:nextCamera + fittingCoordinateBounds:_cameraStop.bounds + edgePadding: padding]; + nextCamera.centerCoordinate = boundsCamera.centerCoordinate; + nextCamera.altitude = boundsCamera.altitude; + hasSetAltitude = true; + } + + if (_cameraStop.pitch != nil) { + nextCamera.pitch = [_cameraStop.pitch floatValue]; + } + + if (_cameraStop.heading != nil) { + nextCamera.heading = [_cameraStop.heading floatValue]; + } + + if (_cameraStop.zoom != nil && hasSetAltitude == false) { + nextCamera.altitude = [mapView + altitudeFromZoom:[_cameraStop.zoom doubleValue] + atLatitude:nextCamera.centerCoordinate.latitude + atPitch:nextCamera.pitch]; + } + + RCTMGLCameraWithPadding* cameraWithPadding = [[RCTMGLCameraWithPadding alloc] init]; + cameraWithPadding.camera = nextCamera; + cameraWithPadding.boundsPadding = padding; + return cameraWithPadding; } - (UIEdgeInsets)_clippedPadding:(UIEdgeInsets)padding forView:(RCTMGLMapView*)mapView @@ -84,67 +141,6 @@ - (UIEdgeInsets)_clippedPadding:(UIEdgeInsets)padding forView:(RCTMGLMapView*)ma return result; } -- (void)_fitBoundsCamera:(RCTMGLMapView*)mapView withCompletionHandler:(void (^)(void))completionHandler -{ - MGLCoordinateBounds bounds = _cameraStop.bounds; - CLLocationCoordinate2D coordinates[] = { - { bounds.ne.latitude, bounds.sw.longitude }, - bounds.sw, - { bounds.sw.latitude, bounds.ne.longitude }, - bounds.ne - }; - - [mapView setVisibleCoordinates:coordinates - count:4 - edgePadding:[self _clippedPadding:_cameraStop.boundsPadding forView:mapView] - direction:mapView.direction - duration:_cameraStop.duration - animationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] - completionHandler:completionHandler]; -} - -- (void)_centerCoordWithZoomCamera:(RCTMGLMapView*)mapView animated:(BOOL)animated withCompletionHandler:(void (^)(void))completionHandler -{ - MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:_cameraStop.coordinate - fromDistance:[mapView altitudeFromZoom:[_cameraStop.zoom doubleValue] atLatitude:_cameraStop.coordinate.latitude] - pitch:[_cameraStop.pitch floatValue] - heading:[_cameraStop.heading floatValue]]; - [mapView setCamera:camera - withDuration:animated ? _cameraStop.duration : 0 - animationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] - completionHandler:completionHandler]; -} - -- (RCTMGLCameraWithPadding*)_makeCamera:(RCTMGLMapView*)mapView -{ - MGLMapCamera *nextCamera = [mapView.camera copy]; - - if (_cameraStop.pitch != nil) { - nextCamera.pitch = [_cameraStop.pitch floatValue]; - } - - if (_cameraStop.heading != nil) { - nextCamera.heading = [_cameraStop.heading floatValue]; - } - - if ([self _isCoordValid:_cameraStop.coordinate]) { - nextCamera.centerCoordinate = _cameraStop.coordinate; - } else if ([self _areBoundsValid:_cameraStop.bounds]) { - MGLMapCamera *boundsCamera = [mapView camera:nextCamera fittingCoordinateBounds:_cameraStop.bounds edgePadding: [self _clippedPadding:_cameraStop.boundsPadding forView:mapView]]; - nextCamera.centerCoordinate = boundsCamera.centerCoordinate; - nextCamera.altitude = boundsCamera.altitude; - } - - if (_cameraStop.zoom != nil) { - nextCamera.altitude = [mapView altitudeFromZoom:[_cameraStop.zoom doubleValue] atLatitude:nextCamera.centerCoordinate.latitude atPitch:nextCamera.pitch]; - } - - RCTMGLCameraWithPadding* cameraWithPadding = [[RCTMGLCameraWithPadding alloc] init]; - cameraWithPadding.camera = nextCamera; - cameraWithPadding.boundsPadding = [self _clippedPadding:_cameraStop.boundsPadding forView:mapView]; - return cameraWithPadding; -} - - (BOOL)_areBoundsValid:(MGLCoordinateBounds)bounds { BOOL isValid = CLLocationCoordinate2DIsValid(bounds.ne) && CLLocationCoordinate2DIsValid(bounds.sw); diff --git a/ios/RCTMGL/MGLModule.m b/ios/RCTMGL/MGLModule.m index d9a795000a..4526c82fe2 100644 --- a/ios/RCTMGL/MGLModule.m +++ b/ios/RCTMGL/MGLModule.m @@ -27,12 +27,20 @@ + (BOOL)requiresMainQueueSetup { // style urls NSMutableDictionary *styleURLS = [[NSMutableDictionary alloc] init]; + +#ifdef RNMGL_USE_MAPLIBRE + for (MGLDefaultStyle* style in [MGLStyle predefinedStyles]) { + [styleURLS setObject:[style.url absoluteString] forKey:style.name]; + } + [styleURLS setObject:[[MGLStyle defaultStyleURL] absoluteString] forKey:@"Default"]; +#else [styleURLS setObject:[MGLStyle.streetsStyleURL absoluteString] forKey:@"Street"]; [styleURLS setObject:[MGLStyle.darkStyleURL absoluteString] forKey:@"Dark"]; [styleURLS setObject:[MGLStyle.lightStyleURL absoluteString] forKey:@"Light"]; [styleURLS setObject:[MGLStyle.outdoorsStyleURL absoluteString] forKey:@"Outdoors"]; [styleURLS setObject:[MGLStyle.satelliteStyleURL absoluteString] forKey:@"Satellite"]; [styleURLS setObject:[MGLStyle.satelliteStreetsStyleURL absoluteString] forKey:@"SatelliteStreet"]; +#endif // event types NSMutableDictionary *eventTypes = [[NSMutableDictionary alloc] init]; @@ -73,6 +81,7 @@ + (BOOL)requiresMainQueueSetup NSMutableDictionary *cameraModes = [[NSMutableDictionary alloc] init]; [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_FLIGHT] forKey:@"Flight"]; [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_EASE] forKey:@"Ease"]; + [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_LINEAR] forKey:@"Linear"]; [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_NONE] forKey:@"None"]; // style sources @@ -239,7 +248,13 @@ + (BOOL)requiresMainQueueSetup RCT_EXPORT_METHOD(setAccessToken:(NSString *)accessToken) { +#ifdef RNMGL_USE_MAPLIBRE + if (accessToken.length > 0) { + [MGLSettings setApiKey:accessToken]; + } +#else [MGLAccountManager setAccessToken:accessToken]; +#endif } RCT_EXPORT_METHOD(addCustomHeader:(NSString *)headerName forHeaderValue:(NSString *) headerValue) @@ -254,7 +269,11 @@ + (BOOL)requiresMainQueueSetup RCT_EXPORT_METHOD(getAccessToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { +#ifdef RNMGL_USE_MAPLIBRE + NSString* accessToken = MGLSettings.apiKey; +#else NSString *accessToken = MGLAccountManager.accessToken; +#endif if (accessToken != nil) { resolve(accessToken); diff --git a/ios/RCTMGL/RCTMGLCallout.m b/ios/RCTMGL/RCTMGLCallout.m index c6c5e137a4..5e995c6337 100644 --- a/ios/RCTMGL/RCTMGLCallout.m +++ b/ios/RCTMGL/RCTMGLCallout.m @@ -7,7 +7,7 @@ // #import "RCTMGLCallout.h" -#import "UIView+React.h" +#import @implementation RCTMGLCallout { diff --git a/ios/RCTMGL/RCTMGLImages.m b/ios/RCTMGL/RCTMGLImages.m index d0d42023dc..2738d0ec71 100644 --- a/ios/RCTMGL/RCTMGLImages.m +++ b/ios/RCTMGL/RCTMGLImages.m @@ -1,5 +1,5 @@ #import "RCTMGLImages.h" -#import "UIView+React.h" +#import #import "RCTMGLMapView.h" #import "RCTMGLUtils.h" #import "RCTMGLEvent.h" diff --git a/ios/RCTMGL/RCTMGLLocation.m b/ios/RCTMGL/RCTMGLLocation.m index bb0ec9406f..bce7a20701 100644 --- a/ios/RCTMGL/RCTMGLLocation.m +++ b/ios/RCTMGL/RCTMGLLocation.m @@ -20,6 +20,7 @@ @implementation RCTMGLLocation coords[@"altitude"] = @(_location.altitude); coords[@"accuracy"] = @(_location.horizontalAccuracy); coords[@"heading"] = @(_heading.trueHeading); + coords[@"course"] = @(_location.course); coords[@"speed"] = @(_location.speed); json[@"coords"] = coords; diff --git a/ios/RCTMGL/RCTMGLLogging.m b/ios/RCTMGL/RCTMGLLogging.m index 0a06b00520..8516b3308b 100644 --- a/ios/RCTMGL/RCTMGLLogging.m +++ b/ios/RCTMGL/RCTMGLLogging.m @@ -2,6 +2,10 @@ @import Mapbox; +@interface RCTMGLLogging() +@property (nonatomic) BOOL hasListeners; +@end + @implementation RCTMGLLogging + (id)allocWithZone:(NSZone *)zone { @@ -37,8 +41,22 @@ + (BOOL)requiresMainQueueSetup return @[@"LogEvent"]; } +- (void)startObserving +{ + [super startObserving]; + self.hasListeners = true; +} + +- (void)stopObserving +{ + [super stopObserving]; + self.hasListeners = false; +} + - (void)sendLogWithLevel:(MGLLoggingLevel)loggingLevel filePath:(NSString*)filePath line:(NSUInteger)line message:(NSString*)message { + if (!self.hasListeners) return; + NSString* level = @"n/a"; switch (loggingLevel) { case MGLLoggingLevelInfo: diff --git a/ios/RCTMGL/RCTMGLMapView.m b/ios/RCTMGL/RCTMGLMapView.m index bbaab06d23..b439cc78b1 100644 --- a/ios/RCTMGL/RCTMGLMapView.m +++ b/ios/RCTMGL/RCTMGLMapView.m @@ -11,7 +11,7 @@ #import "RCTMGLUtils.h" #import "RNMBImageUtils.h" #import "RCTMGLImages.h" -#import "UIView+React.h" +#import #import "RCTMGLNativeUserLocation.h" #import "RCTMGLLogging.h" @@ -301,6 +301,31 @@ - (void)setReactLogoEnabled:(BOOL)reactLogoEnabled self.logoView.hidden = !_reactLogoEnabled; } +- (void)setReactLogoPosition:(NSDictionary *)logoPosition +{ + NSNumber *left = [logoPosition valueForKey:@"left"]; + NSNumber *right = [logoPosition valueForKey:@"right"]; + NSNumber *top = [logoPosition valueForKey:@"top"]; + NSNumber *bottom = [logoPosition valueForKey:@"bottom"]; + if (left != nil && top != nil) { + [self setLogoViewPosition:MGLOrnamentPositionTopLeft]; + [self setLogoViewMargins:CGPointMake([left floatValue], [top floatValue])]; + } else if (right != nil && top != nil) { + [self setLogoViewPosition:MGLOrnamentPositionTopRight]; + [self setLogoViewMargins:CGPointMake([right floatValue], [top floatValue])]; + } else if (bottom != nil && right != nil) { + [self setLogoViewPosition:MGLOrnamentPositionBottomRight]; + [self setLogoViewMargins:CGPointMake([right floatValue], [bottom floatValue])]; + } else if (bottom != nil && left != nil) { + [self setLogoViewPosition:MGLOrnamentPositionBottomLeft]; + [self setLogoViewMargins:CGPointMake([left floatValue], [bottom floatValue])]; + } else { + [self setLogoViewPosition:MGLOrnamentPositionBottomRight]; + [self setLogoViewMargins:CGPointMake(8, 8)]; + } + +} + - (void)setReactCompassEnabled:(BOOL)reactCompassEnabled { _reactCompassEnabled = reactCompassEnabled; @@ -485,7 +510,13 @@ - (RCTMGLSource *)getTouchableSourceWithHighestZIndex:(NSArray * - (NSURL*)_getStyleURLFromKey:(NSString *)styleURL { - return [NSURL URLWithString:styleURL]; + NSURL *url = [NSURL URLWithString:styleURL]; + if (url) { + return url; + } else if (RCTJSONParse(styleURL, nil)) { + return [RCTMGLUtils styleURLFromStyleJSON:styleURL]; + } + return url; } - (void)_removeAllSourcesFromMap diff --git a/ios/RCTMGL/RCTMGLMapViewManager.m b/ios/RCTMGL/RCTMGLMapViewManager.m index c456ff581f..d7d2985839 100644 --- a/ios/RCTMGL/RCTMGLMapViewManager.m +++ b/ios/RCTMGL/RCTMGLMapViewManager.m @@ -80,6 +80,7 @@ - (UIView *)view RCT_REMAP_VIEW_PROPERTY(attributionEnabled, reactAttributionEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(attributionPosition, reactAttributionPosition, NSDictionary) RCT_REMAP_VIEW_PROPERTY(logoEnabled, reactLogoEnabled, BOOL) +RCT_REMAP_VIEW_PROPERTY(logoPosition, reactLogoPosition, NSDictionary) RCT_REMAP_VIEW_PROPERTY(compassEnabled, reactCompassEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(zoomEnabled, reactZoomEnabled, BOOL) @@ -490,7 +491,8 @@ - (void)mapViewRegionIsChanging:(MGLMapView *)mapView - (void)mapView:(MGLMapView *)mapView regionDidChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated { - if ((reason & MGLCameraChangeReasonTransitionCancelled) == MGLCameraChangeReasonTransitionCancelled) return; + if (((reason & MGLCameraChangeReasonTransitionCancelled) == MGLCameraChangeReasonTransitionCancelled) + && ((reason & MGLCameraChangeReasonGesturePan) != MGLCameraChangeReasonGesturePan)) return; ((RCTMGLMapView *) mapView).isUserInteraction = (BOOL)(reason & ~MGLCameraChangeReasonProgrammatic); diff --git a/ios/RCTMGL/RCTMGLPointAnnotation.m b/ios/RCTMGL/RCTMGLPointAnnotation.m index e68501460f..57621c3b12 100644 --- a/ios/RCTMGL/RCTMGLPointAnnotation.m +++ b/ios/RCTMGL/RCTMGLPointAnnotation.m @@ -9,7 +9,7 @@ #import "RCTMGLPointAnnotation.h" #import "RCTMGLMapTouchEvent.h" #import "RCTMGLUtils.h" -#import "UIView+React.h" +#import const float CENTER_X_OFFSET_BASE = -0.5f; const float CENTER_Y_OFFSET_BASE = -0.5f; @@ -162,20 +162,19 @@ - (void)_setCenterOffset:(CGRect)frame float x = [_anchor[@"x"] floatValue]; float y = [_anchor[@"y"] floatValue]; - // (fullWidthOffset - centerWidthOffset) / 2 - float dx = -(x * frame.size.width - (frame.size.width / 2)) / 2; - float dy = -(y * frame.size.height - (frame.size.height / 2)) / 2; + float dx = -(x * frame.size.width - (frame.size.width / 2)); + float dy = -(y * frame.size.height - (frame.size.height / 2)); // special cases 0 and 1 if (x == 0) { dx = frame.size.width / 2; } else if (x == 1) { - dy = -frame.size.height / 2; + dx = -frame.size.width / 2; } if (y == 0) { - dy = frame.size.width / 2; + dy = frame.size.height / 2; } else if (y == 1) { dy = -frame.size.height / 2; } diff --git a/ios/RCTMGL/RCTMGLShapeSource.h b/ios/RCTMGL/RCTMGLShapeSource.h index 816bdecc94..ffa933e06d 100644 --- a/ios/RCTMGL/RCTMGLShapeSource.h +++ b/ios/RCTMGL/RCTMGLShapeSource.h @@ -25,8 +25,28 @@ @property (nonatomic, strong) NSNumber *maxZoomLevel; @property (nonatomic, strong) NSNumber *buffer; @property (nonatomic, strong) NSNumber *tolerance; +@property (nonatomic, strong) NSNumber *lineMetrics; @property (nonatomic, copy) RCTBubblingEventBlock onPress; @property (nonatomic, assign) BOOL hasPressListener; +- (nonnull NSArray> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate; +- (nonnull NSArray> *)getClusterLeaves:(nonnull NSString *)featureJSON + number:(NSUInteger)number + offset:(NSUInteger)offset; +- (nonnull NSArray> *)getClusterChildren:(nonnull NSString *)featureJSON; + +- (double)getClusterExpansionZoom:(nonnull NSString *)featureJSON; + +// Deprecated. Will be removed in 9+ ver. +- (nonnull NSArray> *)getClusterLeavesById:(nonnull NSNumber *)clusterId + number:(NSUInteger)number + offset:(NSUInteger)offset; + +// Deprecated. Will be removed in 9+ ver. +- (nonnull NSArray> *)getClusterChildrenById:(nonnull NSNumber *)clusterId; + +// Deprecated. Will be removed in 9+ ver. +- (double)getClusterExpansionZoomById:(nonnull NSNumber *)clusterId; + @end diff --git a/ios/RCTMGL/RCTMGLShapeSource.m b/ios/RCTMGL/RCTMGLShapeSource.m index fe55f6f49a..d6b4a98887 100644 --- a/ios/RCTMGL/RCTMGLShapeSource.m +++ b/ios/RCTMGL/RCTMGLShapeSource.m @@ -26,7 +26,7 @@ - (void)setUrl: (NSString*) url - (void)setShape:(NSString *)shape { _shape = shape; - + if (self.source != nil) { MGLShapeSource *source = (MGLShapeSource *)self.source; [source setShape: shape == nil ? nil : [RCTMGLUtils shapeFromGeoJSON:_shape]]; @@ -46,19 +46,19 @@ - (void)removeFromMap if (self.map.style == nil) { return; } - + [super removeFromMap]; } - (nullable MGLSource*)makeSource { NSDictionary *options = [self _getOptions]; - + if (_shape != nil) { MGLShape *shape = [RCTMGLUtils shapeFromGeoJSON:_shape]; return [[MGLShapeSource alloc] initWithIdentifier:self.id shape:shape options:options]; } - + if (_url != nil) { NSURL *url = [[NSURL alloc] initWithString:_url]; return [[MGLShapeSource alloc] initWithIdentifier:self.id URL:url options:options]; @@ -69,32 +69,112 @@ - (nullable MGLSource*)makeSource - (NSDictionary*)_getOptions { NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; - + if (_cluster != nil) { options[MGLShapeSourceOptionClustered] = [NSNumber numberWithBool:[_cluster intValue] == 1]; } - + if (_clusterRadius != nil) { options[MGLShapeSourceOptionClusterRadius] = _clusterRadius; } - + if (_clusterMaxZoomLevel != nil) { options[MGLShapeSourceOptionMaximumZoomLevelForClustering] = _clusterMaxZoomLevel; } - + if (_maxZoomLevel != nil) { options[MGLShapeSourceOptionMaximumZoomLevel] = _maxZoomLevel; } - + if (_buffer != nil) { options[MGLShapeSourceOptionBuffer] = _buffer; } - + if (_tolerance != nil) { options[MGLShapeSourceOptionSimplificationTolerance] = _tolerance; } - + + if (_lineMetrics != nil) { + options[MGLShapeSourceOptionLineDistanceMetrics] = _lineMetrics; + } + return options; } +- (nonnull NSArray> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + + return [shapeSource featuresMatchingPredicate:predicate]; +} + +- (double)getClusterExpansionZoom:(nonnull NSString *)featureJSON +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + + MGLPointFeature *feature = (MGLPointFeature*)[RCTMGLUtils shapeFromGeoJSON:featureJSON]; + + return [shapeSource zoomLevelForExpandingCluster:(MGLPointFeatureCluster *)feature]; +} + +- (nonnull NSArray> *)getClusterLeaves:(nonnull NSString *)featureJSON + number:(NSUInteger)number + offset:(NSUInteger)offset +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + + MGLPointFeature *feature = (MGLPointFeature*)[RCTMGLUtils shapeFromGeoJSON:featureJSON]; + + MGLPointFeatureCluster * cluster = (MGLPointFeatureCluster *)feature; + return [shapeSource leavesOfCluster:cluster offset:offset limit:number]; +} + +- (nonnull NSArray> *)getClusterChildren:(nonnull NSString *)featureJSON +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + + MGLPointFeature *feature = (MGLPointFeature*)[RCTMGLUtils shapeFromGeoJSON:featureJSON]; + + MGLPointFeatureCluster * cluster = (MGLPointFeatureCluster *)feature; + return [shapeSource childrenOfCluster:cluster]; +} + + +// Deprecated. Will be removed in 9+ ver. +- (double)getClusterExpansionZoomById:(nonnull NSNumber *)clusterId +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + NSArray> *features = [shapeSource featuresMatchingPredicate: [NSPredicate predicateWithFormat:@"%K = %i", @"cluster_id", clusterId.intValue]]; + if (features.count == 0) { + return -1; + } + return [shapeSource zoomLevelForExpandingCluster:(MGLPointFeatureCluster *)features[0]]; +} + +// Deprecated. Will be removed in 9+ ver. +- (nonnull NSArray> *)getClusterLeavesById:(nonnull NSNumber *)clusterId + number:(NSUInteger)number + offset:(NSUInteger)offset +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + + NSPredicate* predicate = [NSPredicate predicateWithFormat:@"cluster_id == %@", clusterId]; + NSArray> *features = [shapeSource featuresMatchingPredicate:predicate]; + + MGLPointFeatureCluster * cluster = (MGLPointFeatureCluster *)features[0]; + return [shapeSource leavesOfCluster:cluster offset:offset limit:number]; +} + +// Deprecated. Will be removed in 9+ ver. +- (nonnull NSArray> *)getClusterChildrenById:(nonnull NSNumber *)clusterId +{ + MGLShapeSource *shapeSource = (MGLShapeSource *)self.source; + + NSPredicate* predicate = [NSPredicate predicateWithFormat:@"cluster_id == %@", clusterId]; + NSArray> *features = [shapeSource featuresMatchingPredicate:predicate]; + + MGLPointFeatureCluster * cluster = (MGLPointFeatureCluster *)features[0]; + return [shapeSource childrenOfCluster:cluster]; +} + @end diff --git a/ios/RCTMGL/RCTMGLShapeSourceManager.h b/ios/RCTMGL/RCTMGLShapeSourceManager.h index 571393a442..4fde63538f 100644 --- a/ios/RCTMGL/RCTMGLShapeSourceManager.h +++ b/ios/RCTMGL/RCTMGLShapeSourceManager.h @@ -7,7 +7,8 @@ // #import "ViewManager.h" +#import -@interface RCTMGLShapeSourceManager : ViewManager +@interface RCTMGLShapeSourceManager : ViewManager @end diff --git a/ios/RCTMGL/RCTMGLShapeSourceManager.m b/ios/RCTMGL/RCTMGLShapeSourceManager.m index 55b40cee28..a99fa8204d 100644 --- a/ios/RCTMGL/RCTMGLShapeSourceManager.m +++ b/ios/RCTMGL/RCTMGLShapeSourceManager.m @@ -5,13 +5,16 @@ // Created by Nick Italiano on 9/19/17. // Copyright Β© 2017 Mapbox Inc. All rights reserved. // +#import #import "RCTMGLShapeSourceManager.h" #import "RCTMGLShapeSource.h" +#import "FilterParser.h" + @implementation RCTMGLShapeSourceManager -RCT_EXPORT_MODULE() +RCT_EXPORT_MODULE(RCTMGLShapeSource) RCT_EXPORT_VIEW_PROPERTY(id, NSString) RCT_EXPORT_VIEW_PROPERTY(url, NSString) @@ -23,6 +26,7 @@ @implementation RCTMGLShapeSourceManager RCT_EXPORT_VIEW_PROPERTY(maxZoomLevel, NSNumber) RCT_EXPORT_VIEW_PROPERTY(buffer, NSNumber) RCT_EXPORT_VIEW_PROPERTY(tolerance, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(lineMetrics, NSNumber) RCT_EXPORT_VIEW_PROPERTY(images, NSDictionary) RCT_EXPORT_VIEW_PROPERTY(nativeImages, NSArray) RCT_EXPORT_VIEW_PROPERTY(hasPressListener, BOOL) @@ -36,4 +40,166 @@ - (UIView*)view return source; } +RCT_EXPORT_METHOD(features:(nonnull NSNumber*)reactTag + withFilter:(NSArray *)filter + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = viewRegistry[reactTag]; + + if (![shapeSource isKindOfClass:[RCTMGLShapeSource class]]) { + RCTLogError(@"Invalid react tag, could not find RCTMGLMapView"); + return; + } + + NSPredicate* predicate = [FilterParser parse:filter]; + NSArray> *shapes = [shapeSource featuresMatchingPredicate: predicate]; + + NSMutableArray *features = [[NSMutableArray alloc] initWithCapacity:shapes.count]; + for (int i = 0; i < shapes.count; i++) { + [features addObject:shapes[i].geoJSONDictionary]; + } + + resolve(@{ + @"data": @{ @"type": @"FeatureCollection", @"features": features } + }); + }]; +} + +RCT_EXPORT_METHOD(getClusterExpansionZoom:(nonnull NSNumber*)reactTag + featureJSON:(nonnull NSString*)featureJSON + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = (RCTMGLShapeSource *)viewRegistry[reactTag]; + + if (![shapeSource isKindOfClass:[RCTMGLShapeSource class]]) { + RCTLogError(@"Invalid react tag, could not find RCTMGLMapView"); + return; + } + + double zoom = [shapeSource getClusterExpansionZoom: featureJSON]; + if (zoom == -1) { + reject(@"zoom_error", [NSString stringWithFormat:@"Could not get zoom for cluster %@", featureJSON], nil); + return; + } + resolve(@{@"data":@(zoom)}); + }]; +} + + +RCT_EXPORT_METHOD(getClusterLeaves:(nonnull NSNumber*)reactTag + featureJSON:(nonnull NSString*)featureJSON + number:(NSUInteger) number + offset:(NSUInteger) offset + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = (RCTMGLShapeSource *)viewRegistry[reactTag]; + + NSArray> *shapes = [shapeSource getClusterLeaves:featureJSON number:number offset:offset]; + + NSMutableArray *features = [[NSMutableArray alloc] initWithCapacity:shapes.count]; + for (int i = 0; i < shapes.count; i++) { + [features addObject:shapes[i].geoJSONDictionary]; + } + + resolve(@{ + @"data": @{ @"type": @"FeatureCollection", @"features": features } + }); + }]; +} + +RCT_EXPORT_METHOD(getClusterChildren:(nonnull NSNumber*)reactTag + featureJSON:(nonnull NSString*)featureJSON + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = (RCTMGLShapeSource *)viewRegistry[reactTag]; + + NSArray> *shapes = [shapeSource getClusterChildren: featureJSON]; + + NSMutableArray *features = [[NSMutableArray alloc] initWithCapacity:shapes.count]; + for (int i = 0; i < shapes.count; i++) { + [features addObject:shapes[i].geoJSONDictionary]; + } + + resolve(@{ + @"data": @{ @"type": @"FeatureCollection", @"features": features } + }); + }]; +} + +// Deprecated. Will be removed in 9+ ver. +RCT_EXPORT_METHOD(getClusterExpansionZoomById:(nonnull NSNumber*)reactTag + clusterId:(nonnull NSNumber*)clusterId + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = (RCTMGLShapeSource *)viewRegistry[reactTag]; + + if (![shapeSource isKindOfClass:[RCTMGLShapeSource class]]) { + RCTLogError(@"Invalid react tag, could not find RCTMGLMapView"); + return; + } + + double zoom = [shapeSource getClusterExpansionZoomById:clusterId]; + if (zoom == -1) { + reject(@"zoom_error", [NSString stringWithFormat:@"Could not get zoom for cluster id %@", clusterId], nil); + return; + } + resolve(@{@"data":@(zoom)}); + }]; +} + +// Deprecated. Will be removed in 9+ ver. +RCT_EXPORT_METHOD(getClusterLeavesById:(nonnull NSNumber*)reactTag + clusterId:(nonnull NSNumber *)clusterId + number:(NSUInteger) number + offset:(NSUInteger) offset + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = (RCTMGLShapeSource *)viewRegistry[reactTag]; + + NSArray> *shapes = [shapeSource getClusterLeavesById:clusterId number:number offset:offset]; + + NSMutableArray *features = [[NSMutableArray alloc] initWithCapacity:shapes.count]; + for (int i = 0; i < shapes.count; i++) { + [features addObject:shapes[i].geoJSONDictionary]; + } + + resolve(@{ + @"data": @{ @"type": @"FeatureCollection", @"features": features } + }); + }]; +} +// Deprecated. Will be removed in 9+ ver. +RCT_EXPORT_METHOD(getClusterChildrenById:(nonnull NSNumber*)reactTag + clusterId:(nonnull NSNumber *)clusterId + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { + RCTMGLShapeSource* shapeSource = (RCTMGLShapeSource *)viewRegistry[reactTag]; + + NSArray> *shapes = [shapeSource getClusterChildrenById: clusterId]; + + NSMutableArray *features = [[NSMutableArray alloc] initWithCapacity:shapes.count]; + for (int i = 0; i < shapes.count; i++) { + [features addObject:shapes[i].geoJSONDictionary]; + } + + resolve(@{ + @"data": @{ @"type": @"FeatureCollection", @"features": features } + }); + }]; +} + @end diff --git a/ios/RCTMGL/RCTMGLSource.m b/ios/RCTMGL/RCTMGLSource.m index 9886602905..fa2e7ee961 100644 --- a/ios/RCTMGL/RCTMGLSource.m +++ b/ios/RCTMGL/RCTMGLSource.m @@ -7,7 +7,7 @@ // #import "RCTMGLSource.h" -#import "UIView+React.h" +#import #import "RCTMGLMapView.h" #import diff --git a/ios/RCTMGL/RCTMGLSymbolLayer.m b/ios/RCTMGL/RCTMGLSymbolLayer.m index 5c7df30961..475cb45c45 100644 --- a/ios/RCTMGL/RCTMGLSymbolLayer.m +++ b/ios/RCTMGL/RCTMGLSymbolLayer.m @@ -8,7 +8,7 @@ #import "RCTMGLSymbolLayer.h" #import "RCTMGLStyle.h" -#import "UIView+React.h" +#import #import @implementation RCTMGLSymbolLayer diff --git a/ios/RCTMGL/RCTMGLUtils.h b/ios/RCTMGL/RCTMGLUtils.h index b2d843fe21..d32381eabf 100644 --- a/ios/RCTMGL/RCTMGLUtils.h +++ b/ios/RCTMGL/RCTMGLUtils.h @@ -26,5 +26,6 @@ + (void)fetchImages:(RCTBridge *)bridge style:(MGLStyle *)style objects:(NSDictionary*)objects forceUpdate:(BOOL)forceUpdate callback:(void (^)(void))callback; + (CGVector)toCGVector:(NSArray*)arr; + (UIEdgeInsets)toUIEdgeInsets:(NSArray *)arr; ++ (NSURL*)styleURLFromStyleJSON:(NSString *)styleJSON; @end diff --git a/ios/RCTMGL/RCTMGLUtils.m b/ios/RCTMGL/RCTMGLUtils.m index ad74c23aa7..1dca4bed43 100644 --- a/ios/RCTMGL/RCTMGLUtils.m +++ b/ios/RCTMGL/RCTMGLUtils.m @@ -141,4 +141,78 @@ + (void)fetchImages:(RCTBridge *)bridge style:(MGLStyle *)style objects:(NSDicti } } ++ (NSString*)getStyleJsonTempDirectory +{ + static NSString *styleJsonTempDirectory; + if (!styleJsonTempDirectory) { + styleJsonTempDirectory = [NSTemporaryDirectory() stringByAppendingPathComponent:@"RCTMGLStyleJSON"]; + } + return styleJsonTempDirectory; +} + +/** + * Clears cached style-json entries from previous app runs. Can be safely called multiple times as it will + * only perform the action once per app run. + * + * @see styleURLFromStyleJSON: + */ ++ (void)cleanCustomStyleJSONCacheIfNeeded +{ + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *styleJsonTempDirectory = [RCTMGLUtils getStyleJsonTempDirectory]; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if ([fileManager fileExistsAtPath:styleJsonTempDirectory]) { + [fileManager removeItemAtPath:styleJsonTempDirectory error:NULL]; + } + }); +} + +/** + * Provides a way to convert raw style-json into a file so it can be directly referenced / used as styleURL. + * It's a crude / alternative approach to support Android's API: Style.Builder.fromJson(). + */ ++ (NSURL*)styleURLFromStyleJSON:(NSString *)styleJSON +{ + [RCTMGLUtils cleanCustomStyleJSONCacheIfNeeded]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *styleJsonTempDirectory = [RCTMGLUtils getStyleJsonTempDirectory]; + + // attempt to create the temporary directory + if (![fileManager fileExistsAtPath:styleJsonTempDirectory]) { + NSError *error; + [fileManager createDirectoryAtPath:styleJsonTempDirectory + withIntermediateDirectories:YES + attributes:nil + error:&error]; + if (error) { + RCTLogError(@"Failed to create temporary directory '%@' for storing style-json. Error: %@", styleJsonTempDirectory, error); + return nil; + } + } + + // Determine filename based on the md5 hash of the style-json. + // This way, the written file can also act as a cached entry in case + // the same style-json is used again. + NSString *hashedFilename = [RCTMD5Hash(styleJSON) stringByAppendingPathExtension:@"json"]; + + // Construct temporary file path (tempdir + md5 hash for filename) + NSString *styleJsonTempPath = [styleJsonTempDirectory stringByAppendingPathComponent:hashedFilename]; + NSURL* styleJsonTempURL = [NSURL fileURLWithPath:styleJsonTempPath isDirectory:false]; + + // Write style-json to temporary file in case it doesn't already exist + if (![fileManager fileExistsAtPath:styleJsonTempPath isDirectory:false]) { + NSError *error; + [styleJSON writeToURL:styleJsonTempURL atomically:YES encoding:NSUTF8StringEncoding error:&error]; + if (error) { + RCTLogError(@"Failed to write style-json to temporary file '%@'. Error: %@", styleJsonTempURL, error); + return nil; + } + } + + return styleJsonTempURL; +} + @end diff --git a/ios/install.md b/ios/install.md index 2910a012f7..7eed9cfbb3 100644 --- a/ios/install.md +++ b/ios/install.md @@ -2,33 +2,108 @@ ## React-Native > `0.60.0` -If you are using autolinking feature introduced in React-Native `0.60.0`, you just need `npm install @react-native-mapbox-gl/maps`, followed by `pod install` from the `ios` directory. +The following assumes, that you're using autolinking and installed -## Using CocoaPods +`@react-native-mapbox-gl/maps` via `npm` or `yarn`. -To install with CocoaPods, add the following to your `Podfile`: +
+ +The following is required for every following setup + +Add the following to your `ios/Podfile`: ```ruby - # Mapbox - pod 'react-native-mapbox-gl', :path => '../node_modules/@react-native-mapbox-gl/maps' + pre_install do |installer| + $RNMBGL.pre_install(installer) + ... other pre install hooks + end +``` +```ruby + post_install do |installer| + $RNMBGL.post_install(installer) + ... other post install hooks + end ``` -Then run `pod install` and rebuild your project. +Running `pod install` will add Mapbox iOS SDK `5.8.0` + +```sh +# Go to the ios folder +cd ios + +# Run Pod Install +pod install +``` -## use_frameworks! +You are good to go! -Mapbox normally [requires](https://github.com/mapbox/mapbox-gl-native-ios/issues/154) `use_frameworks!` in cocoapods. By default we implement a [workaround](https://github.com/react-native-mapbox-gl/maps/pull/714). In case you need `use_frameworks!` for some reason, you can use the mapbox pod without the workaround with the `DynamicLibrary` subspec: +Read on if you want to edit your Mapbox version or flavor. +
+ +## Mapbox Maps SDK + +It is possible to set a custom version of the Mapbox SDK: + +### New version - since `8.1rc5` + +Add the following to you `ios/Podfile`: ```ruby - # Mapbox - pod 'react-native-mapbox-gl/DynamicLibrary', :path => '../node_modules/@react-native-mapbox-gl/maps' +$ReactNativeMapboxGLIOSVersion = '~> 6.1' +``` + +Check the current version of the SDK [here](https://docs.mapbox.com/ios/maps/overview/). + +### Mapbox Maps SDK > `v6.0.0` + +If you are using version `v6.0.0` of the SDK or later, you will need to authorize your download of the Maps SDK with a secret access token with the `DOWNLOADS:READ` scope. This [guide](https://docs.mapbox.com/ios/maps/guides/install/#configure-credentials) explains how to configure the secret token under section `Configure your secret token`. + +
- ... +## Maplibre - use_frameworks! +[MapLibre](https://github.com/maplibre/maplibre-gl-native) is an OSS fork of MapboxGL +Current default MapLibre version is `5.12.0` + +If you want to use that, simply add this to your `ios/Podfile` + +```ruby +$RNMBGL_Use_SPM = true +$RNMGL_USE_MAPLIBRE = true +``` + +If you want to adjust/ edit your MapLibre version you can also pass a hash + +Example overwrite within your `ios/Podfile`: + +```ruby +$RNMBGL_Use_SPM = { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { + kind: "upToNextMajorVersion", + minimumVersion: "5.12.0" + }, + product_name: "Mapbox" +} +$RNMGL_USE_MAPLIBRE = true ``` +
+ +## React-Native < `0.60.0` + +### Using CocoaPods without autolink + +To install with CocoaPods, add the following to your `Podfile`: + +```ruby + # Mapbox + pod 'react-native-mapbox-gl', :path => '../node_modules/@react-native-mapbox-gl/maps' + +``` + +Then run `pod install` and rebuild your project. diff --git a/javascript/components/BackgroundLayer.js b/javascript/components/BackgroundLayer.js index 85ad495390..e1cf1cf9aa 100644 --- a/javascript/components/BackgroundLayer.js +++ b/javascript/components/BackgroundLayer.js @@ -21,7 +21,9 @@ class BackgroundLayer extends AbstractLayer { id: PropTypes.string.isRequired, /** - * The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. + * The source from which to obtain the data to style. + * If the source has not yet been added to the current style, the behavior is undefined. + * Inferred from parent source only if the layer is a direct child to it. */ sourceID: PropTypes.string, diff --git a/javascript/components/Camera.js b/javascript/components/Camera.js index 91b69d29b7..4dd55ea039 100644 --- a/javascript/components/Camera.js +++ b/javascript/components/Camera.js @@ -15,6 +15,31 @@ const SettingsPropTypes = { */ centerCoordinate: PropTypes.arrayOf(PropTypes.number), + /** + * Padding around edges of map in points + */ + padding: PropTypes.shape({ + /** + * Left padding in points + */ + paddingLeft: PropTypes.number, + + /** + * Right padding in points + */ + paddingRight: PropTypes.number, + + /** + * Top padding in points + */ + paddingTop: PropTypes.number, + + /** + * Bottom padding in points + */ + paddingBottom: PropTypes.number, + }), + /** * Heading on map */ @@ -27,6 +52,7 @@ const SettingsPropTypes = { /** * Represents a rectangle in geographical coordinates marking the visible area of the map. + * The `bounds.padding*` properties are deprecated; use root `padding` property instead. */ bounds: PropTypes.shape({ /** @@ -40,31 +66,31 @@ const SettingsPropTypes = { sw: PropTypes.arrayOf(PropTypes.number).isRequired, /** - * Left camera padding for bounds + * Left padding in points (deprecated; use root `padding` property instead) */ paddingLeft: PropTypes.number, /** - * Right camera padding for bounds + * Right padding in points (deprecated; use root `padding` property instead) */ paddingRight: PropTypes.number, /** - * Top camera padding for bounds + * Top padding in points (deprecated; use root `padding` property instead) */ paddingTop: PropTypes.number, /** - * Bottom camera padding for bounds + * Bottom padding in points (deprecated; use root `padding` property instead) */ paddingBottom: PropTypes.number, - - /** - * Callback that is triggered on user tracking mode changes - */ - onUserTrackingModeChange: PropTypes.func, }), + /** + * Callback that is triggered on user tracking mode changes + */ + onUserTrackingModeChange: PropTypes.func, + /** * Zoom level of the map */ @@ -75,15 +101,20 @@ class Camera extends React.Component { static propTypes = { ...viewPropTypes, + /** + * If false, the camera will not send any props to the native module. Intended to be used to prevent unnecessary tile fetching and improve performance when the map is not visible. Defaults to true. + */ + allowUpdates: PropTypes.bool, + /** * The duration a camera update takes (in ms) */ animationDuration: PropTypes.number, /** - * The animationstyle when the camara updates. One of; `flyTo`, `easeTo`, `moveTo` + * The animationstyle when the camara updates. One of: `flyTo`, `easeTo`, `linearTo`, `moveTo` */ - animationMode: PropTypes.oneOf(['flyTo', 'easeTo', 'moveTo']), + animationMode: PropTypes.oneOf(['flyTo', 'easeTo', 'linearTo', 'moveTo']), /** * Default view settings applied on camera @@ -153,6 +184,7 @@ class Camera extends React.Component { }; static defaultProps = { + allowUpdates: true, animationMode: 'easeTo', animationDuration: 2000, }; @@ -161,6 +193,7 @@ class Camera extends React.Component { Flight: 'flyTo', Move: 'moveTo', Ease: 'easeTo', + Linear: 'linearTo', }; UNSAFE_componentWillReceiveProps(nextProps) { @@ -172,47 +205,90 @@ class Camera extends React.Component { } _handleCameraChange(currentCamera, nextCamera) { - const hasCameraChanged = this._hasCameraChanged(currentCamera, nextCamera); + const c = currentCamera; + const n = nextCamera; + + if (!n.allowUpdates) { + return; + } + + const hasCameraChanged = this._hasCameraChanged(c, n); if (!hasCameraChanged) { return; } - if (currentCamera.followUserLocation && !nextCamera.followUserLocation) { + if (c.followUserLocation && !n.followUserLocation) { this.refs.camera.setNativeProps({followUserLocation: false}); return; } - if (!currentCamera.followUserLocation && nextCamera.followUserLocation) { + if (!c.followUserLocation && n.followUserLocation) { this.refs.camera.setNativeProps({followUserLocation: true}); } - if (nextCamera.followUserLocation) { + if (n.followUserLocation) { this.refs.camera.setNativeProps({ - followUserMode: nextCamera.followUserMode, - followPitch: nextCamera.followPitch || nextCamera.pitch, - followHeading: nextCamera.followHeading || nextCamera.heading, - followZoomLevel: nextCamera.followZoomLevel || nextCamera.zoomLevel, + followUserMode: n.followUserMode, + followPitch: n.followPitch || n.pitch, + followHeading: n.followHeading || n.heading, + followZoomLevel: n.followZoomLevel || n.zoomLevel, }); return; } + if (n.maxBounds) { + this.refs.camera.setNativeProps({ + maxBounds: this._getMaxBounds(), + }); + } + if (n.minZoomLevel) { + this.refs.camera.setNativeProps({ + minZoomLevel: this.props.minZoomLevel, + }); + } + if (n.maxZoomLevel) { + this.refs.camera.setNativeProps({ + maxZoomLevel: this.props.maxZoomLevel, + }); + } + const cameraConfig = { - animationMode: nextCamera.animationMode, - animationDuration: nextCamera.animationDuration, - zoomLevel: nextCamera.zoomLevel, - pitch: nextCamera.pitch, - heading: nextCamera.heading, + bounds: undefined, + centerCoordinate: undefined, + padding: n.padding, + zoomLevel: n.zoomLevel, + pitch: n.pitch, + heading: n.heading, + animationMode: n.animationMode, + animationDuration: n.animationDuration, }; - if ( - nextCamera.bounds && - this._hasBoundsChanged(currentCamera, nextCamera) - ) { - cameraConfig.bounds = nextCamera.bounds; - } else { - cameraConfig.centerCoordinate = nextCamera.centerCoordinate; + const boundsChanged = this._hasBoundsChanged(c.bounds, n.bounds); + const centerCoordinateChanged = this._hasCenterCoordinateChanged( + c.centerCoordinate, + n.centerCoordinate, + ); + const paddingChanged = this._hasPaddingChanged(c.padding, n.padding); + const zoomChanged = this._hasNumberChanged(c.zoomLevel, n.zoomLevel); + const pitchChanged = this._hasNumberChanged(c.pitch, n.pitch); + const headingChanged = this._hasNumberChanged(c.heading, n.heading); + + let shouldUpdate = false; + + if (n.bounds && boundsChanged) { + cameraConfig.bounds = n.bounds; + shouldUpdate = true; + } else if (n.centerCoordinate && centerCoordinateChanged) { + cameraConfig.centerCoordinate = n.centerCoordinate; + shouldUpdate = true; } - this._setCamera(cameraConfig); + if (paddingChanged || zoomChanged || pitchChanged || headingChanged) { + shouldUpdate = true; + } + + if (shouldUpdate) { + this._setCamera(cameraConfig); + } } _hasCameraChanged(currentCamera, nextCamera) { @@ -221,8 +297,12 @@ class Camera extends React.Component { const hasDefaultPropsChanged = c.heading !== n.heading || - this._hasCenterCoordinateChanged(c, n) || - this._hasBoundsChanged(c, n) || + this._hasCenterCoordinateChanged( + c.centerCoordinate, + n.centerCoordinate, + ) || + this._hasBoundsChanged(c.bounds, n.bounds) || + this._hasPaddingChanged(c.padding, n.padding) || c.pitch !== n.pitch || c.zoomLevel !== n.zoomLevel || c.triggerKey !== n.triggerKey; @@ -238,36 +318,34 @@ class Camera extends React.Component { c.animationMode !== n.animationMode || c.animationDuration !== n.animationDuration; + const hasNavigationConstraintsPropsChanged = + this._hasBoundsChanged(c.maxBounds, n.maxBounds) || + c.minZoomLevel !== n.minZoomLevel || + c.maxZoomLevel !== n.maxZoomLevel; + return ( hasDefaultPropsChanged || hasFollowPropsChanged || - hasAnimationPropsChanged + hasAnimationPropsChanged || + hasNavigationConstraintsPropsChanged ); } - _hasCenterCoordinateChanged(currentCamera, nextCamera) { - const cC = currentCamera.centerCoordinate; - const nC = nextCamera.centerCoordinate; + _hasCenterCoordinateChanged(cC, nC) { + if (!cC && !nC) { + return false; + } if (existenceChange(cC, nC)) { return true; } - if (!cC && !nC) { - return false; - } - - const isLngDiff = - currentCamera.centerCoordinate[0] !== nextCamera.centerCoordinate[0]; - const isLatDiff = - currentCamera.centerCoordinate[1] !== nextCamera.centerCoordinate[1]; + const isLngDiff = cC[0] !== nC[0]; + const isLatDiff = cC[1] !== nC[1]; return isLngDiff || isLatDiff; } - _hasBoundsChanged(currentCamera, nextCamera) { - const cB = currentCamera.bounds; - const nB = nextCamera.bounds; - + _hasBoundsChanged(cB, nB) { if (!cB && !nB) { return false; } @@ -288,6 +366,35 @@ class Camera extends React.Component { ); } + _hasPaddingChanged(cP, nP) { + if (!cP && !nP) { + return false; + } + + if (existenceChange(cP, nP)) { + return true; + } + + return ( + cP.paddingTop !== nP.paddingTop || + cP.paddingLeft !== nP.paddingLeft || + cP.paddingRight !== nP.paddingRight || + cP.paddingBottom !== nP.paddingBottom + ); + } + + _hasNumberChanged(prev, next) { + if (existenceChange(prev, next)) { + return true; + } + + if (!prev && !next) { + return false; + } + + return prev !== next; + } + /** * Map camera transitions to fit provided bounds * @@ -339,8 +446,8 @@ class Camera extends React.Component { bounds: { ne: northEastCoordinates, sw: southWestCoordinates, - ...pad, }, + padding: pad, animationDuration, animationMode: Camera.Mode.Ease, }); @@ -377,7 +484,7 @@ class Camera extends React.Component { * @return {void} */ moveTo(coordinates, animationDuration = 0) { - return this._setCamera({ + return this.setCamera({ centerCoordinate: coordinates, animationDuration, }); @@ -395,7 +502,7 @@ class Camera extends React.Component { * @return {void} */ zoomTo(zoomLevel, animationDuration = 2000) { - return this._setCamera({ + return this.setCamera({ zoomLevel, animationDuration, animationMode: Camera.Mode.Flight, @@ -479,21 +586,19 @@ class Camera extends React.Component { } if (config.bounds && config.bounds.ne && config.bounds.sw) { - const { - ne, - sw, - paddingLeft, - paddingRight, - paddingTop, - paddingBottom, - } = config.bounds; + const {ne, sw} = config.bounds; stopConfig.bounds = toJSONString(geoUtils.makeLatLngBounds(ne, sw)); - stopConfig.boundsPaddingTop = paddingTop || 0; - stopConfig.boundsPaddingRight = paddingRight || 0; - stopConfig.boundsPaddingBottom = paddingBottom || 0; - stopConfig.boundsPaddingLeft = paddingLeft || 0; } + stopConfig.paddingTop = + config.padding?.paddingTop || config.bounds?.paddingTop || 0; + stopConfig.paddingRight = + config.padding?.paddingRight || config.bounds?.paddingRight || 0; + stopConfig.paddingBottom = + config.padding?.paddingBottom || config.bounds?.paddingBottom || 0; + stopConfig.paddingLeft = + config.padding?.paddingLeft || config.bounds?.paddingLeft || 0; + return stopConfig; } @@ -503,6 +608,8 @@ class Camera extends React.Component { return MapboxGL.CameraModes.Flight; case Camera.Mode.Move: return MapboxGL.CameraModes.None; + case Camera.Mode.Linear: + return MapboxGL.CameraModes.Linear; default: return MapboxGL.CameraModes.Ease; } diff --git a/javascript/components/CircleLayer.js b/javascript/components/CircleLayer.js index 70a8072372..1b414f32ed 100644 --- a/javascript/components/CircleLayer.js +++ b/javascript/components/CircleLayer.js @@ -26,6 +26,7 @@ class CircleLayer extends AbstractLayer { /** * The source from which to obtain the data to style. * If the source has not yet been added to the current style, the behavior is undefined. + * Inferred from parent source only if the layer is a direct child to it. */ sourceID: PropTypes.string, diff --git a/javascript/components/FillExtrusionLayer.js b/javascript/components/FillExtrusionLayer.js index e1532db71f..8f581f50e9 100644 --- a/javascript/components/FillExtrusionLayer.js +++ b/javascript/components/FillExtrusionLayer.js @@ -24,7 +24,9 @@ class FillExtrusionLayer extends AbstractLayer { id: PropTypes.string.isRequired, /** - * The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. + * The source from which to obtain the data to style. + * If the source has not yet been added to the current style, the behavior is undefined. + * Inferred from parent source only if the layer is a direct child to it. */ sourceID: PropTypes.string, diff --git a/javascript/components/FillLayer.js b/javascript/components/FillLayer.js index dfc4252325..8750b5e2ab 100644 --- a/javascript/components/FillLayer.js +++ b/javascript/components/FillLayer.js @@ -24,7 +24,9 @@ class FillLayer extends AbstractLayer { id: PropTypes.string.isRequired, /** - * The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. + * The source from which to obtain the data to style. + * If the source has not yet been added to the current style, the behavior is undefined. + * Inferred from parent source only if the layer is a direct child to it. */ sourceID: PropTypes.string, diff --git a/javascript/components/HeadingIndicator.js b/javascript/components/HeadingIndicator.js index 9de1d17522..91e6100f0e 100644 --- a/javascript/components/HeadingIndicator.js +++ b/javascript/components/HeadingIndicator.js @@ -9,9 +9,10 @@ const style = { iconImage: headingIcon, iconAllowOverlap: true, iconPitchAlignment: 'map', + iconRotationAlignment: 'map', }; -const HeadingIndicator = (heading) => ( +const HeadingIndicator = heading => ( this._setNativeRef(nativeRef), + ref: nativeRef => this._setNativeRef(nativeRef), onPress: this._onPress, onLongPress: this._onLongPress, onMapChange: this._onChange, @@ -767,8 +794,7 @@ class MapView extends NativeBridgeComponent(React.Component) { + testID={mapView ? null : this.props.testID}> {mapView} ); diff --git a/javascript/components/NativeBridgeComponent.js b/javascript/components/NativeBridgeComponent.js index 9e4c4824f3..e3327daa16 100644 --- a/javascript/components/NativeBridgeComponent.js +++ b/javascript/components/NativeBridgeComponent.js @@ -2,7 +2,7 @@ import {runNativeCommand, isAndroid} from '../utils'; let callbackIncrement = 0; -const NativeBridgeComponent = (B) => +const NativeBridgeComponent = B => class extends B { constructor(props, nativeModuleName) { super(props); @@ -57,7 +57,7 @@ const NativeBridgeComponent = (B) => _runNativeCommand(methodName, nativeRef, args = []) { if (!nativeRef) { - return new Promise((resolve) => { + return new Promise(resolve => { this._preRefMapMethodQueue.push({ method: {name: methodName, args}, resolver: resolve, diff --git a/javascript/components/PointAnnotation.js b/javascript/components/PointAnnotation.js index fd2ef698ad..a89ef7d243 100644 --- a/javascript/components/PointAnnotation.js +++ b/javascript/components/PointAnnotation.js @@ -176,7 +176,7 @@ class PointAnnotation extends NativeBridgeComponent(React.PureComponent) { render() { const props = { ...this.props, - ref: (nativeRef) => this._setNativeRef(nativeRef), + ref: nativeRef => this._setNativeRef(nativeRef), id: this.props.id, title: this.props.title, snippet: this.props.snippet, diff --git a/javascript/components/RasterLayer.js b/javascript/components/RasterLayer.js index 2d00402004..471908e575 100644 --- a/javascript/components/RasterLayer.js +++ b/javascript/components/RasterLayer.js @@ -21,7 +21,9 @@ class RasterLayer extends AbstractLayer { id: PropTypes.string.isRequired, /** - * The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. + * The source from which to obtain the data to style. + * If the source has not yet been added to the current style, the behavior is undefined. + * Inferred from parent source only if the layer is a direct child to it. */ sourceID: PropTypes.string, diff --git a/javascript/components/RasterSource.js b/javascript/components/RasterSource.js index bc4e741b14..9186456385 100644 --- a/javascript/components/RasterSource.js +++ b/javascript/components/RasterSource.js @@ -10,7 +10,7 @@ const MapboxGL = NativeModules.MGLModule; export const NATIVE_MODULE_NAME = 'RCTMGLRasterSource'; -const isTileTemplateUrl = (url) => +const isTileTemplateUrl = url => url && (url.includes('{z}') || url.includes('{bbox-') || url.includes('{quadkey}')); diff --git a/javascript/components/ShapeSource.js b/javascript/components/ShapeSource.js index de3c775e3b..78d3d9ee55 100644 --- a/javascript/components/ShapeSource.js +++ b/javascript/components/ShapeSource.js @@ -85,6 +85,13 @@ class ShapeSource extends NativeBridgeComponent(AbstractSource) { */ tolerance: PropTypes.number, + /** + * Whether to calculate line distance metrics. + * This is required for line layers that specify lineGradient values. + * The default value is false. + */ + lineMetrics: PropTypes.bool, + /** * Source press listener, gets called when a user presses one of the children layers only * if that layer has a higher z-index than another source layers @@ -147,6 +154,118 @@ class ShapeSource extends NativeBridgeComponent(AbstractSource) { return res.data; } + /** + * Returns the zoom needed to expand the cluster. + * + * @example + * const zoom = await shapeSource.getClusterExpansionZoom(clusterId); + * + * @param {Feature} feature - The feature cluster to expand. + * @return {number} + */ + async getClusterExpansionZoom(feature) { + if (typeof feature === 'number') { + console.warn( + 'Using cluster_id is deprecated and will be removed from the future releases. Please use cluster as an argument instead.', + ); + const res = await this._runNativeCommand( + 'getClusterExpansionZoomById', + this._nativeRef, + [feature], + ); + return res.data; + } + + const res = await this._runNativeCommand( + 'getClusterExpansionZoom', + this._nativeRef, + [JSON.stringify(feature)], + ); + return res.data; + } + + /** + * Returns the FeatureCollection from the cluster. + * + * @example + * const collection = await shapeSource.getClusterLeaves(clusterId, limit, offset); + * + * @param {Feature} feature - The feature cluster to expand. + * @param {number} limit - The number of points to return. + * @param {number} offset - The amount of points to skip (for pagination). + * @return {FeatureCollection} + */ + async getClusterLeaves(feature, limit, offset) { + if (typeof feature === 'number') { + console.warn( + 'Using cluster_id is deprecated and will be removed from the future releases. Please use cluster as an argument instead.', + ); + const res = await this._runNativeCommand( + 'getClusterLeavesById', + this._nativeRef, + [feature, limit, offset], + ); + + if (isAndroid()) { + return JSON.parse(res.data); + } + + return res.data; + } + + const res = await this._runNativeCommand( + 'getClusterLeaves', + this._nativeRef, + [JSON.stringify(feature), limit, offset], + ); + + if (isAndroid()) { + return JSON.parse(res.data); + } + + return res.data; + } + + /** + * Returns the FeatureCollection from the cluster (on the next zoom level). + * + * @example + * const collection = await shapeSource.getClusterChildren(clusterId); + * + * @param {Feature} feature - The feature cluster to expand. + * @return {FeatureCollection} + */ + async getClusterChildren(feature) { + if (typeof feature === 'number') { + console.warn( + 'Using cluster_id is deprecated and will be removed from the future releases. Please use cluster as an argument instead.', + ); + const res = await this._runNativeCommand( + 'getClusterChildrenById', + this._nativeRef, + [feature], + ); + + if (isAndroid()) { + return JSON.parse(res.data); + } + + return res.data; + } + + const res = await this._runNativeCommand( + 'getClusterChildren', + this._nativeRef, + [JSON.stringify(feature)], + ); + + if (isAndroid()) { + return JSON.parse(res.data); + } + + return res.data; + } + setNativeProps(props) { const shallowProps = Object.assign({}, props); @@ -179,13 +298,13 @@ class ShapeSource extends NativeBridgeComponent(AbstractSource) { newEvent = copyPropertiesAsDeprecated( event, newEvent, - (key) => { + key => { console.warn( `event.${key} is deprecated on ShapeSource#onPress, please use event.features`, ); }, { - nativeEvent: (origNativeEvent) => ({ + nativeEvent: origNativeEvent => ({ ...origNativeEvent, payload: features[0], }), @@ -208,8 +327,9 @@ class ShapeSource extends NativeBridgeComponent(AbstractSource) { maxZoomLevel: this.props.maxZoomLevel, buffer: this.props.buffer, tolerance: this.props.tolerance, + lineMetrics: this.props.lineMetrics, onPress: undefined, - ref: (nativeRef) => this._setNativeRef(nativeRef), + ref: nativeRef => this._setNativeRef(nativeRef), onAndroidCallback: isAndroid() ? this._onAndroidCallback : undefined, }; diff --git a/javascript/components/Style.js b/javascript/components/Style.js index dff57eb172..0fed7eaefe 100644 --- a/javascript/components/Style.js +++ b/javascript/components/Style.js @@ -15,7 +15,7 @@ import ImageSource from './ImageSource'; import ShapeSource from './ShapeSource'; function toCamelCase(s) { - return s.replace(/([-_][a-z])/gi, ($1) => { + return s.replace(/([-_][a-z])/gi, $1 => { return $1.toUpperCase().replace('-', '').replace('_', ''); }); } @@ -27,7 +27,7 @@ function toCamelCaseKeys(oldObj) { return {}; } const newObj = {}; - Object.keys(oldObj).forEach((key) => { + Object.keys(oldObj).forEach(key => { const value = oldObj[key]; if (key.includes('-')) { newObj[toCamelCase(key)] = value; @@ -169,6 +169,9 @@ function getShapeSource(id, source) { if (source.tolerance !== undefined) { sourceProps.tolerance = source.tolerance; } + if (source.lineMetrics !== undefined) { + sourceProps.lineMetrics = source.lineMetrics; + } return ; } @@ -194,13 +197,13 @@ function asSourceComponent(id, source) { * Only [`sources`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources) & [`layers`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/) are supported. * Other fields such as `sprites`, `glyphs` etc. will be ignored. Not all layer / source attributes from the style spec are supported, in general the supported attributes will mentioned under https://github.com/react-native-mapbox-gl/maps/tree/master/docs. */ -const Style = (props) => { +const Style = props => { const [fetchedJson, setFetchedJson] = useState({}); const json = typeof props.json === 'object' ? props.json : fetchedJson; // Fetch style when props.json is a URL useEffect(() => { - const abortController = new window.AbortController(); + const abortController = new AbortController(); const fetchStyleJson = async () => { try { const response = await fetch(props.json, { @@ -228,7 +231,7 @@ const Style = (props) => { if (!json.layers) { return []; } - return json.layers.map(asLayerComponent).filter((x) => !!x); + return json.layers.map(asLayerComponent).filter(x => !!x); }, [json.layers]); // Extract source components from json @@ -237,8 +240,8 @@ const Style = (props) => { return []; } return Object.keys(json.sources) - .map((id) => asSourceComponent(id, json.sources[id])) - .filter((x) => !!x); + .map(id => asSourceComponent(id, json.sources[id])) + .filter(x => !!x); }, [json.sources]); return ( diff --git a/javascript/components/SymbolLayer.js b/javascript/components/SymbolLayer.js index 8f1a642312..d1a036ad07 100644 --- a/javascript/components/SymbolLayer.js +++ b/javascript/components/SymbolLayer.js @@ -24,7 +24,9 @@ class SymbolLayer extends AbstractLayer { id: PropTypes.string.isRequired, /** - * The source from which to obtain the data to style. If the source has not yet been added to the current style, the behavior is undefined. + * The source from which to obtain the data to style. + * If the source has not yet been added to the current style, the behavior is undefined. + * Inferred from parent source only if the layer is a direct child to it. */ sourceID: PropTypes.string, @@ -83,7 +85,7 @@ class SymbolLayer extends AbstractLayer { return isSnapshot; } - React.Children.forEach(this.props.children, (child) => { + React.Children.forEach(this.props.children, child => { if (child.type === View) { isSnapshot = true; } diff --git a/javascript/components/UserLocation.js b/javascript/components/UserLocation.js index ba50c6a2ff..fb6cc26ac8 100644 --- a/javascript/components/UserLocation.js +++ b/javascript/components/UserLocation.js @@ -198,14 +198,15 @@ class UserLocation extends React.Component { * @return {boolean} */ needsLocationManagerRunning() { - if (this.props.renderMode === UserLocation.RenderMode.Native) { - return false; - } - return !!this.props.onUpdate || this.props.visible; + return ( + !!this.props.onUpdate || + (this.props.renderMode === UserLocation.RenderMode.Normal && + this.props.visible) + ); } _onLocationUpdate(location) { - if (!this._isMounted) { + if (!this._isMounted || !location) { return; } let coordinates = null; @@ -239,13 +240,8 @@ class UserLocation extends React.Component { render() { const {heading, coordinates} = this.state; - const { - children, - visible, - showsUserHeadingIndicator, - onPress, - animated, - } = this.props; + const {children, visible, showsUserHeadingIndicator, onPress, animated} = + this.props; if (!visible) { return null; @@ -267,8 +263,7 @@ class UserLocation extends React.Component { coordinates={coordinates} style={{ iconRotate: heading, - }} - > + }}> {children || normalIcon(showsUserHeadingIndicator, heading)} ); diff --git a/javascript/components/VectorSource.js b/javascript/components/VectorSource.js index e009d1da15..291b385408 100644 --- a/javascript/components/VectorSource.js +++ b/javascript/components/VectorSource.js @@ -145,13 +145,13 @@ class VectorSource extends NativeBridgeComponent(AbstractSource) { newEvent = copyPropertiesAsDeprecated( event, newEvent, - (key) => { + key => { console.warn( `event.${key} is deprecated on VectorSource#onPress, please use event.features`, ); }, { - nativeEvent: (origNativeEvent) => ({ + nativeEvent: origNativeEvent => ({ ...origNativeEvent, payload: features[0], }), @@ -173,7 +173,7 @@ class VectorSource extends NativeBridgeComponent(AbstractSource) { hasPressListener: isFunction(this.props.onPress), onMapboxVectorSourcePress: this.onPress.bind(this), onPress: undefined, - ref: (nativeRef) => this._setNativeRef(nativeRef), + ref: nativeRef => this._setNativeRef(nativeRef), onAndroidCallback: isAndroid() ? this._onAndroidCallback : undefined, }; return ( diff --git a/javascript/components/annotations/Annotation.js b/javascript/components/annotations/Annotation.js index e35df77030..d8c6a470c3 100644 --- a/javascript/components/annotations/Annotation.js +++ b/javascript/components/annotations/Annotation.js @@ -106,8 +106,7 @@ class Annotation extends React.Component { id={this.props.id} ref="source" onPress={this.onPress} - shape={this.state.shape} - > + shape={this.state.shape}> {this.symbolStyle && ( l !== listener); + this._listeners = this._listeners.filter(l => l !== listener); if (this._listeners.length === 0) { this.stop(); } @@ -66,7 +68,7 @@ class LocationManager { if (!this._isListening) { MapboxGLLocationManager.start(displacement); - LocationModuleEventEmitter.addListener( + this.subscription = LocationModuleEventEmitter.addListener( MapboxGL.LocationCallbackName.Update, this.onUpdate, ); @@ -79,10 +81,7 @@ class LocationManager { MapboxGLLocationManager.stop(); if (this._isListening) { - LocationModuleEventEmitter.removeListener( - MapboxGL.LocationCallbackName.Update, - this.onUpdate, - ); + this.subscription.remove(); } this._isListening = false; @@ -95,7 +94,7 @@ class LocationManager { onUpdate(location) { this._lastKnownLocation = location; - this._listeners.forEach((l) => l(location)); + this._listeners.forEach(l => l(location)); } } diff --git a/javascript/modules/offline/offlineManager.js b/javascript/modules/offline/offlineManager.js index ebd193f492..f19daab639 100644 --- a/javascript/modules/offline/offlineManager.js +++ b/javascript/modules/offline/offlineManager.js @@ -26,6 +26,9 @@ class OfflineManager { this._onProgress = this._onProgress.bind(this); this._onError = this._onError.bind(this); + + this.subscriptionProgress = null; + this.subscriptionError = null; } /** @@ -183,7 +186,7 @@ class OfflineManager { async getPacks() { await this._initialize(); return Object.keys(this._offlinePacks).map( - (name) => this._offlinePacks[name], + name => this._offlinePacks[name], ); } @@ -217,7 +220,7 @@ class OfflineManager { /** * Sets the maximum number of Mapbox-hosted tiles that may be downloaded and stored on the current device. - * The Mapbox Terms of Service prohibits changing or bypassing this limit without permission from Mapbox. + * The Mapbox Terms of Service prohibit changing or bypassing this limit without permission from Mapbox. * * @example * MapboxGL.offlineManager.setTileCountLimit(1000); @@ -230,11 +233,11 @@ class OfflineManager { } /** - * Sets the value at which download status events will be sent over the React Native bridge. - * These events happening very very fast default is 500ms. + * Sets the period at which download status events will be sent over the React Native bridge. + * The default is 500ms. * * @example - * MapboxGL.setProgressEventThrottle(500); + * MapboxGL.offlineManager.setProgressEventThrottle(500); * * @param {Number} throttleValue event throttle value in ms. * @return {void} @@ -261,7 +264,7 @@ class OfflineManager { const totalProgressListeners = Object.keys(this._progressListeners).length; if (isFunction(progressListener)) { if (totalProgressListeners === 0) { - OfflineModuleEventEmitter.addListener( + this.subscriptionProgress = OfflineModuleEventEmitter.addListener( MapboxGL.OfflineCallbackName.Progress, this._onProgress, ); @@ -272,7 +275,7 @@ class OfflineManager { const totalErrorListeners = Object.keys(this._errorListeners).length; if (isFunction(errorListener)) { if (totalErrorListeners === 0) { - OfflineModuleEventEmitter.addListener( + this.subscriptionError = OfflineModuleEventEmitter.addListener( MapboxGL.OfflineCallbackName.Error, this._onError, ); @@ -287,7 +290,7 @@ class OfflineManager { // manually set a listener, since listeners are only set on create flow await MapboxGLOfflineManager.setPackObserver(packName); } catch (e) { - console.log('Unable to set pack observer', e); // eslint-disable-line + console.log('Unable to set pack observer', e); } } } @@ -306,18 +309,18 @@ class OfflineManager { delete this._progressListeners[packName]; delete this._errorListeners[packName]; - if (Object.keys(this._progressListeners).length === 0) { - OfflineModuleEventEmitter.removeListener( - MapboxGL.OfflineCallbackName.Progress, - this._onProgress, - ); + if ( + Object.keys(this._progressListeners).length === 0 && + this.subscriptionProgress + ) { + this.subscriptionProgress.remove(); } - if (Object.keys(this._errorListeners).length === 0) { - OfflineModuleEventEmitter.removeListener( - MapboxGL.OfflineCallbackName.Error, - this._onError, - ); + if ( + Object.keys(this._errorListeners).length === 0 && + this.subscriptionError + ) { + this.subscriptionError.remove(); } } diff --git a/javascript/utils/Logger.js b/javascript/utils/Logger.js index 9f7fda4e02..5101fc6639 100644 --- a/javascript/utils/Logger.js +++ b/javascript/utils/Logger.js @@ -71,7 +71,7 @@ class Logger { } subscribe() { - this.subscription = this.loggerEmitter.addListener('LogEvent', (log) => { + this.subscription = this.loggerEmitter.addListener('LogEvent', log => { this.onLog(log); }); } diff --git a/javascript/utils/animated/AnimatedCoordinatesArray.js b/javascript/utils/animated/AnimatedCoordinatesArray.js index fcca640f9c..75ee2b8f5f 100644 --- a/javascript/utils/animated/AnimatedCoordinatesArray.js +++ b/javascript/utils/animated/AnimatedCoordinatesArray.js @@ -31,7 +31,7 @@ class AnimatedCoordinatesArray extends AnimatedWithChildren { * @returns {object} - the state object */ onInitialState(coordinatesArray) { - return {coords: coordinatesArray.map((coord) => [coord[0], coord[1]])}; + return {coords: coordinatesArray.map(coord => [coord[0], coord[1]])}; } /** @@ -72,7 +72,7 @@ class AnimatedCoordinatesArray extends AnimatedWithChildren { coords.length > 0 ? coords[coords.length - 1] : targetCoords[0]; const adding = targetCoords .slice(commonLen, targetCoords.length) - .map((newCoord) => [ + .map(newCoord => [ addingOrig[0] * origF + newCoord[0] * newF, addingOrig[1] * origF + newCoord[1] * newF, ]); @@ -87,7 +87,7 @@ class AnimatedCoordinatesArray extends AnimatedWithChildren { : coords[0]; const dissapearing = coords .slice(commonLen, coords.length) - .map((origCoord) => [ + .map(origCoord => [ origCoord[0] * origF + dissapearingNew[0] * newF, origCoord[1] * origF + dissapearingNew[1] * newF, ]); @@ -105,7 +105,7 @@ class AnimatedCoordinatesArray extends AnimatedWithChildren { * @returns {object} The state */ onStart(state, toValue) { - const targetCoords = toValue.map((coord) => [coord[0], coord[1]]); + const targetCoords = toValue.map(coord => [coord[0], coord[1]]); return { ...state, targetCoords, @@ -115,7 +115,7 @@ class AnimatedCoordinatesArray extends AnimatedWithChildren { animate(progressValue, progressAnimation, config) { const {toValue} = config; - const onAnimationStart = (animation) => { + const onAnimationStart = animation => { if (this.animation) { // there was a started but not finsihed animation const actProgress = this.progressValue.__getValue(); diff --git a/javascript/utils/animated/AnimatedRouteCoordinatesArray.js b/javascript/utils/animated/AnimatedRouteCoordinatesArray.js index 7c8be5e31b..bfadd19831 100644 --- a/javascript/utils/animated/AnimatedRouteCoordinatesArray.js +++ b/javascript/utils/animated/AnimatedRouteCoordinatesArray.js @@ -1,17 +1,9 @@ -import { - lineString, - point, - convertDistance as convertDistanceFn, - convertLength as convertLengthFn, -} from '@turf/helpers'; +import {lineString, point, convertLength} from '@turf/helpers'; import distance from '@turf/distance'; import nearestPointOnLine from '@turf/nearest-point-on-line'; import length from '@turf/length'; import AnimatedCoordinatesArray from './AnimatedCoordinatesArray'; - -const convertLength = convertLengthFn || convertDistanceFn; - export default class AnimatedRouteCoordinatesArray extends AnimatedCoordinatesArray { /** * Calculate initial state @@ -21,7 +13,7 @@ export default class AnimatedRouteCoordinatesArray extends AnimatedCoordinatesAr */ onInitialState(coordinatesArray) { return { - fullRoute: coordinatesArray.map((coord) => [coord[0], coord[1]]), + fullRoute: coordinatesArray.map(coord => [coord[0], coord[1]]), end: {from: 0}, }; } diff --git a/javascript/utils/animated/AnimatedShape.js b/javascript/utils/animated/AnimatedShape.js index 4af8a1beb5..57513427a4 100644 --- a/javascript/utils/animated/AnimatedShape.js +++ b/javascript/utils/animated/AnimatedShape.js @@ -1,7 +1,5 @@ import {Animated} from 'react-native'; -/* eslint-disable guard-for-in */ - // see // https://github.com/facebook/react-native/blob/master/Libraries/Animated/src/nodes/AnimatedWithChildren.js const AnimatedWithChildren = Object.getPrototypeOf(Animated.ValueXY); @@ -29,7 +27,7 @@ class AnimatedShape extends AnimatedWithChildren { _walkShapeAndGetValues(value) { if (Array.isArray(value)) { - return value.map((i) => this._walkShapeAndGetValues(i)); + return value.map(i => this._walkShapeAndGetValues(i)); } if (value instanceof Animated.Node) { return value.__getValue(); @@ -51,7 +49,7 @@ class AnimatedShape extends AnimatedWithChildren { _walkAndProcess(value, cb) { if (Array.isArray(value)) { - value.forEach((i) => this._walkAndProcess(i, cb)); + value.forEach(i => this._walkAndProcess(i, cb)); } else if (value instanceof Animated.Node) { cb(value); } else if (typeof value === 'object') { @@ -62,11 +60,11 @@ class AnimatedShape extends AnimatedWithChildren { } __attach() { - this._walkAndProcess(this.shape, (v) => v.__addChild(this)); + this._walkAndProcess(this.shape, v => v.__addChild(this)); } __detach() { - this._walkAndProcess(this.shape, (v) => v.__removeChild(this)); + this._walkAndProcess(this.shape, v => v.__removeChild(this)); super.__detach(); } } diff --git a/javascript/utils/deprecation.js b/javascript/utils/deprecation.js index c38a198f76..d0e079bacf 100644 --- a/javascript/utils/deprecation.js +++ b/javascript/utils/deprecation.js @@ -2,7 +2,7 @@ * Copy properties from origObject to newObject, which not exists in newObject, * calls onDeprecatedCalled callback in case a copied property is invoked. */ -// eslint-disable-next-line class-methods-use-this + export function copyPropertiesAsDeprecated( origObject, newObject, diff --git a/javascript/utils/index.js b/javascript/utils/index.js index e811fe6d40..bc3c28fe12 100644 --- a/javascript/utils/index.js +++ b/javascript/utils/index.js @@ -95,8 +95,8 @@ export function cloneReactChildrenWithProps(children, propsToAdd = {}) { foundChildren = children; } - const filteredChildren = foundChildren.filter((child) => !!child); // filter out falsy children, since some can be null - return React.Children.map(filteredChildren, (child) => + const filteredChildren = foundChildren.filter(child => !!child); // filter out falsy children, since some can be null + return React.Children.map(filteredChildren, child => React.cloneElement(child, propsToAdd), ); } diff --git a/package.json b/package.json index b5cf534c8a..f1114c8e66 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@react-native-mapbox-gl/maps", "description": "A Mapbox GL react native module for creating custom maps", - "version": "8.1.0-rc.9", + "version": "8.5.0", "publishConfig": { "access": "public" }, @@ -25,55 +25,61 @@ "test": "npm run lint && npm run unittest", "unittest": "jest", "unittest:single": "jest --testNamePattern", - "format": "npm run lint -- --fix", - "lint": "eslint . --ignore-pattern 'example' --fix && cd example/ && eslint ./src --fix" + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "prepare": "yarn build:plugin", + "test:plugin": "expo-module test plugin", + "build:plugin": "tsc --build plugin", + "lint:plugin": "eslint plugin/src/*" }, "peerDependencies": { "prop-types": ">=15.5.8", - "react": "^16.6.1", + "react": ">=16.6.1", "react-native": ">=0.59.9" }, "dependencies": { + "@expo/config-plugins": "^4.0.3", "@mapbox/geo-viewport": ">= 0.4.0", - "@turf/along": ">= 4.0.0 <7.0.0", - "@turf/distance": ">= 4.0.0 <7.0.0", - "@turf/nearest-point-on-line": ">= 4.0.0 <7.0.0", - "@turf/helpers": ">= 4.6.0 <7.0.0", - "@turf/length": ">= 4.6.0 <7.0.0", + "@turf/along": "6.5.0", + "@turf/distance": "6.5.0", + "@turf/helpers": "6.5.0", + "@turf/length": "6.5.0", + "@turf/nearest-point-on-line": "6.5.0", "@types/geojson": "^7946.0.7", "debounce": "^1.2.0" }, "devDependencies": { "@babel/core": "7.5.0", - "@babel/plugin-proposal-class-properties": "7.10.4", - "@babel/plugin-transform-exponentiation-operator": "7.10.4", - "@babel/plugin-transform-flow-strip-types": "7.10.4", - "@babel/plugin-transform-runtime": "7.11.5", + "@babel/plugin-proposal-class-properties": "7.16.0", + "@babel/plugin-transform-runtime": "7.16.4", "@babel/runtime": "7.11.2", + "@react-native-community/eslint-config": "^3.0.1", + "@sinonjs/fake-timers": "^8.0.1", + "@testing-library/react-native": "^8.0.0", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.8.0", "babel-core": "6.26.3", "babel-eslint": "^10.0.1", - "documentation": "13.0.2", + "documentation": "13.2.5", "ejs": "^3.1.3", "ejs-lint": "^1.1.0", - "eslint": "^7.3.0", - "@react-native-community/eslint-config": "^2.0.0", + "eslint": "^7.32.0 ", + "eslint-config-prettier": "^8.1.0", "eslint-plugin-fp": "^2.3.0", - "eslint-plugin-import": "2.22.0", - "flow-bin": "^0.134.0", - "husky": "4.3.0", + "eslint-plugin-import": "2.25.3", + "expo-module-scripts": "^2.0.0", + "husky": "4.3.8", "jest": "25.5.4", - "@sinonjs/fake-timers": "^6.0.0", "jest-cli": "25.5.4", - "lint-staged": "^10.1.3", + "lint-staged": "^12.1.2", "metro-react-native-babel-preset": "0.49.1", "node-dir": "0.1.17", - "react": "16.8.6", + "prettier": "^2.0.4", + "react": "16.8.3", "react-docgen": "^5.0.0-beta.1", "react-native": "0.59.10", - "typescript": "4.0.3", - "react-native-testing-library": "^6.0.0", "react-test-renderer": "16.8.3", - "prettier": "^2.0.4" + "typescript": "^4.4.3" }, "jest": { "preset": "react-native", @@ -86,7 +92,8 @@ ], "modulePathIgnorePatterns": [ "example", - "__tests__/__mocks__" + "__tests__/__mocks__", + "fixtures" ] }, "husky": { @@ -95,8 +102,6 @@ } }, "lint-staged": { - "*.js": [ - "yarn lint" - ] + "*.{js,jsx,ts,tsx}": "yarn lint" } } diff --git a/plugin/install.md b/plugin/install.md new file mode 100644 index 0000000000..8fbd7e5f8e --- /dev/null +++ b/plugin/install.md @@ -0,0 +1,32 @@ +# Expo installation + +> This package cannot be used in the "Expo Go" app because [it requires custom native code](https://docs.expo.io/workflow/customizing/). + +First install the package with yarn, npm, or [`expo install`](https://docs.expo.io/workflow/expo-cli/#expo-install). + +```sh +expo install @react-native-mapbox-gl/maps +``` + +After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`: + +```json +{ + "expo": { + "plugins": ["@react-native-mapbox-gl/maps"] + } +} +``` + +Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide. + +## API + +This plugin doesn't currently provide any additional properties for customization. The plugin simply generates the pre-install block in the `ios/Podfile` (the post-install block is not required for Expo support). No additional changes are done on Android. + +## Manual Setup + +For bare workflow projects, you can follow the manual setup guides: + +- [iOS](/ios/install.md) +- [Android](/android/install.md) diff --git a/plugin/jest.config.js b/plugin/jest.config.js new file mode 100644 index 0000000000..e539b3b187 --- /dev/null +++ b/plugin/jest.config.js @@ -0,0 +1 @@ +module.exports = require('expo-module-scripts/jest-preset-plugin'); diff --git a/plugin/src/__tests__/__snapshots__/withMapbox-test.ts.snap b/plugin/src/__tests__/__snapshots__/withMapbox-test.ts.snap new file mode 100644 index 0000000000..d377b48366 --- /dev/null +++ b/plugin/src/__tests__/__snapshots__/withMapbox-test.ts.snap @@ -0,0 +1,198 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757 + pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-5a7ed0a20d5aff2d61639bc5bb4fd5551233d57c + $RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer + end +# @generated end pre_installer + use_react_native!(:path => config[\\"reactNativePath\\"]) + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +" +`; + +exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757 + pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-5a7ed0a20d5aff2d61639bc5bb4fd5551233d57c + $RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer + end +# @generated end pre_installer + use_react_native!(:path => config[\\"reactNativePath\\"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +" +`; + +exports[`applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + config = use_native_modules! + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757 + pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-5a7ed0a20d5aff2d61639bc5bb4fd5551233d57c + $RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer + end +# @generated end pre_installer + use_react_native!( + :path => config[:reactNativePath], + # to enable hermes on iOS, change \`false\` to \`true\` and then install pods + :hermes_enabled => false + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable the next line. + use_flipper!() + + post_install do |installer| + react_native_post_install(installer) + end +end +" +`; + +exports[`applyCocoaPodsModifications does not work with revisions to blocks after comments 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + # pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-5a7ed0a20d5aff2d61639bc5bb4fd5551233d57c + $RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end + +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 +INVALID_post_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$RNMBGL.post_install(installer) +# @generated end @react-native-mapbox-gl/maps-post_installer +end +# @generated end post_installer +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757 + pre_install do |installer| + end +# @generated end pre_installer + use_react_native!(:path => config[\\"reactNativePath\\"]) + + +end +" +`; + +exports[`applyCocoaPodsModifications works after revisions to blocks 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 +INVALID_post_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$RNMBGL.post_install(installer) +# @generated end @react-native-mapbox-gl/maps-post_installer +end +# @generated end post_installer +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757 + pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-5a7ed0a20d5aff2d61639bc5bb4fd5551233d57c + $RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer + end +# @generated end pre_installer + use_react_native!(:path => config[\\"reactNativePath\\"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +" +`; diff --git a/plugin/src/__tests__/fixtures/cocoapodFiles.ts b/plugin/src/__tests__/fixtures/cocoapodFiles.ts new file mode 100644 index 0000000000..c50f171025 --- /dev/null +++ b/plugin/src/__tests__/fixtures/cocoapodFiles.ts @@ -0,0 +1,173 @@ +export const reactNativeTemplatePodfile = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # to enable hermes on iOS, change \`false\` to \`true\` and then install pods + :hermes_enabled => false + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable the next line. + use_flipper!() + + post_install do |installer| + react_native_post_install(installer) + end +end +`; + +export const expoTemplatePodfile = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + + use_react_native!(:path => config["reactNativePath"]) + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +`; + +export const customExpoTemplatePodfile = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + + use_react_native!(:path => config["reactNativePath"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +`; + +// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID. +export const expoTemplateWithRevisions = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id +INVALID_pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 + INVALID_$RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer +end +# @generated end pre_installer +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 +INVALID_post_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$RNMBGL.post_install(installer) +# @generated end @react-native-mapbox-gl/maps-post_installer +end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +`; + +export const expoTemplateWithRevisionsAfterComments = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '11.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id +INVALID_pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 + INVALID_$RNMBGL.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer +end +# @generated end pre_installer +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 +INVALID_post_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$RNMBGL.post_install(installer) +# @generated end @react-native-mapbox-gl/maps-post_installer +end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + +end +`; + +export const blankTemplatePodfile = ` +platform :ios, '11.0' + +target 'HelloWorld' do +end +`; diff --git a/plugin/src/__tests__/withMapbox-test.ts b/plugin/src/__tests__/withMapbox-test.ts new file mode 100644 index 0000000000..886a58c0a0 --- /dev/null +++ b/plugin/src/__tests__/withMapbox-test.ts @@ -0,0 +1,50 @@ +import {applyCocoaPodsModifications} from '../withMapbox'; + +import * as fixtures from './fixtures/cocoapodFiles'; + +describe(applyCocoaPodsModifications, () => { + it('adds blocks to a react native template podfile', () => { + expect( + applyCocoaPodsModifications(fixtures.reactNativeTemplatePodfile), + ).toMatchSnapshot(); + }); + it('adds blocks to a expo prebuild template podfile', () => { + expect( + applyCocoaPodsModifications(fixtures.expoTemplatePodfile), + ).toMatchSnapshot(); + }); + it('adds blocks to a expo prebuild template podfile with custom modifications ', () => { + expect( + applyCocoaPodsModifications(fixtures.customExpoTemplatePodfile), + ).toMatchSnapshot(); + }); + it('fails to add blocks to a bare podfile', () => { + expect(() => + applyCocoaPodsModifications(fixtures.blankTemplatePodfile), + ).toThrow('Failed to match'); + expect(() => applyCocoaPodsModifications('')).toThrow('Failed to match'); + }); + it('does not re add blocks to an applied template podfile', () => { + const runOnce = applyCocoaPodsModifications( + fixtures.reactNativeTemplatePodfile, + ); + + expect(applyCocoaPodsModifications(runOnce)).toMatch(runOnce); + }); + it('works after revisions to blocks', () => { + const runOnce = applyCocoaPodsModifications( + fixtures.expoTemplateWithRevisions, + ); + + expect(runOnce).toMatchSnapshot(); + }); + // A known issue is that the regex won't work if the template + // has a pre_install/post_install block commented out, before the `use_react_native` function. + it('does not work with revisions to blocks after comments', () => { + const runOnce = applyCocoaPodsModifications( + fixtures.expoTemplateWithRevisionsAfterComments, + ); + + expect(runOnce).toMatchSnapshot(); + }); +}); diff --git a/plugin/src/withMapbox.ts b/plugin/src/withMapbox.ts new file mode 100644 index 0000000000..5be61479c4 --- /dev/null +++ b/plugin/src/withMapbox.ts @@ -0,0 +1,143 @@ +import {promises} from 'fs'; +import path from 'path'; + +import { + ConfigPlugin, + createRunOncePlugin, + withDangerousMod, + withXcodeProject, + XcodeProject, +} from '@expo/config-plugins'; +import { + mergeContents, + removeGeneratedContents, +} from '@expo/config-plugins/build/utils/generateCode'; + +let pkg: {name: string; version?: string} = { + name: '@react-native-mapbox-gl/maps', +}; +try { + pkg = require('@react-native-mapbox-gl/maps/package.json'); +} catch { + // empty catch block +} + +type InstallerBlockName = 'pre' | 'post'; + +/** + * Dangerously adds the custom installer hooks to the Podfile. + * In the future this should be removed in favor of some custom hooks provided by Expo autolinking. + * + * https://github.com/react-native-mapbox-gl/maps/blob/master/ios/install.md#react-native--0600 + * @param config + * @returns + */ +const withCocoaPodsInstallerBlocks: ConfigPlugin = c => { + return withDangerousMod(c, [ + 'ios', + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + async config => { + const file = path.join(config.modRequest.platformProjectRoot, 'Podfile'); + + const contents = await promises.readFile(file, 'utf8'); + + await promises.writeFile( + file, + applyCocoaPodsModifications(contents), + 'utf-8', + ); + return config; + }, + ]); +}; + +// Only the preinstaller block is required, the post installer block is +// used for spm (swift package manager) which Expo doesn't currently support. +export function applyCocoaPodsModifications(contents: string): string { + // Ensure installer blocks exist + let src = addInstallerBlock(contents, 'pre'); + // src = addInstallerBlock(src, "post"); + src = addMapboxInstallerBlock(src, 'pre'); + // src = addMapboxInstallerBlock(src, "post"); + return src; +} + +export function addInstallerBlock( + src: string, + blockName: InstallerBlockName, +): string { + const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`); + const tag = `${blockName}_installer`; + for (const line of src.split('\n')) { + const contents = line.trim(); + // Ignore comments + if (!contents.startsWith('#')) { + // Prevent adding the block if it exists outside of comments. + if (contents.match(matchBlock)) { + // This helps to still allow revisions, since we enabled the block previously. + // Only continue if the generated block exists... + const modified = removeGeneratedContents(src, tag); + if (!modified) { + return src; + } + } + } + } + + return mergeContents({ + tag, + src, + newSrc: [` ${blockName}_install do |installer|`, ' end'].join('\n'), + anchor: /use_react_native/, + // We can't go after the use_react_native block because it might have parameters, causing it to be multi-line (see react-native template). + offset: 0, + comment: '#', + }).contents; +} + +export function addMapboxInstallerBlock( + src: string, + blockName: InstallerBlockName, +): string { + return mergeContents({ + tag: `@react-native-mapbox-gl/maps-${blockName}_installer`, + src, + newSrc: ` $RNMBGL.${blockName}_install(installer)`, + anchor: new RegExp(`${blockName}_install do \\|installer\\|`), + offset: 1, + comment: '#', + }).contents; +} + +/** + * Exclude building for arm64 on simulator devices in the pbxproj project. + * Without this, production builds targeting simulators will fail. + */ +export function setExcludedArchitectures(project: XcodeProject): XcodeProject { + const configurations = project.pbxXCBuildConfigurationSection(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + for (const {buildSettings} of Object.values(configurations || {})) { + // Guessing that this is the best way to emulate Xcode. + // Using `project.addToBuildSettings` modifies too many targets. + if (typeof buildSettings?.PRODUCT_NAME !== 'undefined') { + buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; + } + } + + return project; +} + +const withExcludedSimulatorArchitectures: ConfigPlugin = c => { + return withXcodeProject(c, config => { + config.modResults = setExcludedArchitectures(config.modResults); + return config; + }); +}; + +const withMapbox: ConfigPlugin = config => { + config = withExcludedSimulatorArchitectures(config); + return withCocoaPodsInstallerBlocks(config); +}; + +export default createRunOncePlugin(withMapbox, pkg.name, pkg.version); diff --git a/plugin/tsconfig.json b/plugin/tsconfig.json new file mode 100644 index 0000000000..354bddb433 --- /dev/null +++ b/plugin/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "expo-module-scripts/tsconfig.plugin", + "compilerOptions": { + "outDir": "build", + "rootDir": "src" + }, + "include": ["./src"], + "exclude": ["**/__mocks__/*", "**/__tests__/*"] +} diff --git a/react-native-mapbox-gl.podspec b/react-native-mapbox-gl.podspec index 959c24b5eb..0ea7ce140e 100644 --- a/react-native-mapbox-gl.podspec +++ b/react-native-mapbox-gl.podspec @@ -2,12 +2,84 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) -default_ios_mapbox_version = '~> 5.8.0' +default_ios_mapbox_version = '~> 5.9.0' rnmbgl_ios_version = $ReactNativeMapboxGLIOSVersion || ENV["REACT_NATIVE_MAPBOX_MAPBOX_IOS_VERSION"] || default_ios_mapbox_version if ENV.has_key?("REACT_NATIVE_MAPBOX_MAPBOX_IOS_VERSION") puts "REACT_NATIVE_MAPBOX_MAPBOX_IOS_VERSION env is deprecated please use `$ReactNativeMapboxGLIOSVersion = \"#{rnmbgl_ios_version}\"`" end +TargetsToChangeToDynamic = ['MapboxMobileEvents'] + +$RNMBGL = Object.new + +def $RNMBGL._add_spm_to_target(project, target, url, requirement, product_name) + pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference + ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency + pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url } + if !pkg + pkg = project.new(pkg_class) + pkg.repositoryURL = url + pkg.requirement = requirement + project.root_object.package_references << pkg + end + ref = target.package_product_dependencies.find { |r| r.class == ref_class && r.package == pkg && r.product_name == product_name } + if !ref + ref = project.new(ref_class) + ref.package = pkg + ref.product_name = product_name + target.package_product_dependencies << ref + end +end + +def $RNMBGL.post_install(installer) + if $RNMBGL_Use_SPM + spm_spec = { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { + kind: "exactVersion", + version: "5.12.1" + }, + product_name: "Mapbox" + } + + if $RNMBGL_Use_SPM.is_a?(Hash) + spm_spec = $RNMBGL_Use_SPM + end + project = installer.pods_project + self._add_spm_to_target( + project, + project.targets.find { |t| t.name == "react-native-mapbox-gl"}, + spm_spec[:url], + spm_spec[:requirement], + spm_spec[:product_name] + ) + + installer.aggregate_targets.group_by(&:user_project).each do |project, targets| + targets.each do |target| + target.user_targets.each do |user_target| + self._add_spm_to_target( + project, + user_target, + spm_spec[:url], + spm_spec[:requirement], + spm_spec[:product_name] + ) + end + end + end + end +end + +def $RNMBGL.pre_install(installer) + installer.aggregate_targets.each do |target| + target.pod_targets.select { |p| TargetsToChangeToDynamic.include?(p.name) }.each do |mobile_events_target| + mobile_events_target.instance_variable_set(:@build_type,Pod::BuildType.dynamic_framework) + puts "* Changed #{mobile_events_target.name} to #{mobile_events_target.send(:build_type)}" + fail "Unable to change build_type" unless mobile_events_target.send(:build_type) == Pod::BuildType.dynamic_framework + end + end +end + Pod::Spec.new do |s| s.name = "react-native-mapbox-gl" s.summary = "React Native Component for Mapbox GL" @@ -18,19 +90,24 @@ Pod::Spec.new do |s| s.license = "MIT" s.platform = :ios, "8.0" + if !$RNMBGL_Use_SPM s.dependency 'Mapbox-iOS-SDK', rnmbgl_ios_version + end s.dependency 'React-Core' s.dependency 'React' s.subspec 'DynamicLibrary' do |sp| sp.source_files = "ios/RCTMGL/**/*.{h,m}" + if $RNMGL_USE_MAPLIBRE + sp.compiler_flags = '-DRNMGL_USE_MAPLIBRE=1' + end end if ENV["REACT_NATIVE_MAPBOX_GL_USE_FRAMEWORKS"] s.default_subspecs= ['DynamicLibrary'] else s.subspec 'StaticLibraryFixer' do |sp| - s.dependency '@react-native-mapbox-gl-mapbox-static', rnmbgl_ios_version + # s.dependency '@react-native-mapbox-gl-mapbox-static', rnmbgl_ios_version end s.default_subspecs= ['DynamicLibrary', 'StaticLibraryFixer'] diff --git a/scripts/upgradeExample.sh b/scripts/upgradeExample.sh deleted file mode 100644 index fb623f17aa..0000000000 --- a/scripts/upgradeExample.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -react-native init RNMapboxGLExample -mv example/src RNMapboxGLExample/src -mv example/scripts RNMapboxGLExample/scripts -rm -rf example -mv RNMapboxGLExample example - -# Edit package.json - -cd example -npx json -I -f package.json -e 'this.scripts["copy:changes"]="node ./scripts/watch_rngl.js"' -npx json -I -f package.json -e 'this.scripts["pack:gl"]="./scripts/npm_pack_rngl.sh"' -npx json -I -f package.json -e 'this.scripts["clean:node:modules"]="./scripts/clean_node_modules.sh"' -npx json -I -f package.json -e 'this.scripts.preinstall="npm run pack:gl"' -npx json -I -f package.json -e 'this.scripts.postinstall="node ./scripts/set_access_token.js"' -npx json -I -f package.json -e 'this.scripts["reset:from:gl"]="npm run clean:node:modules && npm install"' -npx json -I -f package.json -e 'this.dependencies["@react-native-mapbox-gl/maps"]="file:../react-native-mapbox-gl-maps.tgz"' - -# Install depencies -touch accesstoken -yarn add @mapbox/geo-viewport@0.4.0 @turf/along@5.1.5 @turf/bearing@5.1.5 @turf/distance@5.1.5 @turf/helpers@4.7.3 @turf/line-distance@4.7.3 @turf/nearest@4.7.3 buffer@5.1.0 install@0.12.2 @mapbox/mapbox-sdk@0.6.0 moment@2.24.0 npm@5.10.0 prop-types@15.7.2 react-native-elements@1.1.0 react-native-vector-icons react-native-safe-area-view@0.13.1 react-navigation@2.18.3 url@0.11.0 -react-native link -rm accesstoken -cd ios && pod install && cd ../ diff --git a/setup-jest.js b/setup-jest.js index f9b555fc95..f9ab416b5f 100644 --- a/setup-jest.js +++ b/setup-jest.js @@ -2,7 +2,7 @@ import {NativeModules} from 'react-native'; function keyMirror(keys) { const obj = {}; - keys.forEach((key) => (obj[key] = key)); + keys.forEach(key => (obj[key] = key)); return obj; } @@ -87,7 +87,7 @@ NativeModules.MGLModule = { }; NativeModules.MGLOfflineModule = { - createPack: (packOptions) => { + createPack: packOptions => { return Promise.resolve({ bounds: packOptions.bounds, metadata: JSON.stringify({name: packOptions.name}), @@ -115,8 +115,8 @@ NativeModules.MGLLocationModule = { pause: jest.fn(), }; -// Mock for AbortController. Will probably not be required during testing. -window = {}; -window.AbortController = class { +// Mock for global AbortController +global.AbortController = class { + signal = 'test-signal'; abort = jest.fn(); }; diff --git a/example/tsconfig.json b/tsconfig.json similarity index 100% rename from example/tsconfig.json rename to tsconfig.json