@attentive-mobile/attentive-react-native-sdk 1.0.3-beta.1 → 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 (34) hide show
  1. package/README.md +150 -0
  2. package/android/build.gradle +4 -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 +438 -0
  6. package/android/src/main/kotlin/com/attentivereactnativesdk/debug/DebugEvent.kt +76 -0
  7. package/attentive-react-native-sdk.podspec +4 -5
  8. package/ios/AttentiveReactNativeSdk.h +6 -6
  9. package/ios/AttentiveReactNativeSdk.mm +325 -35
  10. package/ios/AttentiveReactNativeSdk.xcodeproj/project.pbxproj +2 -2
  11. package/ios/Bridging/ATTNNativeSDK.swift +1118 -3
  12. package/ios/Bridging/AttentiveReactNativeSdk-Bridging-Header.h +3 -0
  13. package/ios/Bridging/AttentiveSDKManager.swift +83 -0
  14. package/ios/Podfile +4 -17
  15. package/lib/commonjs/NativeAttentiveReactNativeSdk.js +14 -0
  16. package/lib/commonjs/NativeAttentiveReactNativeSdk.js.map +1 -0
  17. package/lib/commonjs/index.js +363 -39
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/module/NativeAttentiveReactNativeSdk.js +7 -0
  20. package/lib/module/NativeAttentiveReactNativeSdk.js.map +1 -0
  21. package/lib/module/index.js +346 -38
  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 -33
  28. package/lib/typescript/index.d.ts.map +1 -1
  29. package/package.json +22 -8
  30. package/src/NativeAttentiveReactNativeSdk.ts +152 -0
  31. package/src/eventTypes.tsx +57 -20
  32. package/src/index.tsx +472 -82
  33. package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.java +0 -247
  34. package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.java +0 -28
package/src/index.tsx CHANGED
@@ -1,88 +1,478 @@
1
- import { NativeModules, Platform } from 'react-native';
1
+ import { Platform } from 'react-native'
2
2
  import type {
3
- AddToCartEvent,
4
- ProductViewEvent,
5
- PurchaseEvent,
3
+ UserIdentifiers,
4
+ AttentiveSdkConfiguration,
5
+ ProductView,
6
+ Purchase,
7
+ AddToCart,
6
8
  CustomEvent,
7
- } from './eventTypes';
9
+ Item,
10
+ PushAuthorizationStatus,
11
+ ApplicationState,
12
+ PushNotificationUserInfo,
13
+ PushRegistrationResult,
14
+ } from './eventTypes'
15
+ import NativeAttentiveReactNativeSdkModule, {
16
+ type Spec,
17
+ } from './NativeAttentiveReactNativeSdk'
8
18
 
9
19
  const LINKING_ERROR =
10
20
  `The package 'attentive-react-native-sdk' doesn't seem to be linked. Make sure: \n\n` +
11
- Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
21
+ Platform.select({
22
+ ios: "- You have run 'pod install'\n",
23
+ default: '',
24
+ }) +
12
25
  '- You rebuilt the app after installing the package\n' +
