diff --git a/.eslintignore b/.eslintignore index 34541f1a9..ec5a73f56 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 c86edfece..620ff4b20 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 519f9eec5..d7c861039 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 000000000..6b67aca19 --- /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 6d30136cf..f765d9007 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 000000000..a6a125d53 --- /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 000000000..40baae0f7 --- /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 000000000..9b8109f51 --- /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 000000000..fea7e4670 --- /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 000000000..b014baaec --- /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 20dc4030c..811366a86 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 eb03bb362..8b93d2edf 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 ebf08b064..ad122c64e 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 5c4de1a4f..84196d95f 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 c47ef4b03..000000000 --- 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 f36b61d59..a0c198925 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 a0b06297d..0e77b64a4 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 1edc97563..f13e283de 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 000000000..344699ddd --- /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 8a2bd3146..05d7c6471 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 0c9c7f41a..4dddc1f6d 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 8d112bf0d..864a49797 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 dd8c70cfe..162c58c95 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 1efda4eba..552526965 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 2c5e8e034..ab517b577 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 000000000..96105c0cc --- /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 46eb2cac5..99cc164f5 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 115641588..31c771f64 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 9e33bfc34..c3238cf37 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 8525da5e4..24558a5aa 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 eed8c1dc8..a062eb8e1 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 b4ba3e3ad..a6a126032 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 53d22b509..ec3b1d322 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 739a6f6a5..fa222777f 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 c9ac54da3..805f52001 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 77b4d5e8e..6af8eb82b 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 14fe2c8a3..362db0b56 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 900083a0e..ed806704c 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 f6623b56c..490122ae2 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 11053f463..4bfb472bd 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 daa51cb2e..7bf17454e 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 016fa7096..4d0bc1bcc 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 69fe1d50f..2cf8f0c41 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 559c4823e..19700fe3f 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 074cd70c6..834246537 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 08a1ec8ef..df81804e4 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 f96af25b7..39964b87a 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 c896fad4a..ef3548938 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 c6cd92c03..3e90d253e 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 f3560322a..37a166272 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 249e89402..477197525 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 2d97fdb61..d39c22ef3 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 c5467dd0a..e705ac818 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 75669814a..1b6525b0d 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 92ce50dd0..6b8d8c197 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 f33f61b51..da009739e 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 c6f1d601d..91718dc3c 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 1489a50d9..e06b53145 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 6d9d2b7ec..7e1be2840 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 ef2e77212..5551bd1e6 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 4014f5d1a..3c739dd5c 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 097d45e32..c13fae5bc 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 000000000..d1e04f17b --- /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 000000000..8ad8d94af 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 000000000..966c6d402 Binary files /dev/null and b/assets/indoor_building_map_ios.png differ diff --git a/babel.config.js b/babel.config.js index 2df2b18b9..95dd5a64e 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 fa2af1f0e..000000000 --- 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 500950ff7..d87c29e0d 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 2819b010d..5a2e245f6 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 94dec76b4..872e9f18c 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 bc9487c1e..dda8f7aff 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 5b22ffe56..f743d2742 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 a2b30376e..bb188d32e 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 04c8c1dfd..7a628d01d 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 206f25b08..c204239c9 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 000000000..c7c5f2fca --- /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 429e7fa56..dabefbb1b 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 72bdd91ba..539f7dcb3 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 3b4717215..321ccea18 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 348e595c3..f6e7dbc79 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 7ce2fe058..4d54c7609 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 238884bf2..1453c3dd2 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 d999b6413..5cd835867 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 ceb62ef51..000000000 --- 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 d42ff1835..45a3dcb2a 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 ad6db8935..8a4c58b26 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 000000000..4fe96938e --- /dev/null +++ b/example/.nvmrc @@ -0,0 +1,2 @@ +v14.15.0 + diff --git a/example/README.md b/example/README.md index 14feb2b37..ca50e54a4 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 3d690bae5..000000000 --- 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 8b924f558..7afa99c74 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 fa26aa56e..b2f3ad9fc 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 fd84f4b9c..4a1334c2f 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 d7e96d673..5e807e17d 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 62fe59fa4..24bc06136 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 e958706c1..b9b563910 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 bcf12cdd9..6bae022b9 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 842267020..7665b0fa9 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 c86d88fbc..279743597 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 000000000..5467a9303 --- /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 000000000..7f4fc942f --- /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 000000000..31022533c --- /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 55ec7d62a..6b093c400 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 ecbd496be..000000000 --- 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 ba72822e8..000000000 --- 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 3c024455f..45a904503 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 38655953d..000000000 --- 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 26ccf9ef0..e59cf2cc7 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 e7277bc19..f57ed75f0 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 ba72822e8..000000000 --- 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 14e58028f..000000000 --- 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 cdade5909..672ca3a27 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 48b37a0eb..739ffde61 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 000000000..89ca5eca5 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 000000000..cd05bb519 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 000000000..48089c763 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 000000000..ba0f1e202 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 000000000..c87cbe98f 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 000000000..716e54765 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 000000000..539094aa7 --- /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 000000000..40469cc0e --- /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 24a1f0251..5ea6a7e0e 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 14d67ef18..95b591bf2 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 340e96bd0..bd4e7b191 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 264bf3824..7fd4e9731 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 000000000..429b17cca --- /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 8d69f118a..359836575 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 3f994fec9..e760cf521 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 ef7922ee1..232df0c8c 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 138f9c96a..5c8d9b6d2 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 000000000..034354f97 --- /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 c0da01936..a95bfe21f 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 32653942c..3a30c14dd 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 23ce5751f..9ceb8a1ea 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 dde2c2f4f..d871b1db3 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 1fb04c922..116bc074f 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 9eb02c874..86f30333a 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 04fd2f07f..1b0129ad9 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 de8b58291..000000000 --- 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 37f438832..2308e1a1d 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 39ec6b23e..f12acef22 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 d159d6e53..365acc06a 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 f4b53a214..d41f36b7b 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 4fd8cab29..a8ad2c994 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 0750303e6..000000000 --- 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 000000000..3f1141f44 --- /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 c5e032b23..177c01b18 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 9babe8702..c9be0dfe3 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 2941f691f..644161e9d 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 d9448134a..68b7c4da6 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 8afebcbc7..cc5a53722 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 000000000..82bfa7020 --- /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 95a6faa94..df614b69e 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 a804f63c7..3c52d445e 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 832acd754..e140be712 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 b9bfc2fae..22e3eea66 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 cc32dffb2..138732fbc 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 c0b8fed5e..000000000 --- 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 393200171..34333a18d 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 c01a0be93..b9f3ea6ec 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 000000000..9ac6d28dc --- /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 daece0202..9adb18b02 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 1197bc9fa..6a68bf9f4 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 a27a66029..e57bcde5a 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 401bdf3c1..76296b464 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 13896deb9..d357411a7 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 cb8819f63..ab303bb58 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 46c034af2..b46750698 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 8bd67224d..b27df6caf 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 18386180e..398643542 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 1d40af949..0aaf23c8d 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 515ee0a7b..64018a59b 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 d9a795000..4526c82fe 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 c6c5e137a..5e995c633 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 d0d42023d..2738d0ec7 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 bb0ec9406..bce7a2070 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 0a06b0052..8516b3308 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 bbaab06d2..b439cc78b 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 c456ff581..d7d298583 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 e68501460..57621c3b1 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 816bdecc9..ffa933e06 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 fe55f6f49..d6b4a9888 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 571393a44..4fde63538 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 55b40cee2..a99fa8204 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 988660290..fa2e7ee96 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 5c7df3096..475cb45c4 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 b2d843fe2..d32381eab 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 ad74c23aa..1dca4bed4 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 2910a012f..7eed9cfbb 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 85ad49539..e1cf1cf9a 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 91b69d29b..4dd55ea03 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 70a807237..1b414f32e 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 e1532db71..8f581f50e 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 dfc425232..8750b5e2a 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 9de1d1752..91e6100f0 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 9e4c4824f..e3327daa1 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 fd2ef698a..a89ef7d24 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 2d0040200..471908e57 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 bc4e741b1..918645638 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 de3c775e3..78d3d9ee5 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 dff57eb17..0fed7eaef 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 8f1a64231..d1a036ad0 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 ba50c6a2f..fb6cc26ac 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 e009d1da1..291b38540 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 e35df7703..d8c6a470c 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 ebd193f49..f19daab63 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 9f7fda4e0..5101fc663 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 fcca640f9..75ee2b8f5 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 7c8be5e31..bfadd1983 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 4af8a1beb..57513427a 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 c38a198f7..d0e079bac 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 e811fe6d4..bc3c28fe1 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 b5cf534c8..f1114c8e6 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 000000000..8fbd7e5f8 --- /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 000000000..e539b3b18 --- /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 000000000..d377b4836 --- /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 000000000..c50f17102 --- /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 000000000..886a58c0a --- /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 000000000..5be61479c --- /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 000000000..354bddb43 --- /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 959c24b5e..0ea7ce140 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 fb623f17a..000000000 --- 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 f9b555fc9..f9ab416b5 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