@attentive-mobile/attentive-react-native-sdk 1.0.5 → 2.0.0-beta.1

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 (37) hide show
  1. package/README.md +126 -0
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt +384 -0
  4. package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.kt +36 -0
  5. package/android/src/main/kotlin/com/attentivereactnativesdk/debug/AttentiveDebugHelper.kt +22 -6
  6. package/attentive-react-native-sdk.podspec +3 -3
  7. package/ios/AttentiveReactNativeSdk.h +1 -1
  8. package/ios/AttentiveReactNativeSdk.mm +317 -37
  9. package/ios/Bridging/ATTNNativeSDK.swift +392 -45
  10. package/ios/Bridging/AttentiveReactNativeSdk-Bridging-Header.h +3 -0
  11. package/ios/Bridging/AttentiveSDKManager.swift +83 -0
  12. package/ios/Podfile +3 -16
  13. package/lib/commonjs/NativeAttentiveReactNativeSdk.js +14 -0
  14. package/lib/commonjs/NativeAttentiveReactNativeSdk.js.map +1 -0
  15. package/lib/commonjs/eventTypes.js.map +1 -1
  16. package/lib/commonjs/index.js +362 -52
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/module/NativeAttentiveReactNativeSdk.js +7 -0
  19. package/lib/module/NativeAttentiveReactNativeSdk.js.map +1 -0
  20. package/lib/module/eventTypes.js.map +1 -1
  21. package/lib/module/index.js +345 -50
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts +103 -0
  24. package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts.map +1 -0
  25. package/lib/typescript/eventTypes.d.ts +44 -17
  26. package/lib/typescript/eventTypes.d.ts.map +1 -1
  27. package/lib/typescript/index.d.ts +276 -41
  28. package/lib/typescript/index.d.ts.map +1 -1
  29. package/package.json +21 -7
  30. package/src/NativeAttentiveReactNativeSdk.ts +152 -0
  31. package/src/eventTypes.tsx +57 -20
  32. package/src/index.tsx +472 -96
  33. package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.java +0 -310
  34. package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.java +0 -28
  35. package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  36. package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/xcuserdata/zheref.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  37. package/ios/AttentiveReactNativeSdk.xcodeproj/xcuserdata/zheref.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
package/README.md CHANGED
@@ -3,6 +3,12 @@
3
3
  # Attentive React Native SDK
4
4
  The Attentive React Native SDK provides the functionality to render Attentive creative units and collect Attentive events in React Native mobile applications.
5
5
 
6
+ ## Package Manager
7
+
8
+ This project uses **npm** as the preferred package manager for consistency and alignment with modern React Native best practices. While this library will work with any package manager (npm, yarn, or pnpm), the development scripts are configured to use npm.
9
+
10
+ **Note on package managers:** Modern npm (v7+) has significantly improved performance and features, making it the recommended choice for React Native projects. Both npm and yarn work well with React Native, but this project standardizes on npm for development workflows.
11
+
6
12
  ## Installation
7
13
 
8
14
  Run `npm install @attentive-mobile/attentive-react-native-sdk` from your app's root directory.
@@ -162,3 +168,123 @@ Attentive.identify({phone: '+15556667777'};)
162
168
  // email: 'theusersemail@gmail.com'
163
169
  // phone: '+15556667777'
