@attentive-mobile/attentive-react-native-sdk 2.0.0-beta.5 → 2.0.0-beta.6
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.
- package/README.md +3 -2
- package/android/build.gradle +4 -1
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveNotificationStore.kt +60 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentivePushHelper.kt +15 -7
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt +168 -136
- package/android/src/test/kotlin/com/attentivereactnativesdk/AttentiveNotificationStoreTest.kt +103 -0
- package/ios/AttentiveReactNativeSdk.mm +17 -2
- package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/xcuserdata/zheref.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/Bridging/ATTNNativeSDK.swift +35 -28
- package/lib/commonjs/NativeAttentiveReactNativeSdk.js +1 -1
- package/lib/commonjs/NativeAttentiveReactNativeSdk.js.map +1 -1
- package/lib/commonjs/eventTypes.js.map +1 -1
- package/lib/commonjs/index.js +36 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/NativeAttentiveReactNativeSdk.js +2 -2
- package/lib/module/NativeAttentiveReactNativeSdk.js.map +1 -1
- package/lib/module/eventTypes.js.map +1 -1
- package/lib/module/index.js +36 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts +12 -1
- package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +32 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/NativeAttentiveReactNativeSdk.ts +69 -52
- package/src/index.tsx +36 -1
package/README.md
CHANGED
|
@@ -270,12 +270,13 @@ return () => subscription.remove();
|
|
|
270
270
|
|
|
271
271
|
#### 3. Optional: Register FCM token (Android)
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
**Recommended:** This React Native SDK’s Android native module depends on Attentive Android SDK **2.1.1**, which exposes `AttentiveSdk.getPushTokenWithCallback`. Calling `registerForPushNotifications()` from JS triggers that API: the SDK requests permission (when needed), fetches the FCM token, and registers it with Attentive. No separate native code is required.
|
|
274
|
+
|
|
275
|
+
**Alternative (token from JS):** If you obtain the FCM token elsewhere (e.g. Firebase Messaging), use `registerDeviceTokenWithCallback` and then call `handleRegularOpen` in the callback:
|
|
274
276
|
|
|
275
277
|
```typescript
|
|
276
278
|
import { registerDeviceTokenWithCallback, handleRegularOpen } from 'attentive-react-native-sdk';
|
|
277
279
|
|
|
278
|
-
// When you receive the FCM token (e.g. from Firebase Messaging):
|
|
279
280
|
getPushAuthorizationStatus().then((authStatus) => {
|
|
280
281
|
registerDeviceTokenWithCallback(
|
|
281
282
|
fcmToken,
|
package/android/build.gradle
CHANGED
|
@@ -82,7 +82,10 @@ dependencies {
|
|
|
82
82
|
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
83
83
|
//noinspection GradleDynamicVersion
|
|
84
84
|
implementation "com.facebook.react:react-native:+"
|
|
85
|
-
|
|
85
|
+
// Use `api` so that the Attentive Android SDK types (AttentiveSdk, CustomEvent, etc.)
|
|
86
|
+
// are visible to app-level code (e.g. Bonni's AttentiveFirebaseMessagingService) that
|
|
87
|
+
// depends on this library and needs to call the SDK directly from native components.
|
|
88
|
+
api 'com.attentive:attentive-android-sdk:2.1.3-beta.1'
|
|
86
89
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.10"
|
|
87
90
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.10"))
|
|
88
91
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package com.attentivereactnativesdk
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* In-process store for a pending initial push notification payload.
|
|
5
|
+
*
|
|
6
|
+
* When the user taps an FCM notification while the app is in the killed state,
|
|
7
|
+
* Android launches the app's main activity before the React Native bridge is
|
|
8
|
+
* initialised. The notification payload is written here by the host app's
|
|
9
|
+
* `MainActivity` and consumed exactly once by [AttentiveReactNativeSdkModule.getInitialPushNotification]
|
|
10
|
+
* after the JS layer is ready.
|
|
11
|
+
*
|
|
12
|
+
* ## Usage pattern
|
|
13
|
+
*
|
|
14
|
+
* ```kotlin
|
|
15
|
+
* // In host app's MainActivity.onCreate:
|
|
16
|
+
* intent?.extras?.let { extras ->
|
|
17
|
+
* val payload = // … extract FCM data …
|
|
18
|
+
* AttentiveNotificationStore.setPendingInitialNotification(payload)
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // In JS (App.tsx), after initialize():
|
|
24
|
+
* const initial = await getInitialPushNotification()
|
|
25
|
+
* if (initial) handlePushOpen(initial, authStatus)
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Thread-safety: the two public methods are `@Synchronized` so concurrent access
|
|
29
|
+
* from the main UI thread (writer) and the JS bridge thread (reader) is safe.
|
|
30
|
+
*/
|
|
31
|
+
object AttentiveNotificationStore {
|
|
32
|
+
|
|
33
|
+
@Volatile
|
|
34
|
+
private var pendingInitialNotification: Map<String, String>? = null
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Stores [payload] as the pending initial push notification.
|
|
38
|
+
*
|
|
39
|
+
* Replaces any previously stored value (only one initial notification is tracked at a time).
|
|
40
|
+
*
|
|
41
|
+
* @param payload A map of string key-value pairs representing the notification data.
|
|
42
|
+
*/
|
|
43
|
+
@Synchronized
|
|
44
|
+
fun setPendingInitialNotification(payload: Map<String, String>) {
|
|
45
|
+
pendingInitialNotification = payload
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns the stored initial push notification payload and clears it atomically,
|
|
50
|
+
* ensuring the payload is delivered to the JS layer exactly once.
|
|
51
|
+
*
|
|
52
|
+
* @return The stored payload map, or `null` if no initial notification is pending.
|
|
53
|
+
*/
|
|
54
|
+
@Synchronized
|
|
55
|
+
fun getAndClear(): Map<String, String>? {
|
|
56
|
+
val pending = pendingInitialNotification
|
|
57
|
+
pendingInitialNotification = null
|
|
58
|
+
return pending
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -26,8 +26,8 @@ object AttentivePushHelper {
|
|
|
26
26
|
/**
|
|
27
27
|
* Authorization status values aligned with iOS push authorization for use in handleRegularOpen etc.
|
|
28
28
|
* - "authorized" – user has granted notification permission (or API < 33)
|
|
29
|
-
* - "denied" – user denied
|
|
30
|
-
* - "notDetermined" – not yet requested (API 33+ only)
|
|
29
|
+
* - "denied" – user was asked and denied (API 33+, when determinable via activity)
|
|
30
|
+
* - "notDetermined" – not yet requested, or unable to distinguish (API 33+ only)
|
|
31
31
|
*/
|
|
32
32
|
const val STATUS_AUTHORIZED = "authorized"
|
|
33
33
|
const val STATUS_DENIED = "denied"
|
|
@@ -36,14 +36,18 @@ object AttentivePushHelper {
|
|
|
36
36
|
/**
|
|
37
37
|
* Returns the current push notification authorization status.
|
|
38
38
|
*
|
|
39
|
-
* On API 33+: uses [android.permission.POST_NOTIFICATIONS].
|
|
39
|
+
* On API 33+: uses [android.permission.POST_NOTIFICATIONS]. When permission is not granted,
|
|
40
|
+
* uses [activity] (when provided) and [ActivityCompat.shouldShowRequestPermissionRationale]
|
|
41
|
+
* to distinguish "denied" (user was asked and declined) from "notDetermined" (not yet asked).
|
|
40
42
|
* On API < 33: returns [STATUS_AUTHORIZED] (no runtime permission required).
|
|
41
43
|
*
|
|
42
44
|
* @param context Application or Activity context
|
|
45
|
+
* @param activity Current activity, or null. When non-null on API 33+, used to detect
|
|
46
|
+
* "denied" vs "notDetermined" so downstream logic and analytics are correct for denied users.
|
|
43
47
|
* @return One of [STATUS_AUTHORIZED], [STATUS_DENIED], or [STATUS_NOT_DETERMINED]
|
|
44
48
|
*/
|
|
45
49
|
@JvmStatic
|
|
46
|
-
fun getAuthorizationStatus(context: Context): String {
|
|
50
|
+
fun getAuthorizationStatus(context: Context, activity: Activity? = null): String {
|
|
47
51
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
48
52
|
// API 32 and below: notification permission not required at runtime
|
|
49
53
|
return STATUS_AUTHORIZED
|
|
@@ -51,9 +55,13 @@ object AttentivePushHelper {
|
|
|
51
55
|
return when (ContextCompat.checkSelfPermission(context, android.Manifest.permission.POST_NOTIFICATIONS)) {
|
|
52
56
|
PackageManager.PERMISSION_GRANTED -> STATUS_AUTHORIZED
|
|
53
57
|
else -> {
|
|
54
|
-
// Not granted.
|
|
55
|
-
//
|
|
56
|
-
|
|
58
|
+
// Not granted. Use shouldShowRequestPermissionRationale when we have an Activity
|
|
59
|
+
// so we do not report "denied" users as "notDetermined" (fixes prompt-gating and analytics).
|
|
60
|
+
if (activity != null && ActivityCompat.shouldShowRequestPermissionRationale(activity, android.Manifest.permission.POST_NOTIFICATIONS)) {
|
|
61
|
+
STATUS_DENIED
|
|
62
|
+
} else {
|
|
63
|
+
STATUS_NOT_DETERMINED
|
|
64
|
+
}
|
|
57
65
|
}
|
|
58
66
|
}
|
|
59
67
|
}
|