react-native-pointr 9.2.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.vscode/settings.json +21 -0
  2. package/API_REFERENCE.md +887 -0
  3. package/CHANGELOG.md +45 -0
  4. package/EXTENDING.md +419 -0
  5. package/README.md +221 -117
  6. package/WAYFINDING_EVENTS.md +243 -0
  7. package/android/build.gradle +4 -5
  8. package/android/src/main/java/com/pointr/PTRCoreExtensions.kt +126 -0
  9. package/android/src/main/java/com/pointr/PTRMapWidgetCommandType.kt +2 -1
  10. package/android/src/main/java/com/pointr/PTRMapWidgetManager.kt +78 -16
  11. package/android/src/main/java/com/pointr/PointrModule.kt +106 -3
  12. package/example/pointr_rn_demo/.bundle/config +2 -0
  13. package/example/pointr_rn_demo/.eslintrc.js +4 -0
  14. package/example/pointr_rn_demo/.prettierrc.js +5 -0
  15. package/example/pointr_rn_demo/.watchmanconfig +1 -0
  16. package/example/pointr_rn_demo/App.tsx +323 -0
  17. package/example/pointr_rn_demo/Gemfile +16 -0
  18. package/example/pointr_rn_demo/Gemfile.lock +111 -0
  19. package/example/pointr_rn_demo/README.md +188 -0
  20. package/example/pointr_rn_demo/__tests__/App.test.tsx +13 -0
  21. package/example/pointr_rn_demo/android/app/build.gradle +119 -0
  22. package/example/pointr_rn_demo/android/app/debug.keystore +0 -0
  23. package/example/pointr_rn_demo/android/app/proguard-rules.pro +10 -0
  24. package/example/pointr_rn_demo/android/app/src/main/AndroidManifest.xml +27 -0
  25. package/example/pointr_rn_demo/android/app/src/main/java/com/pointr_rn_demo/MainActivity.kt +22 -0
  26. package/example/pointr_rn_demo/android/app/src/main/java/com/pointr_rn_demo/MainApplication.kt +27 -0
  27. package/example/pointr_rn_demo/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  28. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  29. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  30. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  31. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  32. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  33. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  34. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  35. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  36. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  37. package/example/pointr_rn_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  38. package/example/pointr_rn_demo/android/app/src/main/res/values/strings.xml +3 -0
  39. package/example/pointr_rn_demo/android/app/src/main/res/values/styles.xml +9 -0
  40. package/example/pointr_rn_demo/android/build.gradle +34 -0
  41. package/example/pointr_rn_demo/android/gradle.properties +44 -0
  42. package/example/pointr_rn_demo/android/settings.gradle +6 -0
  43. package/example/pointr_rn_demo/app.json +4 -0
  44. package/example/pointr_rn_demo/babel.config.js +3 -0
  45. package/example/pointr_rn_demo/index.js +16 -0
  46. package/example/pointr_rn_demo/ios/.xcode.env +11 -0
  47. package/example/pointr_rn_demo/ios/Podfile +40 -0
  48. package/example/pointr_rn_demo/ios/pointr_rn_demo/AppDelegate.swift +48 -0
  49. package/example/pointr_rn_demo/ios/pointr_rn_demo/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  50. package/example/pointr_rn_demo/ios/pointr_rn_demo/Images.xcassets/Contents.json +6 -0
  51. package/example/pointr_rn_demo/ios/pointr_rn_demo/Info.plist +63 -0
  52. package/example/pointr_rn_demo/ios/pointr_rn_demo/LaunchScreen.storyboard +47 -0
  53. package/example/pointr_rn_demo/ios/pointr_rn_demo/PrivacyInfo.xcprivacy +37 -0
  54. package/example/pointr_rn_demo/ios/pointr_rn_demo.xcodeproj/project.pbxproj +488 -0
  55. package/example/pointr_rn_demo/ios/pointr_rn_demo.xcodeproj/xcshareddata/xcschemes/pointr_rn_demo.xcscheme +88 -0
  56. package/example/pointr_rn_demo/ios/pointr_rn_demo.xcworkspace/contents.xcworkspacedata +10 -0
  57. package/example/pointr_rn_demo/jest.config.js +3 -0
  58. package/example/pointr_rn_demo/metro.config.js +22 -0
  59. package/example/pointr_rn_demo/package.json +47 -0
  60. package/example/pointr_rn_demo/prepare-demo-distribution.sh +103 -0
  61. package/example/pointr_rn_demo/tsconfig.json +5 -0
  62. package/ios/PTRMapWidgetContainerView.swift +56 -5
  63. package/ios/PTRMapWidgetManager-Bridging.m +7 -0
  64. package/ios/PTRMapWidgetManager.swift +28 -0
  65. package/ios/PTRNativeLibrary-Bridging.m +4 -0
  66. package/ios/PTRNativeLibrary.swift +208 -2
  67. package/package.json +16 -2
  68. package/prepare-distribution.sh +151 -0
  69. package/react-native-pointr.podspec +1 -1
  70. package/src/PTRCommand.ts +13 -0
  71. package/src/PTRMapWidgetUtils.ts +10 -1
  72. package/src/PTRPoiManager.ts +20 -0
  73. package/src/index.tsx +40 -1
  74. package/src/types/PTRGeometry.ts +70 -0
  75. package/src/types/PTRPoi.ts +49 -0
  76. package/src/types/PTRPosition.ts +22 -0
  77. package/src/types/PTRWayfindingEvent.ts +18 -0