13
- '- You are not using Expo Go\n';
14
-
15
- const AttentiveReactNativeSdk = NativeModules.AttentiveReactNativeSdk
16
- ? NativeModules.AttentiveReactNativeSdk
17
- : new Proxy(
18
- {},
19
- {
20
- get() {
21
- throw new Error(LINKING_ERROR);
22
- },
23
- }
24
- );
25
-
26
- export enum Mode {
27
- Production = 'production',
28
- Debug = 'debug',
29
- }
30
-
31
- export type AttentiveConfiguration = {
32
- attentiveDomain: string;
33
- mode: Mode;
34
- skipFatigueOnCreatives?: boolean;
35
- };
36
-
37
- export type UserIdentifiers = {
38
- phone?: string;
39
- email?: string;
40
- klaviyoId?: string;
41
- shopifyId?: string;
42
- clientUserId?: string;
43
- customIdentifiers?: { [key: string]: string };
44
- };
45
-
46
- export class Attentive {
47
- static initialize(configuration: AttentiveConfiguration): void {
48
- AttentiveReactNativeSdk.initialize(configuration);
49
- }
50
-
51
- static identify(userIdentifiers: UserIdentifiers): void {
52
- AttentiveReactNativeSdk.identify(userIdentifiers);
53
- }
54
-
55
- static clearUser(): void {
56
- AttentiveReactNativeSdk.clearUser();
57
- }
58
-
59
- static triggerCreative(creativeId: string | null = null): void {
60
- AttentiveReactNativeSdk.triggerCreative(creativeId);
61
- }
62
-
63
- static destroyCreative(): void {
64
- AttentiveReactNativeSdk.destroyCreative();
65
- }
66
-
67
- static updateDomain(domain: string): void {
68
- AttentiveReactNativeSdk.updateDomain(domain);
69
- }
70
-
71
- static recordProductViewEvent(productViewEvent: ProductViewEvent): void {
72
- AttentiveReactNativeSdk.recordProductViewEvent(productViewEvent);
73
- }
74
-
75
- static recordAddToCartEvent(addToCartEvent: AddToCartEvent): void {
76
- AttentiveReactNativeSdk.recordAddToCartEvent(addToCartEvent);
77
- }
78
-
79
- static recordPurchaseEvent(purchaseEvent: PurchaseEvent): void {
80
- AttentiveReactNativeSdk.recordPurchaseEvent(purchaseEvent);
81
- }
82
-
83
- static recordCustomEvent(customEvent: CustomEvent): void {
84
- AttentiveReactNativeSdk.recordCustomEvent(customEvent);
85
- }
86
- }
87
-
88
- export type { AddToCartEvent, ProductViewEvent, PurchaseEvent, CustomEvent };
26
+ '- You are not using Expo Go\n'
27
+
28
+ const AttentiveReactNativeSdk = (
29
+ NativeAttentiveReactNativeSdkModule
30
+ ? NativeAttentiveReactNativeSdkModule
31
+ : new Proxy(
32
+ {},
33
+ {
34
+ get() {
35
+ throw new Error(LINKING_ERROR)
36
+ },
37
+ }
38
+ )
39
+ ) as Spec
40
+
41
+ /**
42
+ * Initialize the Attentive SDK with the provided configuration
43
+ * @param configuration - Configuration object for the Attentive SDK
44
+ */
45
+ function initialize(configuration: AttentiveSdkConfiguration) {
46
+ AttentiveReactNativeSdk.initialize(
47
+ configuration.attentiveDomain,
48
+ configuration.mode,
49
+ configuration.skipFatigueOnCreatives ?? false,
50
+ configuration.enableDebugger ?? false
51
+ )
52
+ }
53
+
54
+ /**
55
+ * Trigger a creative with an optional creative ID
56
+ * @param creativeId - Optional creative ID to trigger
57
+ */
58
+ function triggerCreative(creativeId?: string) {
59
+ AttentiveReactNativeSdk.triggerCreative(creativeId)
60
+ }
61
+
62
+ /**
63
+ * Destroy the current creative
64
+ */
65
+ function destroyCreative() {
66
+ AttentiveReactNativeSdk.destroyCreative()
67
+ }
68
+
69
+ /**
70
+ * Update the Attentive domain
71
+ * @param domain - New domain to use
72
+ */
73
+ function updateDomain(domain: string) {
74
+ AttentiveReactNativeSdk.updateDomain(domain)
75
+ }
76
+
77
+ /**
78
+ * Identify a user with the provided identifiers
79
+ * @param identifiers - User identifier object containing phone, email, etc.
80
+ */
81
+ function identify(identifiers: UserIdentifiers) {
82
+ AttentiveReactNativeSdk.identify(
83
+ identifiers.phone,
84
+ identifiers.email,
85
+ identifiers.klaviyoId,
86
+ identifiers.shopifyId,
87
+ identifiers.clientUserId,
88
+ identifiers.customIdentifiers
89
+ )
90
+ }
91
+
92
+ /**
93
+ * Clear the current user identification
94
+ */
95
+ function clearUser() {
96
+ AttentiveReactNativeSdk.clearUser()
97
+ }
98
+
99
+ /**
100
+ * Record an add to cart event
101
+ * @param attrs - Event attributes containing items and optional deeplink
102
+ */
103
+ function recordAddToCartEvent(attrs: AddToCart) {
104
+ AttentiveReactNativeSdk.recordAddToCartEvent(attrs.items, attrs.deeplink)
105
+ }
106
+
107
+ /**
108
+ * Record a product view event
109
+ * @param attrs - Event attributes containing items and optional deeplink
110
+ */
111
+ function recordProductViewEvent(attrs: ProductView) {
112
+ AttentiveReactNativeSdk.recordProductViewEvent(attrs.items, attrs.deeplink)
113
+ }
114
+
115
+ /**
116
+ * Record a purchase event
117
+ * @param attrs - Event attributes containing items, order ID, and optional cart details
118
+ */
119
+ function recordPurchaseEvent(attrs: Purchase) {
120
+ AttentiveReactNativeSdk.recordPurchaseEvent(
121
+ attrs.items,
122
+ attrs.orderId,
123
+ attrs.cartId,
124
+ attrs.cartCoupon
125
+ )
126
+ }
127
+
128
+ /**
129
+ * Record a custom event
130
+ * @param attrs - Custom event attributes containing type and properties
131
+ */
132
+ function recordCustomEvent(attrs: CustomEvent) {
133
+ AttentiveReactNativeSdk.recordCustomEvent(attrs.type, attrs.properties)
134
+ }
135
+
136
+ /**
137
+ * Invoke the Attentive debug helper
138
+ */
139
+ function invokeAttentiveDebugHelper() {
140
+ AttentiveReactNativeSdk.invokeAttentiveDebugHelper()
141
+ }
142
+
143
+ /**
144
+ * Export debug logs
145
+ * @returns Promise that resolves to a string containing the debug logs
146
+ */
147
+ function exportDebugLogs(): Promise<string> {
148
+ return AttentiveReactNativeSdk.exportDebugLogs()
149
+ }
150
+
151
+ // =============================================================================
152
+ // Push Notification Methods (iOS only - Android is no-op with TODO stubs)
153
+ // =============================================================================
154
+
155
+ /**
156
+ * Request push notification permission from the user.
157
+ * On iOS, this will trigger the system permission dialog.
158
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * import { registerForPushNotifications } from 'attentive-react-native-sdk';
163
+ *
164
+ * // Request permission (typically called after user onboarding)
165
+ * registerForPushNotifications();
166
+ * ```
167
+ */
168
+ function registerForPushNotifications(): void {
169
+ AttentiveReactNativeSdk.registerForPushNotifications()
170
+ }
171
+
172
+ /**
173
+ * Register the device token received from APNs/FCM with the Attentive backend.
174
+ * Call this from your AppDelegate's didRegisterForRemoteNotificationsWithDeviceToken.
175
+ *
176
+ * On iOS, the token should be the hex-encoded string representation of the device token Data.
177
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
178
+ *
179
+ * @param token - The device token as a hex-encoded string
180
+ * @param authorizationStatus - Current push authorization status
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * import { registerDeviceToken } from 'attentive-react-native-sdk';
185
+ *
186
+ * // In your native module or push notification handler:
187
+ * registerDeviceToken('abc123...', 'authorized');
188
+ * ```
189
+ */
190
+ function registerDeviceToken(
191
+ token: string,
192
+ authorizationStatus: PushAuthorizationStatus
193
+ ): void {
194
+ AttentiveReactNativeSdk.registerDeviceToken(token, authorizationStatus)
195
+ }
196
+
197
+ /**
198
+ * Register the device token received from APNs with a callback.
199
+ * This is the callback-based version that allows you to handle the response from the Attentive API.
200
+ *
201
+ * On iOS, this will register the device token with the Attentive SDK and invoke the callback
202
+ * after the registration completes (success or failure).
203
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
204
+ *
205
+ * @param token - The hex-encoded device token string from APNs
206
+ * @param authorizationStatus - Current push authorization status
207
+ * @param callback - Callback function invoked after registration completes
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * import { registerDeviceTokenWithCallback, handleRegularOpen } from 'attentive-react-native-sdk';
212
+ *
213
+ * // In your AppDelegate equivalent (TypeScript):
214
+ * registerDeviceTokenWithCallback(
215
+ * deviceToken,
216
+ * 'authorized',
217
+ * (data, url, response, error) => {
218
+ * console.log('Registration complete:', { data, url, response, error });
219
+ * // After registration, trigger regular open event
220
+ * handleRegularOpen('authorized');
221
+ * }
222
+ * );
223
+ * ```
224
+ */
225
+ function registerDeviceTokenWithCallback(
226
+ token: string,
227
+ authorizationStatus: PushAuthorizationStatus,
228
+ callback: (
229
+ data?: Object,
230
+ url?: string,
231
+ response?: Object,
232
+ error?: Object
233
+ ) => void
234
+ ): void {
235
+ AttentiveReactNativeSdk.registerDeviceTokenWithCallback(
236
+ token,
237
+ authorizationStatus,
238
+ callback
239
+ )
240
+ }
241
+
242
+ /**
243
+ * Handle regular/direct app open (not from a push notification).
244
+ * This should be called after device token registration to track app opens.
245
+ *
246
+ * This is the TypeScript equivalent of the native iOS AppDelegate method:
247
+ * ```swift
248
+ * func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
249
+ * UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in
250
+ * guard let self = self else { return }
251
+ * let authStatus = settings.authorizationStatus
252
+ * attentiveSdk?.registerDeviceToken(deviceToken, authorizationStatus: authStatus, callback: { data, url, response, error in
253
+ * DispatchQueue.main.async {
254
+ * self.attentiveSdk?.handleRegularOpen(authorizationStatus: authStatus)
255
+ * }
256
+ * })
257
+ * }
258
+ * }
259
+ * ```
260
+ *
261
+ * On iOS, this will notify the Attentive SDK that the app was opened directly
262
+ * (not from a push notification tap).
263
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
264
+ *
265
+ * @param authorizationStatus - Current push authorization status
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * import { registerDeviceTokenWithCallback, handleRegularOpen } from 'attentive-react-native-sdk';
270
+ * import PushNotificationIOS from '@react-native-community/push-notification-ios';
271
+ *
272
+ * // In your device token registration handler:
273
+ * PushNotificationIOS.addEventListener('register', (deviceToken: string) => {
274
+ * PushNotificationIOS.checkPermissions((permissions) => {
275
+ * let authStatus: PushAuthorizationStatus = 'notDetermined'
276
+ * if (permissions.alert || permissions.badge || permissions.sound) {
277
+ * authStatus = 'authorized'
278
+ * }
279
+ *
280
+ * // Register device token with callback
281
+ * registerDeviceTokenWithCallback(deviceToken, authStatus, (data, url, response, error) => {
282
+ * if (error) {
283
+ * console.error('Registration error:', error)
284
+ * }
285
+ * // After registration completes, trigger regular open event
286
+ * handleRegularOpen(authStatus)
287
+ * })
288
+ * })
289
+ * })
290
+ * ```
291
+ */
292
+ function handleRegularOpen(authorizationStatus: PushAuthorizationStatus): void {
293
+ console.log('[AttentiveSDK] 🌉 Calling handleRegularOpen from TypeScript')
294
+ console.log(` Authorization Status: ${authorizationStatus}`)
295
+ console.log(
296
+ ' This should trigger: https://mobile.attentivemobile.com/mtctrl'
297
+ )
298
+
299
+ AttentiveReactNativeSdk.handleRegularOpen(authorizationStatus)
300
+
301
+ console.log('[AttentiveSDK] ✅ handleRegularOpen call completed')
302
+ }
303
+
304
+ /**
305
+ * Handle when a push notification is opened by the user.
306
+ * Call this from your notification handler when the user taps a notification.
307
+ *
308
+ * On iOS, this will track the push open event and handle the notification appropriately
309
+ * based on whether the app was in the foreground, background, or not running.
310
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
311
+ *
312
+ * @param userInfo - The notification payload from the push notification
313
+ * @param applicationState - The app state when the notification was opened ('active', 'inactive', 'background')
314
+ * @param authorizationStatus - Current push authorization status
315
+ *
316
+ * @example
317
+ * ```typescript
318
+ * import { handlePushOpened } from 'attentive-react-native-sdk';
319
+ *
320
+ * // In your notification handler:
321
+ * handlePushOpened(
322
+ * notification.data,
323
+ * 'background',
324
+ * 'authorized'
325
+ * );
326
+ * ```
327
+ */
328
+ function handlePushOpened(
329
+ userInfo: PushNotificationUserInfo,
330
+ applicationState: ApplicationState,
331
+ authorizationStatus: PushAuthorizationStatus
332
+ ): void {
333
+ AttentiveReactNativeSdk.handlePushOpened(
334
+ userInfo as Object,
335
+ applicationState,
336
+ authorizationStatus
337
+ )
338
+ }
339
+
340
+ /**
341
+ * Handle when a push notification arrives while the app is in the foreground.
342
+ * Call this from your notification handler when a notification is received while the app is active.
343
+ *
344
+ * On iOS, this allows the Attentive SDK to track the notification event.
345
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
346
+ *
347
+ * @param userInfo - The notification payload from the push notification
348
+ *
349
+ * @example
350
+ * ```typescript
351
+ * import { handleForegroundNotification } from 'attentive-react-native-sdk';
352
+ *
353
+ * // In your notification handler when app is in foreground:
354
+ * handleForegroundNotification(notification.data);
355
+ * ```
356
+ */
357
+ function handleForegroundNotification(
358
+ userInfo: PushNotificationUserInfo
359
+ ): void {
360
+ AttentiveReactNativeSdk.handleForegroundNotification(userInfo as Object)
361
+ }
362
+
363
+ /**
364
+ * Handle a push notification when the app is in the foreground (active state).
365
+ * This is the React Native equivalent of the native iOS handleForegroundPush method.
366
+ *
367
+ * Call this when you receive a notification response and the app state is 'active'.
368
+ * This is part of implementing the native iOS pattern:
369
+ * ```swift
370
+ * case .active:
371
+ * self.attentiveSdk?.handleForegroundPush(response: response, authorizationStatus: authStatus)
372
+ * ```
373
+ *
374
+ * On iOS, this properly tracks foreground push notifications.
375
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
376
+ *
377
+ * @param userInfo - The notification payload from the push notification
378
+ * @param authorizationStatus - Current push authorization status
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * import { handleForegroundPush } from 'attentive-react-native-sdk';
383
+ * import { AppState } from 'react-native';
384
+ *
385
+ * // In your notification handler:
386
+ * const appState = AppState.currentState;
387
+ * if (appState === 'active') {
388
+ * handleForegroundPush(notification.data, 'authorized');
389
+ * }
390
+ * ```
391
+ */
392
+ function handleForegroundPush(
393
+ userInfo: PushNotificationUserInfo,
394
+ authorizationStatus: PushAuthorizationStatus
395
+ ): void {
396
+ AttentiveReactNativeSdk.handleForegroundPush(
397
+ userInfo as Object,
398
+ authorizationStatus
399
+ )
400
+ }
401
+
402
+ /**
403
+ * Handle when a push notification is opened by the user (app in background/inactive state).
404
+ * This is the React Native equivalent of the native iOS handlePushOpen method.
405
+ *
406
+ * Call this when you receive a notification response and the app state is 'background' or 'inactive'.
407
+ * This is part of implementing the native iOS pattern:
408
+ * ```swift
409
+ * case .background, .inactive:
410
+ * self.attentiveSdk?.handlePushOpen(response: response, authorizationStatus: authStatus)
411
+ * ```
412
+ *
413
+ * On iOS, this properly tracks push notification opens.
414
+ * On Android, this is currently a no-op (TODO: implement FCM integration).
415
+ *
416
+ * @param userInfo - The notification payload from the push notification
417
+ * @param authorizationStatus - Current push authorization status
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * import { handlePushOpen } from 'attentive-react-native-sdk';
422
+ * import { AppState } from 'react-native';
423
+ *
424
+ * // In your notification handler:
425
+ * const appState = AppState.currentState;
426
+ * if (appState === 'background' || appState === 'inactive') {
427
+ * handlePushOpen(notification.data, 'authorized');
428
+ * }
429
+ * ```
430
+ */
431
+ function handlePushOpen(
432
+ userInfo: PushNotificationUserInfo,
433
+ authorizationStatus: PushAuthorizationStatus
434
+ ): void {
435
+ AttentiveReactNativeSdk.handlePushOpen(
436
+ userInfo as Object,
437
+ authorizationStatus
438
+ )
439
+ }
440
+
441
+ export {
442
+ initialize,
443
+ triggerCreative,
444
+ destroyCreative,
445
+ updateDomain,
446
+ identify,
447
+ clearUser,
448
+ recordAddToCartEvent,
449
+ recordProductViewEvent,
450
+ recordPurchaseEvent,
451
+ recordCustomEvent,
452
+ invokeAttentiveDebugHelper,
453
+ exportDebugLogs,
454
+ // Push Notification Methods (iOS only)
455
+ registerForPushNotifications,
456
+ registerDeviceToken,
457
+ registerDeviceTokenWithCallback,
458
+ handleRegularOpen,
459
+ handlePushOpened,
460
+ handleForegroundNotification,
461
+ handleForegroundPush,
462
+ handlePushOpen,
463
+ }
464
+
465
+ export type {
466
+ UserIdentifiers,
467
+ AttentiveSdkConfiguration,
468
+ ProductView,
469
+ Purchase,
470
+ AddToCart,
471
+ CustomEvent,
472
+ Item,
473
+ // Push Notification Types
474
+ PushAuthorizationStatus,
475
+ ApplicationState,
476
+ PushNotificationUserInfo,
477
+ PushRegistrationResult,
478
+ }