164
170
  ```
171
+
172
+ ### Push Notifications (iOS Only)
173
+
174
+ The SDK supports push notification integration for iOS. Android support is planned for a future release.
175
+
176
+ #### Request Push Permission
177
+
178
+ ```typescript
179
+ import { registerForPushNotifications } from 'attentive-react-native-sdk';
180
+
181
+ // Request permission to send push notifications
182
+ // This will show the iOS system permission dialog
183
+ registerForPushNotifications();
184
+ ```
185
+
186
+ #### Register Device Token
187
+
188
+ When your app receives a device token from APNs, register it with the Attentive backend:
189
+
190
+ ```typescript
191
+ import { registerDeviceToken } from 'attentive-react-native-sdk';
192
+
193
+ // In your AppDelegate or push notification handler:
194
+ // Convert the device token Data to a hex string and pass the authorization status
195
+ registerDeviceToken(hexEncodedToken, 'authorized');
196
+ ```
197
+
198
+ The `authorizationStatus` parameter should be one of:
199
+ - `'authorized'` - User has granted permission
200
+ - `'denied'` - User has denied permission
201
+ - `'notDetermined'` - User hasn't been asked yet
202
+ - `'provisional'` - Provisional authorization (quiet notifications)
203
+ - `'ephemeral'` - App Clip notifications
204
+
205
+ #### Handle Push Notification Opens
206
+
207
+ When a user taps on a push notification, track the event:
208
+
209
+ ```typescript
210
+ import { handlePushOpened } from 'attentive-react-native-sdk';
211
+ import type { ApplicationState, PushAuthorizationStatus } from 'attentive-react-native-sdk';
212
+
213
+ // In your notification handler:
214
+ handlePushOpened(
215
+ notificationPayload, // The notification's userInfo/data
216
+ 'background', // App state: 'active', 'inactive', or 'background'
217
+ 'authorized' // Current authorization status
218
+ );
219
+ ```
220
+
221
+ #### Handle Foreground Notifications
222
+
223
+ When a notification arrives while the app is in the foreground:
224
+
225
+ ```typescript
226
+ import { handleForegroundNotification } from 'attentive-react-native-sdk';
227
+
228
+ // In your foreground notification handler:
229
+ handleForegroundNotification(notificationPayload);
230
+ ```
231
+
232
+ #### iOS AppDelegate Integration
233
+
234
+ For proper push notification integration, your iOS AppDelegate needs to:
235
+
236
+ 1. Request notification permissions via the SDK
237
+ 2. Implement `application:didRegisterForRemoteNotificationsWithDeviceToken:` to register the token
238
+ 3. Implement `UNUserNotificationCenterDelegate` methods to handle notification events
239
+
240
+ ##### Callback-Based Registration (Recommended)
241
+
242
+ For more control over the registration flow, you can use the callback-based registration directly in your AppDelegate:
243
+
244
+ ```swift
245
+ // In AppDelegate.swift
246
+ import UserNotifications
247
+ import attentive_react_native_sdk
248
+
249
+ func application(
250
+ _ application: UIApplication,
251
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
252
+ ) {
253
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
254
+ let authStatus = settings.authorizationStatus
255
+
256
+ // Get SDK instance with proper type
257
+ guard let attentiveSdk = AttentiveSDKManager.shared.sdk as? ATTNNativeSDK else {
258
+ print("[Attentive] SDK not initialized")
259
+ return
260
+ }
261
+
262
+ // Register device token with callback
263
+ attentiveSdk.registerDeviceToken(
264
+ deviceToken,
265
+ authorizationStatus: authStatus,
266
+ callback: { data, url, response, error in
267
+ DispatchQueue.main.async {
268
+ // Handle registration result
269
+ if let error = error {
270
+ print("[Attentive] Registration failed: \(error.localizedDescription)")
271
+ }
272
+
273
+ // Trigger regular open event after registration
274
+ attentiveSdk.handleRegularOpen(authorizationStatus: authStatus)
275
+ }
276
+ }
277
+ )
278
+ }
279
+ }
280
+ ```
281
+
282
+ **Documentation:**
283
+ - [Push Token Registration Guide](./PUSH_TOKEN_REGISTRATION_GUIDE.md) - Detailed guide for callback-based registration
284
+ - [AppDelegate Callback Example](./APPDELEGATE_CALLBACK_EXAMPLE.md) - Complete AppDelegate implementation
285
+ - [Push Notifications Setup](./PUSH_NOTIFICATIONS_SETUP.md) - General push notification setup
286
+ - [iOS Native SDK documentation](https://github.com/attentive-mobile/attentive-ios-sdk) - Native SDK reference
287
+
288
+ #### Android Support
289
+
290
+ Android push notification support is not yet implemented. The push notification methods will be no-ops on Android. FCM (Firebase Cloud Messaging) integration is planned for a future release.
@@ -84,6 +84,7 @@ dependencies {
84
84
  implementation "com.facebook.react:react-native:+"
85
85
  implementation 'com.attentive:attentive-android-sdk:1.0.1'
86
86
  implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.10"
87
+ implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.10"))
87
88
  }
88
89
 
89
90
  if (isNewArchitectureEnabled()) {
@@ -0,0 +1,384 @@
1
+ package com.attentivereactnativesdk
2
+
3
+ import android.app.Activity
4
+ import android.util.Log
5
+ import android.view.ViewGroup
6
+ import androidx.annotation.NonNull
7
+ import androidx.annotation.Nullable
8
+ import com.attentive.androidsdk.AttentiveConfig
9
+ import com.attentive.androidsdk.AttentiveEventTracker
10
+ import com.attentive.androidsdk.UserIdentifiers
11
+ import com.attentive.androidsdk.creatives.Creative
12
+ import com.attentive.androidsdk.events.AddToCartEvent
13
+ import com.attentive.androidsdk.events.CustomEvent
14
+ import com.attentive.androidsdk.events.Item
15
+ import com.attentive.androidsdk.events.Order
16
+ import com.attentive.androidsdk.events.Price
17
+ import com.attentive.androidsdk.events.ProductViewEvent
18
+ import com.attentive.androidsdk.events.PurchaseEvent
19
+ import com.facebook.react.bridge.ReactApplicationContext
20
+ import com.facebook.react.bridge.ReactMethod
21
+ import com.facebook.react.bridge.ReadableArray
22
+ import com.facebook.react.bridge.ReadableMap
23
+ import com.facebook.react.bridge.UiThreadUtil
24
+ import com.facebook.react.bridge.Promise
25
+ import com.attentivereactnativesdk.debug.AttentiveDebugHelper
26
+ import java.math.BigDecimal
27
+ import java.security.InvalidParameterException
28
+ import java.util.Currency
29
+ import java.util.Locale
30
+
31
+ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
32
+ NativeAttentiveReactNativeSdkSpec(reactContext) {
33
+
34
+ companion object {
35
+ const val NAME = "AttentiveReactNativeSdk"
36
+ private const val TAG = NAME
37
+ }
38
+
39
+ private var attentiveConfig: AttentiveConfig? = null
40
+ private var creative: Creative? = null
41
+ private val debugHelper: AttentiveDebugHelper
42
+
43
+ init {
44
+ debugHelper = AttentiveDebugHelper(reactContext)
45
+ }
46
+
47
+ override fun getName(): String {
48
+ return NAME
49
+ }
50
+
51
+ override fun initialize(
52
+ attentiveDomain: String,
53
+ mode: String,
54
+ skipFatigueOnCreatives: Boolean,
55
+ enableDebugger: Boolean
56
+ ) {
57
+ // Initialize debug helper
58
+ debugHelper.initialize(enableDebugger)
59
+
60
+ attentiveConfig = AttentiveConfig.Builder()
61
+ .context(reactApplicationContext)
62
+ .domain(attentiveDomain)
63
+ .mode(AttentiveConfig.Mode.valueOf(mode.uppercase(Locale.ROOT)))
64
+ .skipFatigueOnCreatives(skipFatigueOnCreatives)
65
+ .build()
66
+ AttentiveEventTracker.getInstance().initialize(attentiveConfig)
67
+ }
68
+
69
+ override fun triggerCreative(creativeId: String?) {
70
+ Log.i(TAG, "Native Attentive module was called to trigger the creative.")
71
+ try {
72
+ val currentActivity: Activity? = reactApplicationContext.currentActivity
73
+ if (currentActivity != null) {
74
+ val rootView =
75
+ currentActivity.window.decorView.rootView as ViewGroup
76
+ // The following calls edit the view hierarchy so they must run on the UI thread
77
+ UiThreadUtil.runOnUiThread {
78
+ creative = Creative(attentiveConfig, rootView)
79
+ creative?.trigger(null, creativeId)
80
+ if (debugHelper.isDebuggingEnabled()) {
81
+ val debugData = mutableMapOf<String, Any>()
82
+ debugData["type"] = "trigger"
83
+ debugData["creativeId"] = creativeId ?: "default"
84
+ debugHelper.showDebugInfo("Creative Triggered", debugData)
85
+ }
86
+ }
87
+ } else {
88
+ Log.w(TAG, "Could not trigger the Attentive Creative because the current Activity was null")
89
+ }
90
+ } catch (e: Exception) {
91
+ Log.e(TAG, "Exception when triggering the creative: $e")
92
+ }
93
+ }
94
+
95
+ override fun destroyCreative() {
96
+ val creativeToDestroy = creative
97
+ if (creativeToDestroy != null) {
98
+ UiThreadUtil.runOnUiThread {
99
+ creativeToDestroy.destroy()
100
+ creative = null
101
+ }
102
+ }
103
+ }
104
+
105
+ override fun updateDomain(domain: String) {
106
+ attentiveConfig?.changeDomain(domain)
107
+ }
108
+
109
+ override fun clearUser() {
110
+ attentiveConfig?.clearUser()
111
+ }
112
+
113
+ override fun identify(
114
+ phone: String?,
115
+ email: String?,
116
+ klaviyoId: String?,
117
+ shopifyId: String?,
118
+ clientUserId: String?,
119
+ customIdentifiers: ReadableMap?
120
+ ) {
121
+ val idsBuilder = UserIdentifiers.Builder()
122
+ if (!phone.isNullOrEmpty()) {
123
+ idsBuilder.withPhone(phone)
124
+ }
125
+ if (!email.isNullOrEmpty()) {
126
+ idsBuilder.withEmail(email)
127
+ }
128
+ if (!klaviyoId.isNullOrEmpty()) {
129
+ idsBuilder.withKlaviyoId(klaviyoId)
130
+ }
131
+ if (!shopifyId.isNullOrEmpty()) {
132
+ idsBuilder.withShopifyId(shopifyId)
133
+ }
134
+ if (!clientUserId.isNullOrEmpty()) {
135
+ idsBuilder.withClientUserId(clientUserId)
136
+ }
137
+ if (customIdentifiers != null) {
138
+ val customIds = mutableMapOf<String, String>()
139
+ val rawCustomIds = customIdentifiers.toHashMap()
140
+ for ((key, value) in rawCustomIds) {
141
+ if (value is String) {
142
+ customIds[key] = value
143
+ }
144
+ }
145
+ idsBuilder.withCustomIdentifiers(customIds)
146
+ }
147
+
148
+ attentiveConfig?.identify(idsBuilder.build())
149
+ }
150
+
151
+ override fun recordProductViewEvent(items: ReadableArray, deeplink: String?) {
152
+ Log.i(TAG, "Sending product viewed event")
153
+
154
+ val itemsList = buildItems(items)
155
+ val productViewEvent = ProductViewEvent.Builder(itemsList).deeplink(deeplink).build()
156
+
157
+ AttentiveEventTracker.getInstance().recordEvent(productViewEvent)
158
+
159
+ if (debugHelper.isDebuggingEnabled()) {
160
+ val debugData = mutableMapOf<String, Any>()
161
+ debugData["items_count"] = itemsList.size.toString()
162
+ debugData["deeplink"] = deeplink ?: ""
163
+ debugHelper.showDebugInfo("Product View Event", debugData)
164
+ }
165
+ }
166
+
167
+ override fun recordPurchaseEvent(
168
+ items: ReadableArray,
169
+ orderId: String,
170
+ cartId: String?,
171
+ cartCoupon: String?
172
+ ) {
173
+ Log.i(TAG, "Sending purchase event")
174
+ val order = Order.Builder(orderId).build()
175
+
176
+ val itemsList = buildItems(items)
177
+ val purchaseEvent = PurchaseEvent.Builder(itemsList, order).build()
178
+
179
+ AttentiveEventTracker.getInstance().recordEvent(purchaseEvent)
180
+
181
+ if (debugHelper.isDebuggingEnabled()) {
182
+ val debugData = mutableMapOf<String, Any>()
183
+ debugData["items_count"] = itemsList.size.toString()
184
+ debugData["order_id"] = orderId
185
+ if (cartId != null) debugData["cart_id"] = cartId
186
+ if (cartCoupon != null) debugData["cart_coupon"] = cartCoupon
187
+ debugHelper.showDebugInfo("Purchase Event", debugData)
188
+ }
189
+ }
190
+
191
+ override fun recordAddToCartEvent(items: ReadableArray, deeplink: String?) {
192
+ Log.i(TAG, "Sending add to cart event")
193
+
194
+ val itemsList = buildItems(items)
195
+ val addToCartEvent = AddToCartEvent.Builder(itemsList).deeplink(deeplink).build()
196
+
197
+ AttentiveEventTracker.getInstance().recordEvent(addToCartEvent)
198
+
199
+ if (debugHelper.isDebuggingEnabled()) {
200
+ val debugData = mutableMapOf<String, Any>()
201
+ debugData["items_count"] = itemsList.size.toString()
202
+ debugData["deeplink"] = deeplink ?: ""
203
+ debugHelper.showDebugInfo("Add To Cart Event", debugData)
204
+ }
205
+ }
206
+
207
+ override fun recordCustomEvent(type: String, properties: ReadableMap?) {
208
+ Log.i(TAG, "Sending custom event")
209
+ if (properties == null) {
210
+ throw IllegalArgumentException("The CustomEvent 'properties' field cannot be null.")
211
+ }
212
+ val propertiesMap = convertToStringMap(properties.toHashMap())
213
+ val customEvent = CustomEvent.Builder(type, propertiesMap).build()
214
+
215
+ AttentiveEventTracker.getInstance().recordEvent(customEvent)
216
+
217
+ if (debugHelper.isDebuggingEnabled()) {
218
+ val debugData = mutableMapOf<String, Any>()
219
+ debugData["event_type"] = type
220
+ debugData["properties_count"] = propertiesMap.size.toString()
221
+ debugHelper.showDebugInfo("Custom Event", debugData)
222
+ }
223
+ }
224
+
225
+ override fun invokeAttentiveDebugHelper() {
226
+ debugHelper.invokeDebugHelper()
227
+ }
228
+
229
+ override fun exportDebugLogs(promise: Promise) {
230
+ try {
231
+ val exportContent = debugHelper.exportDebugLogs()
232
+ promise.resolve(exportContent)
233
+ } catch (e: Exception) {
234
+ promise.reject("EXPORT_ERROR", "Failed to export debug logs: " + e.message, e)
235
+ }
236
+ }
237
+
238
+ // ==========================================================================
239
+ // MARK: - Push Notification Methods (Android Implementation - TODO)
240
+ // ==========================================================================
241
+ //
242
+ // These methods are stubs for Android push notification support.
243
+ // Android push notifications typically use Firebase Cloud Messaging (FCM)
244
+ // and require different handling than iOS APNs.
245
+ //
246
+ // TODO: Implement Android push notification support
247
+ // - Integrate with Firebase Cloud Messaging (FCM)
248
+ // - Register FCM token with Attentive backend
249
+ // - Handle push notification opens and foreground notifications
250
+ // - Consider using the attentive-android-sdk's push notification features if available
251
+ //
252
+ // Reference: The iOS implementation uses:
253
+ // - registerForPushNotifications() - Request permission
254
+ // - registerDeviceToken() - Send token to backend
255
+ // - handlePushOpened() - Track push open events
256
+ // - handleForegroundNotification() - Handle foreground push display
257
+ // ==========================================================================
258
+
259
+ /**
260
+ * Request push notification permission from the user.
261
+ *
262
+ * TODO: Implement for Android
263
+ * - For Android 13+ (API 33+), request POST_NOTIFICATIONS permission
264
+ * - For older versions, permissions are granted at install time
265
+ * - Initialize FCM and get the registration token
266
+ */
267
+ override fun registerForPushNotifications() {
268
+ Log.i(TAG, "[TODO] registerForPushNotifications called - Android implementation pending")
269
+ // TODO: Implement Android push notification registration
270
+ // 1. Check/request POST_NOTIFICATIONS permission (Android 13+)
271
+ // 2. Initialize Firebase Cloud Messaging
272
+ // 3. Get FCM registration token
273
+ // 4. Register token with Attentive backend
274
+ }
275
+
276
+ /**
277
+ * Register the device token with the Attentive backend.
278
+ *
279
+ * TODO: Implement for Android
280
+ * - Android uses FCM tokens instead of APNs tokens
281
+ * - Token format and registration endpoint may differ
282
+ *
283
+ * @param token The FCM registration token
284
+ * @param authorizationStatus Push authorization status (may not apply to Android)
285
+ */
286
+ override fun registerDeviceToken(token: String, authorizationStatus: String) {
287
+ Log.i(TAG, "[TODO] registerDeviceToken called - Android implementation pending")
288
+ Log.d(TAG, "Token: ${token.take(16)}..., Status: $authorizationStatus")
289
+ // TODO: Implement Android device token registration
290
+ // 1. Send FCM token to Attentive backend
291
+ // 2. Handle token refresh via FirebaseMessagingService.onNewToken()
292
+ }
293
+
294
+ /**
295
+ * Handle when a push notification is opened by the user.
296
+ *
297
+ * TODO: Implement for Android
298
+ * - Track push open events with Attentive
299
+ * - Handle deep linking if present in payload
300
+ *
301
+ * @param userInfo The notification payload (from FCM RemoteMessage data)
302
+ * @param applicationState App state when notification was opened
303
+ * @param authorizationStatus Push authorization status
304
+ */
305
+ override fun handlePushOpened(
306
+ userInfo: ReadableMap,
307
+ applicationState: String,
308
+ authorizationStatus: String
309
+ ) {
310
+ Log.i(TAG, "[TODO] handlePushOpened called - Android implementation pending")
311
+ Log.d(TAG, "App state: $applicationState, Auth status: $authorizationStatus")
312
+ // TODO: Implement Android push open tracking
313
+ // 1. Parse notification payload
314
+ // 2. Send push open event to Attentive backend
315
+ // 3. Handle any deep links in the payload
316
+ }
317
+
318
+ /**
319
+ * Handle when a push notification arrives while the app is in foreground.
320
+ *
321
+ * TODO: Implement for Android
322
+ * - Android handles foreground notifications differently than iOS
323
+ * - By default, FCM data messages don't show UI in foreground
324
+ * - Need to create NotificationCompat.Builder to show notification
325
+ *
326
+ * @param userInfo The notification payload
327
+ */
328
+ override fun handleForegroundNotification(userInfo: ReadableMap) {
329
+ Log.i(TAG, "[TODO] handleForegroundNotification called - Android implementation pending")
330
+ // TODO: Implement Android foreground notification handling
331
+ // 1. Create notification channel (required for Android 8+)
332
+ // 2. Build and display notification using NotificationCompat
333
+ // 3. Track foreground notification event with Attentive
334
+ }
335
+
336
+ private fun convertToStringMap(inputMap: Map<String, Any?>): Map<String, String> {
337
+ val outputMap = mutableMapOf<String, String>()
338
+ for ((key, value) in inputMap) {
339
+ if (value == null) {
340
+ throw InvalidParameterException("The key '$key' has a null value.")
341
+ }
342
+ if (value is String) {
343
+ outputMap[key] = value
344
+ }
345
+ }
346
+ return outputMap
347
+ }
348
+
349
+ private fun buildItems(rawItems: ReadableArray): List<Item> {
350
+ Log.i(TAG, "buildItems method called with rawItems: $rawItems")
351
+ val items = mutableListOf<Item>()
352
+ for (i in 0 until rawItems.size()) {
353
+ val rawItem = rawItems.getMap(i) ?: continue
354
+
355
+ // Price and currency are now flattened, not nested
356
+ val priceValue = rawItem.getString("price")
357
+ val currencyCode = rawItem.getString("currency")
358
+ val price = Price.Builder(BigDecimal(priceValue), Currency.getInstance(currencyCode)).build()
359
+
360
+ val builder = Item.Builder(rawItem.getString("productId"), rawItem.getString("productVariantId"), price)
361
+
362
+ if (rawItem.hasKey("productImage")) {
363
+ builder.productImage(rawItem.getString("productImage"))
364
+ }
365
+
366
+ if (rawItem.hasKey("name")) {
367
+ builder.name(rawItem.getString("name"))
368
+ }
369
+
370
+ if (rawItem.hasKey("quantity")) {
371
+ builder.quantity(rawItem.getInt("quantity"))
372
+ }
373
+
374
+ if (rawItem.hasKey("category")) {
375
+ builder.category(rawItem.getString("category"))
376
+ }
377
+
378
+ val item = builder.build()
379
+ items.add(item)
380
+ }
381
+
382
+ return items
383
+ }
384
+ }
@@ -0,0 +1,36 @@
1
+ package com.attentivereactnativesdk
2
+
3
+ import com.facebook.react.TurboReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+
9
+ class AttentiveReactNativeSdkPackage : TurboReactPackage() {
10
+
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == AttentiveReactNativeSdkModule.NAME) {
13
+ AttentiveReactNativeSdkModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
20
+ return ReactModuleInfoProvider {
21
+ val moduleInfos = mutableMapOf<String, ReactModuleInfo>()
22
+ val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
23
+ moduleInfos[AttentiveReactNativeSdkModule.NAME] = ReactModuleInfo(
24
+ AttentiveReactNativeSdkModule.NAME,
25
+ AttentiveReactNativeSdkModule.NAME,
26
+ false, // canOverrideExistingModule
27
+ false, // needsEagerInit
28
+ true, // hasConstants
29
+ false, // isCxxModule
30
+ isTurboModule // isTurboModule
31
+ )
32
+ moduleInfos
33
+ }
34
+ }
35
+ }
36
+
@@ -38,8 +38,13 @@ class AttentiveDebugHelper(private val reactContext: ReactApplicationContext) {
38
38
  }
39
39
 
40
40
  private val debugHistory = mutableListOf<DebugEvent>()
41
- var isDebuggingEnabled = false
42
- private set
41
+ private var isDebuggingEnabled = false
42
+
43
+ /**
44
+ * Returns whether debugging is currently enabled.
45
+ * Provides explicit getter method for Java interop.
46
+ */
47
+ fun isDebuggingEnabled(): Boolean = isDebuggingEnabled
43
48
 
44
49
  /**
45
50
  * Initializes debugging based on configuration and build type.
@@ -52,7 +57,7 @@ class AttentiveDebugHelper(private val reactContext: ReactApplicationContext) {
52
57
  isDebuggingEnabled = enableDebuggerFromConfig && isDebugBuild
53
58
 
54
59
  Log.i(TAG, "Debug initialization - enableDebuggerFromConfig: $enableDebuggerFromConfig, " +
55
- "isDebugBuild: $isDebugBuild, debuggingEnabled: $isDebuggingEnabled")
60
+ "isDebugBuild: $isDebugBuild, debuggingEnabled: ${isDebuggingEnabled()}")
56
61
  }
57
62
 
58
63
  /**
@@ -62,7 +67,7 @@ class AttentiveDebugHelper(private val reactContext: ReactApplicationContext) {
62
67
  * @param data The event data
63
68
  */
64
69
  fun showDebugInfo(event: String, data: Map<String, Any?>) {
65
- if (!isDebuggingEnabled) return
70
+ if (!isDebuggingEnabled()) return
66
71
 
67
72
  Log.i(TAG, "showDebugInfo called for event: $event, data: $data")
68
73
 
@@ -86,17 +91,28 @@ class AttentiveDebugHelper(private val reactContext: ReactApplicationContext) {
86
91
  * Shows existing debug session data without adding to history.
87
92
  */
88
93
  fun invokeDebugHelper() {
89
- if (!isDebuggingEnabled) return
94
+ Log.i(TAG, "invokeDebugHelper called - isDebuggingEnabled: ${isDebuggingEnabled()}")
95
+
96
+ if (!isDebuggingEnabled()) {
97
+ Log.w(TAG, "Debug helper not invoked because debugging is not enabled")
98
+ return
99
+ }
90
100
 
91
101
  val currentActivity = reactContext.currentActivity
102
+ Log.i(TAG, "Current activity: $currentActivity")
103
+
92
104
  if (currentActivity != null) {
105
+ Log.i(TAG, "Activity is available, running on UI thread")
93
106
  UiThreadUtil.runOnUiThread {
94
107
  val debugData = mapOf(
95
108
  "action" to "manual_debug_call",
96
109
  "session_events" to debugHistory.size.toString()
97
110
  )
111
+ Log.i(TAG, "About to show debug dialog")
98
112
  showDebugDialog(currentActivity, "Manual Debug View", debugData)
99
113
  }
114
+ } else {
115
+ Log.w(TAG, "Current activity is null, cannot show debug dialog")
100
116
  }
101
117
  }
102
118
 
@@ -105,7 +121,7 @@ class AttentiveDebugHelper(private val reactContext: ReactApplicationContext) {
105
121
  * @return A comprehensive formatted string containing all debug events in the current session
106
122
  */
107
123
  fun exportDebugLogs(): String {
108
- if (!isDebuggingEnabled) {
124
+ if (!isDebuggingEnabled()) {
109
125
  return "Debug logging is not enabled. Please enable debugging to export logs."
110
126
  }
111
127
 
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
16
16
 
17
17
  s.source_files = "ios/**/*.{h,m,mm,swift}"
18
18
 
19
- s.dependency 'attentive-ios-sdk', '1.0.0'
19
+ s.dependency 'attentive-ios-sdk', '2.0.8'
20
20
  s.swift_versions = ['5']
21
21
  s.dependency "React-Core"
22
22
 
@@ -24,9 +24,9 @@ Pod::Spec.new do |s|
24
24
  if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
25
25
  s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
26
26
  s.pod_target_xcconfig = {
27
- "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
27
+ "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/Headers/Public/React-NativeModulesApple\" \"$(PODS_ROOT)/Headers/Private/React-NativeModulesApple\" \"$(PODS_ROOT)/Headers/Private/React-Codegen/react/renderer/components\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen/React_Codegen.framework/Headers\"",
28
28
  "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
29
- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
29
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
30
30
  }
31
31
  s.dependency "React-Codegen"
32
32
  s.dependency "RCT-Folly"
@@ -6,7 +6,7 @@
6
6
  //
7
7
 
8
8
  #ifdef RCT_NEW_ARCH_ENABLED
9
- #import "RNAttentiveReactNativeSdkSpec.h"
9
+ #import "AttentiveReactNativeSdkSpec.h"
10
10
 
11
11
  @interface AttentiveReactNativeSdk : NSObject <NativeAttentiveReactNativeSdkSpec>
12
12
  #else