@@ -0,0 +1,22 @@
1
+ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Metro configuration
6
+ * https://reactnative.dev/docs/metro
7
+ *
8
+ * @type {import('@react-native/metro-config').MetroConfig}
9
+ */
10
+ const config = {
11
+ watchFolders: [
12
+ path.resolve(__dirname, '../../../react-native-pointr'),
13
+ ],
14
+ resolver: {
15
+ nodeModulesPaths: [
16
+ path.resolve(__dirname, 'node_modules'),
17
+ path.resolve(__dirname, '../../../react-native-pointr/node_modules'),
18
+ ],
19
+ },
20
+ };
21
+
22
+ module.exports = mergeConfig(getDefaultConfig(__dirname), config);
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "pointr_rn_demo",
3
+ "version": "9.5.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "clean": "watchman watch-del-all || true && rm -rf node_modules ios/Pods ios/Podfile.lock ios/build android/.gradle android/app/build android/build",
7
+ "reset": "npm run clean && npm install && cd ios && pod install && cd ..",
8
+ "start": "npx react-native start",
9
+ "start:clean": "npx react-native start --reset-cache",
10
+ "android": "npx react-native run-android",
11
+ "ios": "npx react-native run-ios",
12
+ "lint": "eslint .",
13
+ "test": "jest",
14
+ "prepare-dist": "chmod +x prepare-demo-distribution.sh && ./prepare-demo-distribution.sh"
15
+ },
16
+ "dependencies": {
17
+ "@react-native/new-app-screen": "0.82.1",
18
+ "react": "19.1.1",
19
+ "react-native": "^0.82.1",
20
+ "react-native-pointr": "file:../../../react-native-pointr",
21
+ "react-native-safe-area-context": "^5.6.2"
22
+ },
23
+ "devDependencies": {
24
+ "@babel/core": "^7.25.2",
25
+ "@babel/preset-env": "^7.25.3",
26
+ "@babel/runtime": "^7.25.0",
27
+ "@react-native-community/cli": "20.0.0",
28
+ "@react-native-community/cli-platform-android": "20.0.0",
29
+ "@react-native-community/cli-platform-ios": "20.0.0",
30
+ "@react-native/babel-preset": "0.82.1",
31
+ "@react-native/eslint-config": "0.82.1",
32
+ "@react-native/gradle-plugin": "^0.83.1",
33
+ "@react-native/metro-config": "0.82.1",
34
+ "@react-native/typescript-config": "0.82.1",
35
+ "@types/jest": "^29.5.13",
36
+ "@types/react": "^19.1.1",
37
+ "@types/react-test-renderer": "^19.1.0",
38
+ "eslint": "^8.19.0",
39
+ "jest": "^29.6.3",
40
+ "prettier": "2.8.8",
41
+ "react-test-renderer": "19.1.1",
42
+ "typescript": "^5.8.3"
43
+ },
44
+ "engines": {
45
+ "node": ">=20"
46
+ }
47
+ }
@@ -0,0 +1,103 @@
1
+ #!/bin/bash
2
+
3
+ # Script to prepare pointr_rn_demo project for distribution
4
+ # This script cleans build artifacts, removes sensitive information, and creates a compressed archive
5
+
6
+ set -e # Exit on error
7
+
8
+ PROJECT_NAME="pointr_rn_demo"
9
+ VERSION=$(grep -o '"version": "[^"]*"' package.json | head -1 | sed 's/"version": "\(.*\)"/\1/')
10
+ OUTPUT_NAME="${PROJECT_NAME}-${VERSION}.zip"
11
+ TEMP_DIR="${PROJECT_NAME}_temp"
12
+ DIST_DIR="../../dist"
13
+
14
+ # Create dist directory if it doesn't exist and get absolute path
15
+ mkdir -p "${DIST_DIR}"
16
+ DIST_DIR_ABS=$(cd "${DIST_DIR}" && pwd)
17
+
18
+ echo "🧹 Starting demo project cleanup and preparation..."
19
+ echo "📌 Project version: ${VERSION}"
20
+
21
+ # Create a temporary copy of the project
22
+ echo "📦 Creating temporary copy..."
23
+ rsync -a --exclude="node_modules" \
24
+ --exclude="ios/Pods" \
25
+ --exclude="ios/build" \
26
+ --exclude="ios/Podfile.lock" \
27
+ --exclude="android/build" \
28
+ --exclude="android/app/build" \
29
+ --exclude="android/.gradle" \
30
+ --exclude="android/local.properties" \
31
+ --exclude=".DS_Store" \
32
+ --exclude="*.log" \
33
+ --exclude=".git" \
34
+ --exclude="prepare-demo-distribution.sh" \
35
+ ./ "../${TEMP_DIR}/"
36
+
37
+ cd "../${TEMP_DIR}"
38
+
39
+ # Remove sensitive information from App.tsx
40
+ echo "🔒 Removing sensitive information..."
41
+ if [ -f "App.tsx" ]; then
42
+ # Replace credentials with placeholder values
43
+ sed -i '' 's/"1ee58e56-fc35-4ec4-b32e-5d49e65fb52d"/"YOUR_CLIENT_ID"/g' App.tsx
44
+ sed -i '' 's/"af5d9431-56a3-46a9-ae0a-65d9757528d1"/"YOUR_LICENSE_KEY"/g' App.tsx
45
+ sed -i '' 's|"https://sample-app-v9.pointr.cloud"|"https://your-instance.pointr.cloud"|g' App.tsx
46
+ echo "✓ Replaced credentials in App.tsx"
47
+ fi
48
+
49
+ # Remove sensitive information from Android build.gradle
50
+ if [ -f "android/build.gradle" ]; then
51
+ sed -i '' "s/username 'discover'/username 'YOUR_MAVEN_USERNAME'/g" android/build.gradle
52
+ sed -i '' "s/password 'AKCp5ejy2i3iwwJbXZzCTvtgcarhYWLztx1TCxXzBdhhMK4Qjhp945jz7JwtMK5SBoARGLkdF'/password 'YOUR_MAVEN_PASSWORD'/g" android/build.gradle
53
+ echo "✓ Replaced Maven credentials in android/build.gradle"
54
+ fi
55
+
56
+ # Remove sensitive information from iOS Podfile
57
+ if [ -f "ios/Podfile" ]; then
58
+ sed -i '' "s|ENV\['POINTR_SDK_TOKEN'\] = 'sv=2023-01-03&st=2024-10-23T06%3A40%3A13Z&se=2035-11-08T06%3A40%3A00Z&sr=c&sp=r&sig=WaCbhxt4pMqf9Oqzgf%2FnDj1SbdvyzwCtFdsWLW4KFqA%3D'|ENV['POINTR_SDK_TOKEN'] = 'YOUR_POINTR_SDK_TOKEN'|g" ios/Podfile
59
+ echo "✓ Replaced Pointr SDK token in ios/Podfile"
60
+ fi
61
+
62
+ # Clean any remaining build artifacts
63
+ echo "🤖 Cleaning build artifacts..."
64
+ rm -rf node_modules
65
+ rm -rf ios/Pods ios/build ios/Podfile.lock
66
+ rm -rf android/build android/app/build android/.gradle
67
+ rm -f android/local.properties
68
+ rm -rf .gradle
69
+ rm -f .DS_Store
70
+ rm -f *.log npm-debug.log* yarn-debug.log* yarn-error.log*
71
+ rm -f package-lock.json yarn.lock
72
+
73
+ # Remove IDE and development files
74
+ echo "🗑️ Removing development files..."
75
+ rm -rf .vscode .idea
76
+ rm -rf .git .gitignore .github
77
+ rm -f .watchmanconfig
78
+
79
+ # Create the zip archive
80
+ echo "📦 Creating compressed archive..."
81
+ cd ..
82
+ zip -r "${OUTPUT_NAME}" "${TEMP_DIR}" -x "*.DS_Store" -q
83
+
84
+ # Move the archive to dist directory
85
+ mv "${OUTPUT_NAME}" "${DIST_DIR_ABS}/"
86
+
87
+ # Clean up temporary directory
88
+ echo "🧼 Cleaning up temporary files..."
89
+ rm -rf "${TEMP_DIR}"
90
+
91
+ cd "${PROJECT_NAME}"
92
+
93
+ echo "✅ Demo distribution package created successfully!"
94
+ echo "📦 Output file: ${OUTPUT_NAME}"
95
+ echo "📍 Location: ${DIST_DIR_ABS}/${OUTPUT_NAME}"
96
+ echo ""
97
+ echo "⚠️ Note: All sensitive credentials have been replaced with placeholders:"
98
+ echo " • App.tsx: YOUR_CLIENT_ID, YOUR_LICENSE_KEY, your-instance.pointr.cloud"
99
+ echo " • android/build.gradle: YOUR_MAVEN_USERNAME, YOUR_MAVEN_PASSWORD"
100
+ echo " • ios/Podfile: YOUR_POINTR_SDK_TOKEN"
101
+ echo ""
102
+ echo " Recipients must configure all credentials before running the app."
103
+ echo " See README.md in the package for detailed setup instructions."
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@react-native/typescript-config",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["**/node_modules", "**/Pods"]
5
+ }
@@ -1,29 +1,79 @@
1
1
  import UIKit
