diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 0000000..cc491ce --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,52 @@ +## Pull request + +Please ensure this PR targets the **`dev` branch** and follows the project conventions. +CI already runs linting, formatting, and build checks automatically. + +--- + +### Before submitting + +- [ ] This PR targets the `dev` branch (not `main`) +- [ ] Commit messages follow the semantic-release format +- [ ] No debug logs or sensitive data included + +--- + +### Summary + +Short description of what this PR changes or adds. + +--- + +### Type of change + +- [ ] Feature +- [ ] Fix +- [ ] Refactor +- [ ] Internal / CI +- [ ] Documentation + +--- + +### Scope + +- [ ] Android +- [ ] iOS +- [ ] Core +- [ ] Example App +- [ ] Docs + +--- + +### Related + +List any related issues, pull requests, or discussions. +Use the format: +`Fixes #123`, `Refs #456`, `Close #789` + +--- + +### Additional notes + +_(Optional – anything relevant for the reviewer)_ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 804ea2e..8b50e6c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,14 @@ name: Release Package + on: push: branches: - main + - dev + +concurrency: + group: release-${{ github.ref_name }} + cancel-in-progress: true permissions: contents: read @@ -15,12 +21,12 @@ jobs: contents: write issues: write pull-requests: write + steps: - name: Checkout uses: actions/checkout@v5.0.0 with: - ref: main - fetch-depth: 0 + fetch-depth: 0 # wichtig für semantic-release, damit alle Tags vorhanden sind - name: Setup uses: ./.github/actions/setup diff --git a/.gitignore b/.gitignore index 3276e88..a137d70 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,8 @@ nitrogen/ example/ios/Secrets.xcconfig example/android/secrets.properties +# Kotlin +android/.kotlin/ +android/.kotlinc/ + tsconfig.tsbuildinfo diff --git a/CHANGELOG.md b/CHANGELOG.md index 7816992..ee477fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,37 +1,165 @@ -## [1.0.2](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.0.1...v1.0.2) (2025-10-02) +## [1.1.0-dev.5](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.1.0-dev.4...v1.1.0-dev.5) (2025-10-08) ### 🐛 Bug Fixes * build script ([d1f11f2](https://github.com/pinpong/react-native-google-maps-plus/commit/d1f11f237900f929689b72dfb41054dac0790a37)) -* build script ([98e194e](https://github.com/pinpong/react-native-google-maps-plus/commit/98e194e61d08af96ce75e156a6f5e3a5378c1b4c)) -* name conflict ([faf8d5e](https://github.com/pinpong/react-native-google-maps-plus/commit/faf8d5e7a0f79bfceb8454510e8e5ad3771fdbd2)) -* name conflict ([7217c11](https://github.com/pinpong/react-native-google-maps-plus/commit/7217c113bc2e5742bbc4b119eec7672c0b240cba)) -* react type ([36e22d5](https://github.com/pinpong/react-native-google-maps-plus/commit/36e22d59f0746ad9759799465eefed8f66a19049)) +* release ([#18](https://github.com/pinpong/react-native-google-maps-plus/issues/18)) ([b271ccc](https://github.com/pinpong/react-native-google-maps-plus/commit/b271ccc69f9cb3e48c865801bdd104fd6065b557)) -## [1.0.1](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.0.0...v1.0.1) (2025-10-02) +# react-native-google-maps-plus -### 🐛 Bug Fixes +[![npm version](https://img.shields.io/npm/v/react-native-google-maps-plus.svg?logo=npm&color=cb0000)](https://www.npmjs.com/package/react-native-google-maps-plus) +[![Dev Release](https://img.shields.io/npm/v/react-native-google-maps-plus/dev.svg?label=dev%20release&color=orange&logo=githubactions)](https://www.npmjs.com/package/react-native-google-maps-plus) +[![Release](https://github.com/pinpong/react-native-google-maps-plus/actions/workflows/release.yml/badge.svg)](https://github.com/pinpong/react-native-google-maps-plus/actions/workflows/release.yml) +[![Issues](https://img.shields.io/github/issues/pinpong/react-native-google-maps-plus?logo=github)](https://github.com/pinpong/react-native-google-maps-plus/issues) +[![License](https://img.shields.io/github/license/pinpong/react-native-google-maps-plus?logo=open-source-initiative&logoColor=green)](./LICENSE) +[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?logo=prettier&logoColor=white)](https://prettier.io/) +[![TypeScript](https://img.shields.io/badge/%3C/%3E-TypeScript-blue.svg?logo=typescript)](https://www.typescriptlang.org/) +[![Lint](https://img.shields.io/badge/lint-eslint-green.svg?logo=eslint&logoColor=white)](https://eslint.org/) +[![React Native](https://img.shields.io/badge/react--native-%3E%3D0.81.0-61dafb.svg?logo=react)](https://reactnative.dev/) +[![Platform: Android](https://img.shields.io/badge/platform-android-green.svg?logo=android&logoColor=white)](https://developer.android.com/) +[![Platform: iOS](https://img.shields.io/badge/platform-iOS-lightgrey.svg?logo=apple&logoColor=black)](https://developer.apple.com/ios/) -* release ([afbb9cd](https://github.com/pinpong/react-native-google-maps-plus/commit/afbb9cdf0261c35fcd4c6423096fbecaa482f704)) -* release ([#18](https://github.com/pinpong/react-native-google-maps-plus/issues/18)) ([b271ccc](https://github.com/pinpong/react-native-google-maps-plus/commit/b271ccc69f9cb3e48c865801bdd104fd6065b557)) +React-native wrapper for android & IOS google maps sdk -### 🛠️ Other changes +## Installation -* format ([e67d939](https://github.com/pinpong/react-native-google-maps-plus/commit/e67d939e23a8db82432334c767f780ebe2320d6c)) +`react-native-nitro-modules` is required as this library relies on [Nitro Modules](https://nitro.margelo.com/). -## 1.0.0 (2025-10-02) +```sh +yarn add react-native-google-maps-plus react-native-nitro-modules +``` -### 🐛 Bug Fixes +### Dependencies + +This package builds on native libraries for SVG rendering and Google Maps integration: + +- **iOS**: [SVGKit](https://github.com/SVGKit/SVGKit) +- **Android**: [AndroidSVG](https://bigbadaboom.github.io/androidsvg/) +- **iOS Maps SDK**: [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios-sdk) +- **Android Maps SDK**: [Google Maps SDK for Android](https://developers.google.com/maps/documentation/android-sdk) +- **Maps Utility Libraries**: [Google Maps Utils for iOS](https://developers.google.com/maps/documentation/ios-sdk/utility) and [Google Maps Utils for Android](https://developers.google.com/maps/documentation/android-sdk/utility) + +These are automatically linked when you install the package, but you may need to clean/rebuild your native projects after first install. + +## Setup API Key + +You will need a valid **Google Maps API Key** from the [Google Cloud Console](https://console.cloud.google.com/). + +### Android + +It's recommend to use [Secrets Gradle Plugin](https://developers.google.com/maps/documentation/android-sdk/secrets-gradle-plugin) to securely manage your Google Maps API Key. + +--- + +### iOS + +See the official [Google Maps iOS SDK configuration guide](https://developers.google.com/maps/documentation/ios-sdk/config#get-key) for more details. + +1. Create a `Secrets.xcconfig` file inside the **ios/** folder: + + ```properties + MAPS_API_KEY=YOUR_IOS_MAPS_API_KEY + ``` + + Include it in your project configuration file: + + ```xcconfig + #include? "Secrets.xcconfig" + ``` + +2. Reference the API key in your **Info.plist**: + + ```xml + MAPS_API_KEY + $(MAPS_API_KEY) + ``` + +3. Provide the key programmatically in **AppDelegate.swift**: + + ```swift + import GoogleMaps + + @UIApplicationMain + class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + if let apiKey = Bundle.main.object(forInfoDictionaryKey: "MAPS_API_KEY") as? String { + GMSServices.provideAPIKey(apiKey) + } + return true + } + } + ``` + +--- + +## Usage + +Checkout the example app in the [example](./example) folder. + +# Troubleshooting + +## Android + +- **API key not found** + Make sure `secrets.properties` exists under `android/` and contains your `MAPS_API_KEY`. + Run `./gradlew clean` and rebuild. + +## iOS + +- **`GMSServices must be configured before use`** + Ensure your key is in `Info.plist` and/or provided via `GMSServices.provideAPIKey(...)` in `AppDelegate.swift`. + +- **Build fails with `Node.h` import error from SVGKit** + SVGKit uses a header `Node.h` which can conflict with iOS system headers. + You can patch it automatically in your **Podfile** inside the `post_install` hook: + + ```ruby + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + ) + # Force iOS 16+ to avoid deployment target warnings + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0' + end + end + + # Patch SVGKit includes to avoid Node.h conflicts + require 'fileutils' + svgkit_path = File.join(installer.sandbox.pod_dir('SVGKit'), 'Source') + Dir.glob(File.join(svgkit_path, '**', '*.{h,m}')).each do |file| + FileUtils.chmod("u+w", file) + text = File.read(file) + new_contents = text.gsub('#import "Node.h"', '#import "SVGKit/Node.h"') + File.open(file, 'w') { |f| f.write(new_contents) } + end + end + ``` + + After applying this, run: + + ```sh + cd ios && pod install --repo-update + ``` + +- **Maps not rendering** + - Check that your API key has **Maps SDK for Android/iOS** enabled in Google Cloud Console. + - Make sure the key is not restricted to wrong bundle IDs or SHA1 fingerprints. + +## Contributing -* set npm publish to true ([ed7544b](https://github.com/pinpong/react-native-google-maps-plus/commit/ed7544b5c0b39cec418a83842e215253ac7b6eef)) +- [Development workflow](CONTRIBUTING.md#development-workflow) +- [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request) +- [Code of conduct](CODE_OF_CONDUCT.md) -### 📚 Documentation +## License -* update README.md ([60936c9](https://github.com/pinpong/react-native-google-maps-plus/commit/60936c9351f95e590b779883d161aad1272f4a1b)) -* update README.md ([00d3f65](https://github.com/pinpong/react-native-google-maps-plus/commit/00d3f656679415a8105fff2ae52fd0bd3106e472)) -* update README.md ([7354d38](https://github.com/pinpong/react-native-google-maps-plus/commit/7354d3822298b75ad28024f5488cc25e70891b9c)) -* update README.md ([bb2bf47](https://github.com/pinpong/react-native-google-maps-plus/commit/bb2bf47d7b273e1dd02a44425713ebe7c9bfb612)) +MIT -### 🛠️ Other changes +--- -* initial commit ([d240a87](https://github.com/pinpong/react-native-google-maps-plus/commit/d240a870fa08e5a01ef8b3e981f7e78c7e113fef)) +Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) diff --git a/README.md b/README.md index eec732e..5fd0c0c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # react-native-google-maps-plus -[![npm version](https://img.shields.io/npm/v/react-native-google-maps-plus.svg)](https://www.npmjs.com/package/react-native-google-maps-plus) +[![npm version](https://img.shields.io/npm/v/react-native-google-maps-plus.svg?logo=npm&color=cb0000)](https://www.npmjs.com/package/react-native-google-maps-plus) +[![Dev Release](https://img.shields.io/npm/v/react-native-google-maps-plus/dev.svg?label=dev%20release&color=orange&logo=githubactions)](https://www.npmjs.com/package/react-native-google-maps-plus) [![Release](https://github.com/pinpong/react-native-google-maps-plus/actions/workflows/release.yml/badge.svg)](https://github.com/pinpong/react-native-google-maps-plus/actions/workflows/release.yml) -[![Issues](https://img.shields.io/github/issues/pinpong/react-native-google-maps-plus)](https://github.com/pinpong/react-native-google-maps-plus/issues) -[![License](https://img.shields.io/github/license/pinpong/react-native-google-maps-plus)](./LICENSE) -[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/) -[![TypeScript](https://img.shields.io/badge/%3C/%3E-TypeScript-blue.svg)](https://www.typescriptlang.org/) -[![Lint](https://img.shields.io/badge/lint-eslint-green.svg)](https://eslint.org/) -[![React Native](https://img.shields.io/badge/react--native-%3E%3D0.81.0-61dafb.svg)](https://reactnative.dev/) -[![Platform: Android](https://img.shields.io/badge/platform-android-green.svg)](https://developer.android.com/) -[![Platform: iOS](https://img.shields.io/badge/platform-iOS-lightgrey.svg)](https://developer.apple.com/ios/) +[![Issues](https://img.shields.io/github/issues/pinpong/react-native-google-maps-plus?logo=github)](https://github.com/pinpong/react-native-google-maps-plus/issues) +[![License](https://img.shields.io/github/license/pinpong/react-native-google-maps-plus?logo=open-source-initiative&logoColor=green)](./LICENSE) +[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?logo=prettier&logoColor=white)](https://prettier.io/) +[![TypeScript](https://img.shields.io/badge/%3C/%3E-TypeScript-blue.svg?logo=typescript)](https://www.typescriptlang.org/) +[![Lint](https://img.shields.io/badge/lint-eslint-green.svg?logo=eslint&logoColor=white)](https://eslint.org/) +[![React Native](https://img.shields.io/badge/react--native-%3E%3D0.81.0-61dafb.svg?logo=react)](https://reactnative.dev/) +[![Platform: Android](https://img.shields.io/badge/platform-android-green.svg?logo=android&logoColor=white)](https://developer.android.com/) +[![Platform: iOS](https://img.shields.io/badge/platform-iOS-lightgrey.svg?logo=apple&logoColor=black)](https://developer.apple.com/ios/) React-native wrapper for android & IOS google maps sdk @@ -21,13 +22,15 @@ React-native wrapper for android & IOS google maps sdk yarn add react-native-google-maps-plus react-native-nitro-modules ``` -Dependencies +# Dependencies -This package builds on native SVG rendering libraries: +This package builds on native libraries for SVG rendering and Google Maps integration: -iOS: [SVGKit](https://github.com/SVGKit/SVGKit) - -Android: [AndroidSVG](https://bigbadaboom.github.io/androidsvg/) +- **iOS**: [SVGKit](https://github.com/SVGKit/SVGKit) +- **Android**: [AndroidSVG](https://bigbadaboom.github.io/androidsvg/) +- **iOS Maps SDK**: [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios-sdk) +- **Android Maps SDK**: [Google Maps SDK for Android](https://developers.google.com/maps/documentation/android-sdk) +- **Maps Utility Libraries**: [Google Maps Utils for iOS](https://developers.google.com/maps/documentation/ios-sdk/utility) and [Google Maps Utils for Android](https://developers.google.com/maps/documentation/android-sdk/utility) These are automatically linked when you install the package, but you may need to clean/rebuild your native projects after first install. diff --git a/RNGoogleMapsPlus.podspec b/RNGoogleMapsPlus.podspec index 4aeb18d..7f279db 100644 --- a/RNGoogleMapsPlus.podspec +++ b/RNGoogleMapsPlus.podspec @@ -21,10 +21,13 @@ Pod::Spec.new do |s| "cpp/**/*.{hpp,cpp}", ] + s.resource_bundles = {'RNGoogleMapsPlusPrivacy' => ['Resources/PrivacyInfo.xcprivacy']} + s.dependency 'React-jsi' s.dependency 'React-callinvoker' - s.dependency 'GoogleMaps', '10.3.0' + s.dependency 'GoogleMaps', '10.4.0' + s.dependency 'Google-Maps-iOS-Utils', '6.1.3' s.dependency 'SVGKit', '3.0.0' load 'nitrogen/generated/ios/RNGoogleMapsPlus+autolinking.rb' diff --git a/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt b/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt index 3d227a4..5979c6a 100644 --- a/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt +++ b/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt @@ -2,6 +2,7 @@ package com.rngooglemapsplus import android.annotation.SuppressLint import android.location.Location +import android.widget.FrameLayout import com.facebook.react.bridge.LifecycleEventListener import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.uimanager.PixelUtil.dpToPx @@ -9,9 +10,11 @@ import com.facebook.react.uimanager.ThemedReactContext import com.google.android.gms.common.ConnectionResult import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.GoogleMapOptions import com.google.android.gms.maps.MapView -import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.model.CameraPosition +import com.google.android.gms.maps.model.Circle +import com.google.android.gms.maps.model.CircleOptions import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.LatLngBounds import com.google.android.gms.maps.model.MapColorScheme @@ -22,59 +25,58 @@ import com.google.android.gms.maps.model.Polygon import com.google.android.gms.maps.model.PolygonOptions import com.google.android.gms.maps.model.Polyline import com.google.android.gms.maps.model.PolylineOptions +import com.google.android.gms.maps.model.TileOverlay +import com.google.android.gms.maps.model.TileOverlayOptions +import com.rngooglemapsplus.extensions.toGooglePriority +import com.rngooglemapsplus.extensions.toLocationErrorCode class GoogleMapsViewImpl( val reactContext: ThemedReactContext, val locationHandler: LocationHandler, val playServiceHandler: PlayServicesHandler, - val markerOptions: com.rngooglemapsplus.MarkerOptions, -) : MapView(reactContext), + val markerBuilder: MapMarkerBuilder, +) : FrameLayout(reactContext), GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraMoveListener, GoogleMap.OnCameraIdleListener, GoogleMap.OnMapClickListener, - OnMapReadyCallback, GoogleMap.OnMarkerClickListener, + GoogleMap.OnPolylineClickListener, + GoogleMap.OnPolygonClickListener, + GoogleMap.OnCircleClickListener, LifecycleEventListener { + private var initialized = false + private var mapReady = false private var googleMap: GoogleMap? = null + private var mapView: MapView? = null - private var pendingBuildingEnabled: Boolean = false - private var pendingTrafficEnabled: Boolean = false - private var pendingCustomMapStyle: MapStyleOptions? = null - private var pendingInitialCamera: CameraPosition = - CameraPosition - .builder() - .target( - LatLng( - 0.0, - 0.0, - ), - ).zoom(0f) - .bearing(0f) - .tilt(0f) - .build() - private var pendingUserInterfaceStyle: Int = MapColorScheme.FOLLOW_SYSTEM - private var pendingMinZoomLevel: Double = 0.0 - private var pendingMaxZoomLevel: Double = 21.0 - private var pendingMapPadding: RNMapPadding = RNMapPadding(0.0, 0.0, 0.0, 0.0) - private val pendingPolygons = mutableListOf>() - private val pendingPolylines = mutableListOf>() private val pendingMarkers = mutableListOf>() - private var cameraMoveReason = -1 + private val pendingPolylines = mutableListOf>() + private val pendingPolygons = mutableListOf>() + private val pendingCircles = mutableListOf>() + private val pendingHeatmaps = mutableListOf>() - private val polygonsById = mutableMapOf() - private val polylinesById = mutableMapOf() private val markersById = mutableMapOf() + private val polylinesById = mutableMapOf() + private val polygonsById = mutableMapOf() + private val circlesById = mutableMapOf() + private val heatmapsById = mutableMapOf() + private var cameraMoveReason = -1 private var lastSubmittedLocation: Location? = null private var lastSubmittedCameraPosition: CameraPosition? = null init { reactContext.addLifecycleEventListener(this) - getMap() } - private fun getMap() { + fun initMapView( + mapId: String?, + liteMode: Boolean?, + cameraPosition: CameraPosition?, + ) { + if (initialized) return + initialized = true val result = playServiceHandler.playServicesAvailability() when (result) { @@ -103,8 +105,38 @@ class GoogleMapsViewImpl( onMapError?.invoke(RNMapErrorCode.UNKNOWN) } - onCreate(null) - getMapAsync(this@GoogleMapsViewImpl) + mapView = + MapView( + reactContext, + GoogleMapOptions().apply { + mapId?.let { mapId(it) } + liteMode?.let { liteMode(it) } + cameraPosition?.let { + camera(it) + } + }, + ) + + super.addView(mapView) + + mapView?.onCreate(null) + mapView?.getMapAsync { map -> + googleMap = map + googleMap?.setOnMapLoadedCallback { + googleMap?.setOnCameraMoveStartedListener(this@GoogleMapsViewImpl) + googleMap?.setOnCameraMoveListener(this@GoogleMapsViewImpl) + googleMap?.setOnCameraIdleListener(this@GoogleMapsViewImpl) + googleMap?.setOnMarkerClickListener(this@GoogleMapsViewImpl) + googleMap?.setOnPolylineClickListener(this@GoogleMapsViewImpl) + googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl) + googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl) + googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl) + } + initLocationCallbacks() + applyPending() + } + mapReady = true + onMapReady?.invoke(true) } override fun onCameraMoveStarted(reason: Int) { @@ -195,22 +227,6 @@ class GoogleMapsViewImpl( ) } - @SuppressLint("PotentialBehaviorOverride") - override fun onMapReady(map: GoogleMap) { - googleMap = map - googleMap?.setOnMapLoadedCallback { - googleMap?.setOnCameraMoveStartedListener(this) - googleMap?.setOnCameraMoveListener(this) - googleMap?.setOnCameraIdleListener(this) - googleMap?.setOnMarkerClickListener(this) - googleMap?.setOnMapClickListener(this) - } - initLocationCallbacks() - applyPending() - - onMapReady?.invoke(true) - } - fun initLocationCallbacks() { locationHandler.onUpdate = { location -> // / only the coordinated are relevant right now @@ -235,27 +251,62 @@ class GoogleMapsViewImpl( fun applyPending() { onUi { - googleMap?.setPadding( - pendingMapPadding.left.dpToPx().toInt(), - pendingMapPadding.top.dpToPx().toInt(), - pendingMapPadding.right.dpToPx().toInt(), - pendingMapPadding.bottom.dpToPx().toInt(), - ) - - pendingInitialCamera.let { - googleMap?.moveCamera( - CameraUpdateFactory.newCameraPosition( - it, - ), + mapPadding?.let { + googleMap?.setPadding( + it.left.dpToPx().toInt(), + it.top.dpToPx().toInt(), + it.right.dpToPx().toInt(), + it.bottom.dpToPx().toInt(), ) } - googleMap?.isBuildingsEnabled = pendingBuildingEnabled - googleMap?.isTrafficEnabled = pendingTrafficEnabled - googleMap?.setMapStyle(pendingCustomMapStyle) - googleMap?.mapColorScheme = pendingUserInterfaceStyle - googleMap?.setMinZoomPreference(pendingMinZoomLevel.toFloat()) - googleMap?.setMaxZoomPreference(pendingMaxZoomLevel.toFloat()) + uiSettings?.let { v -> + googleMap?.uiSettings?.apply { + v.allGesturesEnabled?.let { setAllGesturesEnabled(it) } + v.compassEnabled?.let { isCompassEnabled = it } + v.indoorLevelPickerEnabled?.let { isIndoorLevelPickerEnabled = it } + v.mapToolbarEnabled?.let { isMapToolbarEnabled = it } + v.myLocationButtonEnabled?.let { + googleMap?.setLocationSource(locationHandler) + isMyLocationButtonEnabled = it + } + v.rotateEnabled?.let { isRotateGesturesEnabled = it } + v.scrollEnabled?.let { isScrollGesturesEnabled = it } + v.scrollDuringRotateOrZoomEnabled?.let { + isScrollGesturesEnabledDuringRotateOrZoom = it + } + v.tiltEnabled?.let { isTiltGesturesEnabled = it } + v.zoomControlsEnabled?.let { isZoomControlsEnabled = it } + v.zoomGesturesEnabled?.let { isZoomGesturesEnabled = it } + } + } + + buildingEnabled?.let { + googleMap?.isBuildingsEnabled = it + } + trafficEnabled?.let { + googleMap?.isTrafficEnabled = it + } + indoorEnabled?.let { + googleMap?.isIndoorEnabled = it + } + googleMap?.setMapStyle(customMapStyle) + mapType?.let { + googleMap?.mapType = it + } + userInterfaceStyle?.let { + googleMap?.mapColorScheme = it + } + mapZoomConfig?.let { + googleMap?.setMinZoomPreference(it.min?.toFloat() ?: 2.0f) + googleMap?.setMaxZoomPreference(it.max?.toFloat() ?: 21.0f) + } + } + + locationConfig?.let { + locationHandler.priority = it.android?.priority?.toGooglePriority() + locationHandler.interval = it.android?.interval?.toLong() + locationHandler.minUpdateInterval = it.android?.minUpdateInterval?.toLong() } if (pendingMarkers.isNotEmpty()) { @@ -278,94 +329,156 @@ class GoogleMapsViewImpl( } pendingPolygons.clear() } + + if (pendingCircles.isNotEmpty()) { + pendingCircles.forEach { (id, opts) -> + internalAddCircle(id, opts) + } + pendingCircles.clear() + } + + if (pendingHeatmaps.isNotEmpty()) { + pendingHeatmaps.forEach { (id, opts) -> + internalAddHeatmap(id, opts) + } + pendingHeatmaps.clear() + } } - var buildingEnabled: Boolean - get() = googleMap?.isBuildingsEnabled ?: pendingBuildingEnabled + var uiSettings: RNMapUiSettings? = null set(value) { - pendingBuildingEnabled = value + field = value onUi { - googleMap?.isBuildingsEnabled = value + googleMap?.uiSettings?.apply { + setAllGesturesEnabled(value?.allGesturesEnabled ?: true) + isCompassEnabled = value?.compassEnabled ?: false + isIndoorLevelPickerEnabled = value?.indoorLevelPickerEnabled ?: false + isMapToolbarEnabled = value?.mapToolbarEnabled ?: false + + val myLocationEnabled = value?.myLocationButtonEnabled ?: false + googleMap?.setLocationSource(if (myLocationEnabled) locationHandler else null) + isMyLocationButtonEnabled = myLocationEnabled + + isRotateGesturesEnabled = value?.rotateEnabled ?: true + isScrollGesturesEnabled = value?.scrollEnabled ?: true + isScrollGesturesEnabledDuringRotateOrZoom = + value?.scrollDuringRotateOrZoomEnabled ?: true + isTiltGesturesEnabled = value?.tiltEnabled ?: true + isZoomControlsEnabled = value?.zoomControlsEnabled ?: false + isZoomGesturesEnabled = value?.zoomGesturesEnabled ?: false + } } } - var trafficEnabled: Boolean - get() = googleMap?.isTrafficEnabled ?: pendingTrafficEnabled + @SuppressLint("MissingPermission") + var myLocationEnabled: Boolean? = null set(value) { - pendingTrafficEnabled = value + field = value onUi { - googleMap?.isTrafficEnabled = value + try { + googleMap?.isMyLocationEnabled = value ?: false + } catch (se: SecurityException) { + onLocationError?.invoke(RNLocationErrorCode.PERMISSION_DENIED) + } catch (ex: Exception) { + val error = ex.toLocationErrorCode(context) + onLocationError?.invoke(error) + } } } - var customMapStyle: MapStyleOptions? - get() = pendingCustomMapStyle + var buildingEnabled: Boolean? = null set(value) { - pendingCustomMapStyle = value + field = value onUi { - googleMap?.setMapStyle(value) + googleMap?.isBuildingsEnabled = value ?: false + } + } + + var trafficEnabled: Boolean? = null + set(value) { + field = value + onUi { + googleMap?.isTrafficEnabled = value ?: false } } - var initialCamera: CameraPosition - get() = pendingInitialCamera + var indoorEnabled: Boolean? = null set(value) { - pendingInitialCamera = value + field = value + onUi { + googleMap?.isIndoorEnabled = value ?: false + } } - var userInterfaceStyle: Int - get() = pendingUserInterfaceStyle + var customMapStyle: MapStyleOptions? = null set(value) { - pendingUserInterfaceStyle = value + field = value onUi { - googleMap?.mapColorScheme = value + googleMap?.setMapStyle(value) } } - var minZoomLevel: Double - get() = pendingMinZoomLevel + var userInterfaceStyle: Int? = null set(value) { - pendingMinZoomLevel = value + field = value onUi { - googleMap?.setMinZoomPreference(value.toFloat()) + googleMap?.mapColorScheme = value ?: MapColorScheme.FOLLOW_SYSTEM } } - var maxZoomLevel: Double - get() = pendingMaxZoomLevel + var mapZoomConfig: RNMapZoomConfig? = null set(value) { - pendingMaxZoomLevel = value + field = value onUi { - googleMap?.setMaxZoomPreference(value.toFloat()) + googleMap?.setMinZoomPreference(value?.min?.toFloat() ?: 2.0f) + googleMap?.setMaxZoomPreference(value?.max?.toFloat() ?: 21.0f) } } - var mapPadding: RNMapPadding - get() = pendingMapPadding + var mapPadding: RNMapPadding? = null set(value) { - pendingMapPadding = value + field = value onUi { googleMap?.setPadding( - value.left.dpToPx().toInt(), - value.top.dpToPx().toInt(), - value.right.dpToPx().toInt(), - value.bottom.dpToPx().toInt(), + value?.left?.dpToPx()?.toInt() ?: 0, + value?.top?.dpToPx()?.toInt() ?: 0, + value?.right?.dpToPx()?.toInt() ?: 0, + value?.bottom?.dpToPx()?.toInt() ?: 0, ) } } + var mapType: Int? = null + set(value) { + field = value + onUi { + googleMap?.mapType = value ?: 1 + } + } + + var locationConfig: RNLocationConfig? = null + set(value) { + field = value + locationHandler.priority = value?.android?.priority?.toGooglePriority() + locationHandler.interval = value?.android?.interval?.toLong() + locationHandler.minUpdateInterval = value?.android?.minUpdateInterval?.toLong() + } + var onMapError: ((RNMapErrorCode) -> Unit)? = null var onMapReady: ((Boolean) -> Unit)? = null var onLocationUpdate: ((RNLocation) -> Unit)? = null var onLocationError: ((RNLocationErrorCode) -> Unit)? = null var onMapPress: ((RNLatLng) -> Unit)? = null var onMarkerPress: ((String) -> Unit)? = null + var onPolylinePress: ((String) -> Unit)? = null + var onPolygonPress: ((String) -> Unit)? = null + var onCirclePress: ((String) -> Unit)? = null var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null fun setCamera( - camera: RNCamera, + cameraPosition: CameraPosition, animated: Boolean, durationMS: Int, ) { @@ -374,33 +487,8 @@ class GoogleMapsViewImpl( if (current == null) { return@onUi } - val camPosBuilder = - CameraPosition.Builder( - current, - ) - - camera.center?.let { - camPosBuilder.target( - LatLng( - it.latitude, - it.longitude, - ), - ) - } - - camera.zoom?.let { - camPosBuilder.zoom(it.toFloat()) - } - camera.bearing?.let { - camPosBuilder.bearing(it.toFloat()) - } - camera.tilt?.let { - camPosBuilder.tilt(it.toFloat()) - } - val camPos = camPosBuilder.build() - - val update = CameraUpdateFactory.newCameraPosition(camPos) + val update = CameraUpdateFactory.newCameraPosition(cameraPosition) if (animated) { googleMap?.animateCamera(update, durationMS, null) @@ -429,8 +517,8 @@ class GoogleMapsViewImpl( val latSpan = bounds.northeast.latitude - bounds.southwest.latitude val lngSpan = bounds.northeast.longitude - bounds.southwest.longitude - val latPerPixel = latSpan / height - val lngPerPixel = lngSpan / width + val latPerPixel = latSpan / (mapView?.height ?: 0) + val lngPerPixel = lngSpan / (mapView?.width ?: 0) builder.include( LatLng( @@ -459,8 +547,10 @@ class GoogleMapsViewImpl( val paddedBounds = builder.build() - val adjustedWidth = (width - padding.left.dpToPx() - padding.right.dpToPx()).toInt() - val adjustedHeight = (height - padding.top.dpToPx() - padding.bottom.dpToPx()).toInt() + val adjustedWidth = + ((mapView?.width ?: 0) - padding.left.dpToPx() - padding.right.dpToPx()).toInt() + val adjustedHeight = + ((mapView?.height ?: 0) - padding.top.dpToPx() - padding.bottom.dpToPx()).toInt() val update = CameraUpdateFactory.newLatLngBounds( @@ -639,22 +729,129 @@ class GoogleMapsViewImpl( pendingPolygons.clear() } - fun clearAll() { + fun addCircle( + id: String, + opts: CircleOptions, + ) { + if (googleMap == null) { + pendingCircles.add(id to opts) + return + } + + onUi { + circlesById.remove(id)?.remove() + } + internalAddCircle(id, opts) + } + + private fun internalAddCircle( + id: String, + opts: CircleOptions, + ) { onUi { - markerOptions.cancelAllJobs() + val circle = + googleMap?.addCircle(opts).also { + it?.tag = id + } + if (circle != null) { + circlesById[id] = circle + } + } + } + + fun updateCircle( + id: String, + block: (Circle) -> Unit, + ) { + val circle = circlesById[id] ?: return + onUi { + block(circle) + } + } + + fun removeCircle(id: String) { + onUi { + circlesById.remove(id)?.remove() + } + } + + fun clearCircles() { + onUi { + circlesById.values.forEach { it.remove() } + } + circlesById.clear() + pendingCircles.clear() + } + + fun addHeatmap( + id: String, + opts: TileOverlayOptions, + ) { + if (googleMap == null) { + pendingHeatmaps.add(id to opts) + return + } + + onUi { + heatmapsById.remove(id)?.remove() + } + internalAddHeatmap(id, opts) + } + + private fun internalAddHeatmap( + id: String, + opts: TileOverlayOptions, + ) { + onUi { + val heatmap = + googleMap?.addTileOverlay(opts) + if (heatmap != null) { + heatmapsById[id] = heatmap + } + } + } + + fun removeHeatmap(id: String) { + onUi { + heatmapsById.remove(id)?.remove() + } + } + + fun clearHeatmaps() { + onUi { + heatmapsById.values.forEach { it.remove() } + } + circlesById.clear() + pendingHeatmaps.clear() + } + + fun destroyInternal() { + onUi { + markerBuilder.cancelAllJobs() clearMarkers() clearPolylines() clearPolygons() + clearCircles() + clearHeatmaps() locationHandler.stop() googleMap?.apply { setOnCameraMoveStartedListener(null) setOnCameraMoveListener(null) setOnCameraIdleListener(null) setOnMarkerClickListener(null) + setOnPolylineClickListener(null) + setOnPolygonClickListener(null) + setOnCircleClickListener(null) setOnMapClickListener(null) } - this@GoogleMapsViewImpl.onDestroy() googleMap = null + mapView?.apply { + onPause() + onStop() + onDestroy() + removeAllViews() + } + super.removeAllViews() reactContext.removeLifecycleEventListener(this) } } @@ -684,19 +881,19 @@ class GoogleMapsViewImpl( override fun onHostResume() { onUi { locationHandler.start() - this@GoogleMapsViewImpl.onResume() + mapView?.onResume() } } override fun onHostPause() { onUi { locationHandler.stop() - this@GoogleMapsViewImpl.onPause() + mapView?.onPause() } } override fun onHostDestroy() { - clearAll() + destroyInternal() } override fun onMarkerClick(marker: Marker): Boolean { @@ -704,6 +901,18 @@ class GoogleMapsViewImpl( return true } + override fun onPolylineClick(polyline: Polyline) { + onPolylinePress?.invoke(polyline.tag?.toString() ?: "unknown") + } + + override fun onPolygonClick(polygon: Polygon) { + onPolygonPress?.invoke(polygon.tag?.toString() ?: "unknown") + } + + override fun onCircleClick(circle: Circle) { + onCirclePress?.invoke(circle.tag?.toString() ?: "unknown") + } + override fun onMapClick(coordinates: LatLng) { onMapPress?.invoke( RNLatLng(coordinates.latitude, coordinates.longitude), diff --git a/android/src/main/java/com/rngooglemapsplus/LocationHandler.kt b/android/src/main/java/com/rngooglemapsplus/LocationHandler.kt index 22c9101..42313db 100644 --- a/android/src/main/java/com/rngooglemapsplus/LocationHandler.kt +++ b/android/src/main/java/com/rngooglemapsplus/LocationHandler.kt @@ -10,8 +10,6 @@ import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.UiThreadUtil import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability -import com.google.android.gms.common.api.ApiException -import com.google.android.gms.common.api.CommonStatusCodes import com.google.android.gms.common.api.ResolvableApiException import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationCallback @@ -19,22 +17,43 @@ import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationResult import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationSettingsRequest -import com.google.android.gms.location.LocationSettingsStatusCodes import com.google.android.gms.location.Priority +import com.google.android.gms.maps.LocationSource import com.google.android.gms.tasks.OnSuccessListener +import com.rngooglemapsplus.extensions.toLocationErrorCode private const val REQ_LOCATION_SETTINGS = 2001 +private const val PRIORITY_DEFAULT = Priority.PRIORITY_BALANCED_POWER_ACCURACY +private const val INTERVAL_DEFAULT = 600000L +private const val MIN_UPDATE_INTERVAL = 3600000L class LocationHandler( val context: ReactContext, -) { +) : LocationSource { private val fusedLocationClientProviderClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context) + private var listener: LocationSource.OnLocationChangedListener? = null private var locationRequest: LocationRequest? = null private var locationCallback: LocationCallback? = null - private var priority = Priority.PRIORITY_HIGH_ACCURACY - private var interval: Long = 5000 - private var minUpdateInterval: Long = 5000 + + var priority: Int? = PRIORITY_DEFAULT + set(value) { + field = value ?: PRIORITY_DEFAULT + start() + } + + var interval: Long? = INTERVAL_DEFAULT + set(value) { + field = value ?: INTERVAL_DEFAULT + buildLocationRequest() + } + + var minUpdateInterval: Long? = MIN_UPDATE_INTERVAL + set(value) { + field = value ?: MIN_UPDATE_INTERVAL + buildLocationRequest() + } + var onUpdate: ((Location) -> Unit)? = null var onError: ((RNLocationErrorCode) -> Unit)? = null @@ -90,6 +109,10 @@ class LocationHandler( @Suppress("deprecation") private fun buildLocationRequest() { + val priority = priority ?: Priority.PRIORITY_BALANCED_POWER_ACCURACY + val interval = interval ?: INTERVAL_DEFAULT + val minUpdateInterval = minUpdateInterval ?: MIN_UPDATE_INTERVAL + locationRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { LocationRequest @@ -106,21 +129,6 @@ class LocationHandler( restartLocationUpdates() } - fun setPriority(priority: Int) { - this.priority = priority - buildLocationRequest() - } - - fun setInterval(interval: Int) { - this.interval = interval.toLong() - buildLocationRequest() - } - - fun setFastestInterval(fastestInterval: Int) { - this.minUpdateInterval = fastestInterval.toLong() - buildLocationRequest() - } - private fun restartLocationUpdates() { stop() // 4) Google Play Services checken – früh zurückmelden @@ -146,7 +154,7 @@ class LocationHandler( } }, ).addOnFailureListener { e -> - val error = mapThrowableToCode(e) + val error = e.toLocationErrorCode(context) onError?.invoke(error) } locationCallback = @@ -154,6 +162,7 @@ class LocationHandler( override fun onLocationResult(locationResult: LocationResult) { val location = locationResult.lastLocation if (location != null) { + listener?.onLocationChanged(location) onUpdate?.invoke(location) } else { onError?.invoke(RNLocationErrorCode.POSITION_UNAVAILABLE) @@ -166,40 +175,31 @@ class LocationHandler( locationCallback!!, Looper.getMainLooper(), ).addOnFailureListener { e -> - val error = mapThrowableToCode(e) + val error = e.toLocationErrorCode(context) onError?.invoke(error) } } catch (se: SecurityException) { onError?.invoke(RNLocationErrorCode.PERMISSION_DENIED) } catch (ex: Exception) { - val error = mapThrowableToCode(ex) + val error = ex.toLocationErrorCode(context) onError?.invoke(error) } } - private fun mapThrowableToCode(t: Throwable): RNLocationErrorCode { - if (t is SecurityException) return RNLocationErrorCode.PERMISSION_DENIED - if (t.message?.contains("GoogleApi", ignoreCase = true) == true) { - val gms = GoogleApiAvailability.getInstance() - val status = gms.isGooglePlayServicesAvailable(context) - if (status != ConnectionResult.SUCCESS) return RNLocationErrorCode.PLAY_SERVICE_NOT_AVAILABLE - } - if (t is ApiException) { - when (t.statusCode) { - CommonStatusCodes.NETWORK_ERROR -> return RNLocationErrorCode.POSITION_UNAVAILABLE - LocationSettingsStatusCodes.RESOLUTION_REQUIRED, - LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE, - -> return RNLocationErrorCode.SETTINGS_NOT_SATISFIED - } - return RNLocationErrorCode.INTERNAL_ERROR - } - return RNLocationErrorCode.INTERNAL_ERROR - } - fun stop() { + listener = null if (locationCallback != null) { fusedLocationClientProviderClient.removeLocationUpdates(locationCallback!!) locationCallback = null } } + + override fun activate(listener: LocationSource.OnLocationChangedListener) { + this.listener = listener + start() + } + + override fun deactivate() { + stop() + } } diff --git a/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt b/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt new file mode 100644 index 0000000..07a98f5 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt @@ -0,0 +1,33 @@ +package com.rngooglemapsplus + +import android.graphics.Color +import com.facebook.react.uimanager.PixelUtil.dpToPx +import com.google.android.gms.maps.model.Circle +import com.google.android.gms.maps.model.CircleOptions +import com.google.android.gms.maps.model.LatLng +import com.rngooglemapsplus.extensions.toColor + +class MapCircleBuilder { + fun build(circle: RNCircle): CircleOptions = + CircleOptions().apply { + center(LatLng(circle.center.latitude, circle.center.longitude)) + radius(circle.radius) + circle.strokeWidth?.let { strokeWidth(it.dpToPx()) } + circle.strokeColor?.let { strokeColor(it.toColor()) } + circle.fillColor?.let { fillColor(it.toColor()) } + circle.pressable?.let { clickable(it) } + circle.zIndex?.let { zIndex(it.toFloat()) } + } + + fun update( + circle: Circle, + next: RNCircle, + ) { + circle.center = LatLng(next.center.latitude, next.center.longitude) + circle.radius = next.radius + circle.strokeWidth = next.strokeWidth?.dpToPx() ?: 1f + circle.strokeColor = next.strokeColor?.toColor() ?: Color.BLACK + circle.fillColor = next.fillColor?.toColor() ?: Color.TRANSPARENT + circle.zIndex = next.zIndex?.toFloat() ?: 0f + } +} diff --git a/android/src/main/java/com/rngooglemapsplus/MapHeatmapBuilder.kt b/android/src/main/java/com/rngooglemapsplus/MapHeatmapBuilder.kt new file mode 100644 index 0000000..4346c14 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/MapHeatmapBuilder.kt @@ -0,0 +1,31 @@ +package com.rngooglemapsplus + +import com.facebook.react.uimanager.PixelUtil.dpToPx +import com.google.android.gms.maps.model.TileOverlayOptions +import com.google.maps.android.heatmaps.Gradient +import com.google.maps.android.heatmaps.HeatmapTileProvider +import com.rngooglemapsplus.extensions.toColor +import com.rngooglemapsplus.extensions.toWeightedLatLngs + +class MapHeatmapBuilder { + fun build(heatmap: RNHeatmap): TileOverlayOptions { + val provider = + HeatmapTileProvider + .Builder() + .apply { + weightedData(heatmap.weightedData.toWeightedLatLngs()) + heatmap.radius?.let { radius(it.dpToPx().toInt().coerceIn(10, 50)) } + heatmap.opacity?.let { opacity(it) } + heatmap.gradient?.let { + val colors = it.colors.map { c -> c.toColor() }.toIntArray() + val startPoints = it.startPoints.map { p -> p.toFloat() }.toFloatArray() + gradient(Gradient(colors, startPoints)) + } + }.build() + + return TileOverlayOptions().apply { + tileProvider(provider) + heatmap.zIndex?.let { zIndex(it.toFloat()) } + } + } +} diff --git a/android/src/main/java/com/rngooglemapsplus/MapMarker.kt b/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt similarity index 65% rename from android/src/main/java/com/rngooglemapsplus/MapMarker.kt rename to android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt index 53cc25b..abd0d86 100644 --- a/android/src/main/java/com/rngooglemapsplus/MapMarker.kt +++ b/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt @@ -9,7 +9,10 @@ import com.facebook.react.uimanager.PixelUtil.dpToPx import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.Marker import com.google.android.gms.maps.model.MarkerOptions +import com.rngooglemapsplus.extensions.markerStyleEquals +import com.rngooglemapsplus.extensions.styleHash import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -19,7 +22,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlin.coroutines.coroutineContext -class MarkerOptions( +class MapMarkerBuilder( private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default), ) { private val iconCache = @@ -34,21 +37,48 @@ class MarkerOptions( fun build( m: RNMarker, - icon: BitmapDescriptor, + icon: BitmapDescriptor?, ): MarkerOptions = - MarkerOptions() - .position(LatLng(m.coordinate.latitude, m.coordinate.longitude)) - .zIndex(m.zIndex.toFloat()) - .icon(icon) - .anchor((m.anchor?.x ?: 0.5).toFloat(), (m.anchor?.y ?: 0.5).toFloat()) + MarkerOptions().apply { + position(LatLng(m.coordinate.latitude, m.coordinate.longitude)) + anchor((m.anchor?.x ?: 0.5).toFloat(), (m.anchor?.y ?: 0.5).toFloat()) + icon(icon) + m.zIndex?.let { zIndex(it.toFloat()) } + } + + fun update( + marker: Marker, + prev: RNMarker, + next: RNMarker, + ) { + marker.position = + LatLng( + next.coordinate.latitude, + next.coordinate.longitude, + ) + marker.zIndex = next.zIndex?.toFloat() ?: 0f + + if (!prev.markerStyleEquals(next)) { + buildIconAsync(marker.id, next) { icon -> + marker.setIcon(icon) + } + } + marker.setAnchor( + (next.anchor?.x ?: 0.5).toFloat(), + (next.anchor?.y ?: 0.5).toFloat(), + ) + } fun buildIconAsync( id: String, m: RNMarker, - onReady: (BitmapDescriptor) -> Unit, + onReady: (BitmapDescriptor?) -> Unit, ) { jobsById[id]?.cancel() - + if (m.iconSvg == null) { + onReady(null) + return + } val key = m.styleHash() iconCache.get(key)?.let { cached -> onReady(cached) @@ -97,17 +127,28 @@ class MarkerOptions( private suspend fun renderBitmap(m: RNMarker): Bitmap? { var bmp: Bitmap? = null + if (m.iconSvg == null) { + return null + } try { coroutineContext.ensureActive() - val svg = SVG.getFromString(m.iconSvg) + val svg = SVG.getFromString(m.iconSvg.svgString) coroutineContext.ensureActive() - svg.setDocumentWidth(m.width.dpToPx()) - svg.setDocumentHeight(m.height.dpToPx()) + svg.setDocumentWidth(m.iconSvg.width.dpToPx()) + svg.setDocumentHeight(m.iconSvg.height.dpToPx()) coroutineContext.ensureActive() bmp = - createBitmap(m.width.dpToPx().toInt(), m.height.dpToPx().toInt(), Bitmap.Config.ARGB_8888) + createBitmap( + m.iconSvg.width + .dpToPx() + .toInt(), + m.iconSvg.height + .dpToPx() + .toInt(), + Bitmap.Config.ARGB_8888, + ) coroutineContext.ensureActive() val canvas = Canvas(bmp) @@ -124,22 +165,3 @@ class MarkerOptions( } } } - -fun RNMarker.markerEquals(b: RNMarker): Boolean = - id == b.id && - zIndex == b.zIndex && - coordinate == b.coordinate && - anchor == b.anchor && - markerStyleEquals(b) - -fun RNMarker.markerStyleEquals(b: RNMarker): Boolean = - width == b.width && - height == b.height && - iconSvg == b.iconSvg - -fun RNMarker.styleHash(): Int = - arrayOf( - width, - height, - iconSvg, - ).contentHashCode() diff --git a/android/src/main/java/com/rngooglemapsplus/MapPolygon.kt b/android/src/main/java/com/rngooglemapsplus/MapPolygon.kt deleted file mode 100644 index f2a152e..0000000 --- a/android/src/main/java/com/rngooglemapsplus/MapPolygon.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.rngooglemapsplus - -import com.facebook.react.uimanager.PixelUtil.dpToPx -import com.google.android.gms.maps.model.PolygonOptions - -class MapPolygonOptions { - fun buildPolygonOptions(poly: RNPolygon): PolygonOptions = - PolygonOptions().apply { - poly.coordinates.forEach { pt -> - add( - com.google.android.gms.maps.model - .LatLng(pt.latitude, pt.longitude), - ) - } - poly.fillColor?.let { fillColor(it.toColor()) } - poly.strokeColor?.let { strokeColor(it.toColor()) } - poly.strokeWidth?.let { strokeWidth(it.dpToPx()) } - zIndex(poly.zIndex.toFloat()) - } -} - -fun RNPolygon.polygonEquals(b: RNPolygon): Boolean { - if (zIndex != b.zIndex) return false - if (strokeWidth != b.strokeWidth) return false - if (fillColor != b.fillColor) return false - if (strokeColor != b.strokeColor) return false - val ac = coordinates - val bc = b.coordinates - if (ac.size != bc.size) return false - for (i in ac.indices) { - val p = ac[i] - val q = bc[i] - if (p.latitude != q.latitude || p.longitude != q.longitude) return false - } - return true -} diff --git a/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt b/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt new file mode 100644 index 0000000..f3173ae --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt @@ -0,0 +1,39 @@ +package com.rngooglemapsplus + +import android.graphics.Color +import com.facebook.react.uimanager.PixelUtil.dpToPx +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.Polygon +import com.google.android.gms.maps.model.PolygonOptions +import com.rngooglemapsplus.extensions.toColor + +class MapPolygonBuilder { + fun build(poly: RNPolygon): PolygonOptions = + PolygonOptions().apply { + poly.coordinates.forEach { pt -> + add( + com.google.android.gms.maps.model + .LatLng(pt.latitude, pt.longitude), + ) + } + poly.fillColor?.let { fillColor(it.toColor()) } + poly.strokeColor?.let { strokeColor(it.toColor()) } + poly.strokeWidth?.let { strokeWidth(it.dpToPx()) } + poly.pressable?.let { clickable(it) } + poly.zIndex?.let { zIndex(it.toFloat()) } + } + + fun update( + gmsPoly: Polygon, + next: RNPolygon, + ) { + gmsPoly.points = + next.coordinates.map { + LatLng(it.latitude, it.longitude) + } + gmsPoly.fillColor = next.fillColor?.toColor() ?: Color.TRANSPARENT + gmsPoly.strokeColor = next.strokeColor?.toColor() ?: Color.BLACK + gmsPoly.strokeWidth = next.strokeWidth?.dpToPx() ?: 1f + gmsPoly.zIndex = next.zIndex?.toFloat() ?: 0f + } +} diff --git a/android/src/main/java/com/rngooglemapsplus/MapPolyline.kt b/android/src/main/java/com/rngooglemapsplus/MapPolyline.kt deleted file mode 100644 index 86e01b9..0000000 --- a/android/src/main/java/com/rngooglemapsplus/MapPolyline.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.rngooglemapsplus - -import com.facebook.react.uimanager.PixelUtil.dpToPx -import com.google.android.gms.maps.model.ButtCap -import com.google.android.gms.maps.model.Cap -import com.google.android.gms.maps.model.JointType -import com.google.android.gms.maps.model.PolylineOptions -import com.google.android.gms.maps.model.RoundCap -import com.google.android.gms.maps.model.SquareCap - -class MapPolylineOptions { - fun buildPolylineOptions(pl: RNPolyline): PolylineOptions = - PolylineOptions().apply { - pl.coordinates.forEach { pt -> - add( - com.google.android.gms.maps.model - .LatLng(pt.latitude, pt.longitude), - ) - } - pl.width?.let { width(it.dpToPx()) } - pl.lineCap?.let { startCap(mapLineCap(it)) } - pl.lineCap?.let { endCap(mapLineCap(it)) } - pl.lineJoin?.let { jointType(mapLineJoin(it)) } - pl.color?.let { color(it.toColor()) } - zIndex(pl.zIndex.toFloat()) - } - - fun mapLineCap(type: RNLineCapType?): Cap = - when (type) { - RNLineCapType.ROUND -> RoundCap() - RNLineCapType.SQUARE -> SquareCap() - else -> ButtCap() - } - - fun mapLineJoin(type: RNLineJoinType?): Int = - when (type) { - RNLineJoinType.ROUND -> JointType.ROUND - RNLineJoinType.BEVEL -> JointType.BEVEL - RNLineJoinType.MITER -> JointType.DEFAULT - null -> JointType.DEFAULT - } -} - -fun RNPolyline.polylineEquals(b: RNPolyline): Boolean { - if (zIndex != b.zIndex) return false - if ((width ?: 0.0) != (b.width ?: 0.0)) return false - if (lineCap != b.lineCap) return false - if (lineJoin != b.lineJoin) return false - if (color != b.color) return false - val ac = coordinates - val bc = b.coordinates - if (ac.size != bc.size) return false - for (i in ac.indices) { - val p = ac[i] - val q = bc[i] - if (p.latitude != q.latitude || p.longitude != q.longitude) return false - } - return true -} diff --git a/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt b/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt new file mode 100644 index 0000000..f5571a9 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt @@ -0,0 +1,61 @@ +package com.rngooglemapsplus + +import android.graphics.Color +import com.facebook.react.uimanager.PixelUtil.dpToPx +import com.google.android.gms.maps.model.ButtCap +import com.google.android.gms.maps.model.Cap +import com.google.android.gms.maps.model.JointType +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.Polyline +import com.google.android.gms.maps.model.PolylineOptions +import com.google.android.gms.maps.model.RoundCap +import com.google.android.gms.maps.model.SquareCap +import com.rngooglemapsplus.extensions.toColor + +class MapPolylineBuilder { + fun build(pl: RNPolyline): PolylineOptions = + PolylineOptions().apply { + pl.coordinates.forEach { pt -> + add(LatLng(pt.latitude, pt.longitude)) + } + pl.width?.let { width(it.dpToPx()) } + pl.lineCap?.let { + startCap(mapLineCap(it)) + endCap(mapLineCap(it)) + } + pl.lineJoin?.let { jointType(mapLineJoin(it)) } + pl.color?.let { color(it.toColor()) } + pl.pressable?.let { clickable(it) } + pl.zIndex?.let { zIndex(it.toFloat()) } + } + + fun update( + polyline: Polyline, + next: RNPolyline, + ) { + polyline.points = next.coordinates.map { LatLng(it.latitude, it.longitude) } + + polyline.width = next.width?.dpToPx() ?: 1f + val cap = mapLineCap(next.lineCap ?: RNLineCapType.BUTT) + polyline.startCap = cap + polyline.endCap = cap + polyline.jointType = mapLineJoin(next.lineJoin ?: RNLineJoinType.MITER) + polyline.color = next.color?.toColor() ?: Color.BLACK + polyline.zIndex = next.zIndex?.toFloat() ?: 0f + } + + private fun mapLineCap(type: RNLineCapType?): Cap = + when (type) { + RNLineCapType.ROUND -> RoundCap() + RNLineCapType.SQUARE -> SquareCap() + else -> ButtCap() + } + + private fun mapLineJoin(type: RNLineJoinType?): Int = + when (type) { + RNLineJoinType.ROUND -> JointType.ROUND + RNLineJoinType.BEVEL -> JointType.BEVEL + RNLineJoinType.MITER -> JointType.DEFAULT + null -> JointType.DEFAULT + } +} diff --git a/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt b/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt index 32de0f3..40ed111 100644 --- a/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt +++ b/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt @@ -2,133 +2,157 @@ package com.rngooglemapsplus import com.facebook.proguard.annotations.DoNotStrip import com.facebook.react.bridge.UiThreadUtil -import com.facebook.react.uimanager.PixelUtil.dpToPx import com.facebook.react.uimanager.ThemedReactContext -import com.google.android.gms.maps.model.CameraPosition -import com.google.android.gms.maps.model.MapColorScheme import com.google.android.gms.maps.model.MapStyleOptions import com.margelo.nitro.core.Promise +import com.rngooglemapsplus.extensions.circleEquals +import com.rngooglemapsplus.extensions.markerEquals +import com.rngooglemapsplus.extensions.polygonEquals +import com.rngooglemapsplus.extensions.polylineEquals +import com.rngooglemapsplus.extensions.toCameraPosition +import com.rngooglemapsplus.extensions.toMapColorScheme @DoNotStrip class RNGoogleMapsPlusView( val context: ThemedReactContext, ) : HybridRNGoogleMapsPlusViewSpec() { - private var currentCustomMapStyle: String = "" + private var currentCustomMapStyle: String? = null private var permissionHandler = PermissionHandler(context) private var locationHandler = LocationHandler(context) private var playServiceHandler = PlayServicesHandler(context) - private val markerOptions = MarkerOptions() + + private val markerBuilder = MapMarkerBuilder() + private val polylineBuilder = MapPolylineBuilder() + private val polygonBuilder = MapPolygonBuilder() + private val circleBuilder = MapCircleBuilder() + private val heatmapBuilder = MapHeatmapBuilder() override val view = - GoogleMapsViewImpl(context, locationHandler, playServiceHandler, markerOptions) + GoogleMapsViewImpl(context, locationHandler, playServiceHandler, markerBuilder) + + override var initialProps: RNInitialProps? = null + set(value) { + if (field == value) return + field = value + view.initMapView( + value?.mapId, + value?.liteMode, + value?.camera?.toCameraPosition(), + ) + } - private val polylineOptions = MapPolylineOptions() - private val polygonOptions = MapPolygonOptions() + override var uiSettings: RNMapUiSettings? = null + set(value) { + if (field == value) return + field = value + view.uiSettings = value + } + + override var myLocationEnabled: Boolean? = null + set(value) { + if (field == value) return + field = value + view.myLocationEnabled = value + } - override var buildingEnabled: Boolean - get() = view.buildingEnabled + override var buildingEnabled: Boolean? = null set(value) { + if (field == value) return + field = value view.buildingEnabled = value } - override var trafficEnabled: Boolean - get() = view.trafficEnabled + override var trafficEnabled: Boolean? = null set(value) { + if (field == value) return + field = value view.trafficEnabled = value } - override var customMapStyle: String - get() = currentCustomMapStyle + override var indoorEnabled: Boolean? = null set(value) { - currentCustomMapStyle = value - view.customMapStyle = MapStyleOptions(value) + if (field == value) return + field = value + view.indoorEnabled = value } - override var initialCamera: RNCamera - get() = mapCameraPotionToCamera(view.initialCamera) + override var customMapStyle: String? = null set(value) { - view.initialCamera = mapCameraToCameraPosition(value) + if (field == value) return + field = value + currentCustomMapStyle = value + value?.let { + view.customMapStyle = MapStyleOptions(it) + } } - override var userInterfaceStyle: RNUserInterfaceStyle - get() = mapColorSchemeToUserInterfaceStyle(view.userInterfaceStyle) + override var userInterfaceStyle: RNUserInterfaceStyle? = null set(value) { - view.userInterfaceStyle = userInterfaceStyleToMapColorScheme(value) + if (field == value) return + field = value + view.userInterfaceStyle = value.toMapColorScheme() } - override var minZoomLevel: Double - get() = view.minZoomLevel + override var mapZoomConfig: RNMapZoomConfig? = null set(value) { - view.minZoomLevel = value + if (field == value) return + field = value + view.mapZoomConfig = value } - override var maxZoomLevel: Double - get() = view.maxZoomLevel + override var mapPadding: RNMapPadding? = null set(value) { - view.maxZoomLevel = value + if (field == value) return + field = value + view.mapPadding = value } - override var mapPadding: RNMapPadding - get() = view.mapPadding + override var mapType: RNMapType? = null set(value) { - view.mapPadding = value + if (field == value) return + field = value + value?.let { + view.mapType = it.value + } } - override var markers: Array = emptyArray() + override var markers: Array? = null set(value) { - val prevById = field.associateBy { it.id } - val nextById = value.associateBy { it.id } + if (field.contentEquals(value)) return + val prevById = field?.associateBy { it.id } ?: emptyMap() + val nextById = value?.associateBy { it.id } ?: emptyMap() + field = value (prevById.keys - nextById.keys).forEach { id -> - markerOptions.cancelIconJob(id) + markerBuilder.cancelIconJob(id) view.removeMarker(id) } nextById.forEach { (id, next) -> val prev = prevById[id] if (prev == null) { - markerOptions.buildIconAsync(id, next) { icon -> + markerBuilder.buildIconAsync(id, next) { icon -> view.addMarker( id, - markerOptions.build(next, icon), + markerBuilder.build(next, icon), ) } } else if (!prev.markerEquals(next)) { - view.updateMarker(id) { m -> + view.updateMarker(id) { marker -> onUi { - if (prev.coordinate != next.coordinate) { - m.position = - com.google.android.gms.maps.model.LatLng( - next.coordinate.latitude, - next.coordinate.longitude, - ) - } - if (prev.zIndex != next.zIndex) { - m.zIndex = next.zIndex.toFloat() - } - if (!prev.markerStyleEquals(next)) { - markerOptions.buildIconAsync(id, next) { icon -> - m.setIcon(icon) - } - } - if (prev.anchor != next.anchor) { - m.setAnchor( - (next.anchor?.x ?: 0.5).toFloat(), - (next.anchor?.y ?: 0.5).toFloat(), - ) - } + markerBuilder.update(marker, next, prev) } } } } - field = value } - override var polylines: Array = emptyArray() + override var polylines: Array? = null set(value) { - val prevById = field.associateBy { it.id } - val nextById = value.associateBy { it.id } - + if (field.contentEquals(value)) return + val prevById = field?.associateBy { it.id } ?: emptyMap() + val nextById = value?.associateBy { it.id } ?: emptyMap() + field = value (prevById.keys - nextById.keys).forEach { id -> view.removePolyline(id) } @@ -136,35 +160,23 @@ class RNGoogleMapsPlusView( nextById.forEach { (id, next) -> val prev = prevById[id] if (prev == null) { - view.addPolyline(id, polylineOptions.buildPolylineOptions(next)) + view.addPolyline(id, polylineBuilder.build(next)) } else if (!prev.polylineEquals(next)) { - view.updatePolyline(id) { gms -> + view.updatePolyline(id) { polyline -> onUi { - gms.points = - next.coordinates.map { - com.google.android.gms.maps.model - .LatLng(it.latitude, it.longitude) - } - next.width?.let { gms.width = it.dpToPx() } - next.lineCap?.let { - val cap = polylineOptions.mapLineCap(it) - gms.startCap = cap - gms.endCap = cap - } - next.lineJoin?.let { gms.jointType = polylineOptions.mapLineJoin(it) } - next.color?.let { gms.color = it.toColor() } - gms.zIndex = next.zIndex.toFloat() + polylineBuilder.update(polyline, next) } } } } - field = value } - override var polygons: Array = emptyArray() + override var polygons: Array? = null set(value) { - val prevById = field.associateBy { it.id } - val nextById = value.associateBy { it.id } + if (field.contentEquals(value)) return + val prevById = field?.associateBy { it.id } ?: emptyMap() + val nextById = value?.associateBy { it.id } ?: emptyMap() + field = value (prevById.keys - nextById.keys).forEach { id -> view.removePolygon(id) @@ -173,75 +185,118 @@ class RNGoogleMapsPlusView( nextById.forEach { (id, next) -> val prev = prevById[id] if (prev == null) { - view.addPolygon(id, polygonOptions.buildPolygonOptions(next)) + view.addPolygon(id, polygonBuilder.build(next)) } else if (!prev.polygonEquals(next)) { - view.updatePolygon(id) { gmsPoly -> + view.updatePolygon(id) { polygon -> + onUi { polygonBuilder.update(polygon, next) } + } + } + } + } + + override var circles: Array? = null + set(value) { + if (field.contentEquals(value)) return + val prevById = field?.associateBy { it.id } ?: emptyMap() + val nextById = value?.associateBy { it.id } ?: emptyMap() + field = value + + (prevById.keys - nextById.keys).forEach { id -> + view.removeCircle(id) + } + + nextById.forEach { (id, next) -> + val prev = prevById[id] + if (prev == null) { + view.addCircle(id, circleBuilder.build(next)) + } else if (!prev.circleEquals(next)) { + view.updateCircle(id) { circle -> onUi { - gmsPoly.points = - next.coordinates.map { - com.google.android.gms.maps.model - .LatLng(it.latitude, it.longitude) - } - next.fillColor?.let { gmsPoly.fillColor = it.toColor() } - next.strokeColor?.let { gmsPoly.strokeColor = it.toColor() } - next.strokeWidth?.let { gmsPoly.strokeWidth = it.dpToPx() } - gmsPoly.zIndex = next.zIndex.toFloat() + circleBuilder.update(circle, next) } } } } + } + + override var heatmaps: Array? = null + set(value) { + if (field.contentEquals(value)) return + val prevById = field?.associateBy { it.id } ?: emptyMap() + val nextById = value?.associateBy { it.id } ?: emptyMap() field = value + (prevById.keys - nextById.keys).forEach { id -> + view.removeHeatmap(id) + } + + nextById.forEach { (id, next) -> + view.addHeatmap(id, heatmapBuilder.build(next)) + } + } + + override var locationConfig: RNLocationConfig? = null + set(value) { + if (field == value) return + field = value + view.locationConfig = value } - override var onMapError: ((RNMapErrorCode) -> Unit)? - get() = view.onMapError + override var onMapError: ((RNMapErrorCode) -> Unit)? = null set(cb) { view.onMapError = cb } - override var onMapReady: ((Boolean) -> Unit)? - get() = view.onMapReady + override var onMapReady: ((Boolean) -> Unit)? = null set(cb) { view.onMapReady = cb } - override var onLocationUpdate: ((RNLocation) -> Unit)? - get() = view.onLocationUpdate + + override var onLocationUpdate: ((RNLocation) -> Unit)? = null set(cb) { view.onLocationUpdate = cb } - override var onLocationError: ((RNLocationErrorCode) -> Unit)? - get() = view.onLocationError + override var onLocationError: ((RNLocationErrorCode) -> Unit)? = null set(cb) { view.onLocationError = cb } - override var onMapPress: ((RNLatLng) -> Unit)? - get() = view.onMapPress + override var onMapPress: ((RNLatLng) -> Unit)? = null set(cb) { view.onMapPress = cb } - override var onMarkerPress: ((String) -> Unit)? - get() = view.onMarkerPress + override var onMarkerPress: ((String) -> Unit)? = null set(cb) { view.onMarkerPress = cb } - override var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? - get() = view.onCameraChangeStart + override var onPolylinePress: ((String) -> Unit)? = null + set(cb) { + view.onPolylinePress = cb + } + + override var onPolygonPress: ((String) -> Unit)? = null + set(cb) { + view.onPolygonPress = cb + } + + override var onCirclePress: ((String) -> Unit)? = null + set(cb) { + view.onCirclePress = cb + } + + override var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null set(cb) { view.onCameraChangeStart = cb } - override var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? - get() = view.onCameraChange + override var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null set(cb) { view.onCameraChange = cb } - override var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? - get() = view.onCameraChangeComplete + override var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null set(cb) { view.onCameraChangeComplete = cb } @@ -251,7 +306,7 @@ class RNGoogleMapsPlusView( animated: Boolean?, durationMS: Double?, ) { - view.setCamera(camera, animated == true, durationMS?.toInt() ?: 3000) + view.setCamera(camera.toCameraPosition(), animated == true, durationMS?.toInt() ?: 3000) } override fun setCameraToCoordinates( @@ -279,53 +334,6 @@ class RNGoogleMapsPlusView( override fun requestLocationPermission(): Promise = permissionHandler.requestLocationPermission() override fun isGooglePlayServicesAvailable(): Boolean = playServiceHandler.isPlayServicesAvailable() - - fun userInterfaceStyleToMapColorScheme(value: RNUserInterfaceStyle): Int = - when (value) { - RNUserInterfaceStyle.LIGHT -> { - MapColorScheme.LIGHT - } - - RNUserInterfaceStyle.DARK -> { - MapColorScheme.DARK - } - - RNUserInterfaceStyle.DEFAULT -> { - MapColorScheme.FOLLOW_SYSTEM - } - } - - fun mapCameraToCameraPosition(camera: RNCamera): CameraPosition { - val builder = CameraPosition.builder() - camera.center?.let { - builder.target( - com.google.android.gms.maps.model.LatLng( - camera.center.latitude, - camera.center.longitude, - ), - ) - } - camera.zoom?.let { builder.zoom(it.toFloat()) } - camera.bearing?.let { builder.bearing(it.toFloat()) } - camera.tilt?.let { builder.tilt(it.toFloat()) } - - return builder.build() - } - - fun mapCameraPotionToCamera(cameraPosition: CameraPosition): RNCamera = - RNCamera( - center = RNLatLng(cameraPosition.target.latitude, cameraPosition.target.longitude), - zoom = cameraPosition.zoom.toDouble(), - bearing = cameraPosition.bearing.toDouble(), - tilt = cameraPosition.tilt.toDouble(), - ) - - fun mapColorSchemeToUserInterfaceStyle(value: Int): RNUserInterfaceStyle = - when (value) { - MapColorScheme.LIGHT -> RNUserInterfaceStyle.LIGHT - MapColorScheme.DARK -> RNUserInterfaceStyle.DARK - else -> RNUserInterfaceStyle.DEFAULT - } } private inline fun onUi(crossinline block: () -> Unit) { diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNCameraExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNCameraExtension.kt new file mode 100644 index 0000000..ed29245 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNCameraExtension.kt @@ -0,0 +1,19 @@ +package com.rngooglemapsplus.extensions + +import com.google.android.gms.maps.model.CameraPosition +import com.google.android.gms.maps.model.LatLng +import com.rngooglemapsplus.RNCamera + +fun RNCamera.toCameraPosition(): CameraPosition { + val builder = CameraPosition.builder() + + center?.let { + builder.target(LatLng(it.latitude, it.longitude)) + } + + zoom?.let { builder.zoom(it.toFloat()) } + bearing?.let { builder.bearing(it.toFloat()) } + tilt?.let { builder.tilt(it.toFloat()) } + + return builder.build() +} diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNHeatmapPointExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNHeatmapPointExtension.kt new file mode 100644 index 0000000..4ac8504 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNHeatmapPointExtension.kt @@ -0,0 +1,9 @@ +package com.rngooglemapsplus.extensions + +import com.google.android.gms.maps.model.LatLng +import com.google.maps.android.heatmaps.WeightedLatLng +import com.rngooglemapsplus.RNHeatmapPoint + +fun RNHeatmapPoint.toWeightedLatLng(): WeightedLatLng = WeightedLatLng(LatLng(latitude, longitude), weight) + +fun Array.toWeightedLatLngs(): Collection = map { it.toWeightedLatLng() } diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNLocationPriorityExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNLocationPriorityExtension.kt new file mode 100644 index 0000000..d794703 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNLocationPriorityExtension.kt @@ -0,0 +1,12 @@ +package com.rngooglemapsplus.extensions + +import com.google.android.gms.location.Priority +import com.rngooglemapsplus.RNAndroidLocationPriority + +fun RNAndroidLocationPriority.toGooglePriority(): Int = + when (this) { + RNAndroidLocationPriority.PRIORITY_HIGH_ACCURACY -> Priority.PRIORITY_HIGH_ACCURACY + RNAndroidLocationPriority.PRIORITY_BALANCED_POWER_ACCURACY -> Priority.PRIORITY_BALANCED_POWER_ACCURACY + RNAndroidLocationPriority.PRIORITY_LOW_POWER -> Priority.PRIORITY_LOW_POWER + RNAndroidLocationPriority.PRIORITY_PASSIVE -> Priority.PRIORITY_PASSIVE + } diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt new file mode 100644 index 0000000..8de84e4 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt @@ -0,0 +1,14 @@ +package com.rngooglemapsplus.extensions + +import com.rngooglemapsplus.RNCircle + +fun RNCircle.circleEquals(b: RNCircle): Boolean { + if (zIndex != b.zIndex) return false + if (pressable != b.pressable) return false + if (center != b.center) return false + if (radius != b.radius) return false + if (strokeWidth != b.strokeWidth) return false + if (strokeColor != b.strokeColor) return false + if (fillColor != b.fillColor) return false + return true +} diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt new file mode 100644 index 0000000..01fde5c --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt @@ -0,0 +1,17 @@ +package com.rngooglemapsplus.extensions + +import com.rngooglemapsplus.RNMarker + +fun RNMarker.markerEquals(b: RNMarker): Boolean = + id == b.id && + zIndex == b.zIndex && + coordinate == b.coordinate && + anchor == b.anchor && + markerStyleEquals(b) + +fun RNMarker.markerStyleEquals(b: RNMarker): Boolean = iconSvg == b.iconSvg + +fun RNMarker.styleHash(): Int = + arrayOf( + iconSvg, + ).contentHashCode() diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt new file mode 100644 index 0000000..cc037e8 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt @@ -0,0 +1,20 @@ +package com.rngooglemapsplus.extensions + +import com.rngooglemapsplus.RNPolygon + +fun RNPolygon.polygonEquals(b: RNPolygon): Boolean { + if (zIndex != b.zIndex) return false + if (pressable != b.pressable) return false + if (strokeWidth != b.strokeWidth) return false + if (fillColor != b.fillColor) return false + if (strokeColor != b.strokeColor) return false + val ac = coordinates + val bc = b.coordinates + if (ac.size != bc.size) return false + for (i in ac.indices) { + val p = ac[i] + val q = bc[i] + if (p.latitude != q.latitude || p.longitude != q.longitude) return false + } + return true +} diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt new file mode 100644 index 0000000..61b4ae7 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt @@ -0,0 +1,21 @@ +package com.rngooglemapsplus.extensions + +import com.rngooglemapsplus.RNPolyline + +fun RNPolyline.polylineEquals(b: RNPolyline): Boolean { + if (zIndex != b.zIndex) return false + if (pressable != b.pressable) return false + if ((width ?: 0.0) != (b.width ?: 0.0)) return false + if (lineCap != b.lineCap) return false + if (lineJoin != b.lineJoin) return false + if (color != b.color) return false + val ac = coordinates + val bc = b.coordinates + if (ac.size != bc.size) return false + for (i in ac.indices) { + val p = ac[i] + val q = bc[i] + if (p.latitude != q.latitude || p.longitude != q.longitude) return false + } + return true +} diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNUserInterfaceExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNUserInterfaceExtension.kt new file mode 100644 index 0000000..8fd4c5a --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNUserInterfaceExtension.kt @@ -0,0 +1,12 @@ +package com.rngooglemapsplus.extensions + +import com.google.android.gms.maps.model.MapColorScheme +import com.rngooglemapsplus.RNUserInterfaceStyle + +fun RNUserInterfaceStyle?.toMapColorScheme(): Int? = + when (this) { + RNUserInterfaceStyle.LIGHT -> MapColorScheme.LIGHT + RNUserInterfaceStyle.DARK -> MapColorScheme.DARK + RNUserInterfaceStyle.DEFAULT -> MapColorScheme.FOLLOW_SYSTEM + null -> null + } diff --git a/android/src/main/java/com/rngooglemapsplus/Color.kt b/android/src/main/java/com/rngooglemapsplus/extensions/StringExtension.kt similarity index 97% rename from android/src/main/java/com/rngooglemapsplus/Color.kt rename to android/src/main/java/com/rngooglemapsplus/extensions/StringExtension.kt index dff40bf..f7ae761 100644 --- a/android/src/main/java/com/rngooglemapsplus/Color.kt +++ b/android/src/main/java/com/rngooglemapsplus/extensions/StringExtension.kt @@ -1,4 +1,4 @@ -package com.rngooglemapsplus +package com.rngooglemapsplus.extensions import android.graphics.Color import androidx.core.graphics.toColorInt diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/ThrowableExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/ThrowableExtension.kt new file mode 100644 index 0000000..3c29cc4 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/ThrowableExtension.kt @@ -0,0 +1,38 @@ +package com.rngooglemapsplus.extensions + +import android.content.Context +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.GoogleApiAvailability +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.location.LocationSettingsStatusCodes +import com.rngooglemapsplus.RNLocationErrorCode + +fun Throwable.toLocationErrorCode(context: Context): RNLocationErrorCode { + return when (this) { + is SecurityException -> RNLocationErrorCode.PERMISSION_DENIED + + is ApiException -> + when (statusCode) { + CommonStatusCodes.NETWORK_ERROR -> + RNLocationErrorCode.POSITION_UNAVAILABLE + LocationSettingsStatusCodes.RESOLUTION_REQUIRED, + LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE, + -> + RNLocationErrorCode.SETTINGS_NOT_SATISFIED + else -> + RNLocationErrorCode.INTERNAL_ERROR + } + + else -> { + if (message?.contains("GoogleApi", ignoreCase = true) == true) { + val gms = GoogleApiAvailability.getInstance() + val status = gms.isGooglePlayServicesAvailable(context) + if (status != ConnectionResult.SUCCESS) { + return RNLocationErrorCode.PLAY_SERVICE_NOT_AVAILABLE + } + } + RNLocationErrorCode.INTERNAL_ERROR + } + } +} diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b8e29d4..7755d98 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -5,16 +5,18 @@ PODS: - CocoaLumberjack/Core (3.9.0) - DoubleConversion (1.1.6) - fast_float (8.0.0) - - FBLazyVector (0.82.0-rc.4) + - FBLazyVector (0.82.0) - fmt (11.0.2) - glog (0.3.5) - - GoogleMaps (10.3.0): - - GoogleMaps/Maps (= 10.3.0) - - GoogleMaps/Maps (10.3.0) - - hermes-engine (0.82.0-rc.4): - - hermes-engine/Pre-built (= 0.82.0-rc.4) - - hermes-engine/Pre-built (0.82.0-rc.4) - - NitroModules (0.29.6): + - Google-Maps-iOS-Utils (6.1.3): + - GoogleMaps (~> 10.0) + - GoogleMaps (10.4.0): + - GoogleMaps/Maps (= 10.4.0) + - GoogleMaps/Maps (10.4.0) + - hermes-engine (0.82.0): + - hermes-engine/Pre-built (= 0.82.0) + - hermes-engine/Pre-built (0.82.0) + - NitroModules (0.29.8): - boost - DoubleConversion - fast_float @@ -62,27 +64,27 @@ PODS: - fast_float (= 8.0.0) - fmt (= 11.0.2) - glog - - RCTDeprecation (0.82.0-rc.4) - - RCTRequired (0.82.0-rc.4) - - RCTTypeSafety (0.82.0-rc.4): - - FBLazyVector (= 0.82.0-rc.4) - - RCTRequired (= 0.82.0-rc.4) - - React-Core (= 0.82.0-rc.4) - - React (0.82.0-rc.4): - - React-Core (= 0.82.0-rc.4) - - React-Core/DevSupport (= 0.82.0-rc.4) - - React-Core/RCTWebSocket (= 0.82.0-rc.4) - - React-RCTActionSheet (= 0.82.0-rc.4) - - React-RCTAnimation (= 0.82.0-rc.4) - - React-RCTBlob (= 0.82.0-rc.4) - - React-RCTImage (= 0.82.0-rc.4) - - React-RCTLinking (= 0.82.0-rc.4) - - React-RCTNetwork (= 0.82.0-rc.4) - - React-RCTSettings (= 0.82.0-rc.4) - - React-RCTText (= 0.82.0-rc.4) - - React-RCTVibration (= 0.82.0-rc.4) - - React-callinvoker (0.82.0-rc.4) - - React-Core (0.82.0-rc.4): + - RCTDeprecation (0.82.0) + - RCTRequired (0.82.0) + - RCTTypeSafety (0.82.0): + - FBLazyVector (= 0.82.0) + - RCTRequired (= 0.82.0) + - React-Core (= 0.82.0) + - React (0.82.0): + - React-Core (= 0.82.0) + - React-Core/DevSupport (= 0.82.0) + - React-Core/RCTWebSocket (= 0.82.0) + - React-RCTActionSheet (= 0.82.0) + - React-RCTAnimation (= 0.82.0) + - React-RCTBlob (= 0.82.0) + - React-RCTImage (= 0.82.0) + - React-RCTLinking (= 0.82.0) + - React-RCTNetwork (= 0.82.0) + - React-RCTSettings (= 0.82.0) + - React-RCTText (= 0.82.0) + - React-RCTVibration (= 0.82.0) + - React-callinvoker (0.82.0) + - React-Core (0.82.0): - boost - DoubleConversion - fast_float @@ -92,7 +94,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - RCTDeprecation - - React-Core/Default (= 0.82.0-rc.4) + - React-Core/Default (= 0.82.0) - React-cxxreact - React-featureflags - React-hermes @@ -107,7 +109,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/CoreModulesHeaders (0.82.0-rc.4): + - React-Core/CoreModulesHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -132,7 +134,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/Default (0.82.0-rc.4): + - React-Core/Default (0.82.0): - boost - DoubleConversion - fast_float @@ -156,7 +158,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/DevSupport (0.82.0-rc.4): + - React-Core/DevSupport (0.82.0): - boost - DoubleConversion - fast_float @@ -166,8 +168,8 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - RCTDeprecation - - React-Core/Default (= 0.82.0-rc.4) - - React-Core/RCTWebSocket (= 0.82.0-rc.4) + - React-Core/Default (= 0.82.0) + - React-Core/RCTWebSocket (= 0.82.0) - React-cxxreact - React-featureflags - React-hermes @@ -182,7 +184,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTActionSheetHeaders (0.82.0-rc.4): + - React-Core/RCTActionSheetHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -207,7 +209,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTAnimationHeaders (0.82.0-rc.4): + - React-Core/RCTAnimationHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -232,7 +234,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTBlobHeaders (0.82.0-rc.4): + - React-Core/RCTBlobHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -257,7 +259,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTImageHeaders (0.82.0-rc.4): + - React-Core/RCTImageHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -282,7 +284,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTLinkingHeaders (0.82.0-rc.4): + - React-Core/RCTLinkingHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -307,7 +309,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTNetworkHeaders (0.82.0-rc.4): + - React-Core/RCTNetworkHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -332,7 +334,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTSettingsHeaders (0.82.0-rc.4): + - React-Core/RCTSettingsHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -357,7 +359,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTTextHeaders (0.82.0-rc.4): + - React-Core/RCTTextHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -382,7 +384,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTVibrationHeaders (0.82.0-rc.4): + - React-Core/RCTVibrationHeaders (0.82.0): - boost - DoubleConversion - fast_float @@ -407,7 +409,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-Core/RCTWebSocket (0.82.0-rc.4): + - React-Core/RCTWebSocket (0.82.0): - boost - DoubleConversion - fast_float @@ -417,7 +419,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - RCTDeprecation - - React-Core/Default (= 0.82.0-rc.4) + - React-Core/Default (= 0.82.0) - React-cxxreact - React-featureflags - React-hermes @@ -432,7 +434,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-CoreModules (0.82.0-rc.4): + - React-CoreModules (0.82.0): - boost - DoubleConversion - fast_float @@ -440,21 +442,21 @@ PODS: - glog - RCT-Folly - RCT-Folly/Fabric - - RCTTypeSafety (= 0.82.0-rc.4) - - React-Core/CoreModulesHeaders (= 0.82.0-rc.4) + - RCTTypeSafety (= 0.82.0) + - React-Core/CoreModulesHeaders (= 0.82.0) - React-debug - - React-jsi (= 0.82.0-rc.4) + - React-jsi (= 0.82.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-NativeModulesApple - React-RCTBlob - React-RCTFBReactNativeSpec - - React-RCTImage (= 0.82.0-rc.4) + - React-RCTImage (= 0.82.0) - React-runtimeexecutor - ReactCommon - SocketRocket - - React-cxxreact (0.82.0-rc.4): + - React-cxxreact (0.82.0): - boost - DoubleConversion - fast_float @@ -463,19 +465,19 @@ PODS: - hermes-engine - RCT-Folly - RCT-Folly/Fabric - - React-callinvoker (= 0.82.0-rc.4) - - React-debug (= 0.82.0-rc.4) - - React-jsi (= 0.82.0-rc.4) + - React-callinvoker (= 0.82.0) + - React-debug (= 0.82.0) + - React-jsi (= 0.82.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - - React-logger (= 0.82.0-rc.4) - - React-perflogger (= 0.82.0-rc.4) + - React-logger (= 0.82.0) + - React-perflogger (= 0.82.0) - React-runtimeexecutor - - React-timing (= 0.82.0-rc.4) + - React-timing (= 0.82.0) - SocketRocket - - React-debug (0.82.0-rc.4) - - React-defaultsnativemodule (0.82.0-rc.4): + - React-debug (0.82.0) + - React-defaultsnativemodule (0.82.0): - boost - DoubleConversion - fast_float @@ -493,7 +495,7 @@ PODS: - React-RCTFBReactNativeSpec - React-webperformancenativemodule - SocketRocket - - React-domnativemodule (0.82.0-rc.4): + - React-domnativemodule (0.82.0): - boost - DoubleConversion - fast_float @@ -513,7 +515,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-Fabric (0.82.0-rc.4): + - React-Fabric (0.82.0): - boost - DoubleConversion - fast_float @@ -527,23 +529,23 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 0.82.0-rc.4) - - React-Fabric/attributedstring (= 0.82.0-rc.4) - - React-Fabric/bridging (= 0.82.0-rc.4) - - React-Fabric/componentregistry (= 0.82.0-rc.4) - - React-Fabric/componentregistrynative (= 0.82.0-rc.4) - - React-Fabric/components (= 0.82.0-rc.4) - - React-Fabric/consistency (= 0.82.0-rc.4) - - React-Fabric/core (= 0.82.0-rc.4) - - React-Fabric/dom (= 0.82.0-rc.4) - - React-Fabric/imagemanager (= 0.82.0-rc.4) - - React-Fabric/leakchecker (= 0.82.0-rc.4) - - React-Fabric/mounting (= 0.82.0-rc.4) - - React-Fabric/observers (= 0.82.0-rc.4) - - React-Fabric/scheduler (= 0.82.0-rc.4) - - React-Fabric/telemetry (= 0.82.0-rc.4) - - React-Fabric/templateprocessor (= 0.82.0-rc.4) - - React-Fabric/uimanager (= 0.82.0-rc.4) + - React-Fabric/animations (= 0.82.0) + - React-Fabric/attributedstring (= 0.82.0) + - React-Fabric/bridging (= 0.82.0) + - React-Fabric/componentregistry (= 0.82.0) + - React-Fabric/componentregistrynative (= 0.82.0) + - React-Fabric/components (= 0.82.0) + - React-Fabric/consistency (= 0.82.0) + - React-Fabric/core (= 0.82.0) + - React-Fabric/dom (= 0.82.0) + - React-Fabric/imagemanager (= 0.82.0) + - React-Fabric/leakchecker (= 0.82.0) + - React-Fabric/mounting (= 0.82.0) + - React-Fabric/observers (= 0.82.0) + - React-Fabric/scheduler (= 0.82.0) + - React-Fabric/telemetry (= 0.82.0) + - React-Fabric/templateprocessor (= 0.82.0) + - React-Fabric/uimanager (= 0.82.0) - React-featureflags - React-graphics - React-jsi @@ -555,7 +557,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/animations (0.82.0-rc.4): + - React-Fabric/animations (0.82.0): - boost - DoubleConversion - fast_float @@ -580,7 +582,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/attributedstring (0.82.0-rc.4): + - React-Fabric/attributedstring (0.82.0): - boost - DoubleConversion - fast_float @@ -605,7 +607,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/bridging (0.82.0-rc.4): + - React-Fabric/bridging (0.82.0): - boost - DoubleConversion - fast_float @@ -630,7 +632,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/componentregistry (0.82.0-rc.4): + - React-Fabric/componentregistry (0.82.0): - boost - DoubleConversion - fast_float @@ -655,7 +657,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/componentregistrynative (0.82.0-rc.4): + - React-Fabric/componentregistrynative (0.82.0): - boost - DoubleConversion - fast_float @@ -680,7 +682,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/components (0.82.0-rc.4): + - React-Fabric/components (0.82.0): - boost - DoubleConversion - fast_float @@ -694,10 +696,10 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.82.0-rc.4) - - React-Fabric/components/root (= 0.82.0-rc.4) - - React-Fabric/components/scrollview (= 0.82.0-rc.4) - - React-Fabric/components/view (= 0.82.0-rc.4) + - React-Fabric/components/legacyviewmanagerinterop (= 0.82.0) + - React-Fabric/components/root (= 0.82.0) + - React-Fabric/components/scrollview (= 0.82.0) + - React-Fabric/components/view (= 0.82.0) - React-featureflags - React-graphics - React-jsi @@ -709,7 +711,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/components/legacyviewmanagerinterop (0.82.0-rc.4): + - React-Fabric/components/legacyviewmanagerinterop (0.82.0): - boost - DoubleConversion - fast_float @@ -734,7 +736,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/components/root (0.82.0-rc.4): + - React-Fabric/components/root (0.82.0): - boost - DoubleConversion - fast_float @@ -759,7 +761,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/components/scrollview (0.82.0-rc.4): + - React-Fabric/components/scrollview (0.82.0): - boost - DoubleConversion - fast_float @@ -784,7 +786,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/components/view (0.82.0-rc.4): + - React-Fabric/components/view (0.82.0): - boost - DoubleConversion - fast_float @@ -811,7 +813,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-Fabric/consistency (0.82.0-rc.4): + - React-Fabric/consistency (0.82.0): - boost - DoubleConversion - fast_float @@ -836,7 +838,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/core (0.82.0-rc.4): + - React-Fabric/core (0.82.0): - boost - DoubleConversion - fast_float @@ -861,7 +863,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/dom (0.82.0-rc.4): + - React-Fabric/dom (0.82.0): - boost - DoubleConversion - fast_float @@ -886,7 +888,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/imagemanager (0.82.0-rc.4): + - React-Fabric/imagemanager (0.82.0): - boost - DoubleConversion - fast_float @@ -911,7 +913,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/leakchecker (0.82.0-rc.4): + - React-Fabric/leakchecker (0.82.0): - boost - DoubleConversion - fast_float @@ -936,7 +938,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/mounting (0.82.0-rc.4): + - React-Fabric/mounting (0.82.0): - boost - DoubleConversion - fast_float @@ -961,7 +963,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/observers (0.82.0-rc.4): + - React-Fabric/observers (0.82.0): - boost - DoubleConversion - fast_float @@ -975,7 +977,7 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/observers/events (= 0.82.0-rc.4) + - React-Fabric/observers/events (= 0.82.0) - React-featureflags - React-graphics - React-jsi @@ -987,7 +989,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/observers/events (0.82.0-rc.4): + - React-Fabric/observers/events (0.82.0): - boost - DoubleConversion - fast_float @@ -1012,7 +1014,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/scheduler (0.82.0-rc.4): + - React-Fabric/scheduler (0.82.0): - boost - DoubleConversion - fast_float @@ -1040,7 +1042,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/telemetry (0.82.0-rc.4): + - React-Fabric/telemetry (0.82.0): - boost - DoubleConversion - fast_float @@ -1065,7 +1067,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/templateprocessor (0.82.0-rc.4): + - React-Fabric/templateprocessor (0.82.0): - boost - DoubleConversion - fast_float @@ -1090,7 +1092,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/uimanager (0.82.0-rc.4): + - React-Fabric/uimanager (0.82.0): - boost - DoubleConversion - fast_float @@ -1104,7 +1106,7 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/uimanager/consistency (= 0.82.0-rc.4) + - React-Fabric/uimanager/consistency (= 0.82.0) - React-featureflags - React-graphics - React-jsi @@ -1117,7 +1119,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-Fabric/uimanager/consistency (0.82.0-rc.4): + - React-Fabric/uimanager/consistency (0.82.0): - boost - DoubleConversion - fast_float @@ -1143,7 +1145,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket - - React-FabricComponents (0.82.0-rc.4): + - React-FabricComponents (0.82.0): - boost - DoubleConversion - fast_float @@ -1158,8 +1160,8 @@ PODS: - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components (= 0.82.0-rc.4) - - React-FabricComponents/textlayoutmanager (= 0.82.0-rc.4) + - React-FabricComponents/components (= 0.82.0) + - React-FabricComponents/textlayoutmanager (= 0.82.0) - React-featureflags - React-graphics - React-jsi @@ -1172,7 +1174,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components (0.82.0-rc.4): + - React-FabricComponents/components (0.82.0): - boost - DoubleConversion - fast_float @@ -1187,18 +1189,18 @@ PODS: - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.82.0-rc.4) - - React-FabricComponents/components/iostextinput (= 0.82.0-rc.4) - - React-FabricComponents/components/modal (= 0.82.0-rc.4) - - React-FabricComponents/components/rncore (= 0.82.0-rc.4) - - React-FabricComponents/components/safeareaview (= 0.82.0-rc.4) - - React-FabricComponents/components/scrollview (= 0.82.0-rc.4) - - React-FabricComponents/components/switch (= 0.82.0-rc.4) - - React-FabricComponents/components/text (= 0.82.0-rc.4) - - React-FabricComponents/components/textinput (= 0.82.0-rc.4) - - React-FabricComponents/components/unimplementedview (= 0.82.0-rc.4) - - React-FabricComponents/components/virtualview (= 0.82.0-rc.4) - - React-FabricComponents/components/virtualviewexperimental (= 0.82.0-rc.4) + - React-FabricComponents/components/inputaccessory (= 0.82.0) + - React-FabricComponents/components/iostextinput (= 0.82.0) + - React-FabricComponents/components/modal (= 0.82.0) + - React-FabricComponents/components/rncore (= 0.82.0) + - React-FabricComponents/components/safeareaview (= 0.82.0) + - React-FabricComponents/components/scrollview (= 0.82.0) + - React-FabricComponents/components/switch (= 0.82.0) + - React-FabricComponents/components/text (= 0.82.0) + - React-FabricComponents/components/textinput (= 0.82.0) + - React-FabricComponents/components/unimplementedview (= 0.82.0) + - React-FabricComponents/components/virtualview (= 0.82.0) + - React-FabricComponents/components/virtualviewexperimental (= 0.82.0) - React-featureflags - React-graphics - React-jsi @@ -1211,7 +1213,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/inputaccessory (0.82.0-rc.4): + - React-FabricComponents/components/inputaccessory (0.82.0): - boost - DoubleConversion - fast_float @@ -1238,7 +1240,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/iostextinput (0.82.0-rc.4): + - React-FabricComponents/components/iostextinput (0.82.0): - boost - DoubleConversion - fast_float @@ -1265,7 +1267,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/modal (0.82.0-rc.4): + - React-FabricComponents/components/modal (0.82.0): - boost - DoubleConversion - fast_float @@ -1292,7 +1294,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/rncore (0.82.0-rc.4): + - React-FabricComponents/components/rncore (0.82.0): - boost - DoubleConversion - fast_float @@ -1319,7 +1321,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/safeareaview (0.82.0-rc.4): + - React-FabricComponents/components/safeareaview (0.82.0): - boost - DoubleConversion - fast_float @@ -1346,7 +1348,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/scrollview (0.82.0-rc.4): + - React-FabricComponents/components/scrollview (0.82.0): - boost - DoubleConversion - fast_float @@ -1373,7 +1375,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/switch (0.82.0-rc.4): + - React-FabricComponents/components/switch (0.82.0): - boost - DoubleConversion - fast_float @@ -1400,7 +1402,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/text (0.82.0-rc.4): + - React-FabricComponents/components/text (0.82.0): - boost - DoubleConversion - fast_float @@ -1427,7 +1429,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/textinput (0.82.0-rc.4): + - React-FabricComponents/components/textinput (0.82.0): - boost - DoubleConversion - fast_float @@ -1454,7 +1456,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/unimplementedview (0.82.0-rc.4): + - React-FabricComponents/components/unimplementedview (0.82.0): - boost - DoubleConversion - fast_float @@ -1481,7 +1483,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/virtualview (0.82.0-rc.4): + - React-FabricComponents/components/virtualview (0.82.0): - boost - DoubleConversion - fast_float @@ -1508,7 +1510,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/components/virtualviewexperimental (0.82.0-rc.4): + - React-FabricComponents/components/virtualviewexperimental (0.82.0): - boost - DoubleConversion - fast_float @@ -1535,7 +1537,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricComponents/textlayoutmanager (0.82.0-rc.4): + - React-FabricComponents/textlayoutmanager (0.82.0): - boost - DoubleConversion - fast_float @@ -1562,7 +1564,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - React-FabricImage (0.82.0-rc.4): + - React-FabricImage (0.82.0): - boost - DoubleConversion - fast_float @@ -1571,21 +1573,21 @@ PODS: - hermes-engine - RCT-Folly - RCT-Folly/Fabric - - RCTRequired (= 0.82.0-rc.4) - - RCTTypeSafety (= 0.82.0-rc.4) + - RCTRequired (= 0.82.0) + - RCTTypeSafety (= 0.82.0) - React-Fabric - React-featureflags - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.82.0-rc.4) + - React-jsiexecutor (= 0.82.0) - React-logger - React-rendererdebug - React-utils - ReactCommon - SocketRocket - Yoga - - React-featureflags (0.82.0-rc.4): + - React-featureflags (0.82.0): - boost - DoubleConversion - fast_float @@ -1594,7 +1596,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - SocketRocket - - React-featureflagsnativemodule (0.82.0-rc.4): + - React-featureflagsnativemodule (0.82.0): - boost - DoubleConversion - fast_float @@ -1609,7 +1611,7 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - SocketRocket - - React-graphics (0.82.0-rc.4): + - React-graphics (0.82.0): - boost - DoubleConversion - fast_float @@ -1622,7 +1624,7 @@ PODS: - React-jsiexecutor - React-utils - SocketRocket - - React-hermes (0.82.0-rc.4): + - React-hermes (0.82.0): - boost - DoubleConversion - fast_float @@ -1631,17 +1633,17 @@ PODS: - hermes-engine - RCT-Folly - RCT-Folly/Fabric - - React-cxxreact (= 0.82.0-rc.4) + - React-cxxreact (= 0.82.0) - React-jsi - - React-jsiexecutor (= 0.82.0-rc.4) + - React-jsiexecutor (= 0.82.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-oscompat - - React-perflogger (= 0.82.0-rc.4) + - React-perflogger (= 0.82.0) - React-runtimeexecutor - SocketRocket - - React-idlecallbacksnativemodule (0.82.0-rc.4): + - React-idlecallbacksnativemodule (0.82.0): - boost - DoubleConversion - fast_float @@ -1657,7 +1659,7 @@ PODS: - React-runtimescheduler - ReactCommon/turbomodule/core - SocketRocket - - React-ImageManager (0.82.0-rc.4): + - React-ImageManager (0.82.0): - boost - DoubleConversion - fast_float @@ -1672,7 +1674,7 @@ PODS: - React-rendererdebug - React-utils - SocketRocket - - React-jserrorhandler (0.82.0-rc.4): + - React-jserrorhandler (0.82.0): - boost - DoubleConversion - fast_float @@ -1687,7 +1689,7 @@ PODS: - React-jsi - ReactCommon/turbomodule/bridging - SocketRocket - - React-jsi (0.82.0-rc.4): + - React-jsi (0.82.0): - boost - DoubleConversion - fast_float @@ -1697,7 +1699,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - SocketRocket - - React-jsiexecutor (0.82.0-rc.4): + - React-jsiexecutor (0.82.0): - boost - DoubleConversion - fast_float @@ -1715,7 +1717,7 @@ PODS: - React-perflogger - React-runtimeexecutor - SocketRocket - - React-jsinspector (0.82.0-rc.4): + - React-jsinspector (0.82.0): - boost - DoubleConversion - fast_float @@ -1730,10 +1732,10 @@ PODS: - React-jsinspectornetwork - React-jsinspectortracing - React-oscompat - - React-perflogger (= 0.82.0-rc.4) + - React-perflogger (= 0.82.0) - React-runtimeexecutor - SocketRocket - - React-jsinspectorcdp (0.82.0-rc.4): + - React-jsinspectorcdp (0.82.0): - boost - DoubleConversion - fast_float @@ -1742,7 +1744,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - SocketRocket - - React-jsinspectornetwork (0.82.0-rc.4): + - React-jsinspectornetwork (0.82.0): - boost - DoubleConversion - fast_float @@ -1755,7 +1757,7 @@ PODS: - React-performancetimeline - React-timing - SocketRocket - - React-jsinspectortracing (0.82.0-rc.4): + - React-jsinspectortracing (0.82.0): - boost - DoubleConversion - fast_float @@ -1766,7 +1768,7 @@ PODS: - React-oscompat - React-timing - SocketRocket - - React-jsitooling (0.82.0-rc.4): + - React-jsitooling (0.82.0): - boost - DoubleConversion - fast_float @@ -1774,17 +1776,17 @@ PODS: - glog - RCT-Folly - RCT-Folly/Fabric - - React-cxxreact (= 0.82.0-rc.4) + - React-cxxreact (= 0.82.0) - React-debug - - React-jsi (= 0.82.0-rc.4) + - React-jsi (= 0.82.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-runtimeexecutor - SocketRocket - - React-jsitracing (0.82.0-rc.4): + - React-jsitracing (0.82.0): - React-jsi - - React-logger (0.82.0-rc.4): + - React-logger (0.82.0): - boost - DoubleConversion - fast_float @@ -1793,7 +1795,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - SocketRocket - - React-Mapbuffer (0.82.0-rc.4): + - React-Mapbuffer (0.82.0): - boost - DoubleConversion - fast_float @@ -1803,7 +1805,7 @@ PODS: - RCT-Folly/Fabric - React-debug - SocketRocket - - React-microtasksnativemodule (0.82.0-rc.4): + - React-microtasksnativemodule (0.82.0): - boost - DoubleConversion - fast_float @@ -1817,7 +1819,7 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - SocketRocket - - React-NativeModulesApple (0.82.0-rc.4): + - React-NativeModulesApple (0.82.0): - boost - DoubleConversion - fast_float @@ -1838,8 +1840,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - SocketRocket - - React-oscompat (0.82.0-rc.4) - - React-perflogger (0.82.0-rc.4): + - React-oscompat (0.82.0) + - React-perflogger (0.82.0): - boost - DoubleConversion - fast_float @@ -1848,7 +1850,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - SocketRocket - - React-performancecdpmetrics (0.82.0-rc.4): + - React-performancecdpmetrics (0.82.0): - boost - DoubleConversion - fast_float @@ -1862,7 +1864,7 @@ PODS: - React-runtimeexecutor - React-timing - SocketRocket - - React-performancetimeline (0.82.0-rc.4): + - React-performancetimeline (0.82.0): - boost - DoubleConversion - fast_float @@ -1875,9 +1877,9 @@ PODS: - React-perflogger - React-timing - SocketRocket - - React-RCTActionSheet (0.82.0-rc.4): - - React-Core/RCTActionSheetHeaders (= 0.82.0-rc.4) - - React-RCTAnimation (0.82.0-rc.4): + - React-RCTActionSheet (0.82.0): + - React-Core/RCTActionSheetHeaders (= 0.82.0) + - React-RCTAnimation (0.82.0): - boost - DoubleConversion - fast_float @@ -1893,7 +1895,7 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - SocketRocket - - React-RCTAppDelegate (0.82.0-rc.4): + - React-RCTAppDelegate (0.82.0): - boost - DoubleConversion - fast_float @@ -1927,7 +1929,7 @@ PODS: - React-utils - ReactCommon - SocketRocket - - React-RCTBlob (0.82.0-rc.4): + - React-RCTBlob (0.82.0): - boost - DoubleConversion - fast_float @@ -1946,7 +1948,7 @@ PODS: - React-RCTNetwork - ReactCommon - SocketRocket - - React-RCTFabric (0.82.0-rc.4): + - React-RCTFabric (0.82.0): - boost - DoubleConversion - fast_float @@ -1982,7 +1984,7 @@ PODS: - React-utils - SocketRocket - Yoga - - React-RCTFBReactNativeSpec (0.82.0-rc.4): + - React-RCTFBReactNativeSpec (0.82.0): - boost - DoubleConversion - fast_float @@ -1996,10 +1998,10 @@ PODS: - React-Core - React-jsi - React-NativeModulesApple - - React-RCTFBReactNativeSpec/components (= 0.82.0-rc.4) + - React-RCTFBReactNativeSpec/components (= 0.82.0) - ReactCommon - SocketRocket - - React-RCTFBReactNativeSpec/components (0.82.0-rc.4): + - React-RCTFBReactNativeSpec/components (0.82.0): - boost - DoubleConversion - fast_float @@ -2022,7 +2024,7 @@ PODS: - ReactCommon - SocketRocket - Yoga - - React-RCTImage (0.82.0-rc.4): + - React-RCTImage (0.82.0): - boost - DoubleConversion - fast_float @@ -2038,14 +2040,14 @@ PODS: - React-RCTNetwork - ReactCommon - SocketRocket - - React-RCTLinking (0.82.0-rc.4): - - React-Core/RCTLinkingHeaders (= 0.82.0-rc.4) - - React-jsi (= 0.82.0-rc.4) + - React-RCTLinking (0.82.0): + - React-Core/RCTLinkingHeaders (= 0.82.0) + - React-jsi (= 0.82.0) - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - ReactCommon/turbomodule/core (= 0.82.0-rc.4) - - React-RCTNetwork (0.82.0-rc.4): + - ReactCommon/turbomodule/core (= 0.82.0) + - React-RCTNetwork (0.82.0): - boost - DoubleConversion - fast_float @@ -2064,7 +2066,7 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - SocketRocket - - React-RCTRuntime (0.82.0-rc.4): + - React-RCTRuntime (0.82.0): - boost - DoubleConversion - fast_float @@ -2085,7 +2087,7 @@ PODS: - React-runtimeexecutor - React-RuntimeHermes - SocketRocket - - React-RCTSettings (0.82.0-rc.4): + - React-RCTSettings (0.82.0): - boost - DoubleConversion - fast_float @@ -2100,10 +2102,10 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - SocketRocket - - React-RCTText (0.82.0-rc.4): - - React-Core/RCTTextHeaders (= 0.82.0-rc.4) + - React-RCTText (0.82.0): + - React-Core/RCTTextHeaders (= 0.82.0) - Yoga - - React-RCTVibration (0.82.0-rc.4): + - React-RCTVibration (0.82.0): - boost - DoubleConversion - fast_float @@ -2117,11 +2119,11 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - SocketRocket - - React-rendererconsistency (0.82.0-rc.4) - - React-renderercss (0.82.0-rc.4): + - React-rendererconsistency (0.82.0) + - React-renderercss (0.82.0): - React-debug - React-utils - - React-rendererdebug (0.82.0-rc.4): + - React-rendererdebug (0.82.0): - boost - DoubleConversion - fast_float @@ -2131,7 +2133,7 @@ PODS: - RCT-Folly/Fabric - React-debug - SocketRocket - - React-RuntimeApple (0.82.0-rc.4): + - React-RuntimeApple (0.82.0): - boost - DoubleConversion - fast_float @@ -2160,7 +2162,7 @@ PODS: - React-runtimescheduler - React-utils - SocketRocket - - React-RuntimeCore (0.82.0-rc.4): + - React-RuntimeCore (0.82.0): - boost - DoubleConversion - fast_float @@ -2182,7 +2184,7 @@ PODS: - React-runtimescheduler - React-utils - SocketRocket - - React-runtimeexecutor (0.82.0-rc.4): + - React-runtimeexecutor (0.82.0): - boost - DoubleConversion - fast_float @@ -2192,10 +2194,10 @@ PODS: - RCT-Folly/Fabric - React-debug - React-featureflags - - React-jsi (= 0.82.0-rc.4) + - React-jsi (= 0.82.0) - React-utils - SocketRocket - - React-RuntimeHermes (0.82.0-rc.4): + - React-RuntimeHermes (0.82.0): - boost - DoubleConversion - fast_float @@ -2216,7 +2218,7 @@ PODS: - React-runtimeexecutor - React-utils - SocketRocket - - React-runtimescheduler (0.82.0-rc.4): + - React-runtimescheduler (0.82.0): - boost - DoubleConversion - fast_float @@ -2238,9 +2240,9 @@ PODS: - React-timing - React-utils - SocketRocket - - React-timing (0.82.0-rc.4): + - React-timing (0.82.0): - React-debug - - React-utils (0.82.0-rc.4): + - React-utils (0.82.0): - boost - DoubleConversion - fast_float @@ -2250,9 +2252,9 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - React-debug - - React-jsi (= 0.82.0-rc.4) + - React-jsi (= 0.82.0) - SocketRocket - - React-webperformancenativemodule (0.82.0-rc.4): + - React-webperformancenativemodule (0.82.0): - boost - DoubleConversion - fast_float @@ -2268,9 +2270,9 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/core - SocketRocket - - ReactAppDependencyProvider (0.82.0-rc.4): + - ReactAppDependencyProvider (0.82.0): - ReactCodegen - - ReactCodegen (0.82.0-rc.4): + - ReactCodegen (0.82.0): - boost - DoubleConversion - fast_float @@ -2296,7 +2298,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - SocketRocket - - ReactCommon (0.82.0-rc.4): + - ReactCommon (0.82.0): - boost - DoubleConversion - fast_float @@ -2304,9 +2306,9 @@ PODS: - glog - RCT-Folly - RCT-Folly/Fabric - - ReactCommon/turbomodule (= 0.82.0-rc.4) + - ReactCommon/turbomodule (= 0.82.0) - SocketRocket - - ReactCommon/turbomodule (0.82.0-rc.4): + - ReactCommon/turbomodule (0.82.0): - boost - DoubleConversion - fast_float @@ -2315,15 +2317,15 @@ PODS: - hermes-engine - RCT-Folly - RCT-Folly/Fabric - - React-callinvoker (= 0.82.0-rc.4) - - React-cxxreact (= 0.82.0-rc.4) - - React-jsi (= 0.82.0-rc.4) - - React-logger (= 0.82.0-rc.4) - - React-perflogger (= 0.82.0-rc.4) - - ReactCommon/turbomodule/bridging (= 0.82.0-rc.4) - - ReactCommon/turbomodule/core (= 0.82.0-rc.4) + - React-callinvoker (= 0.82.0) + - React-cxxreact (= 0.82.0) + - React-jsi (= 0.82.0) + - React-logger (= 0.82.0) + - React-perflogger (= 0.82.0) + - ReactCommon/turbomodule/bridging (= 0.82.0) + - ReactCommon/turbomodule/core (= 0.82.0) - SocketRocket - - ReactCommon/turbomodule/bridging (0.82.0-rc.4): + - ReactCommon/turbomodule/bridging (0.82.0): - boost - DoubleConversion - fast_float @@ -2332,13 +2334,13 @@ PODS: - hermes-engine - RCT-Folly - RCT-Folly/Fabric - - React-callinvoker (= 0.82.0-rc.4) - - React-cxxreact (= 0.82.0-rc.4) - - React-jsi (= 0.82.0-rc.4) - - React-logger (= 0.82.0-rc.4) - - React-perflogger (= 0.82.0-rc.4) + - React-callinvoker (= 0.82.0) + - React-cxxreact (= 0.82.0) + - React-jsi (= 0.82.0) + - React-logger (= 0.82.0) + - React-perflogger (= 0.82.0) - SocketRocket - - ReactCommon/turbomodule/core (0.82.0-rc.4): + - ReactCommon/turbomodule/core (0.82.0): - boost - DoubleConversion - fast_float @@ -2347,22 +2349,23 @@ PODS: - hermes-engine - RCT-Folly - RCT-Folly/Fabric - - React-callinvoker (= 0.82.0-rc.4) - - React-cxxreact (= 0.82.0-rc.4) - - React-debug (= 0.82.0-rc.4) - - React-featureflags (= 0.82.0-rc.4) - - React-jsi (= 0.82.0-rc.4) - - React-logger (= 0.82.0-rc.4) - - React-perflogger (= 0.82.0-rc.4) - - React-utils (= 0.82.0-rc.4) + - React-callinvoker (= 0.82.0) + - React-cxxreact (= 0.82.0) + - React-debug (= 0.82.0) + - React-featureflags (= 0.82.0) + - React-jsi (= 0.82.0) + - React-logger (= 0.82.0) + - React-perflogger (= 0.82.0) + - React-utils (= 0.82.0) - SocketRocket - - RNGoogleMapsPlus (0.1.1): + - RNGoogleMapsPlus (1.1.0-dev.2): - boost - DoubleConversion - fast_float - fmt - glog - - GoogleMaps (= 10.3.0) + - Google-Maps-iOS-Utils (= 6.1.3) + - GoogleMaps (= 10.4.0) - hermes-engine - NitroModules - RCT-Folly @@ -2475,6 +2478,7 @@ DEPENDENCIES: SPEC REPOS: trunk: - CocoaLumberjack + - Google-Maps-iOS-Utils - GoogleMaps - SocketRocket - SVGKit @@ -2637,81 +2641,82 @@ SPEC CHECKSUMS: CocoaLumberjack: 5644158777912b7de7469fa881f8a3f259c2512a DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 - FBLazyVector: 4963926890f2f2b981e1c4e2af79c37d0a9121f3 + FBLazyVector: 41b4dd99afd0aad861444ee141abdedaa6c3d238 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 - GoogleMaps: 197af8911284ddf36db063c74faee4c4ab15e9d9 - hermes-engine: 883ad6cdbcb152221038266a5908c1fa6c1002f7 - NitroModules: cfda296ca7e5f9092bd54477d5eb35416e78e6d8 + Google-Maps-iOS-Utils: bed22fa703c919259b3901449434d60d994fae20 + GoogleMaps: a40d3b1f511f0fa2036e7b08c920c33279b1d5fd + hermes-engine: 8642d8f14a548ab718ec112e9bebdfdd154138b5 + NitroModules: 73c42f3089bd74f411172ea8e69024aac4a2ac9f RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 - RCTDeprecation: 5f54be68187ef1fc21b4d70a262f0d05d6fba854 - RCTRequired: e52808dfaf897a452bf296466de8c3c5a3ca9332 - RCTTypeSafety: 0c96598083fbfd1c605997a7c2d51a25ac97c35b - React: 0fde0b13f6cca9b4e4525d888dd952e87e5f2c28 - React-callinvoker: 726b0350220fa95dfdf03c81eee83f65bc88de3f - React-Core: f50d50abad4b3a216367c3ed0b86ec720775c28a - React-CoreModules: d762f1a139da379ede82234a70493bec77b15131 - React-cxxreact: f821b84f1c641c70d1cf87e5d04e083da6122365 - React-debug: 4004135b2db87a33a6af353f299d2deab9bb4666 - React-defaultsnativemodule: 930e1ee08dc25cb0d28a895face06ad974502c8a - React-domnativemodule: fab7aba674dd031079bdb8e09c24fbcf0d1cc1db - React-Fabric: 282cf34eb33f7ea565c045722aeb892abc6d6a75 - React-FabricComponents: 509efc8e81cb9f1784632ceccdef5bc65d0ee979 - React-FabricImage: 07790306fe0f51c1b994a4fc82672ad5d542ed9f - React-featureflags: 453b355a05c8be7c66b3ee445a4c7e1a6b997f7f - React-featureflagsnativemodule: 68871335a86a89456485ac9168733807cd80b2a1 - React-graphics: 867cf9d7e3b43dc0c0f9cec777964910e1443f24 - React-hermes: dc25666c0164862cdb720bf15d001b6446105f5f - React-idlecallbacksnativemodule: 24da8136fcdf090b789b789c5c580d9370607144 - React-ImageManager: 87f947a54cbd2033ed8acd52bba97725af1ec8f9 - React-jserrorhandler: 5f4c31548b6ee0b930c442b67ba21e2965050f65 - React-jsi: ec757a030440df8715a385560e6313c40774ac13 - React-jsiexecutor: 52213d9c9c692ae020b16f7af3b615835e41fe42 - React-jsinspector: 94b1ed99e954c6cfc31f40c64270bf4b9b094c8d - React-jsinspectorcdp: 30a39fa619bd6fff1ae855e1f1f04ee800ef1168 - React-jsinspectornetwork: 144fb8a0e44410965b00ff0c22005a22334e9714 - React-jsinspectortracing: ccd8f58c4ae50033921179642609e1ac93496902 - React-jsitooling: 1a3f499c6351fcafb0ed8dc9f5e58af27f979652 - React-jsitracing: 3a671434a24e38487763f6fcf237c37091d8e3a5 - React-logger: e4ba9237d477e4cdcd4e67233dd3587e523ef7da - React-Mapbuffer: 88707a55b1b25bb9f205f10cd14b45d0f2ed3262 - React-microtasksnativemodule: bac6cd9443cdef5a526f90f3ab44728557277f78 - React-NativeModulesApple: 13d2ad3af5f79f6b40242b449d545352b204eeda - React-oscompat: 1a6aa7919259f5f49f1f1cf6041d2941acb6dc70 - React-perflogger: f97ac7a4796dc8b51e1a8a3c3473ab50f4bbe0d1 - React-performancecdpmetrics: 32cb7ed23d66c14ec33f2089f5a5d6541cd09e52 - React-performancetimeline: e2cd2cf67541fb24db8617744515a8ce2c080ab1 - React-RCTActionSheet: abf57df1aca1a93f2fb018a48cbf9628617e1081 - React-RCTAnimation: eedf2176d131b06b660eedb5994d3b958bef0cce - React-RCTAppDelegate: 95cfb5a0ed4da695397dd25c64638570ab38e3bd - React-RCTBlob: a809bf4b388b616102b68c7a194891300b10cca6 - React-RCTFabric: 0bf0d232a4917b7c634355e83e6a880c0b17abbd - React-RCTFBReactNativeSpec: 143f7906e0d32199b2247eab0d1f5810b640f9d7 - React-RCTImage: 948aaea5a6ce00754779490eedb428a26a87b254 - React-RCTLinking: fe0ef2b12e253b2f8ccc8cf6ef9d9bdff5209741 - React-RCTNetwork: e60409b60529da7c84daab2d7ad53ce8e042afe8 - React-RCTRuntime: 4e4784feac1322ba442632f74ef5a4dff309a789 - React-RCTSettings: 0238ffe3949e17b069a210bebb453348c82bc703 - React-RCTText: 7baa79da111fa42470bd7338fd6c80f639661400 - React-RCTVibration: b9a95bbd35163dbb31345ce28293f6af972b6e54 - React-rendererconsistency: 8ad28504bb3bd4ba499bbde500343241305755f2 - React-renderercss: 5aed4f9e1b9496f1270249513ed1b85ea0ee8c2a - React-rendererdebug: 3ac72fabeac204828f7fc66698a121a1d5000380 - React-RuntimeApple: d3db055889a690ecdec1ecea5e94df9ac063c865 - React-RuntimeCore: 336dd2d7ebcca13971239a80f156cee433cd6489 - React-runtimeexecutor: d4bb76c9cb7fe42adb261a5ffd19d7cbf26d977c - React-RuntimeHermes: 780b3427f46889df83e60e501cb6cadbc39bd7c0 - React-runtimescheduler: 7eab1f94d5865e8aa133e50607f4428c1df4f30b - React-timing: c5b4f5c798dab68a9c818038508c7087578cff64 - React-utils: 6adbeec9b182d8d6b74dc28c79332c1fbbe5cfec - React-webperformancenativemodule: 120dfcd0fdbcf2b7d5989f4b7a2ccdd00559f8c9 - ReactAppDependencyProvider: 1202b833d8cca6c917dabcf679837c34a9ca5723 - ReactCodegen: 19febbd1fdc8928493972f8d5290f2952e14c9d2 - ReactCommon: 2caf7281b37aa1ca389e18839dd594099efb1489 - RNGoogleMapsPlus: 8a1a26a15dcd3b87de67a86243f91030fc27e6dc + RCTDeprecation: 22bf66112da540a7d40e536366ddd8557934fca1 + RCTRequired: a0ed4dc41b35f79fbb6d8ba320e06882a8c792cf + RCTTypeSafety: 59a046ff1e602409a86b89fcd6edff367a5b14af + React: ade831e2e38887292c2c7d40f2f4098826a9dda4 + React-callinvoker: fb097304922c5da47152147a5fb0712713438575 + React-Core: 60e3adb5af2863587d4a0650a0bbf8d5b1327502 + React-CoreModules: 8647d480cf788eb0e0ae353db836dbb5edb98eb0 + React-cxxreact: 2be8c8494b345bd1896f542bafc18dff72335c55 + React-debug: bea64dbec4b0493bf4d9bdf42342df22b4c2d8f0 + React-defaultsnativemodule: 2c4e3fefc1f7e8eb51ba30ac2eb6d8c9833c6888 + React-domnativemodule: 6c7a612578f600f4cfb03707b722f8ab7ad8e564 + React-Fabric: b16a6c59dcf4580328caf3ef217696706ca960f0 + React-FabricComponents: af2ea0b9df5801f1162c5f8460cf63df8ef89053 + React-FabricImage: 60234653688bda50fe6b82b66ac3442bb164ac28 + React-featureflags: c30268c9ef7b22816604c68b79e11e853053ece6 + React-featureflagsnativemodule: 3809f6195ccfb5affe7e7c68b37307dbe48dbb95 + React-graphics: 42b4fcd5c0ebb614bce839079e17a0cbd953cf0c + React-hermes: 5061dfbb68b7cc4a015302b4c9125c5d7426f9f9 + React-idlecallbacksnativemodule: abfc8719332b030461048a8212ede289a8a5d9a8 + React-ImageManager: 99e9ac91ada428e770ddfb14d547f8335f3fe0c7 + React-jserrorhandler: 1f72ff9966f2107a920fc2650f3ededacd4360db + React-jsi: a884efb76496c1492c8063918d5588f3e2ab8b42 + React-jsiexecutor: 47e858b79890e212469a76d61edd871b1444e869 + React-jsinspector: c19da371d6268c3b357fc99a481c4b67c0e2b690 + React-jsinspectorcdp: dd94e1991050d9de748fab34729dcf3a7651af79 + React-jsinspectornetwork: eaed3fb3fd796ee778302500498aeb972d7c3ae2 + React-jsinspectortracing: d59cbe6bab4a3db5912a3cd7f645a7ae4c23f2ba + React-jsitooling: 2e655b82de76bda624e38148916b593a4395abc3 + React-jsitracing: db61f645acfd557bfe8bce266d6bf4f18fcda09b + React-logger: 30adf849117e87cf86e88dca1824bb0f18f87e10 + React-Mapbuffer: 2a5edca6905cb1b3a40fb7ed3f4496df4f1bc60e + React-microtasksnativemodule: 6d775fdf71445f58dbedbd66ed9cb08b48ae2797 + React-NativeModulesApple: b2ee5b48020439fd81d1fd9cba40ebf0c3af5636 + React-oscompat: 80ca388c4831481cd03a6b45ecfc82739ca9a95e + React-perflogger: 9725c8b401ca406f52e4bb59bf0b22ef9354f96a + React-performancecdpmetrics: 91607ad59e9c2f6f37e2f5bb8872fdd25e135385 + React-performancetimeline: fc253fc9f99b5e1a792de7569ab7f065b469eff3 + React-RCTActionSheet: 2f0a844b3f4b749ce54bee10e5006aacbcb754e0 + React-RCTAnimation: 680cd054a53b6525b587e6e1f1aeb885135e28cd + React-RCTAppDelegate: 5f8969018d773b22ca0b4c9679c3bad73767c5c7 + React-RCTBlob: 9bcdb5549e877fc08684f129047fbf029e37eabe + React-RCTFabric: 252abb656109f0ab70f63327d532c5712246489a + React-RCTFBReactNativeSpec: 417f8184fe95d1cd9b81cba0ac7a981e62f95e0a + React-RCTImage: 937d9ebf5b92f688c2c501de731af47a4df2c208 + React-RCTLinking: b0fde8f005ffd6bdbb9e274a8f132f0e61cb0185 + React-RCTNetwork: 0c23d5f6a3544c98065fed622ef7ed2bce593cb5 + React-RCTRuntime: 5e526e271b78157fbe39748c2e396d1524c29f64 + React-RCTSettings: 093d5fba8bfb4c80a409b06f1e99156e4b7ffa8b + React-RCTText: 286dc4b5314a45b8beb8d06d7fd46b0f9da264ac + React-RCTVibration: 080c11b0ec39f1202bbd91e468dba50894fe4233 + React-rendererconsistency: 3537142cc00a3fef9fb0383cc77ba0a119684b6e + React-renderercss: 8d4d428e593ec024dbdae54a22dd93e6086a5d19 + React-rendererdebug: 66d3b02c4780a6d5cd7a52dd2c040d50d42f92b8 + React-RuntimeApple: bd1bf26f38c61d3eef9c47af2801d51b596e1905 + React-RuntimeCore: 3e8ac1b88ec2b1f61dfcbb7bd4630bdb42bab408 + React-runtimeexecutor: 42ac11dec5410ba8374ae55e6e499919b92da60c + React-RuntimeHermes: 68e5b32bf2b31804728ac969824fed4d8b851f3e + React-runtimescheduler: 85ac70815b81957952c2c56e4d6792f5a95d3647 + React-timing: 50fd9b92abaa03f1dcdcbda6bb45e28e6473c1fe + React-utils: d8f6841c0ac1bf2bf65c2eb3b052fad439d0c6e2 + React-webperformancenativemodule: 001b3b079fee9074e4e3216f562c74716448fc33 + ReactAppDependencyProvider: c5c4f5280e4ae0f9f4a739c64c4260fe0b3edaf1 + ReactCodegen: 3873d7ac09960375f7845384ff47d53e478462dc + ReactCommon: f5527f5d97a9957ab46eb5db78875d3579e03b97 + RNGoogleMapsPlus: 9b638ea84ab0231430a8b5a109a20130ad4a722a SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 SVGKit: 1ad7513f8c74d9652f94ed64ddecda1a23864dea - Yoga: 2fb906b2084fd388a52edae73c54c39c3f50e86c + Yoga: ce55ebb197c21e22b6700cd36e3f36b7ec26e6f8 PODFILE CHECKSUM: ada9cd8bbcd5ad8a6a2eae598262b8f8bce77633 diff --git a/example/package.json b/example/package.json index c14cadc..3f14cb6 100644 --- a/example/package.json +++ b/example/package.json @@ -6,14 +6,14 @@ "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", - "ios:pods": "bundle install && bundle exec pod repo update --verbose && bundle exec pod install --project-directory=ios", + "ios:pods": "bundle install && bundle exec pod repo update --verbose && bundle exec pod install --repo-update --project-directory=ios", "build:android": "react-native build-android", "build:ios": "react-native build-ios --mode Debug --extra-params 'CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=\"\" EXPANDED_CODE_SIGN_IDENTITY=\"\" DEVELOPMENT_TEAM=\"\" COCOAPODS_PARALLEL_CODE_SIGN=false'" }, "dependencies": { "react": "19.1.1", - "react-native": "0.82.0-rc.4", - "react-native-nitro-modules": "0.29.6" + "react-native": "0.82.0", + "react-native-nitro-modules": "0.29.8" }, "devDependencies": { "@babel/core": "7.28.4", @@ -22,10 +22,10 @@ "@react-native-community/cli": "20.0.2", "@react-native-community/cli-platform-android": "20.0.2", "@react-native-community/cli-platform-ios": "20.0.2", - "@react-native/babel-preset": "0.82.0-rc.4", - "@react-native/metro-config": "0.82.0-rc.4", - "@react-native/typescript-config": "0.82.0-rc.4", - "@types/react": "19.1.15", + "@react-native/babel-preset": "0.82.0", + "@react-native/metro-config": "0.82.0", + "@react-native/typescript-config": "0.82.0", + "@types/react": "19.1.1", "react-native-builder-bob": "0.40.13", "react-native-monorepo-config": "0.2.1" }, diff --git a/example/src/App.tsx b/example/src/App.tsx index 7ebd2b4..4597e37 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -7,6 +7,7 @@ import { Text, } from 'react-native'; import { useEffect, useMemo, useRef, useState } from 'react'; +import { RNAndroidLocationPriority, RNIOSLocationAccuracy } from '../../src'; import type { RNCamera, RNMapStyleElement, @@ -17,6 +18,8 @@ import type { GoogleMapsViewRef, RNRegion, RNLatLng, + RNCircle, + RNHeatmap, } from '../../src'; import { GoogleMapsView, GoogleMapsModule } from '../../src'; import { callback } from 'react-native-nitro-modules'; @@ -202,9 +205,20 @@ const randomCoordinates = ( longitude: baseLng + (Math.random() - 0.5) * offset, }); +const randomWeightedCoordinates = ( + baseLat: number, + baseLng: number, + offset = 0.01 +) => ({ + latitude: baseLat + (Math.random() - 0.5) * offset, + longitude: baseLng + (Math.random() - 0.5) * offset, + weight: Math.floor(Math.random() * (100 - 10 + 1)) + 10, +}); + const makePolygon = (id: number): RNPolygon => ({ id: id.toString(), zIndex: id, + pressable: true, coordinates: [ randomCoordinates(37.7749, -122.4194, 0.01), randomCoordinates(37.7749, -122.4194, 0.01), @@ -219,6 +233,7 @@ const makePolygon = (id: number): RNPolygon => ({ const makePolyline = (id: number): RNPolyline => ({ id: id.toString(), zIndex: id, + pressable: true, coordinates: [ randomCoordinates(37.7749, -122.4194, 0.02), randomCoordinates(37.7749, -122.4194, 0.02), @@ -227,8 +242,40 @@ const makePolyline = (id: number): RNPolyline => ({ lineCap: id % 2 === 0 ? 'round' : 'square', lineJoin: id % 3 === 0 ? 'bevel' : 'round', - color: id % 2 === 0 ? '#ff0000' : '#0000ff', - width: 1 + (id % 4), + color: id % 2 === 0 ? '#00ff00' : '#ff0000', + width: 2 + (id % 4), +}); + +const makeCircle = (id: number): RNCircle => ({ + id: id.toString(), + zIndex: id, + pressable: true, + center: randomCoordinates(37.7749, -122.4194, 0.02), + radius: 100 + (id % 5), + strokeWidth: 1 + (id % 5), + strokeColor: '#ff0000', + fillColor: '#0000ff', +}); + +const makeHeatmap = (id: number): RNHeatmap => ({ + id: id.toString(), + zIndex: id, + weightedData: [ + randomWeightedCoordinates(37.7749, -122.4194, 0.02), + randomWeightedCoordinates(37.7749, -122.4194, 0.03), + randomWeightedCoordinates(37.7749, -122.4194, 0.05), + randomWeightedCoordinates(37.7749, -122.4194, 0.01), + randomWeightedCoordinates(37.7749, -122.4194, 0.08), + randomWeightedCoordinates(37.7749, -122.4194, 0.03), + randomWeightedCoordinates(37.7749, -122.4194, 0.09), + ], + gradient: { + colors: ['#00f', '#0ff', '#0f0', '#ff0', '#f00'], + startPoints: [0.1, 0.3, 0.5, 0.7, 1], + colorMapSize: 256, + }, + radius: 100, + opacity: 1, }); export const makeMarker = (id: number): RNMarker => ({ @@ -236,18 +283,72 @@ export const makeMarker = (id: number): RNMarker => ({ zIndex: id, coordinate: randomCoordinates(37.7749, -122.4194, 0.2), anchor: { x: 0.5, y: 1.0 }, - width: (64 / 100) * 50, - height: (88 / 100) * 50, - iconSvg: makeSvgIcon(64, 88), + iconSvg: + id % 2 === 0 + ? { + width: (64 / 100) * 50, + height: (88 / 100) * 50, + svgString: makeSvgIcon(64, 88), + } + : undefined, }); export default function App() { const mapRef = useRef(null); - const [show, setShow] = useState(false); const [stressTest, setStressTest] = useState(false); const [normalStyle, setNormalStyle] = useState(true); const [controlButtonsExpanded, setControlButtonsExpanded] = useState(false); + const [initialProps] = useState({ + /// mapStyle not working with mapId + /// mapId: '111', + camera: { + center: { + latitude: 37.7749, + longitude: -122.4194, + }, + zoom: 15, + }, + }); + + const [uiSettings] = useState({ + allGesturesEnabled: true, + compassEnabled: true, + indoorLevelPickerEnabled: true, + mapToolbarEnabled: true, + myLocationButtonEnabled: true, + rotateEnabled: true, + scrollEnabled: true, + scrollDuringRotateOrZoomEnabled: true, + tiltEnabled: true, + zoomControlsEnabled: true, + zoomGesturesEnabled: true, + }); + + const [mapPadding] = useState({ + top: 20, + left: 20, + bottom: 20, + right: 20, + }); + + const [mapZoomConfig] = useState({ + min: 0, + max: 20, + }); + + const [locationConfig] = useState({ + android: { + priority: RNAndroidLocationPriority.PRIORITY_BALANCED_POWER_ACCURACY, + interval: 5000, + minUpdateInterval: 5000, + }, + ios: { + desiredAccuracy: RNIOSLocationAccuracy.ACCURACY_BEST, + distanceFilterMeters: 10, + }, + }); + const [markers, setMaker] = useState( Array.from({ length: 0 }, (_, i) => makeMarker(i + 1)) ); @@ -260,27 +361,39 @@ export default function App() { Array.from({ length: 1 }, (_, i) => makePolyline(i + 1)) ); - useEffect(() => { - const interval = setInterval(() => { - if (stressTest) { - setMaker((m) => { - let newMarkers = [...m]; + const [circles] = useState( + Array.from({ length: 1 }, (_, i) => makeCircle(i + 1)) + ); - while (newMarkers.length > 100) { - newMarkers.shift(); - } + const [heatmaps] = useState( + Array.from({ length: 1 }, (_, i) => makeHeatmap(i + 1)) + ); - for (let i = 0; i < 500; i++) { - newMarkers.push(makeMarker(newMarkers.length + 1)); - } + useEffect(() => { + if (!stressTest) return; - return newMarkers; - }); - } + const interval = setInterval(() => { + setMaker((m) => { + const newMarkers = [...m]; + while (newMarkers.length > 100) { + newMarkers.shift(); + } + for (let i = 0; i < 500; i++) { + newMarkers.push(makeMarker(newMarkers.length + 1)); + } + + return newMarkers; + }); }, 100); + return () => clearInterval(interval); }, [stressTest]); + const mapStyle = useMemo( + () => JSON.stringify(normalStyle ? standardMapStyle : silverMapStyle), + [normalStyle] + ); + const buttons = useMemo(() => { return [ { @@ -304,10 +417,6 @@ export default function App() { ); }, }, - { - title: `${show ? 'Hide' : 'Show'} Marker`, - onPress: () => setShow(!show), - }, { title: `${stressTest ? 'Stop' : 'Start'} stress test`, onPress: () => setStressTest(!stressTest), @@ -343,7 +452,7 @@ export default function App() { console.log(mapRef.current?.isGooglePlayServicesAvailable()), }, ]; - }, [markers, normalStyle, show, stressTest]); + }, [markers, normalStyle, stressTest]); return ( @@ -353,29 +462,20 @@ export default function App() { mapRef.current = ref; }, }} + initialProps={initialProps} + uiSettings={uiSettings} onMapReady={callback((ready) => console.log('Map is ready! ' + ready))} style={styles.map} + myLocationEnabled={true} buildingEnabled={true} trafficEnabled={true} - customMapStyle={JSON.stringify( - normalStyle ? standardMapStyle : silverMapStyle - )} - initialCamera={{ - center: { - latitude: 37.7749, - longitude: -122.4194, - }, - zoom: 15, - }} + indoorEnabled={true} + customMapStyle={mapStyle} userInterfaceStyle={'light'} - maxZoomLevel={20} - minZoomLevel={0} - mapPadding={{ - top: 20, - left: 20, - bottom: 20, - right: 20, - }} + mapType={'normal'} + mapZoomConfig={mapZoomConfig} + mapPadding={mapPadding} + locationConfig={locationConfig} onMapPress={{ f: function (coordinate: RNLatLng): void { console.log('Map pressed', coordinate); @@ -386,6 +486,21 @@ export default function App() { console.log('Marker pressed', id); }, }} + onPolylinePress={{ + f: function (id: string): void { + console.log('Polyline pressed', id); + }, + }} + onPolygonPress={{ + f: function (id: string): void { + console.log('Polygon pressed', id); + }, + }} + onCirclePress={{ + f: function (id: string): void { + console.log('Circle pressed', id); + }, + }} onCameraChangeStart={{ f: function ( region: RNRegion, @@ -423,9 +538,11 @@ export default function App() { console.log('Location error:', error); }, }} - markers={show ? [...markers] : []} - polygons={show ? polygons : []} - polylines={show ? polylines : []} + markers={markers} + polygons={polygons} + polylines={polylines} + circles={circles} + heatmaps={heatmaps} /> @@ -435,7 +552,7 @@ export default function App() { activeOpacity={0.8} > - {controlButtonsExpanded ? '▼ Hide Controls' : '▶ Show Controls'} + {controlButtonsExpanded ? 'Hide Controls' : 'Show Controls'} diff --git a/ios/.swiftformat b/ios/.swiftformat new file mode 100644 index 0000000..a4c1ee5 --- /dev/null +++ b/ios/.swiftformat @@ -0,0 +1,17 @@ +--swiftversion 5.10 +--disable conditionalAssignment +--disable redundantType +--disable unusedArguments +--binary-grouping none +--decimal-grouping none +--hex-literal-case lowercase +--indent 2 +--no-space-operators +--semicolons never +--trailing-commas never +--rules braces,indent +--wrap-arguments preserve +--wrap-parameters preserve +--wrap-collections preserve +--closing-paren same-line +--xcode-indentation enabled diff --git a/ios/.swiftlint.yml b/ios/.swiftlint.yml index 3343b1b..8d80b0f 100644 --- a/ios/.swiftlint.yml +++ b/ios/.swiftlint.yml @@ -1,6 +1,10 @@ disabled_rules: - file_length - type_body_length + - cyclomatic_complexity + - function_body_length + - closure_parameter_position + - todo identifier_name: min_length: @@ -8,7 +12,7 @@ identifier_name: error: 1 max_length: warning: 40 - error: 50 + error: 100 line_length: warning: 150 diff --git a/ios/GoogleMapViewImpl.swift b/ios/GoogleMapViewImpl.swift index ecb8c8a..91387f2 100644 --- a/ios/GoogleMapViewImpl.swift +++ b/ios/GoogleMapViewImpl.swift @@ -1,69 +1,41 @@ import CoreLocation import GoogleMaps +import GoogleMapsUtils import UIKit final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { private let locationHandler: LocationHandler - private let markerOptions: MapMarkerOptions - private var mapView: GMSMapView! + private let markerBuilder: MapMarkerBuilder + private var mapView: GMSMapView? + private var initialized = false private var mapReady = false - private var pendingBuildingEnabled: Bool = false - private var pendingTrafficEnabled: Bool = false - private var pendingCustomMapStyle: GMSMapStyle? - private var pendingInitialCamera: GMSCameraPosition = - GMSCameraPosition.camera(withLatitude: 0, longitude: 0, zoom: 0) - private var pendingUserInterfaceStyle = UIUserInterfaceStyle.unspecified - private var pendingMinZoomLevel: Double = 0.0 - private var pendingMaxZoomLevel: Double = 21.0 - private var pendingMapPadding: RNMapPadding = .init( - top: 0, - left: 0, - bottom: 0, - right: 0 - ) - - private var pendingPolygons: [(id: String, polygon: GMSPolygon)] = [] - private var pendingPolylines: [(id: String, polyline: GMSPolyline)] = [] private var pendingMarkers: [(id: String, marker: GMSMarker)] = [] + private var pendingPolylines: [(id: String, polyline: GMSPolyline)] = [] + private var pendingPolygons: [(id: String, polygon: GMSPolygon)] = [] + private var pendingCircles: [(id: String, circle: GMSCircle)] = [] + private var pendingHeatmaps: [(id: String, heatmap: GMUHeatmapTileLayer)] = [] - private var polygonsById: [String: GMSPolygon] = [:] - private var polylinesById: [String: GMSPolyline] = [:] private var markersById: [String: GMSMarker] = [:] + private var polylinesById: [String: GMSPolyline] = [:] + private var polygonsById: [String: GMSPolygon] = [:] + private var circlesById: [String: GMSCircle] = [:] + private var heatmapsById: [String: GMUHeatmapTileLayer] = [:] private var cameraMoveReasonIsGesture: Bool = false private var lastSubmittedCameraPosition: GMSCameraPosition? private var lastSubmittedLocation: CLLocation? - var onMapError: ((RNMapErrorCode) -> Void)? - var onMapReady: ((Bool) -> Void)? - var onLocationUpdate: ((RNLocation) -> Void)? - var onLocationError: ((_ error: RNLocationErrorCode) -> Void)? - var onMapPress: ((RNLatLng) -> Void)? - var onMarkerPress: ((String) -> Void)? - var onCameraChangeStart: ((RNRegion, RNCamera, Bool) -> Void)? - var onCameraChange: ((RNRegion, RNCamera, Bool) -> Void)? - var onCameraChangeComplete: ((RNRegion, RNCamera, Bool) -> Void)? - init( frame: CGRect = .zero, locationHandler: LocationHandler, - markerOptions: MapMarkerOptions + markerBuilder: MapMarkerBuilder ) { self.locationHandler = locationHandler - self.markerOptions = markerOptions + self.markerBuilder = markerBuilder super.init(frame: frame) setupAppLifecycleObservers() - setupMap() - - /// wait 1 second if alle setter called - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in - self?.initLocationCallbacks() - self?.applyPending() - self?.onMapReady?(true) - self?.mapReady = true - } } private func setupAppLifecycleObservers() { @@ -86,14 +58,25 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { } @MainActor - private func setupMap() { + func initMapView(mapId: String?, liteMode: Bool?, camera: GMSCameraPosition?) { + if initialized { return } + initialized = true let options = GMSMapViewOptions() options.frame = bounds + + mapId.map { options.mapID = GMSMapID(identifier: $0) } + liteMode.map { _ in /* not supported */ } + camera.map { options.camera = $0 } + mapView = GMSMapView.init(options: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.paddingAdjustmentBehavior = .never - mapView.delegate = self - addSubview(mapView) + mapView?.delegate = self + mapView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] + mapView?.paddingAdjustmentBehavior = .never + mapView.map { addSubview($0) } + initLocationCallbacks() + applyPending() + onMapReady?(true) + mapReady = true } private func initLocationCallbacks() { @@ -102,7 +85,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { if self.lastSubmittedLocation?.coordinate.latitude != loc.coordinate.latitude || self.lastSubmittedLocation?.coordinate.longitude - != loc.coordinate.longitude { + != loc.coordinate.longitude { self.onLocationUpdate?( RNLocation( RNLatLng( @@ -123,26 +106,53 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { @MainActor private func applyPending() { - mapView.padding = UIEdgeInsets( - top: pendingMapPadding.top, - left: pendingMapPadding.left, - bottom: pendingMapPadding.bottom, - right: pendingMapPadding.right - ) + mapPadding.map { + mapView?.padding = UIEdgeInsets( + top: $0.top, + left: $0.left, + bottom: $0.bottom, + right: $0.right + ) + } - mapView.mapStyle = pendingCustomMapStyle - mapView.isBuildingsEnabled = pendingBuildingEnabled - mapView.isTrafficEnabled = pendingTrafficEnabled - mapView.overrideUserInterfaceStyle = pendingUserInterfaceStyle - mapView.setMinZoom( - Float(pendingMinZoomLevel), - maxZoom: Float(pendingMaxZoomLevel) - ) + if let v = uiSettings { + v.allGesturesEnabled.map { mapView?.settings.setAllGesturesEnabled($0) } + v.compassEnabled.map { mapView?.settings.compassButton = $0 } + v.indoorLevelPickerEnabled.map { mapView?.settings.indoorPicker = $0 } + v.mapToolbarEnabled.map { _ in /* not supported */ } + v.myLocationButtonEnabled.map { mapView?.settings.myLocationButton = $0 } + v.rotateEnabled.map { mapView?.settings.rotateGestures = $0 } + v.scrollEnabled.map { mapView?.settings.scrollGestures = $0 } + v.scrollDuringRotateOrZoomEnabled.map { + mapView?.settings.allowScrollGesturesDuringRotateOrZoom = $0 + } + v.tiltEnabled.map { mapView?.settings.tiltGestures = $0 } + v.zoomControlsEnabled.map { _ in /* not supported */ } + v.zoomGesturesEnabled.map { mapView?.settings.zoomGestures = $0 } + } + myLocationEnabled.map { mapView?.isMyLocationEnabled = $0 } + buildingEnabled.map { mapView?.isBuildingsEnabled = $0 } + trafficEnabled.map { mapView?.isTrafficEnabled = $0 } + indoorEnabled.map { mapView?.isIndoorEnabled = $0 } + customMapStyle.map { mapView?.mapStyle = $0 } + mapType.map { mapView?.mapType = $0 } + userInterfaceStyle.map { mapView?.overrideUserInterfaceStyle = $0 } + + mapZoomConfig.map { + mapView?.setMinZoom( + Float($0.min ?? 2), + maxZoom: Float($0.max ?? 21) + ) + } + + locationConfig.map { + locationHandler.desiredAccuracy = + $0.ios?.desiredAccuracy?.toCLLocationAccuracy + locationHandler.distanceFilterMeters = $0.ios?.distanceFilterMeters + } if !pendingMarkers.isEmpty { - pendingMarkers.forEach { - addMarkerInternal(id: $0.id, marker: $0.marker) - } + pendingMarkers.forEach { addMarkerInternal(id: $0.id, marker: $0.marker) } pendingMarkers.removeAll() } if !pendingPolylines.isEmpty { @@ -157,119 +167,147 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { } pendingPolygons.removeAll() } + if !pendingCircles.isEmpty { + pendingCircles.forEach { addCircleInternal(id: $0.id, circle: $0.circle) } + pendingCircles.removeAll() + } + if !pendingHeatmaps.isEmpty { + pendingHeatmaps.forEach { + addHeatmapInternal(id: $0.id, heatmap: $0.heatmap) + } + pendingHeatmaps.removeAll() + } } - var currentCamera: GMSCameraPosition { - mapView.camera + var currentCamera: GMSCameraPosition? { + mapView?.camera } @MainActor - var buildingEnabled: Bool { - get { mapView.isBuildingsEnabled } - set { - pendingBuildingEnabled = newValue - mapView.isBuildingsEnabled = newValue + var uiSettings: RNMapUiSettings? { + didSet { + mapView?.settings.setAllGesturesEnabled( + uiSettings?.allGesturesEnabled ?? true + ) + mapView?.settings.compassButton = uiSettings?.compassEnabled ?? false + mapView?.settings.indoorPicker = + uiSettings?.indoorLevelPickerEnabled ?? false + mapView?.settings.myLocationButton = + uiSettings?.myLocationButtonEnabled ?? false + mapView?.settings.rotateGestures = uiSettings?.rotateEnabled ?? true + mapView?.settings.scrollGestures = uiSettings?.scrollEnabled ?? true + mapView?.settings.allowScrollGesturesDuringRotateOrZoom = + uiSettings?.scrollDuringRotateOrZoomEnabled ?? true + mapView?.settings.tiltGestures = uiSettings?.tiltEnabled ?? true + mapView?.settings.zoomGestures = uiSettings?.zoomGesturesEnabled ?? false } } @MainActor - var trafficEnabled: Bool { - get { mapView.isTrafficEnabled } - set { - pendingTrafficEnabled = newValue - mapView.isTrafficEnabled = newValue + var myLocationEnabled: Bool? { + didSet { + mapView?.isMyLocationEnabled = myLocationEnabled ?? false } } @MainActor - var customMapStyle: GMSMapStyle? { - get { pendingCustomMapStyle } - set { - pendingCustomMapStyle = newValue - mapView.mapStyle = newValue + var buildingEnabled: Bool? { + didSet { + mapView?.isBuildingsEnabled = buildingEnabled ?? false } } @MainActor - var initialCamera: GMSCameraPosition { - get { pendingInitialCamera } - set { - pendingInitialCamera = newValue - if mapView != nil && !mapReady { - mapView.camera = newValue - } + var trafficEnabled: Bool? { + didSet { + mapView?.isTrafficEnabled = false } } @MainActor - var userInterfaceStyle: UIUserInterfaceStyle { - get { pendingUserInterfaceStyle } - set { - pendingUserInterfaceStyle = newValue - mapView.overrideUserInterfaceStyle = newValue + var indoorEnabled: Bool? { + didSet { + mapView?.isIndoorEnabled = indoorEnabled ?? false } } @MainActor - var minZoomLevel: Double { - get { pendingMinZoomLevel } - set { - pendingMinZoomLevel = newValue - mapView.setMinZoom(Float(newValue), maxZoom: Float(pendingMaxZoomLevel)) + var customMapStyle: GMSMapStyle? { + didSet { + mapView?.mapStyle = customMapStyle } } @MainActor - var maxZoomLevel: Double { - get { pendingMaxZoomLevel } - set { - pendingMaxZoomLevel = newValue - mapView.setMinZoom(Float(pendingMinZoomLevel), maxZoom: Float(newValue)) + var userInterfaceStyle: UIUserInterfaceStyle? { + didSet { + mapView?.overrideUserInterfaceStyle = userInterfaceStyle ?? .unspecified } } @MainActor - var mapPadding: RNMapPadding { - get { pendingMapPadding } - set { - pendingMapPadding = newValue - mapView.padding = UIEdgeInsets( - top: newValue.top, - left: newValue.left, - bottom: newValue.bottom, - right: newValue.right + var mapZoomConfig: RNMapZoomConfig? { + didSet { + mapView?.setMinZoom( + Float(mapZoomConfig?.min ?? 2), + maxZoom: Float(mapZoomConfig?.max ?? 21) ) } } - func setCamera(camera: RNCamera, animated: Bool, durationMS: Double) { - let current = mapView.camera + @MainActor var mapPadding: RNMapPadding? { + didSet { + mapView?.padding = + mapPadding.map { + UIEdgeInsets( + top: $0.top, + left: $0.left, + bottom: $0.bottom, + right: $0.right + ) + } ?? .zero + } + } - let zoom = Float(camera.zoom ?? Double(current.zoom)) - let bearing = camera.bearing ?? current.bearing - let viewingAngle = camera.bearing ?? current.viewingAngle + @MainActor var mapType: GMSMapViewType? { + didSet { + mapView?.mapType = mapType ?? .normal + } + } - let target = CLLocationCoordinate2D( - latitude: camera.center?.latitude ?? mapView.camera.target.latitude, - longitude: camera.center?.longitude ?? mapView.camera.target.longitude - ) + @MainActor var locationConfig: RNLocationConfig? { + didSet { + locationHandler.desiredAccuracy = + locationConfig?.ios?.desiredAccuracy?.toCLLocationAccuracy + locationHandler.distanceFilterMeters = + locationConfig?.ios?.distanceFilterMeters + } + } - let cam = GMSCameraPosition.camera( - withTarget: target, - zoom: zoom, - bearing: bearing, - viewingAngle: viewingAngle - ) + var onMapError: ((RNMapErrorCode) -> Void)? + var onMapReady: ((Bool) -> Void)? + var onLocationUpdate: ((RNLocation) -> Void)? + var onLocationError: ((_ error: RNLocationErrorCode) -> Void)? + var onMapPress: ((RNLatLng) -> Void)? + var onMarkerPress: ((String) -> Void)? + var onPolylinePress: ((String) -> Void)? + var onPolygonPress: ((String) -> Void)? + var onCirclePress: ((String) -> Void)? + var onCameraChangeStart: ((RNRegion, RNCamera, Bool) -> Void)? + var onCameraChange: ((RNRegion, RNCamera, Bool) -> Void)? + var onCameraChangeComplete: ((RNRegion, RNCamera, Bool) -> Void)? + + func setCamera(camera: GMSCameraPosition, animated: Bool, durationMS: Double) { if animated { withCATransaction( disableActions: false, duration: durationMS / 1000.0 ) { - mapView.animate(to: cam) + mapView?.animate(to: camera) } } else { - let update = GMSCameraUpdate.setCamera(cam) - mapView.moveCamera(update) + let update = GMSCameraUpdate.setCamera(camera) + mapView?.moveCamera(update) } } @@ -315,10 +353,10 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { disableActions: false, duration: durationMS / 1000.0 ) { - mapView.animate(with: update) + mapView?.animate(with: update) } } else { - mapView.moveCamera(update) + mapView?.moveCamera(update) } } @@ -328,7 +366,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { pendingMarkers.append((id, marker)) return } - if let old = markersById.removeValue(forKey: id) { old.map = nil } + markersById.removeValue(forKey: id).map { $0.map = nil } addMarkerInternal(id: id, marker: marker) } @@ -341,13 +379,12 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { @MainActor func updateMarker(id: String, block: @escaping (GMSMarker) -> Void) { - guard let m = markersById[id] else { return } - block(m) + markersById[id].map { block($0) } } @MainActor func removeMarker(id: String) { - if let m = markersById.removeValue(forKey: id) { m.map = nil } + markersById.removeValue(forKey: id).map { $0.map = nil } } @MainActor @@ -363,7 +400,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { pendingPolylines.append((id, polyline)) return } - if let old = polylinesById.removeValue(forKey: id) { old.map = nil } + polylinesById.removeValue(forKey: id).map { $0.map = nil } addPolylineInternal(id: id, polyline: polyline) } @@ -376,13 +413,12 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { @MainActor func updatePolyline(id: String, block: @escaping (GMSPolyline) -> Void) { - guard let pl = polylinesById[id] else { return } - block(pl) + polylinesById[id].map { block($0) } } @MainActor func removePolyline(id: String) { - if let pl = polylinesById.removeValue(forKey: id) { pl.map = nil } + polylinesById.removeValue(forKey: id).map { $0.map = nil } } @MainActor @@ -398,7 +434,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { pendingPolygons.append((id, polygon)) return } - if let old = polygonsById.removeValue(forKey: id) { old.map = nil } + polygonsById.removeValue(forKey: id).map { $0.map = nil } addPolygonInternal(id: id, polygon: polygon) } @@ -411,13 +447,12 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { @MainActor func updatePolygon(id: String, block: @escaping (GMSPolygon) -> Void) { - guard let pg = polygonsById[id] else { return } - block(pg) + polygonsById[id].map { block($0) } } @MainActor func removePolygon(id: String) { - if let pg = polygonsById.removeValue(forKey: id) { pg.map = nil } + polygonsById.removeValue(forKey: id).map { $0.map = nil } } @MainActor @@ -427,19 +462,78 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { pendingPolygons.removeAll() } - func clearAll() { - markerOptions.cancelAllIconTasks() + @MainActor + func addCircle(id: String, circle: GMSCircle) { + if mapView == nil { + pendingCircles.append((id, circle)) + return + } + circlesById.removeValue(forKey: id).map { $0.map = nil } + addCircleInternal(id: id, circle: circle) + } + + @MainActor + private func addCircleInternal(id: String, circle: GMSCircle) { + circle.map = mapView + circle.userData = id + circlesById[id] = circle + } + + @MainActor + func updateCircle(id: String, block: @escaping (GMSCircle) -> Void) { + circlesById[id].map { block($0) } + } + + @MainActor + func removeCircle(id: String) { + circlesById.removeValue(forKey: id).map { $0.map = nil } + } + + @MainActor + func clearCircles() { + circlesById.values.forEach { $0.map = nil } + circlesById.removeAll() + pendingCircles.removeAll() + } + + @MainActor + func addHeatmap(id: String, heatmap: GMUHeatmapTileLayer) { + if mapView == nil { + pendingHeatmaps.append((id, heatmap)) + return + } + heatmapsById.removeValue(forKey: id).map { $0.map = nil } + addHeatmapInternal(id: id, heatmap: heatmap) + } + + @MainActor + private func addHeatmapInternal(id: String, heatmap: GMUHeatmapTileLayer) { + heatmap.map = mapView + heatmapsById[id] = heatmap + } + + @MainActor + func removeHeatmap(id: String) { + heatmapsById.removeValue(forKey: id).map { $0.map = nil } + } + + @MainActor + func clearHeatmaps() { + heatmapsById.values.forEach { $0.map = nil } + heatmapsById.removeAll() + pendingHeatmaps.removeAll() + } + + func deinitInternal() { + markerBuilder.cancelAllIconTasks() clearMarkers() clearPolylines() clearPolygons() + clearCircles() + clearHeatmaps() locationHandler.stop() - clearMap() - } - - @MainActor - func clearMap() { - mapView.clear() - mapView.delegate = nil + mapView?.clear() + mapView?.delegate = nil mapView = nil } @@ -457,7 +551,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { super.didMoveToWindow() if window != nil { if mapView != nil && mapReady { - onMapReady?(true) + onMapReady?(true) } locationHandler.start() } else { @@ -467,7 +561,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { deinit { NotificationCenter.default.removeObserver(self) - clearAll() + deinitInternal() } func mapView(_ mapView: GMSMapView, willMove gesture: Bool) { @@ -504,11 +598,11 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { if let last = lastSubmittedCameraPosition, - last.target.latitude == position.target.latitude, - last.target.longitude == position.target.longitude, - last.zoom == position.zoom, - last.bearing == position.bearing, - last.viewingAngle == position.viewingAngle { + last.target.latitude == position.target.latitude, + last.target.longitude == position.target.longitude, + last.zoom == position.zoom, + last.bearing == position.bearing, + last.viewingAngle == position.viewingAngle { return } let visibleRegion = mapView.projection.visibleRegion() @@ -576,10 +670,11 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { didTapAt coordinate: CLLocationCoordinate2D ) { onMapPress?( - RNLatLng( - latitude: coordinate.latitude, - longitude: coordinate.longitude - )) + RNLatLng( + latitude: coordinate.latitude, + longitude: coordinate.longitude + ) + ) } func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { @@ -587,4 +682,23 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate { onMarkerPress?(id) return true } + + func mapView(_ mapView: GMSMapView, didTap overlay: GMSOverlay) { + switch overlay { + case let circle as GMSCircle: + let id = (circle.userData as? String) ?? "unknown" + onCirclePress?(id) + + case let polygon as GMSPolygon: + let id = (polygon.userData as? String) ?? "unknown" + onPolygonPress?(id) + + case let polyline as GMSPolyline: + let id = (polyline.userData as? String) ?? "unknown" + onPolylinePress?(id) + + default: + break + } + } } diff --git a/ios/LocationHandler.swift b/ios/LocationHandler.swift index 9cca06a..3ebe414 100644 --- a/ios/LocationHandler.swift +++ b/ios/LocationHandler.swift @@ -2,43 +2,35 @@ import CoreLocation import Foundation import UIKit +private let kCLLocationAccuracyDefault: CLLocationAccuracy = + kCLLocationAccuracyBest +private let kCLDistanceFilterNoneDefault: CLLocationDistance = + kCLDistanceFilterNone + final class LocationHandler: NSObject, CLLocationManagerDelegate { private let manager = CLLocationManager() - private var priority: Int = Priority.highAccuracy.rawValue - private var interval: TimeInterval = 5.0 - private var minUpdateInterval: TimeInterval = 5.0 - private var distanceFilterMeters: CLLocationDistance = kCLDistanceFilterNone + + var desiredAccuracy: CLLocationAccuracy? = kCLLocationAccuracyDefault { + didSet { + manager.desiredAccuracy = desiredAccuracy ?? kCLLocationAccuracyBest + } + } + + var distanceFilterMeters: CLLocationDistance? = kCLDistanceFilterNoneDefault { + didSet { + manager.distanceFilter = distanceFilterMeters ?? kCLDistanceFilterNone + } + } var onUpdate: ((CLLocation) -> Void)? var onError: ((_ error: RNLocationErrorCode) -> Void)? - private var lastEmit: Date? - override init() { super.init() manager.delegate = self manager.pausesLocationUpdatesAutomatically = true manager.activityType = .other - applyPriority() - } - - func setPriority(_ priority: Int) { - self.priority = priority - applyPriority() - } - - func setInterval(_ seconds: Int) { - self.interval = max(0, TimeInterval(seconds)) - } - - func setFastestInterval(_ seconds: Int) { - self.minUpdateInterval = max(0, TimeInterval(seconds)) - } - - func setDistanceFilter(_ meters: Double) { - self.distanceFilterMeters = meters >= 0 ? meters : kCLDistanceFilterNone - manager.distanceFilter = distanceFilterMeters } func showLocationDialog() { @@ -110,36 +102,13 @@ final class LocationHandler: NSObject, CLLocationManagerDelegate { } } - private func applyPriority() { - guard let p = Priority(rawValue: priority) else { - manager.desiredAccuracy = kCLLocationAccuracyBest - return - } - switch p { - case .highAccuracy: - manager.desiredAccuracy = kCLLocationAccuracyBest - case .balanced: - manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters - case .lowPower: - manager.desiredAccuracy = kCLLocationAccuracyHundredMeters - case .passive: - manager.desiredAccuracy = kCLLocationAccuracyKilometer - } - } - private func startUpdates() { - manager.distanceFilter = distanceFilterMeters + manager.desiredAccuracy = desiredAccuracy ?? kCLLocationAccuracyDefault + manager.distanceFilter = + distanceFilterMeters ?? kCLDistanceFilterNoneDefault manager.startUpdatingLocation() } - private func shouldEmit(now: Date) -> Bool { - if let last = lastEmit { - let delta = now.timeIntervalSince(last) - if delta < minUpdateInterval { return false } - } - return true - } - func locationManager( _ manager: CLLocationManager, didFailWithError error: Error @@ -166,12 +135,7 @@ final class LocationHandler: NSObject, CLLocationManagerDelegate { didUpdateLocations locations: [CLLocation] ) { guard let loc = locations.last else { return } - let now = Date() - - if shouldEmit(now: now) { - lastEmit = now - onUpdate?(loc) - } + onUpdate?(loc) } private static func topMostViewController() -> UIViewController? { @@ -191,15 +155,3 @@ final class LocationHandler: NSObject, CLLocationManagerDelegate { } } - -extension LocationHandler { - enum Priority: Int { - case highAccuracy = 100 - /// Android: PRIORITY_BALANCED_POWER_ACCURACY - case balanced = 102 - /// Android: PRIORITY_LOW_POWER - case lowPower = 104 - /// Android: PRIORITY_PASSIVE - case passive = 105 - } -} diff --git a/ios/MapCircleBuilder.swift b/ios/MapCircleBuilder.swift new file mode 100644 index 0000000..729c7f6 --- /dev/null +++ b/ios/MapCircleBuilder.swift @@ -0,0 +1,35 @@ +import GoogleMaps + +final class MapCircleBuilder { + func build(_ c: RNCircle) -> GMSCircle { + let circle = GMSCircle() + circle.position = CLLocationCoordinate2D( + latitude: c.center.latitude, + longitude: c.center.longitude + ) + + circle.radius = c.radius + c.fillColor.map { circle.fillColor = $0.toUIColor() } + c.strokeColor.map { circle.strokeColor = $0.toUIColor() } + c.strokeWidth.map { circle.strokeWidth = CGFloat($0) } + c.pressable.map { circle.isTappable = $0 } + c.zIndex.map { circle.zIndex = Int32($0) } + + return circle + } + + func update(_ next: RNCircle, _ c: GMSCircle) { + c.position = CLLocationCoordinate2D( + latitude: next.center.latitude, + longitude: next.center.longitude + ) + + c.radius = next.radius + c.fillColor = next.fillColor?.toUIColor() ?? nil + c.strokeColor = next.strokeColor?.toUIColor() ?? .black + c.strokeWidth = CGFloat(next.strokeWidth ?? 1.0) + c.isTappable = next.pressable ?? false + c.zIndex = Int32(next.zIndex ?? 0) + } + +} diff --git a/ios/MapHeatmapBuilder.swift b/ios/MapHeatmapBuilder.swift new file mode 100644 index 0000000..07ea637 --- /dev/null +++ b/ios/MapHeatmapBuilder.swift @@ -0,0 +1,27 @@ +import Foundation +import GoogleMaps +import GoogleMapsUtils +import UIKit + +final class MapHeatmapBuilder { + func build(_ h: RNHeatmap) -> GMUHeatmapTileLayer { + let heatmap = GMUHeatmapTileLayer() + heatmap.weightedData = h.weightedData.toWeightedLatLngs() + + h.radius.map { heatmap.radius = UInt($0) } + h.opacity.map { heatmap.opacity = Float($0) } + h.zIndex.map { heatmap.zIndex = Int32($0) } + + h.gradient.map { g in + let colors = g.colors.map { $0.toUIColor() } + let startPoints = g.startPoints.map { NSNumber(value: $0) } + heatmap.gradient = GMUGradient( + colors: colors, + startPoints: startPoints, + colorMapSize: 256 + ) + } + + return heatmap + } +} diff --git a/ios/MapHelper.swift b/ios/MapHelper.swift index 7623522..b8ebaf0 100644 --- a/ios/MapHelper.swift +++ b/ios/MapHelper.swift @@ -9,10 +9,12 @@ func withCATransaction( _ body: () -> Void ) { CATransaction.begin() - if disableActions { CATransaction.setDisableActions(true) } - if let d = duration { CATransaction.setAnimationDuration(d) } - if let tf = timingFunction { CATransaction.setAnimationTimingFunction(tf) } - if let c = completion { CATransaction.setCompletionBlock(c) } + + CATransaction.setDisableActions(disableActions) + duration.map { CATransaction.setAnimationDuration($0) } + timingFunction.map { CATransaction.setAnimationTimingFunction($0) } + completion.map { CATransaction.setCompletionBlock($0) } + body() CATransaction.commit() } diff --git a/ios/MapMarker.swift b/ios/MapMarkerBuilder.swift similarity index 66% rename from ios/MapMarker.swift rename to ios/MapMarkerBuilder.swift index 7902b32..98e2fe6 100644 --- a/ios/MapMarker.swift +++ b/ios/MapMarkerBuilder.swift @@ -2,7 +2,7 @@ import GoogleMaps import SVGKit import UIKit -final class MapMarkerOptions { +final class MapMarkerBuilder { private let iconCache = NSCache() private var tasks: [String: Task] = [:] private let queue = DispatchQueue( @@ -11,14 +11,13 @@ final class MapMarkerOptions { attributes: .concurrent ) - func build(_ m: RNMarker, icon: UIImage) -> GMSMarker { + func build(_ m: RNMarker, icon: UIImage?) -> GMSMarker { let marker = GMSMarker( position: CLLocationCoordinate2D( latitude: m.coordinate.latitude, longitude: m.coordinate.longitude ) ) - marker.zIndex = Int32(m.zIndex) marker.userData = m.id marker.tracksViewChanges = true marker.icon = icon @@ -26,12 +25,49 @@ final class MapMarkerOptions { x: m.anchor?.x ?? 0.5, y: m.anchor?.y ?? 0.5 ) + + m.zIndex.map { marker.zIndex = Int32($0) } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak marker] in marker?.tracksViewChanges = false } + return marker } + @MainActor + func update(_ prev: RNMarker, _ next: RNMarker, _ m: GMSMarker) { + m.position = CLLocationCoordinate2D( + latitude: next.coordinate.latitude, + longitude: next.coordinate.longitude + ) + + m.zIndex = Int32(next.zIndex ?? 0) + + m.groundAnchor = CGPoint( + x: next.anchor?.x ?? 0.5, + y: next.anchor?.y ?? 0.5 + ) + + if !prev.markerStyleEquals(next) { + buildIconAsync(next.id, next) { img in + m.tracksViewChanges = true + m.icon = img + + if prev.anchor?.x != next.anchor?.x || prev.anchor?.y != next.anchor?.y { + m.groundAnchor = CGPoint( + x: next.anchor?.x ?? 0.5, + y: next.anchor?.y ?? 0.5 + ) + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak m] in + m?.tracksViewChanges = false + } + } + } + } + @MainActor func buildIconAsync( _ id: String, @@ -40,6 +76,11 @@ final class MapMarkerOptions { ) { tasks[id]?.cancel() + if m.iconSvg == nil { + onReady(nil) + return + } + let key = m.styleHash() if let cached = iconCache.object(forKey: key) { onReady(cached) @@ -48,14 +89,10 @@ final class MapMarkerOptions { let task = Task(priority: .userInitiated) { [weak self] in guard let self else { return } - defer { - self.tasks.removeValue(forKey: id) - } + defer { self.tasks.removeValue(forKey: id) } let img = await self.renderUIImage(m) - guard let img, !Task.isCancelled else { - return - } + guard let img, !Task.isCancelled else { return } self.iconCache.setObject(img, forKey: key) @@ -63,52 +100,11 @@ final class MapMarkerOptions { guard !Task.isCancelled else { return } onReady(img) } - } tasks[id] = task } - @MainActor - func updateMarker(_ prev: RNMarker, _ next: RNMarker, _ m: GMSMarker) { - if prev.coordinate.latitude != next.coordinate.latitude - || prev.coordinate.longitude != next.coordinate.longitude { - m.position = CLLocationCoordinate2D( - latitude: next.coordinate.latitude, - longitude: next.coordinate.longitude - ) - } - - if prev.zIndex != next.zIndex { - m.zIndex = Int32(next.zIndex) - } - - if prev.anchor?.x != next.anchor?.x || prev.anchor?.y != next.anchor?.y { - m.groundAnchor = CGPoint( - x: next.anchor?.x ?? 0.5, - y: next.anchor?.y ?? 0.5 - ) - } - - if !prev.markerStyleEquals(next) { - buildIconAsync(next.id, next) { img in - guard let img else { return } - m.tracksViewChanges = true - m.icon = img - if prev.anchor?.x != next.anchor?.x || prev.anchor?.y != next.anchor?.y { - m.groundAnchor = CGPoint( - x: next.anchor?.x ?? 0.5, - y: next.anchor?.y ?? 0.5 - ) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak m] in - m?.tracksViewChanges = false - } - } - } - } - func cancelIconTask(_ id: String) { tasks[id]?.cancel() tasks.removeValue(forKey: id) @@ -124,21 +120,26 @@ final class MapMarkerOptions { } private func renderUIImage(_ m: RNMarker) async -> UIImage? { - await withTaskCancellationHandler( + guard let iconSvg = m.iconSvg else { + return nil + } + + return await withTaskCancellationHandler( operation: { - await withCheckedContinuation { cont in + await withCheckedContinuation { + (cont: CheckedContinuation) in queue.async { if Task.isCancelled { cont.resume(returning: nil) return } - let targetW = max(1, Int(CGFloat(m.width))) - let targetH = max(1, Int(CGFloat(m.height))) + let targetW = max(1, Int(CGFloat(iconSvg.width))) + let targetH = max(1, Int(CGFloat(iconSvg.height))) let size = CGSize(width: targetW, height: targetH) guard - let data = m.iconSvg.data(using: .utf8), + let data = iconSvg.svgString.data(using: .utf8), let svgImg = SVGKImage(data: data) else { cont.resume(returning: nil) @@ -179,29 +180,4 @@ final class MapMarkerOptions { onCancel: {} ) } - -} - -extension RNMarker { - func markerEquals(_ b: RNMarker) -> Bool { - id == b.id && zIndex == b.zIndex - && coordinate.latitude == b.coordinate.latitude - && coordinate.longitude == b.coordinate.longitude - && anchor?.x == b.anchor?.x && anchor?.y == b.anchor?.y - && markerStyleEquals(b) - } - - func markerStyleEquals(_ b: RNMarker) -> Bool { - width == b.width && height == b.height - && iconSvg == b.iconSvg - } - - func styleHash() -> NSString { - var hasher = Hasher() - hasher.combine(width) - hasher.combine(height) - hasher.combine(iconSvg) - return String(hasher.finalize()) as NSString - } - } diff --git a/ios/MapPolygon.swift b/ios/MapPolygon.swift deleted file mode 100644 index f9cfb6d..0000000 --- a/ios/MapPolygon.swift +++ /dev/null @@ -1,55 +0,0 @@ -import GoogleMaps - -class MapPolygonOptions { - - func buildPolygon(_ p: RNPolygon) -> GMSPolygon { - let path = GMSMutablePath() - p.coordinates.forEach { - path.add( - CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) - ) - } - let pg = GMSPolygon(path: path) - if let fc = p.fillColor?.toUIColor() { pg.fillColor = fc } - if let sc = p.strokeColor?.toUIColor() { pg.strokeColor = sc } - if let sw = p.strokeWidth { pg.strokeWidth = CGFloat(sw) } - pg.zIndex = Int32(p.zIndex) - return pg - } -} - -extension RNPolygon { - func updatePolygon(_ next: RNPolygon, _ pg: GMSPolygon) { - let path = GMSMutablePath() - next.coordinates.forEach { - path.add( - CLLocationCoordinate2D( - latitude: $0.latitude, - longitude: $0.longitude - ) - ) - } - pg.path = path - - if let fc = next.fillColor?.toUIColor() { pg.fillColor = fc } - if let sc = next.strokeColor?.toUIColor() { pg.strokeColor = sc } - if let sw = next.strokeWidth { pg.strokeWidth = CGFloat(sw) } - pg.zIndex = Int32(next.zIndex) - } - - func polygonEquals(_ b: RNPolygon) -> Bool { - guard zIndex == b.zIndex, - strokeWidth == b.strokeWidth, - fillColor == b.fillColor, - strokeColor == b.strokeColor, - coordinates.count == b.coordinates.count - else { return false } - for i in 0.. GMSPolygon { + let path = GMSMutablePath() + p.coordinates.forEach { + path.add( + CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) + ) + } + + let pg = GMSPolygon(path: path) + + p.fillColor.map { pg.fillColor = $0.toUIColor() } + p.strokeColor.map { pg.strokeColor = $0.toUIColor() } + p.strokeWidth.map { pg.strokeWidth = CGFloat($0) } + p.pressable.map { pg.isTappable = $0 } + p.zIndex.map { pg.zIndex = Int32($0) } + + return pg + } + + func update(_ next: RNPolygon, _ pg: GMSPolygon) { + let path = GMSMutablePath() + next.coordinates.forEach { + path.add( + CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) + ) + } + pg.path = path + + pg.fillColor = next.fillColor?.toUIColor() ?? .clear + pg.strokeColor = next.strokeColor?.toUIColor() ?? .black + pg.strokeWidth = CGFloat(next.strokeWidth ?? 1.0) + pg.isTappable = next.pressable ?? false + pg.zIndex = Int32(next.zIndex ?? 0) + } +} diff --git a/ios/MapPolyline.swift b/ios/MapPolyline.swift deleted file mode 100644 index 28d6b6f..0000000 --- a/ios/MapPolyline.swift +++ /dev/null @@ -1,83 +0,0 @@ -import GoogleMaps - -class MapPolylineOptions { - func buildPolyline(_ p: RNPolyline) -> GMSPolyline { - let path = GMSMutablePath() - p.coordinates.forEach { - path.add( - CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) - ) - } - let pl = GMSPolyline(path: path) - if let w = p.width { pl.strokeWidth = CGFloat(w) } - if let c = p.color?.toUIColor() { pl.strokeColor = c } - pl.zIndex = Int32(p.zIndex) - if let cap = p.lineCap { - /// pl.lineCap = mapLineCap(cap) - } - if let join = p.lineJoin { - /// pl.strokeJoin = mapLineJoin(join) - } - return pl - } -} - -func mapLineCap(_ t: RNLineCapType?) -> CGLineCap { - switch t { - case .round: return .round - case .square: return .square - default: return .butt - } -} - -func mapLineJoin(_ t: RNLineJoinType?) -> CGLineJoin { - switch t { - case .round: return .round - case .bevel: return .bevel - default: return .miter - } -} - -extension RNPolyline { - func updatePolyline(_ next: RNPolyline, _ pl: GMSPolyline) { - let path = GMSMutablePath() - next.coordinates.forEach { - path.add( - CLLocationCoordinate2D( - latitude: $0.latitude, - longitude: $0.longitude - ) - ) - } - pl.path = path - if let w = next.width { pl.strokeWidth = CGFloat(w) } - if let cap = next.lineCap { - pl.spans = nil - /// gms.lineCap = mapLineCap(cap) - } - if let join = next.lineJoin { - /// gms.strokeJoin = mapLineJoin(join) - } - if let c = next.color?.toUIColor() { - pl.strokeColor = c - } - pl.zIndex = Int32(next.zIndex) - } - - func polylineEquals(_ b: RNPolyline) -> Bool { - guard zIndex == b.zIndex, - (width ?? 0) == (b.width ?? 0), - lineCap == b.lineCap, - lineJoin == b.lineJoin, - color == b.color, - coordinates.count == b.coordinates.count - else { return false } - for i in 0.. GMSPolyline { + let path = GMSMutablePath() + p.coordinates.forEach { + path.add( + CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) + ) + } + + let pl = GMSPolyline(path: path) + + p.width.map { pl.strokeWidth = CGFloat($0) } + p.color.map { pl.strokeColor = $0.toUIColor() } + p.lineCap.map { _ in /* not supported */ } + p.lineJoin.map { _ in /* not supported */ } + p.pressable.map { pl.isTappable = $0 } + p.zIndex.map { pl.zIndex = Int32($0) } + + return pl + } + + func update(_ next: RNPolyline, _ pl: GMSPolyline) { + let path = GMSMutablePath() + next.coordinates.forEach { + path.add( + CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude) + ) + } + pl.path = path + + /* lineCap not supported */ + /* lineJoin not supported */ + pl.strokeWidth = CGFloat(next.width ?? 1.0) + pl.strokeColor = next.color?.toUIColor() ?? .black + pl.isTappable = next.pressable ?? false + pl.zIndex = Int32(next.zIndex ?? 0) + } +} diff --git a/ios/PermissionHandler.swift b/ios/PermissionHandler.swift index 7cf1cf1..1df9531 100644 --- a/ios/PermissionHandler.swift +++ b/ios/PermissionHandler.swift @@ -14,7 +14,7 @@ final class PermissionHandler: NSObject, CLLocationManagerDelegate { } func requestLocationPermission() - -> NitroModules.Promise { + -> NitroModules.Promise { let promise = NitroModules.Promise() let status = manager.authorizationStatus diff --git a/ios/RNGoogleMapsPlusModule.swift b/ios/RNGoogleMapsPlusModule.swift index cae6fef..82c7336 100644 --- a/ios/RNGoogleMapsPlusModule.swift +++ b/ios/RNGoogleMapsPlusModule.swift @@ -16,7 +16,7 @@ final class RNGoogleMapsPlusModule: HybridRNGoogleMapsPlusModuleSpec { } func requestLocationPermission() - -> NitroModules.Promise { + -> NitroModules.Promise { return permissionHandler.requestLocationPermission() } diff --git a/ios/RNGoogleMapsPlusView.swift b/ios/RNGoogleMapsPlusView.swift index 30676f5..47edfd3 100644 --- a/ios/RNGoogleMapsPlusView.swift +++ b/ios/RNGoogleMapsPlusView.swift @@ -8,90 +8,114 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { private let permissionHandler: PermissionHandler private let locationHandler: LocationHandler + private let markerBuilder = MapMarkerBuilder() + private let polylineBuilder = MapPolylineBuilder() + private let polygonBuilder = MapPolygonBuilder() + private let circleBuilder = MapCircleBuilder() + private let heatmapBuilder = MapHeatmapBuilder() + private let impl: GoogleMapsViewImpl var view: UIView { return impl } - private var currentCustomMapStyle: String = "" - private let markerOptions = MapMarkerOptions() - private let polylineOptions = MapPolylineOptions() - private let polygonOptions = MapPolygonOptions() - override init() { self.permissionHandler = PermissionHandler() self.locationHandler = LocationHandler() self.impl = GoogleMapsViewImpl( locationHandler: locationHandler, - markerOptions: markerOptions + markerBuilder: markerBuilder ) } + /* + /// TODO: prepareForRecycle + override func prepareForRecycle() { + impl.clearAll() + } + */ + @MainActor - var buildingEnabled: Bool { - get { impl.buildingEnabled } - set { impl.buildingEnabled = newValue } + var initialProps: RNInitialProps? { + didSet { + impl.initMapView( + mapId: initialProps?.mapId, + liteMode: initialProps?.liteMode, + camera: initialProps?.camera?.toGMSCameraPosition(current: nil) + ) + } } @MainActor - var trafficEnabled: Bool { - get { impl.trafficEnabled } - set { impl.trafficEnabled = newValue } + var uiSettings: RNMapUiSettings? { + didSet { impl.uiSettings = uiSettings } } @MainActor - var customMapStyle: String { - get { currentCustomMapStyle } - set { - currentCustomMapStyle = newValue - impl.customMapStyle = try? GMSMapStyle(jsonString: newValue) - } + var myLocationEnabled: Bool? { + didSet { impl.myLocationEnabled = myLocationEnabled } } @MainActor - var initialCamera: RNCamera { - get { mapCameraPositionToCamera(impl.initialCamera) } - set { impl.initialCamera = mapCameraToGMSCamera(newValue) } + var buildingEnabled: Bool? { + didSet { impl.buildingEnabled = buildingEnabled } } @MainActor - var userInterfaceStyle: RNUserInterfaceStyle { - get { mapUIUserInterfaceStyletoUserInterfaceStyle(impl.userInterfaceStyle) } - set { - impl.userInterfaceStyle = mapUserInterfaceStyleToUIUserInterfaceStyle( - newValue - ) + var trafficEnabled: Bool? { + didSet { impl.trafficEnabled = trafficEnabled } + } + + @MainActor + var indoorEnabled: Bool? { + didSet { impl.indoorEnabled = indoorEnabled } + } + + @MainActor + var customMapStyle: String? { + didSet { + if let value = customMapStyle { + impl.customMapStyle = try? GMSMapStyle(jsonString: value) + } } } @MainActor - var minZoomLevel: Double { - get { impl.minZoomLevel } - set { impl.minZoomLevel = newValue } + var userInterfaceStyle: RNUserInterfaceStyle? { + didSet { + impl.userInterfaceStyle = userInterfaceStyle?.toUIUserInterfaceStyle + } + } + + @MainActor + var mapZoomConfig: RNMapZoomConfig? { + didSet { impl.mapZoomConfig = mapZoomConfig } } @MainActor - var maxZoomLevel: Double { - get { impl.maxZoomLevel } - set { impl.maxZoomLevel = newValue } + var mapPadding: RNMapPadding? { + didSet { impl.mapPadding = mapPadding } } @MainActor - var mapPadding: RNMapPadding { - get { impl.mapPadding } - set { impl.mapPadding = newValue } + var mapType: RNMapType? { + didSet { + impl.mapType = mapType.map { + GMSMapViewType(rawValue: UInt($0.rawValue)) ?? .normal + } + } } @MainActor - var markers: [RNMarker] = [] { + var markers: [RNMarker]? { didSet { let prevById = Dictionary( - oldValue.map { ($0.id, $0) }, + (oldValue ?? []).map { ($0.id, $0) }, uniquingKeysWith: { _, new in new } ) let nextById = Dictionary( - markers.map { ($0.id, $0) }, + (markers ?? []).map { ($0.id, $0) }, uniquingKeysWith: { _, new in new } ) @@ -100,20 +124,19 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { removed.forEach { impl.removeMarker(id: $0) - markerOptions.cancelIconTask($0) + markerBuilder.cancelIconTask($0) } for (id, next) in nextById { if let prev = prevById[id] { if !prev.markerEquals(next) { impl.updateMarker(id: id) { m in - self.markerOptions.updateMarker(prev, next, m) + self.markerBuilder.update(prev, next, m) } } } else { - markerOptions.buildIconAsync(next.id, next) { icon in - guard let icon else { return } - let marker = self.markerOptions.build(next, icon: icon) + markerBuilder.buildIconAsync(next.id, next) { icon in + let marker = self.markerBuilder.build(next, icon: icon) self.impl.addMarker(id: id, marker: marker) } } @@ -123,14 +146,14 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { } @MainActor - var polylines: [RNPolyline] = [] { + var polylines: [RNPolyline]? { didSet { let prevById = Dictionary( - oldValue.map { ($0.id, $0) }, + (oldValue ?? []).map { ($0.id, $0) }, uniquingKeysWith: { _, new in new } ) let nextById = Dictionary( - polylines.map { ($0.id, $0) }, + (polylines ?? []).map { ($0.id, $0) }, uniquingKeysWith: { _, new in new } ) @@ -140,29 +163,29 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { for (id, next) in nextById { if let prev = prevById[id] { if !prev.polylineEquals(next) { - impl.updatePolyline(id: id) { gms in - prev.updatePolyline(next, gms) + impl.updatePolyline(id: id) { pl in + self.polylineBuilder.update(next, pl) } - } else { - impl.addPolyline( - id: id, - polyline: polylineOptions.buildPolyline(next) - ) } + } else { + impl.addPolyline( + id: id, + polyline: polylineBuilder.build(next) + ) } } } } @MainActor - var polygons: [RNPolygon] = [] { + var polygons: [RNPolygon]? { didSet { let prevById = Dictionary( - oldValue.map { ($0.id, $0) }, + (oldValue ?? []).map { ($0.id, $0) }, uniquingKeysWith: { _, new in new } ) let nextById = Dictionary( - polygons.map { ($0.id, $0) }, + (polygons ?? []).map { ($0.id, $0) }, uniquingKeysWith: { _, new in new } ) @@ -173,77 +196,134 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { if let prev = prevById[id] { if !prev.polygonEquals(next) { impl.updatePolygon(id: id) { pg in - prev.updatePolygon(next, pg) + self.polygonBuilder.update(next, pg) } } } else { - impl.addPolygon(id: id, polygon: polygonOptions.buildPolygon(next)) + impl.addPolygon(id: id, polygon: polygonBuilder.build(next)) } } } } - func setCamera(camera: RNCamera, animated: Bool?, durationMS: Double?) { - onMain { - self.impl.setCamera( - camera: camera, - animated: animated ?? true, - durationMS: durationMS ?? 3000 + @MainActor + var circles: [RNCircle]? { + didSet { + let prevById = Dictionary( + (oldValue ?? []).map { ($0.id, $0) }, + uniquingKeysWith: { _, new in new } ) + let nextById = Dictionary( + (circles ?? []).map { ($0.id, $0) }, + uniquingKeysWith: { _, new in new } + ) + + let removed = Set(prevById.keys).subtracting(nextById.keys) + removed.forEach { impl.removeCircle(id: $0) } + + for (id, next) in nextById { + if let prev = prevById[id] { + if !prev.circleEquals(next) { + impl.updateCircle(id: id) { circle in + self.circleBuilder.update(next, circle) + } + } + } else { + impl.addCircle(id: id, circle: circleBuilder.build(next)) + } + } } } - func setCameraToCoordinates( - coordinates: [RNLatLng], - padding: RNMapPadding?, - animated: Bool?, - durationMS: Double? - ) { - onMain { - self.impl.setCameraToCoordinates( - coordinates: coordinates, - padding: padding ?? RNMapPadding(0, 0, 0, 0), - animated: animated ?? true, - durationMS: durationMS ?? 3000 + @MainActor + var heatmaps: [RNHeatmap]? { + didSet { + let prevById = Dictionary( + (oldValue ?? []).map { ($0.id, $0) }, + uniquingKeysWith: { _, new in new } + ) + let nextById = Dictionary( + (heatmaps ?? []).map { ($0.id, $0) }, + uniquingKeysWith: { _, new in new } ) + + let removed = Set(prevById.keys).subtracting(nextById.keys) + removed.forEach { impl.removeHeatmap(id: $0) } + + for (id, next) in nextById { + impl.addHeatmap(id: id, heatmap: heatmapBuilder.build(next)) + } + } + } + + @MainActor var locationConfig: RNLocationConfig? { + didSet { + impl.locationConfig = locationConfig } } var onMapError: ((RNMapErrorCode) -> Void)? { - get { impl.onMapError } - set { impl.onMapError = newValue } + didSet { impl.onMapError = onMapError } } var onMapReady: ((Bool) -> Void)? { - get { impl.onMapReady } - set { impl.onMapReady = newValue } + didSet { impl.onMapReady = onMapReady } } var onLocationUpdate: ((RNLocation) -> Void)? { - get { impl.onLocationUpdate } - set { impl.onLocationUpdate = newValue } + didSet { impl.onLocationUpdate = onLocationUpdate } } var onLocationError: ((_ error: RNLocationErrorCode) -> Void)? { - get { impl.onLocationError } - set { impl.onLocationError = newValue } + didSet { impl.onLocationError = onLocationError } } var onMapPress: ((RNLatLng) -> Void)? { - get { impl.onMapPress } - set { impl.onMapPress = newValue } + didSet { impl.onMapPress = onMapPress } } var onMarkerPress: ((String) -> Void)? { - get { impl.onMarkerPress } - set { impl.onMarkerPress = newValue } + didSet { impl.onMarkerPress = onMarkerPress } + } + var onPolylinePress: ((String) -> Void)? { + didSet { impl.onPolylinePress = onPolylinePress } + } + var onPolygonPress: ((String) -> Void)? { + didSet { impl.onPolygonPress = onPolygonPress } + } + var onCirclePress: ((String) -> Void)? { + didSet { impl.onCirclePress = onCirclePress } } var onCameraChangeStart: ((RNRegion, RNCamera, Bool) -> Void)? { - get { impl.onCameraChangeStart } - set { impl.onCameraChangeStart = newValue } + didSet { impl.onCameraChangeStart = onCameraChangeStart } } var onCameraChange: ((RNRegion, RNCamera, Bool) -> Void)? { - get { impl.onCameraChange } - set { impl.onCameraChange = newValue } + didSet { impl.onCameraChange = onCameraChange } } var onCameraChangeComplete: ((RNRegion, RNCamera, Bool) -> Void)? { - get { impl.onCameraChangeComplete } - set { impl.onCameraChangeComplete = newValue } + didSet { impl.onCameraChangeComplete = onCameraChangeComplete } + } + + func setCamera(camera: RNCamera, animated: Bool?, durationMS: Double?) { + let cam = camera.toGMSCameraPosition(current: impl.currentCamera) + onMain { + self.impl.setCamera( + camera: cam, + animated: animated ?? true, + durationMS: durationMS ?? 3000 + ) + } + } + + func setCameraToCoordinates( + coordinates: [RNLatLng], + padding: RNMapPadding?, + animated: Bool?, + durationMS: Double? + ) { + onMain { + self.impl.setCameraToCoordinates( + coordinates: coordinates, + padding: padding ?? RNMapPadding(0, 0, 0, 0), + animated: animated ?? true, + durationMS: durationMS ?? 3000 + ) + } } func showLocationDialog() { @@ -255,7 +335,7 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { } func requestLocationPermission() - -> NitroModules.Promise { + -> NitroModules.Promise { return permissionHandler.requestLocationPermission() } @@ -263,79 +343,6 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { /// not supported return true } - - private func mapCameraToGMSCamera(_ c: RNCamera) -> GMSCameraPosition { - let current = impl.currentCamera - let center = CLLocationCoordinate2D( - latitude: c.center?.latitude ?? current.target.latitude, - longitude: c.center?.longitude ?? current.target.longitude - ) - let z = Float(c.zoom ?? Double(current.zoom)) - let b = c.bearing ?? current.bearing - let t = c.tilt ?? current.viewingAngle - - return GMSCameraPosition.camera( - withTarget: center, - zoom: z, - bearing: b, - viewingAngle: t - ) - } - - private func mapCameraPositionToCamera(_ cp: GMSCameraPosition) - -> RNCamera { - return RNCamera( - center: RNLatLng( - latitude: cp.target.latitude, - longitude: cp.target.longitude - ), - zoom: Double(cp.zoom), - bearing: cp.bearing, - tilt: cp.viewingAngle - ) - } - - func mapUserInterfaceStyleToUIUserInterfaceStyle( - _ style: RNUserInterfaceStyle - ) - -> UIUserInterfaceStyle { - switch style { - case .light: return .light - case .dark: return .dark - case .default: return .unspecified - } - } - - func mapUIUserInterfaceStyletoUserInterfaceStyle( - _ uiStyle: UIUserInterfaceStyle - ) -> RNUserInterfaceStyle { - switch uiStyle { - case .light: return .light - case .dark: return .dark - case .unspecified: return .default - @unknown default: return .default - } - } -} - -extension UIUserInterfaceStyle { - init?(fromString string: String) { - switch string.lowercased() { - case "light": self = .light - case "dark": self = .dark - case "default": self = .unspecified - default: return nil - } - } - - var stringValue: String { - switch self { - case .light: return "light" - case .dark: return "dark" - case .unspecified: return "default" - @unknown default: return "default" - } - } } @inline(__always) diff --git a/ios/Resources/PrivacyInfo.xcprivacy b/ios/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..ef4cc91 --- /dev/null +++ b/ios/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,69 @@ + + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePreciseLocation + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeCoarseLocation + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA91.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryLocation + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + + diff --git a/ios/extensions/RNCamera+Extension.swift b/ios/extensions/RNCamera+Extension.swift new file mode 100644 index 0000000..c1075ab --- /dev/null +++ b/ios/extensions/RNCamera+Extension.swift @@ -0,0 +1,22 @@ +import CoreLocation +import GoogleMaps + +extension RNCamera { + func toGMSCameraPosition(current: GMSCameraPosition?) -> GMSCameraPosition { + let center = CLLocationCoordinate2D( + latitude: center?.latitude ?? current?.target.latitude ?? 0, + longitude: center?.longitude ?? current?.target.longitude ?? 0 + ) + + let zoom = Float(zoom ?? Double(current?.zoom ?? 0)) + let bearing = bearing ?? current?.bearing ?? 0 + let tilt = tilt ?? current?.viewingAngle ?? 0 + + return GMSCameraPosition.camera( + withTarget: center, + zoom: zoom, + bearing: bearing, + viewingAngle: tilt + ) + } +} diff --git a/ios/extensions/RNCircle+Extension.swift b/ios/extensions/RNCircle+Extension.swift new file mode 100644 index 0000000..8d2557b --- /dev/null +++ b/ios/extensions/RNCircle+Extension.swift @@ -0,0 +1,11 @@ +import GoogleMaps + +extension RNCircle { + func circleEquals(_ b: RNCircle) -> Bool { + zIndex == b.zIndex && pressable == b.pressable + && center.latitude == b.center.latitude + && center.longitude == b.center.longitude && radius == b.radius + && strokeWidth == b.strokeWidth && strokeColor == b.strokeColor + && fillColor == b.fillColor + } +} diff --git a/ios/extensions/RNHeatmap+Extension.swift b/ios/extensions/RNHeatmap+Extension.swift new file mode 100644 index 0000000..e143ea2 --- /dev/null +++ b/ios/extensions/RNHeatmap+Extension.swift @@ -0,0 +1,16 @@ +import CoreLocation +import Foundation +import GoogleMapsUtils + +extension RNHeatmapPoint { + func toWeightedLatLng() -> GMUWeightedLatLng { + let coord = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) + return GMUWeightedLatLng(coordinate: coord, intensity: Float(weight)) + } +} + +extension Array where Element == RNHeatmapPoint { + func toWeightedLatLngs() -> [GMUWeightedLatLng] { + map { $0.toWeightedLatLng() } + } +} diff --git a/ios/extensions/RNIOSLocationAccuracy+Extensions.swift b/ios/extensions/RNIOSLocationAccuracy+Extensions.swift new file mode 100644 index 0000000..3c2112c --- /dev/null +++ b/ios/extensions/RNIOSLocationAccuracy+Extensions.swift @@ -0,0 +1,19 @@ +import CoreLocation + +extension RNIOSLocationAccuracy { + var toCLLocationAccuracy: CLLocationAccuracy { + switch self { + case .accuracyBest: + return kCLLocationAccuracyBest + + case .accuracyNearestTenMeter: + return kCLLocationAccuracyNearestTenMeters + + case .accuracyNearestHundredMeter: + return kCLLocationAccuracyHundredMeters + + case .accuracyKilometer: + return kCLLocationAccuracyKilometer + } + } +} diff --git a/ios/extensions/RNMarker+Extension.swift b/ios/extensions/RNMarker+Extension.swift new file mode 100644 index 0000000..e3636e1 --- /dev/null +++ b/ios/extensions/RNMarker+Extension.swift @@ -0,0 +1,25 @@ +import GoogleMaps + +extension RNMarker { + func markerEquals(_ b: RNMarker) -> Bool { + id == b.id && zIndex == b.zIndex + && coordinate.latitude == b.coordinate.latitude + && coordinate.longitude == b.coordinate.longitude + && anchor?.x == b.anchor?.x && anchor?.y == b.anchor?.y + && markerStyleEquals(b) + } + + func markerStyleEquals(_ b: RNMarker) -> Bool { + iconSvg?.width == b.iconSvg?.width && iconSvg?.height == b.iconSvg?.height + && iconSvg?.svgString == b.iconSvg?.svgString + + } + + func styleHash() -> NSString { + var hasher = Hasher() + hasher.combine(iconSvg?.width) + hasher.combine(iconSvg?.height) + hasher.combine(iconSvg?.svgString) + return String(hasher.finalize()) as NSString + } +} diff --git a/ios/extensions/RNPolygon+Extension.swift.swift b/ios/extensions/RNPolygon+Extension.swift.swift new file mode 100644 index 0000000..7aee161 --- /dev/null +++ b/ios/extensions/RNPolygon+Extension.swift.swift @@ -0,0 +1,22 @@ +import GoogleMaps + +extension RNPolygon { + func polygonEquals(_ b: RNPolygon) -> Bool { + guard zIndex == b.zIndex, + pressable == b.pressable, + strokeWidth == b.strokeWidth, + fillColor == b.fillColor, + strokeColor == b.strokeColor, + coordinates.count == b.coordinates.count + else { return false } + + for i in 0.. Bool { + guard zIndex == b.zIndex, + (width ?? 0) == (b.width ?? 0), + lineCap == b.lineCap, + lineJoin == b.lineJoin, + color == b.color, + coordinates.count == b.coordinates.count + else { return false } + + for i in 0.. CGLineCap { + switch t { + case .round: return .round + case .square: return .square + default: return .butt + } + } + + private func mapLineJoin(_ t: RNLineJoinType) -> CGLineJoin { + switch t { + case .round: return .round + case .bevel: return .bevel + default: return .miter + } + } +} diff --git a/ios/extensions/RNUserInterface+Extension.swift b/ios/extensions/RNUserInterface+Extension.swift new file mode 100644 index 0000000..4ff0571 --- /dev/null +++ b/ios/extensions/RNUserInterface+Extension.swift @@ -0,0 +1,16 @@ +import UIKit + +extension RNUserInterfaceStyle { + var toUIUserInterfaceStyle: UIUserInterfaceStyle { + switch self { + case .light: + return .light + case .dark: + return .dark + case .default: + return .unspecified + @unknown default: + return .unspecified + } + } +} diff --git a/ios/Color.swift b/ios/extensions/String+Extensions.swift similarity index 76% rename from ios/Color.swift rename to ios/extensions/String+Extensions.swift index 5b1f327..9ea47fd 100644 --- a/ios/Color.swift +++ b/ios/extensions/String+Extensions.swift @@ -41,17 +41,17 @@ extension UIColor { private static func fromRGBFunction(_ s: String) -> UIColor? { let nums = s - .replacingOccurrences(of: "rgba", with: "") - .replacingOccurrences(of: "rgb", with: "") - .replacingOccurrences(of: "(", with: "") - .replacingOccurrences(of: ")", with: "") - .split(separator: ",") - .map { $0.trimmingCharacters(in: .whitespaces) } + .replacingOccurrences(of: "rgba", with: "") + .replacingOccurrences(of: "rgb", with: "") + .replacingOccurrences(of: "(", with: "") + .replacingOccurrences(of: ")", with: "") + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } guard nums.count == 3 || nums.count == 4, - let r = Double(nums[0]), - let g = Double(nums[1]), - let b = Double(nums[2]) + let r = Double(nums[0]), + let g = Double(nums[1]), + let b = Double(nums[2]) else { return nil } let a = (nums.count == 4) ? (Double(nums[3]) ?? 1.0) : 1.0 return UIColor( @@ -65,18 +65,18 @@ extension UIColor { private static func fromHSLFunction(_ s: String) -> UIColor? { let parts = s - .replacingOccurrences(of: "hsla", with: "") - .replacingOccurrences(of: "hsl", with: "") - .replacingOccurrences(of: "(", with: "") - .replacingOccurrences(of: ")", with: "") - .replacingOccurrences(of: "%", with: "") - .split(separator: ",") - .map { $0.trimmingCharacters(in: .whitespaces) } + .replacingOccurrences(of: "hsla", with: "") + .replacingOccurrences(of: "hsl", with: "") + .replacingOccurrences(of: "(", with: "") + .replacingOccurrences(of: ")", with: "") + .replacingOccurrences(of: "%", with: "") + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } guard parts.count == 3 || parts.count == 4, - let h = Double(parts[0]), - let sPerc = Double(parts[1]), - let lPerc = Double(parts[2]) + let h = Double(parts[0]), + let sPerc = Double(parts[1]), + let lPerc = Double(parts[2]) else { return nil } let a = (parts.count == 4) ? (Double(parts[3]) ?? 1.0) : 1.0 @@ -87,7 +87,7 @@ extension UIColor { let x = c * (1 - Swift.abs((h / 60).truncatingRemainder(dividingBy: 2) - 1)) let m = l - c / 2 -// swiftlint:disable:next large_tuple + // swiftlint:disable:next large_tuple let (r1, g1, b1): (Double, Double, Double) switch h { case 0..<60: (r1, g1, b1) = (c, x, 0) diff --git a/package.json b/package.json index 7711ad4..085e59d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-google-maps-plus", - "version": "1.0.2", + "version": "1.1.0-dev.5", "description": "React-native wrapper for android & IOS google maps sdk", "main": "./lib/module/index.js", "module": "./lib/module/index.js", @@ -11,17 +11,17 @@ "lint": "yarn lint:js && yarn lint:android && yarn lint:ios", "lint:js": "eslint . --max-warnings 0 && yarn prettier --check .", "lint:android": "cd android && ktlint -F ./**/*.kt*", - "lint:ios": "cd ios && swiftlint --quiet", + "lint:ios": "cd ios && swiftlint --quiet && swiftformat . --lint", "format": "yarn format:js && yarn format:ios && yarn format:android", "format:js": "prettier --write .", - "format:android": "cd android && ktlint ./**/*.kt*", - "format:ios": "cd ios && swiftlint --quiet --fix", + "format:android": "cd android && ktlint --format ./**/*.kt*", + "format:ios": "cd ios && swiftformat .", "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", "test": "jest", "git:clean": "git clean -dfX", "release": "semantic-release", "build": "yarn nitrogen && bob build", - "nitrogen": "nitrogen --logLevel=\"debug\" && node scripts/post-script.js", + "nitrogen": "nitrogen --logLevel=\"debug\" && node scripts/nitrogen-patch.js", "prepare": "bob build" }, "keywords": [ @@ -71,36 +71,36 @@ "provenance": true }, "devDependencies": { - "@commitlint/cli": "20.0.0", + "@commitlint/cli": "20.1.0", "@commitlint/config-conventional": "20.0.0", "@eslint/compat": "1.4.0", "@eslint/eslintrc": "3.3.1", - "@eslint/js": "9.36.0", + "@eslint/js": "9.37.0", "@jamesacarr/eslint-formatter-github-actions": "0.2.0", - "@react-native/babel-preset": "0.82.0-rc.4", - "@react-native/eslint-config": "0.82.0-rc.4", + "@react-native/babel-preset": "0.82.0", + "@react-native/eslint-config": "0.82.0", "@semantic-release/changelog": "6.0.3", "@semantic-release/git": "10.0.1", "@semantic-release/npm": "13.0.0-beta.1", "@types/jest": "30.0.0", - "@types/react": "19.1.15", + "@types/react": "19.2.2", "clang-format-node": "2.0.1", "conventional-changelog-conventionalcommits": "9.1.0", "del-cli": "7.0.0", - "eslint": "9.36.0", + "eslint": "9.37.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-ft-flow": "3.0.11", "eslint-plugin-prettier": "5.5.4", "jest": "30.2.0", - "lefthook": "1.13.5", + "lefthook": "1.13.6", "nitrogen": "0.29.6", "prettier": "3.6.2", - "react": "19.1.1", - "react-native": "0.82.0-rc.4", + "react": "19.2.0", + "react-native": "0.82.0", "react-native-builder-bob": "0.40.13", - "react-native-nitro-modules": "0.29.6", + "react-native-nitro-modules": "0.29.8", "semantic-release": "25.0.0-beta.6", - "typescript": "5.9.2" + "typescript": "5.9.3" }, "overrides": { "@semantic-release/npm": "13.0.0-beta.1", diff --git a/release.config.cjs b/release.config.cjs index 119f5d8..bfb8b1b 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -15,7 +15,7 @@ const sortMap = Object.fromEntries( * @type {import('semantic-release').GlobalConfig} */ module.exports = { - branches: ['main', { name: 'next', prerelease: 'next' }], + branches: ['main', { name: 'dev', prerelease: 'dev' }], plugins: [ [ '@semantic-release/commit-analyzer', diff --git a/scripts/post-script.js b/scripts/nitrogen-patch.js similarity index 54% rename from scripts/post-script.js rename to scripts/nitrogen-patch.js index c2f73b2..ce7832f 100644 --- a/scripts/post-script.js +++ b/scripts/nitrogen-patch.js @@ -1,16 +1,24 @@ /** * Recursively patches all generated Android files: * - Replaces 'com.margelo.nitro.rngooglemapsplus' -> 'com.rngooglemapsplus' - * - Replaces 'com/margelo/nitro/rngooglemapsplus' -> 'com/rngooglemapsplus' (for C++ descriptors) - * - Removes 'margelo/nitro/' in GoogleMapsPlusOnLoad.cpp + * - Replaces 'com/margelo/nitro/rngooglemapsplus' -> 'com/rngooglemapsplus' + * - Removes 'margelo/nitro/' in RNGoogleMapsPlusOnLoad.cpp + * - Inserts `prepareToRecycleView()` under `onDropViewInstance()` if missing */ -const path = require('node:path'); -const { readdir, readFile, writeFile } = require('node:fs/promises'); +import { fileURLToPath } from 'url'; +import { basename } from 'path'; +import path from 'node:path'; +import { readdir, readFile, writeFile } from 'node:fs/promises'; const ROOT_DIR = path.join(process.cwd(), 'nitrogen', 'generated', 'android'); console.log(ROOT_DIR); const ANDROID_ONLOAD_FILE = path.join(ROOT_DIR, 'RNGoogleMapsPlusOnLoad.cpp'); +const HYBRID_VIEW_MANAGER = path.join( + ROOT_DIR, + 'kotlin/com/margelo/nitro/rngooglemapsplus/views/HybridRNGoogleMapsPlusViewManager.kt' +); + const REPLACEMENTS = [ { regex: /com\.margelo\.nitro\.rngooglemapsplus/g, @@ -22,8 +30,19 @@ const REPLACEMENTS = [ }, ]; +const __filename = fileURLToPath(import.meta.url); +const filename = basename(__filename); + +const RECYCLE_METHOD = ` + /// added by ${filename} + override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? { + return null + } +`; + +// Patch-Routine async function processFile(filePath) { - const content = await readFile(filePath, { encoding: 'utf8' }); + let content = await readFile(filePath, 'utf8'); let updated = content; for (const { regex, replacement } of REPLACEMENTS) { @@ -34,8 +53,22 @@ async function processFile(filePath) { updated = updated.replace(/margelo\/nitro\//g, ''); } + console.log(filePath); + if (path.resolve(filePath) === path.resolve(HYBRID_VIEW_MANAGER)) { + if (!/override fun prepareToRecycleView/.test(updated)) { + const pattern = + /(override fun onDropViewInstance\(view: View\)\s*\{[^}]+\}\s*)/m; + + if (pattern.test(updated)) { + updated = updated.replace(pattern, `$1${RECYCLE_METHOD}\n`); + } else { + updated = updated.replace(/}\s*$/m, `${RECYCLE_METHOD}\n}\n`); + } + } + } + if (updated !== content) { - await writeFile(filePath, updated); + await writeFile(filePath, updated, 'utf8'); console.log(`✔ Updated: ${filePath}`); } } diff --git a/src/RNGoogleMapsPlusView.nitro.ts b/src/RNGoogleMapsPlusView.nitro.ts index 3f7c4a5..dc29a66 100644 --- a/src/RNGoogleMapsPlusView.nitro.ts +++ b/src/RNGoogleMapsPlusView.nitro.ts @@ -16,26 +16,42 @@ import type { RNRegion, RNLocation, RNMapErrorCode, + RNMapType, + RNInitialProps, + RNCircle, + RNMapUiSettings, + RNLocationConfig, + RNMapZoomConfig, + RNHeatmap, } from './types'; export interface RNGoogleMapsPlusViewProps extends HybridViewProps { - buildingEnabled: boolean; - trafficEnabled: boolean; - customMapStyle: string; - initialCamera: RNCamera; - userInterfaceStyle: RNUserInterfaceStyle; - minZoomLevel: number; - maxZoomLevel: number; - mapPadding: RNMapPadding; - markers: RNMarker[]; - polygons: RNPolygon[]; - polylines: RNPolyline[]; + initialProps?: RNInitialProps; + uiSettings?: RNMapUiSettings; + myLocationEnabled?: boolean; + buildingEnabled?: boolean; + trafficEnabled?: boolean; + indoorEnabled?: boolean; + customMapStyle?: string; + userInterfaceStyle?: RNUserInterfaceStyle; + mapZoomConfig?: RNMapZoomConfig; + mapPadding?: RNMapPadding; + mapType?: RNMapType; + markers?: RNMarker[]; + polygons?: RNPolygon[]; + polylines?: RNPolyline[]; + circles?: RNCircle[]; + heatmaps?: RNHeatmap[]; + locationConfig?: RNLocationConfig; onMapError?: (error: RNMapErrorCode) => void; onMapReady?: (ready: boolean) => void; onLocationUpdate?: (location: RNLocation) => void; onLocationError?: (error: RNLocationErrorCode) => void; onMapPress?: (coordinate: RNLatLng) => void; onMarkerPress?: (id: string) => void; + onPolylinePress?: (id: string) => void; + onPolygonPress?: (id: string) => void; + onCirclePress?: (id: string) => void; onCameraChangeStart?: ( region: RNRegion, camera: RNCamera, diff --git a/src/types.ts b/src/types.ts index 0bccb4a..5a6d9e5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,26 @@ import type { HybridView } from 'react-native-nitro-modules'; export type GoogleMapsViewRef = HybridView; +export type RNInitialProps = { + mapId?: string; + liteMode?: boolean; + camera?: RNCamera; +}; + +export type RNMapUiSettings = { + allGesturesEnabled?: boolean; + compassEnabled?: boolean; + indoorLevelPickerEnabled?: boolean; + mapToolbarEnabled?: boolean; + myLocationButtonEnabled?: boolean; + rotateEnabled?: boolean; + scrollEnabled?: boolean; + scrollDuringRotateOrZoomEnabled?: boolean; + tiltEnabled?: boolean; + zoomControlsEnabled?: boolean; + zoomGesturesEnabled?: boolean; +}; + export type RNLatLng = { latitude: number; longitude: number }; export type RNBoundingBox = { northEast: RNLatLng; southWest: RNLatLng }; @@ -14,6 +34,8 @@ export type RNMapPadding = { right: number; }; +export type RNMapType = 'none' | 'normal' | 'hybrid' | 'satellite' | 'terrain'; + export type RNUserInterfaceStyle = 'light' | 'dark' | 'default'; export type RNFeatureType = string; @@ -101,22 +123,32 @@ export type RNPosition = { y: number; }; +export type RNMapZoomConfig = { + min?: number; + max?: number; +}; + export type RNLineCapType = 'butt' | 'round' | 'square'; export type RNLineJoinType = 'miter' | 'round' | 'bevel'; export type RNMarker = { id: string; - zIndex: number; + zIndex?: number; coordinate: RNLatLng; anchor?: RNPosition; + iconSvg?: RNMarkerSvg; +}; + +export type RNMarkerSvg = { width: number; height: number; - iconSvg: string; + svgString: string; }; export type RNPolygon = { id: string; - zIndex: number; + zIndex?: number; + pressable?: boolean; coordinates: RNLatLng[]; fillColor?: string; strokeColor?: string; @@ -125,7 +157,8 @@ export type RNPolygon = { export type RNPolyline = { id: string; - zIndex: number; + zIndex?: number; + pressable?: boolean; coordinates: RNLatLng[]; lineCap?: RNLineCapType; lineJoin?: RNLineJoinType; @@ -133,6 +166,68 @@ export type RNPolyline = { width?: number; }; +export type RNCircle = { + id: string; + pressable?: boolean; + zIndex?: number; + center: RNLatLng; + radius: number; + strokeWidth?: number; + strokeColor?: string; + fillColor?: string; +}; + +export type RNHeatmap = { + id: string; + pressable?: boolean; + zIndex?: number; + weightedData: RNHeatmapPoint[]; + radius?: number; + opacity?: number; + gradient?: RNHeatmapGradient; +}; + +export type RNHeatmapPoint = { + latitude: number; + longitude: number; + weight: number; +}; + +export type RNHeatmapGradient = { + colors: string[]; + startPoints: number[]; + colorMapSize: number; +}; + +export type RNLocationConfig = { + android?: RNAndroidLocationConfig; + ios?: RNIOSLocationConfig; +}; +export type RNAndroidLocationConfig = { + priority?: RNAndroidLocationPriority; + interval?: number; + minUpdateInterval?: number; +}; + +export enum RNAndroidLocationPriority { + PRIORITY_HIGH_ACCURACY = 0, + PRIORITY_BALANCED_POWER_ACCURACY = 1, + PRIORITY_LOW_POWER = 2, + PRIORITY_PASSIVE = 3, +} + +export type RNIOSLocationConfig = { + desiredAccuracy?: RNIOSLocationAccuracy; + distanceFilterMeters?: number; +}; + +export enum RNIOSLocationAccuracy { + ACCURACY_BEST = 0, + ACCURACY_NEAREST_TEN_METER = 1, + ACCURACY_NEAREST_HUNDRED_METER = 2, + ACCURACY_KILOMETER = 3, +} + export type RNLocationPermissionResult = { android?: RNAndroidLocationPermissionResult; ios?: RNIOSPermissionResult; diff --git a/yarn.lock b/yarn.lock index d6e677f..d2c4e1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1566,20 +1566,20 @@ __metadata: languageName: node linkType: hard -"@commitlint/cli@npm:20.0.0": - version: 20.0.0 - resolution: "@commitlint/cli@npm:20.0.0" +"@commitlint/cli@npm:20.1.0": + version: 20.1.0 + resolution: "@commitlint/cli@npm:20.1.0" dependencies: "@commitlint/format": ^20.0.0 "@commitlint/lint": ^20.0.0 - "@commitlint/load": ^20.0.0 + "@commitlint/load": ^20.1.0 "@commitlint/read": ^20.0.0 "@commitlint/types": ^20.0.0 tinyexec: ^1.0.0 yargs: ^17.0.0 bin: commitlint: ./cli.js - checksum: 83ac95aa47449b51337691b8e3a0d81dd6059025105eafdc6f7e9f66ff10f7afa98b6f979a760eb52d194051093cc2cc031b5fb89a26471a6574a5b13b0c9f5f + checksum: 8273860d2841dcc628bdafa3cb72bcb8f526d0a357ebdff3545e488c57a5e9f242943a2a39e89afa294595c86ee71dc7bf01b5936e80362eef84ce213d4ff2c9 languageName: node linkType: hard @@ -1656,7 +1656,7 @@ __metadata: languageName: node linkType: hard -"@commitlint/load@npm:^20.0.0": +"@commitlint/load@npm:^20.1.0": version: 20.1.0 resolution: "@commitlint/load@npm:20.1.0" dependencies: @@ -1828,19 +1828,12 @@ __metadata: languageName: node linkType: hard -"@eslint/config-helpers@npm:^0.3.1": - version: 0.3.1 - resolution: "@eslint/config-helpers@npm:0.3.1" - checksum: b95c239264078a430761afb344402d517134289a7d8b69a6ff1378ebe5eec9da6ad22b5e6d193b9e02899aeda30817ac47178d5927247092cc6d73a52f8d07c9 - languageName: node - linkType: hard - -"@eslint/core@npm:^0.15.2": - version: 0.15.2 - resolution: "@eslint/core@npm:0.15.2" +"@eslint/config-helpers@npm:^0.4.0": + version: 0.4.0 + resolution: "@eslint/config-helpers@npm:0.4.0" dependencies: - "@types/json-schema": ^7.0.15 - checksum: 535fc4e657760851826ceae325a72dde664b99189bd975715de3526db655c66d7a35b72dbb1c7641ab9201ed4e2130f79c5be51f96c820b5407c3766dcf94f23 + "@eslint/core": ^0.16.0 + checksum: f17af9d6de60e0d8be5131451ef489f32984f92aff00cb1c5c8f1790baf07ea7ad803e0f21f1519eded4ce247871ffe593b7e51ddc094b5337d22f29dd720ba5 languageName: node linkType: hard @@ -1870,10 +1863,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.36.0": - version: 9.36.0 - resolution: "@eslint/js@npm:9.36.0" - checksum: 17ff28272337357783b55e76417e61306e528dced99bb49d49e06298023b4071cb30f4aeb0bf30a337817d3eb3132784db6b8edd3a90118c5217833136712713 +"@eslint/js@npm:9.37.0": + version: 9.37.0 + resolution: "@eslint/js@npm:9.37.0" + checksum: 916f2ff7f70eadaa3a1c3f7d6d375fccfb676723484e1c54c5d63ff8a462746090097b73d21f4cb876ff2276d04af3f1c4c9e9a93729a9305213ca3aaa75008c languageName: node linkType: hard @@ -1884,13 +1877,13 @@ __metadata: languageName: node linkType: hard -"@eslint/plugin-kit@npm:^0.3.5": - version: 0.3.5 - resolution: "@eslint/plugin-kit@npm:0.3.5" +"@eslint/plugin-kit@npm:^0.4.0": + version: 0.4.0 + resolution: "@eslint/plugin-kit@npm:0.4.0" dependencies: - "@eslint/core": ^0.15.2 + "@eslint/core": ^0.16.0 levn: ^0.4.1 - checksum: 1808d7e2538335b8e4536ef372840e93468ecc6f4a5bf72ad665795290b6a8a72f51ef4ffd8bcfc601b133a5d5f67b59ab256d945f8c825c5c307aad29efaf86 + checksum: bb82be19c99eea256f7ec8e0996d28bd4b95b796bd1b27659b92e83278ef813485ada55995314887e7812cca02b0a9672d63f547c2a110eb5a7f0022c8e0f23d languageName: node linkType: hard @@ -3029,26 +3022,26 @@ __metadata: languageName: node linkType: hard -"@react-native/assets-registry@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/assets-registry@npm:0.82.0-rc.4" - checksum: fd60b6bb6c301bf8ed892cf74f387a533b0ae350109df89d26be73b4b23d6359682f5845b60942b3c10e7facad754f204de34212e1a96d0e20d51e884fa61e8a +"@react-native/assets-registry@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/assets-registry@npm:0.82.0" + checksum: 21416e3f4c088aeb56bd9f19ce88950ee629d7b4d044f36fde63757d9fbe2a81b4688538ae7d81bb046d922cfc3c4e1f51a2815fcfb7f0af2bb380f7e8c1bde8 languageName: node linkType: hard -"@react-native/babel-plugin-codegen@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/babel-plugin-codegen@npm:0.82.0-rc.4" +"@react-native/babel-plugin-codegen@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/babel-plugin-codegen@npm:0.82.0" dependencies: "@babel/traverse": ^7.25.3 - "@react-native/codegen": 0.82.0-rc.4 - checksum: 31ffcbb7cc10ab8e8da44912b2c7d1bb233dabf7314b7ab57d899a0028120fe862d567bce4ca79256e6decbf8553d3831ea01354d54888daf201aa90709c1188 + "@react-native/codegen": 0.82.0 + checksum: f9b8b64a325dc69dd6fe83682443f95cdb6c5d81a259ffbdedab1d5408bca5f144489ea04d250d0b23f5c6175ddb1fac4419a8a8339a0c2b673f0a8e079a83bd languageName: node linkType: hard -"@react-native/babel-preset@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/babel-preset@npm:0.82.0-rc.4" +"@react-native/babel-preset@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/babel-preset@npm:0.82.0" dependencies: "@babel/core": ^7.25.2 "@babel/plugin-proposal-export-default-from": ^7.24.7 @@ -3091,19 +3084,19 @@ __metadata: "@babel/plugin-transform-typescript": ^7.25.2 "@babel/plugin-transform-unicode-regex": ^7.24.7 "@babel/template": ^7.25.0 - "@react-native/babel-plugin-codegen": 0.82.0-rc.4 + "@react-native/babel-plugin-codegen": 0.82.0 babel-plugin-syntax-hermes-parser: 0.32.0 babel-plugin-transform-flow-enums: ^0.0.2 react-refresh: ^0.14.0 peerDependencies: "@babel/core": "*" - checksum: 5698ff1f611b148b8c8cafba6e13b9fea61bf098fae2007a380ad5a237e1ed3273d46dd9c4d2621cdbbb28248a8dcf62f329595f57845275690fde04fa001428 + checksum: f869a10d0dc3be8f6b43d7fd8dc3cb0f7ebe708ea275f847f665d0cba13c347b6100175e052f65d1eda56b30ec0c2f025e22443fc97b9888f7b4e4a37d3a7dbf languageName: node linkType: hard -"@react-native/codegen@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/codegen@npm:0.82.0-rc.4" +"@react-native/codegen@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/codegen@npm:0.82.0" dependencies: "@babel/core": ^7.25.2 "@babel/parser": ^7.25.3 @@ -3114,15 +3107,15 @@ __metadata: yargs: ^17.6.2 peerDependencies: "@babel/core": "*" - checksum: 71a04d211432778a5c091329572ecaf8af82e9c57d05bc84c1254e6ce9855b97bfba32172f87c0a3d0d32e3585c440d28628ccd0159b4f5eb35014f51b23249d + checksum: 939092807e511d2cd9c3110230644135deace8eaf80f389985549be3713ce8783168e51e9fc7037ce0f71c3f2a3261410f0abf37341e9675c820b2f70459c62b languageName: node linkType: hard -"@react-native/community-cli-plugin@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/community-cli-plugin@npm:0.82.0-rc.4" +"@react-native/community-cli-plugin@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/community-cli-plugin@npm:0.82.0" dependencies: - "@react-native/dev-middleware": 0.82.0-rc.4 + "@react-native/dev-middleware": 0.82.0 debug: ^4.4.0 invariant: ^2.2.4 metro: ^0.83.1 @@ -3137,34 +3130,34 @@ __metadata: optional: true "@react-native/metro-config": optional: true - checksum: 46fffd4edab31cd6840cf04cc278dd018005ae746a13643be587838e178390306e32d15510b885056be6e0e7c810b392c7ff58099d4a294a7791a8440c10fb55 + checksum: b62e337a88edab5c57cd4d50442184766950d91b27afdea9886f79e09609d09461d32e042caf9adcdcb325467cdaac04384296940da755261e6d138a255e65c6 languageName: node linkType: hard -"@react-native/debugger-frontend@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/debugger-frontend@npm:0.82.0-rc.4" - checksum: 53fde260c9669d33d2fef7ab00dc61512c5dda3194568a427b10ed1f4f0a36bc73db02c1402c42f27fd1ce8a1690f14ecaf450a523d80f727a85f901d449f022 +"@react-native/debugger-frontend@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/debugger-frontend@npm:0.82.0" + checksum: 2f7a9e97eded22e093e8d8475efbfa571a139e01239e9917839bab23f564620f7ae84abe9589780f4aaf54ae059af0a0a896664c382a1ccd80ce2373c61de91a languageName: node linkType: hard -"@react-native/debugger-shell@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/debugger-shell@npm:0.82.0-rc.4" +"@react-native/debugger-shell@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/debugger-shell@npm:0.82.0" dependencies: cross-spawn: ^7.0.6 fb-dotslash: 0.5.8 - checksum: 96f4356ce39e16d722057e822b56e19df3f7773eda073c0d58342ebd935171c86ca77b72d073f4661a8b52abc50c7546e036d688e53c35d2a0500115498cd9ea + checksum: 2b0be80d3d6a1d794edb6cade641dec10b8b34634cdb05ff7be864d95bf53d4ed801b9f8e3b03999bbc138b41e0b9669fe6cd641c25bfdcdfcada3f4fa4a47f7 languageName: node linkType: hard -"@react-native/dev-middleware@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/dev-middleware@npm:0.82.0-rc.4" +"@react-native/dev-middleware@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/dev-middleware@npm:0.82.0" dependencies: "@isaacs/ttlcache": ^1.4.1 - "@react-native/debugger-frontend": 0.82.0-rc.4 - "@react-native/debugger-shell": 0.82.0-rc.4 + "@react-native/debugger-frontend": 0.82.0 + "@react-native/debugger-shell": 0.82.0 chrome-launcher: ^0.15.2 chromium-edge-launcher: ^0.2.0 connect: ^3.6.5 @@ -3174,17 +3167,17 @@ __metadata: open: ^7.0.3 serve-static: ^1.16.2 ws: ^6.2.3 - checksum: 5ce52ac9642353695fd3002576d4b9fdcfc05dbd092f6780d866d989bf786b94f4927d2c140ef9e47138056e357f14f486ebdb9ff9b99f1ec2bf2a1df9c07c4e + checksum: d6b57ed8a96082c99802aa64d4d826f4750996e6259f0b724715f9688de39f41e426d3da5ef4732a595b517f691cc2896b599ebc6fca3e4026f5e72d92fea304 languageName: node linkType: hard -"@react-native/eslint-config@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/eslint-config@npm:0.82.0-rc.4" +"@react-native/eslint-config@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/eslint-config@npm:0.82.0" dependencies: "@babel/core": ^7.25.2 "@babel/eslint-parser": ^7.25.1 - "@react-native/eslint-plugin": 0.82.0-rc.4 + "@react-native/eslint-plugin": 0.82.0 "@typescript-eslint/eslint-plugin": ^8.36.0 "@typescript-eslint/parser": ^8.36.0 eslint-config-prettier: ^8.5.0 @@ -3197,74 +3190,74 @@ __metadata: peerDependencies: eslint: ">=8" prettier: ">=2" - checksum: b3fbd2c8c10b179ad6ee013128a8c18ab33b3b92940c0be13b304e30c1dfdbe4b917688f158e495ed3a3d716060d5d7b1d45266853b62acacaf7aca246d15045 + checksum: 2728a97fa1f784e3ee154e3a00b3b08821cd8d2fef9354a58508076be98be43bd8f8084e95c6a8e23654e583990cb5bcedc70fb10608b63220168ea9176c0c90 languageName: node linkType: hard -"@react-native/eslint-plugin@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/eslint-plugin@npm:0.82.0-rc.4" - checksum: 94af26bf74acb7a22c6a109fd999bdfe903a43678f9330f52a20b378a0537c6f4503f03b76b1ea191895079405cda22adaa4dfcaba5cd2f59308333b5c0919f8 +"@react-native/eslint-plugin@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/eslint-plugin@npm:0.82.0" + checksum: b456cde2911a90745ab811a577535a4c8afe602ff4f706dce6812fd5c2d5de7d2e426e2fc240aac405675065fc622983db83c657f54ca33eeda6d6c6440d8cdc languageName: node linkType: hard -"@react-native/gradle-plugin@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/gradle-plugin@npm:0.82.0-rc.4" - checksum: 06938bc4c1074988a1a96b4d9c05cc76f76dd0629ce4a54d79a277a4701b317430b5745fb822e99e6aa63c8e441be24e26e21d7062bf8fff930e1f078d6651f7 +"@react-native/gradle-plugin@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/gradle-plugin@npm:0.82.0" + checksum: 58cf9e293a4c8b038d262d0e8b1a3c0800793e3103ce0c0437f23318c3ea1a8aeea1747b506edbd4a450fa2b7b0fe5d7501a2e69cd74459ac418f785cf6acca4 languageName: node linkType: hard -"@react-native/js-polyfills@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/js-polyfills@npm:0.82.0-rc.4" - checksum: c602d2b8e14851d3554ae0a3238363a2606ffa889314c4dfa9dd5ad02acf5869849667d15a678650b3c5c52d84d9c1b8f07377797119300c8ee52db5a8c72402 +"@react-native/js-polyfills@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/js-polyfills@npm:0.82.0" + checksum: 364a8711a888e0cedf9b624eb594ddcd7e84c7bee63e7e46d7986c8306e91f4ad6673d562ba620ad3267f6d297d9c4a3a0c27e27d5eadd72ba2c14b5aeb3faa9 languageName: node linkType: hard -"@react-native/metro-babel-transformer@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/metro-babel-transformer@npm:0.82.0-rc.4" +"@react-native/metro-babel-transformer@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/metro-babel-transformer@npm:0.82.0" dependencies: "@babel/core": ^7.25.2 - "@react-native/babel-preset": 0.82.0-rc.4 + "@react-native/babel-preset": 0.82.0 hermes-parser: 0.32.0 nullthrows: ^1.1.1 peerDependencies: "@babel/core": "*" - checksum: 46c4d213c827a9a4cd839899e793b292b82ccf71505102e9573f28011240e42359268cedbe5ceff347c78639081ec67ad2467164bf5411f3257eb6f02022e49f + checksum: fb9decb29afa210157265df58f29d9ffba4fd990544db1f721e526f0b608e51f4915322103e229f0f6b15d9e15ff744d2ecf79c74f4207f4fe12397da517a8f6 languageName: node linkType: hard -"@react-native/metro-config@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/metro-config@npm:0.82.0-rc.4" +"@react-native/metro-config@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/metro-config@npm:0.82.0" dependencies: - "@react-native/js-polyfills": 0.82.0-rc.4 - "@react-native/metro-babel-transformer": 0.82.0-rc.4 + "@react-native/js-polyfills": 0.82.0 + "@react-native/metro-babel-transformer": 0.82.0 metro-config: ^0.83.1 metro-runtime: ^0.83.1 - checksum: ad9004198283a307f4ceb19cda8c89c83040b7a950225719bb31db7d837aa7d3851197420f70b8ba87efb8f937f4aa045d76d05102e25ef961d0f87054791a28 + checksum: 850213a281ee27a9fd9f7cfeb4372bb3427d1775417041293b6833e65e637ea87731752cfb124a61ed7a9e738b46f60a98c3420248915fb47792a028c135f03b languageName: node linkType: hard -"@react-native/normalize-colors@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/normalize-colors@npm:0.82.0-rc.4" - checksum: 2400f4998deaafdc3dfc530bbc3a1892fd18fc7d7973387f381f634f2c2a031045666911de590ebe558e55f1a6fb03dbec1623d2389ed0885add282c4c2bcbe8 +"@react-native/normalize-colors@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/normalize-colors@npm:0.82.0" + checksum: 850d79cc62b2230d374aa764e7d550dbeb30f32de1e7c5153a06c9b9231fa87c6331c627831a98469c3c3a3dbbec3a48096c9c78d91da6f12c36fc7acbd62f40 languageName: node linkType: hard -"@react-native/typescript-config@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/typescript-config@npm:0.82.0-rc.4" - checksum: 1903fbcbb942e826dba078aee4387974ce3c4f737ef7996d1ab89468d42749e79cf7fe4b4cf051df771c2c98bc67932e29669700177a4b500e83e04067373511 +"@react-native/typescript-config@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/typescript-config@npm:0.82.0" + checksum: 85ab797177854e01f7599c6deca933591b83232a818bce0f2e48eec79ca90a43e29be68ed704f09e345664c003723051f8aa95e2312f8506a055a40297163ba3 languageName: node linkType: hard -"@react-native/virtualized-lists@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "@react-native/virtualized-lists@npm:0.82.0-rc.4" +"@react-native/virtualized-lists@npm:0.82.0": + version: 0.82.0 + resolution: "@react-native/virtualized-lists@npm:0.82.0" dependencies: invariant: ^2.2.4 nullthrows: ^1.1.1 @@ -3275,7 +3268,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: abda1c9a79f6568700bb23551a0ef3e37bd95bd5f863f4910aa2a0a75b34907d9e5335f2a0f6e739da1e890956777ea20ddc111845e61a4f768b3e9c0cbe0931 + checksum: 5188b5d6163aad7dd5f635df940c06702035978b41e69fcb5341e8c8c73f4d8805ff927897512a237416ab71295bac93defd15bed4b0864339a2a32506c9e6a2 languageName: node linkType: hard @@ -3708,11 +3701,11 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 24.6.2 - resolution: "@types/node@npm:24.6.2" + version: 24.7.0 + resolution: "@types/node@npm:24.7.0" dependencies: - undici-types: ~7.13.0 - checksum: 95766998060f005403a1aea198c2c472fd1d695cb9e7cebb62adb0e3aceb871ae1201e90577df31a7c1d6a2c2fadccbd9a9868f9014cb77ebd3232104de8f4fb + undici-types: ~7.14.0 + checksum: 154e6113dae3e551386d37d9e84e15bbf2a81ee14700ce42815f123ff35904363ab86a5650f98b555a892f1502b45a0aaa91666a979ec8860d95b09179d7100f languageName: node linkType: hard @@ -3723,12 +3716,21 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:19.1.15": - version: 19.1.15 - resolution: "@types/react@npm:19.1.15" +"@types/react@npm:19.1.1": + version: 19.1.1 + resolution: "@types/react@npm:19.1.1" + dependencies: + csstype: ^3.0.2 + checksum: 1daebda3dfe30944c3322da6ae352697e4a81dc6a64125110f1610fd5b2a98581c2fc7ef8a896d8f91d01d92642a72c941ee283d0a914a04e0a78597642f9242 + languageName: node + linkType: hard + +"@types/react@npm:19.2.2": + version: 19.2.2 + resolution: "@types/react@npm:19.2.2" dependencies: csstype: ^3.0.2 - checksum: 3fcdeaddcca2ca09aadbce93c7f8105a46933f5f9ca8643b5f3599f41207c46a9982267e9095b67b2b69c5b4ac6e4450c74698cd99406baf8d31fa5333e86e22 + checksum: 7eb2d316dd5a6c02acb416524b50bae932c38d055d26e0f561ca23c009c686d16a2b22fcbb941eecbe2ecb167f119e29b9d0142d9d056dd381352c43413b60da languageName: node linkType: hard @@ -3756,105 +3758,105 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^8.36.0": - version: 8.45.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.45.0" + version: 8.46.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.46.0" dependencies: "@eslint-community/regexpp": ^4.10.0 - "@typescript-eslint/scope-manager": 8.45.0 - "@typescript-eslint/type-utils": 8.45.0 - "@typescript-eslint/utils": 8.45.0 - "@typescript-eslint/visitor-keys": 8.45.0 + "@typescript-eslint/scope-manager": 8.46.0 + "@typescript-eslint/type-utils": 8.46.0 + "@typescript-eslint/utils": 8.46.0 + "@typescript-eslint/visitor-keys": 8.46.0 graphemer: ^1.4.0 ignore: ^7.0.0 natural-compare: ^1.4.0 ts-api-utils: ^2.1.0 peerDependencies: - "@typescript-eslint/parser": ^8.45.0 + "@typescript-eslint/parser": ^8.46.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: ad912b7a3d43c190f648ecdcdfa26217f5f948360b03c9501e112c92fa4bdf37c0d12aafbe73001f1e98eaa1fefa88f9ff4509eca988dd575ddd8a734b5010b1 + checksum: b3a33bbdeffeefc5798abde387b440cfbc1c0ec6778ed2fe16238f10adae28193015ecf923f305bf9a67fcb108dced47216c9dbc6778736b6db5a97e71e212af languageName: node linkType: hard "@typescript-eslint/parser@npm:^8.36.0": - version: 8.45.0 - resolution: "@typescript-eslint/parser@npm:8.45.0" + version: 8.46.0 + resolution: "@typescript-eslint/parser@npm:8.46.0" dependencies: - "@typescript-eslint/scope-manager": 8.45.0 - "@typescript-eslint/types": 8.45.0 - "@typescript-eslint/typescript-estree": 8.45.0 - "@typescript-eslint/visitor-keys": 8.45.0 + "@typescript-eslint/scope-manager": 8.46.0 + "@typescript-eslint/types": 8.46.0 + "@typescript-eslint/typescript-estree": 8.46.0 + "@typescript-eslint/visitor-keys": 8.46.0 debug: ^4.3.4 peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: c7d416625003c4322fccf4ce5761e0666c9511bbfd75830c3949eecf29663c9d4474d2aa47801df4e4ff54a1f44849b10c2a88c28b5566a288ff369609a63a83 + checksum: 9447250aa770eee131d81475784404b2b07caacf9bae8cef38b9ee639d8225504849a5586b5746b575f2c5dfbc9c612eb742acd8612bb1c425245f324f574613 languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/project-service@npm:8.45.0" +"@typescript-eslint/project-service@npm:8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/project-service@npm:8.46.0" dependencies: - "@typescript-eslint/tsconfig-utils": ^8.45.0 - "@typescript-eslint/types": ^8.45.0 + "@typescript-eslint/tsconfig-utils": ^8.46.0 + "@typescript-eslint/types": ^8.46.0 debug: ^4.3.4 peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 6bde077c1ce8bea3abf34f714389c3cd60a927ae9a6dd15815b2b0a69af57bebd1045a69bb7f38fea6c6ef7051344855a044b7ab42ccd7083f2dc7e89542adb2 + checksum: ae8365cdbae5c8ee622727295f7cb59c42ccb0a4672d72692f2f31b26a052b7a9e46f58326740ca8d471a7e85998b885858be6c21921d465ce57de1d3ea7355f languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/scope-manager@npm:8.45.0" +"@typescript-eslint/scope-manager@npm:8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/scope-manager@npm:8.46.0" dependencies: - "@typescript-eslint/types": 8.45.0 - "@typescript-eslint/visitor-keys": 8.45.0 - checksum: b7e29552b81769b1be70e593a3e0e21d52ec8f8aa09a6f2f0f7c4dcd8e2a623488d1be0f43859c81ec5b2385992c9925a10d72780412fa29b3c78c7f6ead5486 + "@typescript-eslint/types": 8.46.0 + "@typescript-eslint/visitor-keys": 8.46.0 + checksum: 0995be736f153314b7744594b7b5e27e63cf7b00b64b3a8cf23b4f01fc9cc01b9e652e433da438fe93efe63e505d61adb5c25319fe25e9f0ccdfea1ad7848fba languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.45.0, @typescript-eslint/tsconfig-utils@npm:^8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.45.0" +"@typescript-eslint/tsconfig-utils@npm:8.46.0, @typescript-eslint/tsconfig-utils@npm:^8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.46.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 105cf3dbf858f193d2983f12fdc238af706d5221a76c32b76dc7a219f2b4842e51268cefbe4a5cf4e820a8db000430fa5bef2c91891cd19b11e0c7bd0a86dcb7 + checksum: d4516fb18c577a47f614efe6233354efefc582eaa4e915ae3d20c23f3b17e098b254594aa26d9c51eec1116d18665f06d9ed51229600df3ce3daecae83c76865 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/type-utils@npm:8.45.0" +"@typescript-eslint/type-utils@npm:8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/type-utils@npm:8.46.0" dependencies: - "@typescript-eslint/types": 8.45.0 - "@typescript-eslint/typescript-estree": 8.45.0 - "@typescript-eslint/utils": 8.45.0 + "@typescript-eslint/types": 8.46.0 + "@typescript-eslint/typescript-estree": 8.46.0 + "@typescript-eslint/utils": 8.46.0 debug: ^4.3.4 ts-api-utils: ^2.1.0 peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 3ebf10396a9588f922d76d389764a30220ea20958a2b97f901c8b49c5a5ba10ff7c1c9a9b35e6d9a824b9e73d96e7517a8e0cadd3e4c31fb1ec1005aa04397c7 + checksum: 864f7bc0df053089d09bc757abf4f728f6fc942e162baa727f24cf68d1d79f53ccd1dff151e74b0e43c25dc53d5ce32f916a2218786d365e1027d99c6799d6d9 languageName: node linkType: hard -"@typescript-eslint/types@npm:8.45.0, @typescript-eslint/types@npm:^8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/types@npm:8.45.0" - checksum: 924750faa94b3a9456ec36f1a048fb270449985f52fbe558c8327c8f5beea4ccd5c0bf9a1b2063368f449340ea9348de9a2bac9d3aa67221f77696d55fa5cb8f +"@typescript-eslint/types@npm:8.46.0, @typescript-eslint/types@npm:^8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/types@npm:8.46.0" + checksum: 71b7e0845da160cbd8ef1a5f853a1b8626f5bd00a1db56b75218eb94d5f3433f7815635e70df52118657c57109458f2e0d2bec8dcca0c620af10c66205fe54cd languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.45.0" +"@typescript-eslint/typescript-estree@npm:8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.46.0" dependencies: - "@typescript-eslint/project-service": 8.45.0 - "@typescript-eslint/tsconfig-utils": 8.45.0 - "@typescript-eslint/types": 8.45.0 - "@typescript-eslint/visitor-keys": 8.45.0 + "@typescript-eslint/project-service": 8.46.0 + "@typescript-eslint/tsconfig-utils": 8.46.0 + "@typescript-eslint/types": 8.46.0 + "@typescript-eslint/visitor-keys": 8.46.0 debug: ^4.3.4 fast-glob: ^3.3.2 is-glob: ^4.0.3 @@ -3863,32 +3865,32 @@ __metadata: ts-api-utils: ^2.1.0 peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: c0149120ffb480fa5ce53af4321c8d1ee145c394f5206049fea517675e6dbf34805dca514d9e7a2d4db9142dd2b2a8e12b282904e73acb023d6e56464c049103 + checksum: 70f5523d266097c96e5de2cf28c86c5bb3c9d4f48ba129a9c13e620733d395008dc809c77f1af19fc4617133c0665bf65a6a688fbf40da29d5a6ebe137ea41ae languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.45.0, @typescript-eslint/utils@npm:^8.0.0": - version: 8.45.0 - resolution: "@typescript-eslint/utils@npm:8.45.0" +"@typescript-eslint/utils@npm:8.46.0, @typescript-eslint/utils@npm:^8.0.0": + version: 8.46.0 + resolution: "@typescript-eslint/utils@npm:8.46.0" dependencies: "@eslint-community/eslint-utils": ^4.7.0 - "@typescript-eslint/scope-manager": 8.45.0 - "@typescript-eslint/types": 8.45.0 - "@typescript-eslint/typescript-estree": 8.45.0 + "@typescript-eslint/scope-manager": 8.46.0 + "@typescript-eslint/types": 8.46.0 + "@typescript-eslint/typescript-estree": 8.46.0 peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 76bff7c93661e5f0bcd2167b197dd1d81487f360b9e26ad1bf2ceb42226d5ee6170fb2f74d48a753433be352f4f44f5ed755ff3ad45f991756c3412f3ce6bedb + checksum: 63c9f4df8f823ef7f83fe2c53f85fd5e278d60240d41414f69c8ecb37061fec74ad34851faf28283042a1a0b983ddca57dbd97a7e653073068c7f22e919f84ea languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.45.0": - version: 8.45.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.45.0" +"@typescript-eslint/visitor-keys@npm:8.46.0": + version: 8.46.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.46.0" dependencies: - "@typescript-eslint/types": 8.45.0 + "@typescript-eslint/types": 8.46.0 eslint-visitor-keys: ^4.2.1 - checksum: 81df0a5dfcc569552ff962ff7529b077028a214ddc544557c41d88ae8c9ab21098f62f23efa4701ff21bce5ba99dae064465506f41bfe7c48c44e255a3a78edc + checksum: 888adc68bd8d80adb185520f2016b81a934f793db323cd62452027fad2e76a5ab64ed9500c4e5a2be2e5d2458e071776ea86a62e40e32faa4348ca4ab84dddda languageName: node linkType: hard @@ -4666,11 +4668,11 @@ __metadata: linkType: hard "baseline-browser-mapping@npm:^2.8.9": - version: 2.8.10 - resolution: "baseline-browser-mapping@npm:2.8.10" + version: 2.8.13 + resolution: "baseline-browser-mapping@npm:2.8.13" bin: baseline-browser-mapping: dist/cli.js - checksum: 42e28fb10472bf35abdcb881e599d8b968e3d0d723380a8e7ca1b0424811f111d89f78b1121d9c3cadf777e57af7a23cb0a3931e4135d94f70653b353fde0cba + checksum: 62c08d1b9c119c4796e82a65a7d3efd7d4b12abe4495b1112ff629dba0a7994da5359209f3f77618402f4f6f554d12b667252389fa6b5a08cbc8b9c3a137a009 languageName: node linkType: hard @@ -4908,9 +4910,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001746": - version: 1.0.30001746 - resolution: "caniuse-lite@npm:1.0.30001746" - checksum: 3d2a310b49bc414d87184a73ca7c3b1aea0a1547cc9e3bfd8a6ee45129d269917a94b8e773e7b2821cfc3b9e1256759a34e4b7242c56e3f70f575213baa6c880 + version: 1.0.30001749 + resolution: "caniuse-lite@npm:1.0.30001749" + checksum: 0a2692a7d51e4f4cecd2e8714e1d3d9982479fb59fa2fc8d6a462844bb7f5243ffe0bc94b25a1ff944f63bb2372ff5f6d01ef422729ca3c262975f1b91d78c07 languageName: node linkType: hard @@ -4999,9 +5001,9 @@ __metadata: linkType: hard "ci-info@npm:^4.0.0, ci-info@npm:^4.2.0, ci-info@npm:^4.3.0": - version: 4.3.0 - resolution: "ci-info@npm:4.3.0" - checksum: 77a851ec826e1fbcd993e0e3ef402e6a5e499c733c475af056b7808dea9c9ede53e560ed433020489a8efea2d824fd68ca203446c9988a0bac8475210b0d4491 + version: 4.3.1 + resolution: "ci-info@npm:4.3.1" + checksum: 66c159d92648e8a07acab0a3a0681bff6ccc39aa44916263208c4d97bbbeedbbc886d7611fd30c21df1aa624ce3c6fcdfde982e74689e3e014e064e1d0805f94 languageName: node linkType: hard @@ -5784,9 +5786,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.227": - version: 1.5.228 - resolution: "electron-to-chromium@npm:1.5.228" - checksum: fc7158588289f82ba16aaff2056b5faec1690e916981227e80c5ac759161f0c1613864745b8530a4eaff4b4b90c815b5a90615754bef6f604dd251e21cd2012a + version: 1.5.233 + resolution: "electron-to-chromium@npm:1.5.233" + checksum: 84c36a12b6099ef2584cee8e181f01e8efc4d1d81f1e5802c8beaae18d50ca03e9706267f6b93f3b95716ed084b5b628dfe340accf0d8b1670f714a90bccc4c0 languageName: node linkType: hard @@ -5875,11 +5877,11 @@ __metadata: linkType: hard "envinfo@npm:^7.13.0": - version: 7.15.0 - resolution: "envinfo@npm:7.15.0" + version: 7.17.0 + resolution: "envinfo@npm:7.17.0" bin: envinfo: dist/cli.js - checksum: 38595c11134ecb66a40289980d8ca82e89fdcd68849dd72560c1bbc3cfc55c867573b4150967707ff9ff2e5cad6f1d0cb6cc56c333a6eccdcd3533452141c0a8 + checksum: d09e6d2d5dea999f9b5e1a8c496337b5e470f843c046843603e28132a7f391eef18589735c5bc8cc529a3cd8848bd1d4750fe8851f5de7b9d0d6b1d2f415adf9 languageName: node linkType: hard @@ -6302,18 +6304,18 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.36.0": - version: 9.36.0 - resolution: "eslint@npm:9.36.0" +"eslint@npm:9.37.0": + version: 9.37.0 + resolution: "eslint@npm:9.37.0" dependencies: "@eslint-community/eslint-utils": ^4.8.0 "@eslint-community/regexpp": ^4.12.1 "@eslint/config-array": ^0.21.0 - "@eslint/config-helpers": ^0.3.1 - "@eslint/core": ^0.15.2 + "@eslint/config-helpers": ^0.4.0 + "@eslint/core": ^0.16.0 "@eslint/eslintrc": ^3.3.1 - "@eslint/js": 9.36.0 - "@eslint/plugin-kit": ^0.3.5 + "@eslint/js": 9.37.0 + "@eslint/plugin-kit": ^0.4.0 "@humanfs/node": ^0.16.6 "@humanwhocodes/module-importer": ^1.0.1 "@humanwhocodes/retry": ^0.4.2 @@ -6348,7 +6350,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 08a02a1d474cf7ea63ef9328e638751c939a1c08b99f7812f0f44a96e3b8346ab3bbca3af57da8b3e74cbc6619e41645fd3dcb3adda94d1cb826f02664e2d44c + checksum: 78e813174acef58d361d557a4d083d2d03f20cd70dd96f59973414305acaedf72bad52271c789174a19ee0407f8bece017ce42a05c89014b93e457d033285aeb languageName: node linkType: hard @@ -7356,11 +7358,11 @@ __metadata: linkType: hard "hosted-git-info@npm:^9.0.0": - version: 9.0.0 - resolution: "hosted-git-info@npm:9.0.0" + version: 9.0.1 + resolution: "hosted-git-info@npm:9.0.1" dependencies: lru-cache: ^11.1.0 - checksum: 24dcc6fadba826c2a2079c0f39acf3897cb09fd7853cfd24d21f2a055d259352b32b5e798f2e3c534506cfa5a0278a4c7b61a9e04d617106d2326160898e02d2 + checksum: 7079026b3ae80868452817be8c32014f24f9cd4166bd9cc115af4e4cf2620f35a2d02ccee5ac5c2fc7901a7fc00029fd50e8136f22e45519a20e69f19a572b65 languageName: node linkType: hard @@ -9035,90 +9037,90 @@ __metadata: languageName: node linkType: hard -"lefthook-darwin-arm64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-darwin-arm64@npm:1.13.5" +"lefthook-darwin-arm64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-darwin-arm64@npm:1.13.6" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"lefthook-darwin-x64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-darwin-x64@npm:1.13.5" +"lefthook-darwin-x64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-darwin-x64@npm:1.13.6" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"lefthook-freebsd-arm64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-freebsd-arm64@npm:1.13.5" +"lefthook-freebsd-arm64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-freebsd-arm64@npm:1.13.6" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"lefthook-freebsd-x64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-freebsd-x64@npm:1.13.5" +"lefthook-freebsd-x64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-freebsd-x64@npm:1.13.6" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"lefthook-linux-arm64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-linux-arm64@npm:1.13.5" +"lefthook-linux-arm64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-linux-arm64@npm:1.13.6" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"lefthook-linux-x64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-linux-x64@npm:1.13.5" +"lefthook-linux-x64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-linux-x64@npm:1.13.6" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"lefthook-openbsd-arm64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-openbsd-arm64@npm:1.13.5" +"lefthook-openbsd-arm64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-openbsd-arm64@npm:1.13.6" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"lefthook-openbsd-x64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-openbsd-x64@npm:1.13.5" +"lefthook-openbsd-x64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-openbsd-x64@npm:1.13.6" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"lefthook-windows-arm64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-windows-arm64@npm:1.13.5" +"lefthook-windows-arm64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-windows-arm64@npm:1.13.6" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"lefthook-windows-x64@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook-windows-x64@npm:1.13.5" +"lefthook-windows-x64@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook-windows-x64@npm:1.13.6" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"lefthook@npm:1.13.5": - version: 1.13.5 - resolution: "lefthook@npm:1.13.5" +"lefthook@npm:1.13.6": + version: 1.13.6 + resolution: "lefthook@npm:1.13.6" dependencies: - lefthook-darwin-arm64: 1.13.5 - lefthook-darwin-x64: 1.13.5 - lefthook-freebsd-arm64: 1.13.5 - lefthook-freebsd-x64: 1.13.5 - lefthook-linux-arm64: 1.13.5 - lefthook-linux-x64: 1.13.5 - lefthook-openbsd-arm64: 1.13.5 - lefthook-openbsd-x64: 1.13.5 - lefthook-windows-arm64: 1.13.5 - lefthook-windows-x64: 1.13.5 + lefthook-darwin-arm64: 1.13.6 + lefthook-darwin-x64: 1.13.6 + lefthook-freebsd-arm64: 1.13.6 + lefthook-freebsd-x64: 1.13.6 + lefthook-linux-arm64: 1.13.6 + lefthook-linux-x64: 1.13.6 + lefthook-openbsd-arm64: 1.13.6 + lefthook-openbsd-x64: 1.13.6 + lefthook-windows-arm64: 1.13.6 + lefthook-windows-x64: 1.13.6 dependenciesMeta: lefthook-darwin-arm64: optional: true @@ -9142,7 +9144,7 @@ __metadata: optional: true bin: lefthook: bin/index.js - checksum: 33c550a87c61c0428db4f86d264dacae20bc8e08bd0078b7facfc6aa5e64690f09451bc549a453a10e7f2e26e4548f9054460fdb9c9420565a1b6e33b4045c07 + checksum: 3f84a085285e58026774228a8d84dbb812e1e4556e1766a603bf824ca73ae2fd60733b09bac44e3b8846054dcbc34a950b16c1e43f66538e2de2dcf6138237bb languageName: node linkType: hard @@ -10135,11 +10137,11 @@ __metadata: linkType: hard "napi-postinstall@npm:^0.3.0": - version: 0.3.3 - resolution: "napi-postinstall@npm:0.3.3" + version: 0.3.4 + resolution: "napi-postinstall@npm:0.3.4" bin: napi-postinstall: lib/cli.js - checksum: b18f36be61045821423f6fdfa68fcf27ef781d2f7d65ef16c611ee2d815439c7db0c2482f3982d26b0bdafbaaa0e8387cbc84172080079c506364686971d76fb + checksum: 01672ae6568e2b3a6d985371f1504a6e1c791aa308b94c9f89736fde8251b7b8ab3227d1a5ede8d0eb0552099e069970b038c6958052c01b2bdc5aae31f0a88c languageName: node linkType: hard @@ -10247,9 +10249,9 @@ __metadata: linkType: hard "node-releases@npm:^2.0.21": - version: 2.0.21 - resolution: "node-releases@npm:2.0.21" - checksum: 191f8245e18272971650eb45151c5891313bca27507a8f634085bd8c98a9cb9492686ef6182176866ceebff049646ef6cd5fb5ca46d5b5ca00ce2c69185d84c4 + version: 2.0.23 + resolution: "node-releases@npm:2.0.23" + checksum: dc3194ffdf04975f8525a5e175c03f5a95cecd7607b6b0e80d28aaa03900706d920722b5f2ae2e8e28e029e6ae75f0d0f7eae87e8ee2a363c704785e3118f13d languageName: node linkType: hard @@ -10340,14 +10342,14 @@ __metadata: linkType: hard "npm-package-arg@npm:^13.0.0": - version: 13.0.0 - resolution: "npm-package-arg@npm:13.0.0" + version: 13.0.1 + resolution: "npm-package-arg@npm:13.0.1" dependencies: hosted-git-info: ^9.0.0 proc-log: ^5.0.0 semver: ^7.3.5 validate-npm-package-name: ^6.0.0 - checksum: 6c2dc4029f6633300dfcc7223dcdcee713014e3702daee76410dfe48e8e93d4db35703721569fcec3fdeb03fefa398eb38b799d6e9af46b92cc8162827eb9fa7 + checksum: aba57acfaa0f18c42e3fe432cafd5e316b4dffa8c2c3ddaa36c2af3186968eb9cd89fcd5dfb18410fbbcb4ffc3b2b0cf02b2c0630d5642b33ab2f3fb500aa2b5 languageName: node linkType: hard @@ -11446,15 +11448,15 @@ __metadata: "@react-native-community/cli": 20.0.2 "@react-native-community/cli-platform-android": 20.0.2 "@react-native-community/cli-platform-ios": 20.0.2 - "@react-native/babel-preset": 0.82.0-rc.4 - "@react-native/metro-config": 0.82.0-rc.4 - "@react-native/typescript-config": 0.82.0-rc.4 - "@types/react": 19.1.15 + "@react-native/babel-preset": 0.82.0 + "@react-native/metro-config": 0.82.0 + "@react-native/typescript-config": 0.82.0 + "@types/react": 19.1.1 react: 19.1.1 - react-native: 0.82.0-rc.4 + react-native: 0.82.0 react-native-builder-bob: 0.40.13 react-native-monorepo-config: 0.2.1 - react-native-nitro-modules: 0.29.6 + react-native-nitro-modules: 0.29.8 languageName: unknown linkType: soft @@ -11462,36 +11464,36 @@ __metadata: version: 0.0.0-use.local resolution: "react-native-google-maps-plus@workspace:." dependencies: - "@commitlint/cli": 20.0.0 + "@commitlint/cli": 20.1.0 "@commitlint/config-conventional": 20.0.0 "@eslint/compat": 1.4.0 "@eslint/eslintrc": 3.3.1 - "@eslint/js": 9.36.0 + "@eslint/js": 9.37.0 "@jamesacarr/eslint-formatter-github-actions": 0.2.0 - "@react-native/babel-preset": 0.82.0-rc.4 - "@react-native/eslint-config": 0.82.0-rc.4 + "@react-native/babel-preset": 0.82.0 + "@react-native/eslint-config": 0.82.0 "@semantic-release/changelog": 6.0.3 "@semantic-release/git": 10.0.1 "@semantic-release/npm": 13.0.0-beta.1 "@types/jest": 30.0.0 - "@types/react": 19.1.15 + "@types/react": 19.2.2 clang-format-node: 2.0.1 conventional-changelog-conventionalcommits: 9.1.0 del-cli: 7.0.0 - eslint: 9.36.0 + eslint: 9.37.0 eslint-config-prettier: 10.1.8 eslint-plugin-ft-flow: 3.0.11 eslint-plugin-prettier: 5.5.4 jest: 30.2.0 - lefthook: 1.13.5 + lefthook: 1.13.6 nitrogen: 0.29.6 prettier: 3.6.2 - react: 19.1.1 - react-native: 0.82.0-rc.4 + react: 19.2.0 + react-native: 0.82.0 react-native-builder-bob: 0.40.13 - react-native-nitro-modules: 0.29.6 + react-native-nitro-modules: 0.29.8 semantic-release: 25.0.0-beta.6 - typescript: 5.9.2 + typescript: 5.9.3 peerDependencies: react: "*" react-native: "*" @@ -11519,17 +11521,7 @@ __metadata: languageName: node linkType: hard -"react-native-nitro-modules@npm:0.29.6": - version: 0.29.6 - resolution: "react-native-nitro-modules@npm:0.29.6" - peerDependencies: - react: "*" - react-native: "*" - checksum: 3a11d79aca124fb03462b90a6b75056d423fcbcc51312e62196143e71c259c7cb56692bca35b5d22c8a7d7dbb769843698c3b1e0727f58a1daf90114351368b1 - languageName: node - linkType: hard - -"react-native-nitro-modules@npm:^0.29.6": +"react-native-nitro-modules@npm:0.29.8, react-native-nitro-modules@npm:^0.29.6": version: 0.29.8 resolution: "react-native-nitro-modules@npm:0.29.8" peerDependencies: @@ -11539,18 +11531,18 @@ __metadata: languageName: node linkType: hard -"react-native@npm:0.82.0-rc.4": - version: 0.82.0-rc.4 - resolution: "react-native@npm:0.82.0-rc.4" +"react-native@npm:0.82.0": + version: 0.82.0 + resolution: "react-native@npm:0.82.0" dependencies: "@jest/create-cache-key-function": ^29.7.0 - "@react-native/assets-registry": 0.82.0-rc.4 - "@react-native/codegen": 0.82.0-rc.4 - "@react-native/community-cli-plugin": 0.82.0-rc.4 - "@react-native/gradle-plugin": 0.82.0-rc.4 - "@react-native/js-polyfills": 0.82.0-rc.4 - "@react-native/normalize-colors": 0.82.0-rc.4 - "@react-native/virtualized-lists": 0.82.0-rc.4 + "@react-native/assets-registry": 0.82.0 + "@react-native/codegen": 0.82.0 + "@react-native/community-cli-plugin": 0.82.0 + "@react-native/gradle-plugin": 0.82.0 + "@react-native/js-polyfills": 0.82.0 + "@react-native/normalize-colors": 0.82.0 + "@react-native/virtualized-lists": 0.82.0 abort-controller: ^3.0.0 anser: ^1.4.9 ansi-regex: ^5.0.0 @@ -11586,7 +11578,7 @@ __metadata: optional: true bin: react-native: cli.js - checksum: 7a70cea3d6f1e7096e3e374be6d064ab4f1cbb8d034a2b0186653c35d301e960f742a31d0ec6045132f7c999ed378909354d357c64fb2720335e4fee0341ee8c + checksum: 05a4c66a2f54e8ee9d2a8dd4e63b39298cc432672c9d975d1472398562bb8f26424027194e926ee854cf14b331a6c255bd206f00bd87bb2e119b2fc7ac3f8ea7 languageName: node linkType: hard @@ -11604,6 +11596,13 @@ __metadata: languageName: node linkType: hard +"react@npm:19.2.0": + version: 19.2.0 + resolution: "react@npm:19.2.0" + checksum: 33dd01bf699e1c5040eb249e0f552519adf7ee90b98c49d702a50bf23af6852ea46023a5f7f93966ab10acd7a45428fa0f193c686ecdaa7a75a03886e53ec3fe + languageName: node + linkType: hard + "read-cmd-shim@npm:^5.0.0": version: 5.0.0 resolution: "read-cmd-shim@npm:5.0.0" @@ -12031,11 +12030,11 @@ __metadata: linkType: hard "semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.2": - version: 7.7.2 - resolution: "semver@npm:7.7.2" + version: 7.7.3 + resolution: "semver@npm:7.7.3" bin: semver: bin/semver.js - checksum: dd94ba8f1cbc903d8eeb4dd8bf19f46b3deb14262b6717d0de3c804b594058ae785ef2e4b46c5c3b58733c99c83339068203002f9e37cfe44f7e2cc5e3d2f621 + checksum: f013a3ee4607857bcd3503b6ac1d80165f7f8ea94f5d55e2d3e33df82fce487aa3313b987abf9b39e0793c83c9fc67b76c36c067625141a9f6f704ae0ea18db2 languageName: node linkType: hard @@ -13116,23 +13115,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.9.2": - version: 5.9.2 - resolution: "typescript@npm:5.9.2" +"typescript@npm:5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f619cf6773cfe31409279711afd68cdf0859780006c50bc2a7a0c3227f85dea89a3b97248846326f3a17dad72ea90ec27cf61a8387772c680b2252fd02d8497b + checksum: 0d0ffb84f2cd072c3e164c79a2e5a1a1f4f168e84cb2882ff8967b92afe1def6c2a91f6838fb58b168428f9458c57a2ba06a6737711fdd87a256bbe83e9a217f languageName: node linkType: hard -"typescript@patch:typescript@5.9.2#~builtin": - version: 5.9.2 - resolution: "typescript@patch:typescript@npm%3A5.9.2#~builtin::version=5.9.2&hash=14eedb" +"typescript@patch:typescript@5.9.3#~builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#~builtin::version=5.9.3&hash=14eedb" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: e42a701947325500008334622321a6ad073f842f5e7d5e7b588a6346b31fdf51d56082b9ce5cef24312ecd3e48d6c0d4d44da7555f65e2feec18cf62ec540385 + checksum: 8bb8d86819ac86a498eada254cad7fb69c5f74778506c700c2a712daeaff21d3a6f51fd0d534fe16903cb010d1b74f89437a3d02d4d0ff5ca2ba9a4660de8497 languageName: node linkType: hard @@ -13164,10 +13163,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~7.13.0": - version: 7.13.0 - resolution: "undici-types@npm:7.13.0" - checksum: fcb3e1195a36615fce3935eb97c21ebe4dbafe968f831ed00e6f22e8e73c0655b8e3242acc6ba4ff0f3c34e3f3f860f19fbb59c00b261bd4e20b515abbc2de7c +"undici-types@npm:~7.14.0": + version: 7.14.0 + resolution: "undici-types@npm:7.14.0" + checksum: bd28cb36b33a51359f02c27b84bfe8563cdad57bdab0aa6ac605ce64d51aff49fd0aa4cb2d3b043caaa93c3ec42e96b5757df5d2d9bcc06a5f3e71899c765035 languageName: node linkType: hard @@ -13848,8 +13847,8 @@ __metadata: linkType: hard "zod@npm:^4.0.5": - version: 4.1.11 - resolution: "zod@npm:4.1.11" - checksum: 022d59f85ebe054835fbcdc96a93c01479a64321104f846cb5644812c91e00d17ae3479f823956ec9b04e4351dd32841e1f12c567e81bc43f6e21ef5cc02ce3c + version: 4.1.12 + resolution: "zod@npm:4.1.12" + checksum: 91174acc7d2ca5572ad522643474ddd60640cf6877b5d76e5d583eb25e3c4072c6f5eb92ab94f231ec5ce61c6acdfc3e0166de45fb1005b1ea54986b026b765f languageName: node linkType: hard