2
2
  import PointrKit
3
3
 
4
- @objc class PTRMapWidgetContainerView: UIView, PTRSiteManagerDelegate, PTRDataManagerDelegate, PTRWayfindingManagerDelegate {
4
+ @objc class PTRMapWidgetContainerView: UIView, PTRSiteManagerDelegate, PTRDataManagerDelegate, PTRWayfindingManagerDelegate, PTRMapWidgetEventsListener {
5
5
 
6
6
  @objc var onMapWidgetDidEndLoading: RCTBubblingEventBlock?
7
+
8
+ @objc var onWayfindingEvent: RCTBubblingEventBlock?
7
9
 
8
10
  private(set) var mapWidget: PTRMapWidgetViewController?
9
11
  var semaphore: DispatchSemaphore?
10
12
  var site: PTRSite?
11
13
 
14
+ // Adapter to bridge PTRWayfindingEventsHandler (class) to our container callbacks
15
+ private class WayfindingEventsAdapter: PTRWayfindingEventsHandler {
16
+ weak var container: PTRMapWidgetContainerView?
17
+
18
+ init(container: PTRMapWidgetContainerView, mapWidget: PTRMapWidgetViewController) {
19
+ self.container = container
20
+ super.init(mapWidget: mapWidget)
21
+ }
22
+
23
+ override func wayfindingDidTapCancel(destination: PTRFeature) {
24
+ print("PTRMapWidget - wayfinding cancelled")
25
+ super.wayfindingDidTapCancel(destination: destination)
26
+ container?.ptrWayfindingEvent(withParameters: ["type": -1, "poi": destination.dict])
27
+ }
28
+
29
+ override func wayfindingDidTapClose(destination: PTRFeature) {
30
+ print("PTRMapWidget - wayfinding ended")
31
+ super.wayfindingDidTapClose(destination: destination)
32
+ container?.ptrWayfindingEvent(withParameters: ["type": 1, "poi": destination.dict])
33
+ }
34
+ }
35
+
36
+ func mapWidgetWillPresentWayfinding(_ mapWidget: PTRMapWidgetViewController) {
37
+ switch (mapWidget.layoutState) {
38
+ case .staticWayfinding(_, let destination, _):
39
+ print("PTRMapWidget - wayfinding started. Static path")
40
+ ptrWayfindingEvent(withParameters: ["type": 0, "poi": destination.dict])
41
+ case .wayfinding(let destination, _):
42
+ print("PTRMapWidget - wayfinding started. Dynamic path")
43
+ ptrWayfindingEvent(withParameters: ["type": 0, "poi": destination.dict])
44
+ default:
45
+ print("PTRMapWidget - Widget presented without wayfinding")
46
+ }
47
+ }
48
+
49
+ private var wayfindingEventsAdapter: WayfindingEventsAdapter?
50
+
12
51
  private let kLayerStaticPath = "static-path"
13
52
 
14
53
  @objc func ptrMapWidgetDidEndLoading(withParameters parameters: [String: Any]) {
15
54
  onMapWidgetDidEndLoading?(parameters)
16
55
  }
56
+
57
+ @objc func ptrWayfindingEvent(withParameters parameters: [String: Any]) {
58
+ print("PTRMapWidget - Sending wayfinding event \(parameters)")
59
+ onWayfindingEvent?(parameters)
60
+ }
17
61
 
18
62
  @objc func getMapWidget(_ action: PTRMapWidgetAction) -> PTRMapWidgetViewController {
19
63
  if PTRNativeLibrary.mapWidgetConfiguration.sdkParams == nil {
20
64
  if waitForPointrState() != .running {
21
- print("Pointr is not running")
65
+ print("PTRMapWidget - Pointr is not running")
22
66
  }
23
67
  }
24
- print("initializing map widget")
25
- mapWidget = PTRMapWidgetViewController(action: action, configuration: PTRNativeLibrary.mapWidgetConfiguration)
26
- return mapWidget!
68
+ print("PTRMapWidget - initializing map widget")
69
+ let mapWidget = PTRMapWidgetViewController(action: action, configuration: PTRNativeLibrary.mapWidgetConfiguration)
70
+ self.mapWidget?.removeListener(self)
71
+ self.mapWidget = mapWidget
72
+ let adapter = WayfindingEventsAdapter(container: self, mapWidget: mapWidget)
73
+ self.wayfindingEventsAdapter = adapter
74
+ mapWidget.wayfindingEventsHandler = adapter
75
+ mapWidget.addListener(self)
76
+ return mapWidget
27
77
  }
28
78
 
29
79
  @objc func present(_ mapWidget: PTRMapWidgetViewController) {
@@ -246,3 +296,4 @@ import PointrKit
246
296
  return Pointr.shared.state
247
297
  }
248
298
  }
299
+
@@ -15,6 +15,8 @@
15
15
 
16
16
  RCT_EXPORT_VIEW_PROPERTY(onMapWidgetDidEndLoading, RCTBubblingEventBlock)
17
17
 
18
+ RCT_EXPORT_VIEW_PROPERTY(onWayfindingEvent, RCTBubblingEventBlock)
19
+
18
20
  RCT_EXTERN_METHOD(site:(nonnull NSNumber *)reactTag
19
21
  siteExternalIdentifier:(NSString *)siteExternalIdentifier)
20
22
  RCT_EXTERN_METHOD(building:(nonnull NSNumber *)reactTag
@@ -39,6 +41,11 @@ RCT_EXTERN_METHOD(staticPath:(nonnull NSNumber *)reactTag
39
41
  fromPoiExternalIdentifier:(NSString *)fromPoiExternalIdentifier
40
42
  toPoiExternalIdentifier:(NSString *)toPoiExternalIdentifier)
41
43
 
44
+ RCT_EXTERN_METHOD(staticWayfinding:(nonnull NSNumber *)reactTag
45
+ siteExternalIdentifier:(NSString *)siteExternalIdentifier
46
+ sourcePoiExternalIdentifier:(NSString *)sourcePoiExternalIdentifier
47
+ destinationPoiExternalIdentifier:(NSString *)destinationPoiExternalIdentifier)
48
+
42
49
  RCT_EXTERN_METHOD(markMyCarForLevel:(nonnull NSNumber *)reactTag
43
50
  siteExternalIdentifier:(NSString *)siteExternalIdentifier
44
51
  buildingExternalIdentifier:(NSString *)buildingExternalIdentifier
@@ -116,6 +116,34 @@ public class PTRMapWidgetManager: RCTViewManager {
116
116
  }
117
117
  }
118
118
 
119
+ @objc func staticWayfinding(_ reactTag: NSNumber, siteExternalIdentifier: String, sourcePoiExternalIdentifier: String, destinationPoiExternalIdentifier: String) {
120
+ self.bridge.uiManager.addUIBlock { (uiManager, viewRegistry) in
121
+ guard let ptrMapWidgetContainerView = uiManager?.view(forReactTag: reactTag) as? PTRMapWidgetContainerView else {
122
+ print("Cannot find PTRMapWidgetContainerView with tag #\(reactTag)")
123
+ return
124
+ }
125
+
126
+ let sourcePoiLocation = PTRMapWidgetPoiLocation(siteIdentifier: siteExternalIdentifier, poiIdentifier: sourcePoiExternalIdentifier, isExternalIdentifiers: true)
127
+ let destinationPoiLocation = PTRMapWidgetPoiLocation(siteIdentifier: siteExternalIdentifier, poiIdentifier: destinationPoiExternalIdentifier, isExternalIdentifiers: true)
128
+
129
+ let action = PTRMapWidgetStaticWayfindingAction(poiSource: sourcePoiLocation, poiDestination: destinationPoiLocation)
130
+ action.completion = { error in
131
+ let ptrCommand: [String: Any] = [
132
+ "command": "staticWayfinding",
133
+ "siteExternalIdentifier": siteExternalIdentifier,
134
+ "sourcePoiExternalIdentifier": sourcePoiExternalIdentifier,
135
+ "destinationPoiExternalIdentifier": destinationPoiExternalIdentifier,
136
+ "error": error?.localizedDescription ?? ""
137
+ ]
138
+ ptrMapWidgetContainerView.ptrMapWidgetDidEndLoading(withParameters: ptrCommand)
139
+ }
140
+
141
+ let mapWidget = ptrMapWidgetContainerView.getMapWidget(action)
142
+ print("calling show static wayfinding method of mapWidget")
143
+ ptrMapWidgetContainerView.present(mapWidget)
144
+ }
145
+ }
146
+
119
147
  @objc func markMyCarForLevel(_ reactTag: NSNumber, siteExternalIdentifier: String, buildingExternalIdentifier: String, levelIndex: Int, shouldShowPopup: Bool, animationType: Int) {
120
148
  self.bridge.uiManager.addUIBlock { (uiManager, viewRegistry) in
121
149
  guard let ptrMapWidgetContainerView = uiManager?.view(forReactTag: reactTag) as? PTRMapWidgetContainerView else {
@@ -31,4 +31,8 @@ RCT_EXTERN_METHOD(requestPermissions)
31
31
 
32
32
  RCT_EXTERN_METHOD(isMyCarMarked:(RCTResponseSenderBlock)callback)
33
33
 
34
+ RCT_EXTERN_METHOD(getPois:(NSString *)siteId
35
+ resolver:(RCTPromiseResolveBlock)resolve
36
+ rejecter:(RCTPromiseRejectBlock)reject)
37
+
34
38
  @end
@@ -1,4 +1,3 @@
1
- import Foundation
2
1
  import PointrKit
3
2
  import React
4
3
 
@@ -6,6 +5,7 @@ import React
6
5
  func onPositionManagerCalculatedLocation(location: [String: Any])
7
6
  func onBuildingClicked(_ building: [String: Any])
8
7
  func onSiteClicked(_ site: [String: Any])
8
+ func onGeofenceEvent(_ geofenceEvent: [String: Any])
9
9
  }
10
10
 
11
11
  @objc(PTRNativePointrLibrary)
@@ -63,7 +63,7 @@ class PTRNativeLibrary: RCTEventEmitter, PTREventManagerDelegate {
63
63
  }
64
64
 
65
65
  override func supportedEvents() -> [String]! {
66
- return ["OnPositionManagerCalculatedLocation", "OnBuildingClicked", "OnSiteClicked"]
66
+ return ["OnPositionManagerCalculatedLocation", "OnBuildingClicked", "OnSiteClicked", "OnGeofenceEvent"]
67
67
  }
68
68
 
69
69
  override func startObserving() {
@@ -106,6 +106,48 @@ class PTRNativeLibrary: RCTEventEmitter, PTREventManagerDelegate {
106
106
  return dictionary
107
107
  }
108
108
 
109
+ // Converts a PTRGeofenceEvent object to [String: Any] dictionary by extracting its explicit fields
110
+ func convertGeofenceEventToDict(_ geofenceEvent: PTRGeofenceEvent) -> [String: Any] {
111
+ var dictionary = [String: Any]()
112
+
113
+ // Event type
114
+ let typeString = geofenceEvent.type == .enter ? "enter" : "exit"
115
+ dictionary.updateValue(typeString, forKey: "eventType")
116
+
117
+ // Event date (convert to milliseconds)
118
+ dictionary.updateValue(geofenceEvent.date.timeIntervalSince1970 * 1000, forKey: "timestamp")
119
+
120
+ // Geofence information
121
+ var geofenceDict = [String: Any]()
122
+ geofenceDict.updateValue(geofenceEvent.geofence.identifier, forKey: "id")
123
+ geofenceDict.updateValue(geofenceEvent.geofence.externalIdentifier, forKey: "externalId")
124
+ geofenceDict.updateValue(geofenceEvent.geofence.name, forKey: "name")
125
+
126
+ // Geofence type
127
+ let geofenceTypeString = geofenceEvent.geofence.type == .beacon ? "beacon" : "gps"
128
+ geofenceDict.updateValue(geofenceTypeString, forKey: "geofenceType")
129
+
130
+ // Geofence position (if available)
131
+ var positionDict = [String: Any]()
132
+ let position = geofenceEvent.geofence.position
133
+ positionDict.updateValue(position.coordinate.latitude, forKey: "latitude")
134
+ positionDict.updateValue(position.coordinate.longitude, forKey: "longitude")
135
+ geofenceDict.updateValue(positionDict, forKey: "position")
136
+
137
+ dictionary.updateValue(geofenceDict, forKey: "geofence")
138
+
139
+ // Notification information (if available)
140
+ if let notification = geofenceEvent.notification {
141
+ var notificationDict = [String: Any]()
142
+ notificationDict.updateValue(notification.identifier, forKey: "id")
143
+ notificationDict.updateValue(notification.message, forKey: "message")
144
+ notificationDict.updateValue(notification.date.timeIntervalSince1970 * 1000, forKey: "timestamp")
145
+ dictionary.updateValue(notificationDict, forKey: "geofenceNotification")
146
+ }
147
+
148
+ return dictionary
149
+ }
150
+
109
151
  func getAnimationTypeFromInt(value: Int) -> PTRMapAnimationType {
110
152
  switch value {
111
153
  case 0:
@@ -176,6 +218,7 @@ class PTRNativeLibrary: RCTEventEmitter, PTREventManagerDelegate {
176
218
  dispatchGroup.wait()
177
219
  if Pointr.shared.state == .running {
178
220
  Pointr.shared.positioningManager?.addListener(self)
221
+ Pointr.shared.geofenceManager?.addListener(self)
179
222
  }
180
223
  callback([getPointrStateStringFromPointrState(state: Pointr.shared.state)])
181
224
  }
@@ -183,6 +226,7 @@ class PTRNativeLibrary: RCTEventEmitter, PTREventManagerDelegate {
183
226
 
184
227
  @objc func stop() {
185
228
  Pointr.shared.positioningManager?.removeListener(self)
229
+ Pointr.shared.geofenceManager?.removeListener(self)
186
230
  Pointr.shared.stop()
187
231
  }
188
232
 
@@ -265,6 +309,23 @@ class PTRNativeLibrary: RCTEventEmitter, PTREventManagerDelegate {
265
309
  callback([])
266
310
  }
267
311
 
312
+ @objc(getPois:resolver:rejecter:)
313
+ func getPois(_ siteId: String,
314
+ resolver resolve: @escaping RCTPromiseResolveBlock,
315
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
316
+
317
+ // Ensure Pointr SDK is initialized
318
+ guard let site = Pointr.shared.siteManager?.site(withExternalIdentifier: siteId) else {
319
+ reject("ERROR", "Site not found: \(siteId)", nil)
320
+ return
321
+ }
322
+ // Get all POIs for the site
323
+ let pois = Pointr.shared.poiManager?.pois(for: site)?.getPoiList() ?? []
324
+ // Convert POIs to dictionary format (aligned with Kotlin implementation)
325
+ let poisData = pois.map { return $0.dict }
326
+ resolve(poisData)
327
+ }
328
+
268
329
  // MARK: - Event Manager Delegate
269
330
 
270
331
  func onPositionManagerCalculatedLocation(location: [String : Any]) {
@@ -272,6 +333,11 @@ class PTRNativeLibrary: RCTEventEmitter, PTREventManagerDelegate {
272
333
  sendEvent(withName: "OnPositionManagerCalculatedLocation", body: location)
273
334
  }
274
335
 
336
+ func onGeofenceEvent(_ geofenceEvent: [String: Any]) {
337
+ guard hasListeners else { return }
338
+ sendEvent(withName: "OnGeofenceEvent", body: geofenceEvent)
339
+ }
340
+
275
341
  func onBuildingClicked(_ building: [String : Any]) {
276
342
  guard hasListeners else { return }
277
343
  sendEvent(withName: "OnBuildingClicked", body: building)
@@ -290,6 +356,12 @@ extension PTRNativeLibrary: PTRPositioningManagerDelegate {
290
356
  }
291
357
  }
292
358
 
359
+ extension PTRNativeLibrary: PTRGeofenceManagerDelegate {
360
+ func onGeofenceManagerGeofenceEvent(_ event: PTRGeofenceEvent) {
361
+ eventManagerDelegate?.onGeofenceEvent(self.convertGeofenceEventToDict(event))
362
+ }
363
+ }
364
+
293
365
  // MARK: - PTRExitButtonEventsListener
294
366
  extension PTRNativeLibrary: PTRExitButtonEventsListener {
295
367
  func exitButtonDidTap() {
@@ -355,3 +427,137 @@ extension PTRNativeLibrary: PTRPermissionManagerDelegate {
355
427
  return PTRNativeLibrary.shouldRequestPermissionsAtStartup
356
428
  }
357
429
  }
430
+
431
+ extension PTRFeature {
432
+ public var dict: [String: Any] {
433
+ var result: [String: Any] = [
434
+ "identifier": identifier,
435
+ "externalIdentifier": externalIdentifier,
436
+ "typeCode": typeCode,
437
+ "name": name
438
+ ]
439
+ if let geometry = geometry {
440
+ result["geometry"] = geometry.dict
441
+ }
442
+ result["attributes"] = attributes.toDict
443
+ result["position"] = position.dict
444
+ return result
445
+ }
446
+ }
447
+
448
+ extension PTRPosition {
449
+ public var dict: [String: Any] {
450
+ var result: [String: Any] = [
451
+ "lat": coordinate.latitude,
452
+ "lon": coordinate.longitude,
453
+ "isValid": isValid(),
454
+ ]
455
+ if let site = site {
456
+ result["sid"] = site.identifier
457
+ }
458
+ if let building = building {
459
+ result["bid"] = building.identifier
460
+ }
461
+ if let level = level {
462
+ result["lvl"] = level.index
463
+ }
464
+ return result
465
+ }
466
+ }
467
+
468
+ private extension Dictionary where Key == String, Value == Any {
469
+ var toDict: [String: Any] {
470
+ var result: [String: Any] = [:]
471
+ for (key, value) in self {
472
+ switch value {
473
+ case let v as Int: result[key] = v
474
+ case let v as Double: result[key] = v
475
+ case let v as Bool: result[key] = v
476
+ case let v as String: result[key] = v
477
+ case let v as [Any]:
478
+ result[key] = v.map { ($0 as? [String: Any])?.toDict ?? $0 }
479
+ case let v as [String: Any]:
480
+ result[key] = v.toDict
481
+ default: break
482
+ }
483
+ }
484
+ return result
485
+ }
486
+ }
487
+
488
+ extension PTRGeometry {
489
+
490
+ public var typeName : String {
491
+ switch (type) {
492
+ case .circle:
493
+ return "Circle"
494
+ case .multiLineString:
495
+ return "MultiLineString"
496
+ case .multiPoint:
497
+ return "MultiPoint"
498
+ case .multiPolygon:
499
+ return "MultiPolygon"
500
+ case .point:
501
+ return "Point"
502
+ case .polygon:
503
+ return "Polygon"
504
+ case .lineString:
505
+ return "LineString"
506
+ case .geometryCollection:
507
+ return "GeometryCollection"
508
+ @unknown default:
509
+ return "Unknown"
510
+ }
511
+
512
+ }
513
+
514
+ public var dict: [String: Any] {
515
+ // Default type name
516
+ var result: [String: Any] = ["type": typeName]
517
+
518
+ // Handle known geometry subclasses from PointrKit
519
+ switch self {
520
+ case let point as PTRGeoPoint:
521
+ // Single coordinate
522
+ let coord = point.coordinate
523
+ result["lat"] = coord.latitude
524
+ result["lon"] = coord.longitude
525
+
526
+ case let line as PTRGeoLineString:
527
+ // Array of coordinates
528
+ let coords = line.points.map { $0.dict }
529
+ result["points"] = coords
530
+
531
+ case let polygon as PTRGeoPolygon:
532
+ // Outer ring + inner rings (holes) if any
533
+ let exterior = polygon.outer.map { $0.dict }
534
+ let holes = polygon.inners.map { ring in
535
+ ring.map { $0.dict }
536
+ }
537
+ result["outer"] = exterior
538
+ result["inners"] = holes
539
+
540
+ case let multiPoint as PTRGeoMultiPoint:
541
+ let coords = multiPoint.points.map { $0.dict }
542
+ result["points"] = coords
543
+
544
+ case let multiLine as PTRGeoMultiLineString:
545
+ let lines = multiLine.lineStrings.map { line in
546
+ line.dict
547
+ }
548
+ result["lineStrings"] = lines
549
+
550
+ case let multiPolygon as PTRGeoMultiPolygon:
551
+ let polys = multiPolygon.polygons.map { poly in
552
+ poly.dict
553
+ }
554
+ result["polygons"] = polys
555
+
556
+ default:
557
+ // Unknown geometry type; keep only the type string
558
+ break
559
+ }
560
+
561
+ return result
562
+ }
563
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-pointr",
3
- "version": "9.2.0",
3
+ "version": "9.5.0",
4
4
  "description": "Pointr React-Native Module",
5
5
  "main": "src/index",
6
6
  "author": " <> ()",
@@ -10,5 +10,19 @@
10
10
  "type": "module-legacy",
11
11
  "languages": "kotlin-swift",
12
12
  "version": "0.41.0"
13
+ },
14
+ "peerDependencies": {
15
+ "react-native": ">=0.64.0",
16
+ "react": ">=17.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^24.10.1",
20
+ "react-native": "^0.82.1"
21
+ },
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "scripts": {
26
+ "prepare-dist": "chmod +x prepare-distribution.sh && ./prepare-distribution.sh"
13
27
  }
14
- }
28
+ }