@dashx/react-native 1.1.5 → 1.2.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.
- package/.DS_Store +0 -0
- package/.claude/settings.local.json +18 -1
- package/.gitignore +1 -1
- package/DashXReactNative.podspec +3 -24
- package/README.md +3 -1
- package/__tests__/fallback.test.js +72 -0
- package/__tests__/index.test.js +17 -4
- package/android/build.gradle +27 -1
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/dashx/rn/sdk/DashXReactNativeModuleImpl.kt +384 -0
- package/android/src/newarch/java/com/dashx/rn/sdk/DashXReactNativeModule.kt +85 -0
- package/android/src/newarch/java/com/dashx/rn/sdk/DashXReactNativePackage.kt +33 -0
- package/android/src/oldarch/java/com/dashx/rn/sdk/DashXReactNativeModule.kt +106 -0
- package/android/src/oldarch/java/com/dashx/rn/sdk/DashXReactNativePackage.kt +33 -0
- package/index.d.ts +3 -1
- package/index.js +6 -8
- package/ios/DashXNotificationHandler.swift +222 -0
- package/ios/DashXReactNative-Bridging-Header.h +0 -2
- package/ios/DashXReactNative.mm +32 -9
- package/ios/DashXReactNative.swift +26 -18
- package/package.json +11 -2
- package/src/NativeDashXReactNative.ts +42 -0
- package/android/src/main/java/com/dashx/rn/sdk/DashXReactNativeModule.kt +0 -231
- package/android/src/main/java/com/dashx/rn/sdk/DashXReactNativePackage.kt +0 -18
- package/ios/DashXRCTAppDelegate.h +0 -3
- package/ios/DashXRCTAppDelegate.swift +0 -266
- package/ios/DashXReactNative.h +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.dashx.rn.sdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
|
|
7
|
+
class DashXReactNativeModule(reactContext: ReactApplicationContext) :
|
|
8
|
+
NativeDashXReactNativeSpec(reactContext) {
|
|
9
|
+
|
|
10
|
+
private val impl = DashXReactNativeModuleImpl(reactContext)
|
|
11
|
+
|
|
12
|
+
override fun getName(): String = DashXReactNativeModuleImpl.NAME
|
|
13
|
+
|
|
14
|
+
override fun initialize() {
|
|
15
|
+
super.initialize()
|
|
16
|
+
impl.initialize()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun invalidate() {
|
|
20
|
+
impl.invalidate()
|
|
21
|
+
super.invalidate()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun configure(options: ReadableMap) = impl.configure(options)
|
|
25
|
+
|
|
26
|
+
override fun identify(options: ReadableMap?) = impl.identify(options)
|
|
27
|
+
|
|
28
|
+
override fun setIdentity(uid: String?, token: String?) = impl.setIdentity(uid, token)
|
|
29
|
+
|
|
30
|
+
override fun reset() = impl.reset()
|
|
31
|
+
|
|
32
|
+
override fun track(event: String, data: ReadableMap?) = impl.track(event, data)
|
|
33
|
+
|
|
34
|
+
override fun screen(screenName: String, data: ReadableMap?) = impl.screen(screenName, data)
|
|
35
|
+
|
|
36
|
+
override fun fetchRecord(urn: String, options: ReadableMap?, promise: Promise) =
|
|
37
|
+
impl.fetchRecord(urn, options, promise)
|
|
38
|
+
|
|
39
|
+
override fun searchRecords(resource: String, options: ReadableMap?, promise: Promise) =
|
|
40
|
+
impl.searchRecords(resource, options, promise)
|
|
41
|
+
|
|
42
|
+
override fun fetchStoredPreferences(promise: Promise) = impl.fetchStoredPreferences(promise)
|
|
43
|
+
|
|
44
|
+
override fun saveStoredPreferences(preferenceData: ReadableMap?, promise: Promise) =
|
|
45
|
+
impl.saveStoredPreferences(preferenceData, promise)
|
|
46
|
+
|
|
47
|
+
override fun subscribe() = impl.subscribe()
|
|
48
|
+
|
|
49
|
+
override fun unsubscribe() = impl.unsubscribe()
|
|
50
|
+
|
|
51
|
+
override fun setLogLevel(level: Double) = impl.setLogLevel(level)
|
|
52
|
+
|
|
53
|
+
override fun enableLifecycleTracking() = impl.enableLifecycleTracking()
|
|
54
|
+
|
|
55
|
+
override fun enableAdTracking() = impl.enableAdTracking()
|
|
56
|
+
|
|
57
|
+
override fun processURL(url: String, source: String?, forwardToLinkHandler: Boolean) =
|
|
58
|
+
impl.processURL(url, source, forwardToLinkHandler)
|
|
59
|
+
|
|
60
|
+
override fun trackNotificationNavigation(action: ReadableMap?, notificationId: String?) =
|
|
61
|
+
impl.trackNotificationNavigation(action, notificationId)
|
|
62
|
+
|
|
63
|
+
override fun uploadAsset(
|
|
64
|
+
filePath: String,
|
|
65
|
+
resource: String,
|
|
66
|
+
attribute: String,
|
|
67
|
+
promise: Promise
|
|
68
|
+
) = impl.uploadAsset(filePath, resource, attribute, promise)
|
|
69
|
+
|
|
70
|
+
override fun fetchAsset(assetId: String, promise: Promise) = impl.fetchAsset(assetId, promise)
|
|
71
|
+
|
|
72
|
+
override fun requestNotificationPermission(promise: Promise) =
|
|
73
|
+
impl.requestNotificationPermission(promise)
|
|
74
|
+
|
|
75
|
+
override fun getNotificationPermissionStatus(promise: Promise) =
|
|
76
|
+
impl.getNotificationPermissionStatus(promise)
|
|
77
|
+
|
|
78
|
+
override fun addListener(eventName: String) {
|
|
79
|
+
// Required for RN built-in Event Emitter Calls
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override fun removeListeners(count: Double) {
|
|
83
|
+
// Required for RN built-in Event Emitter Calls
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.dashx.rn.sdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
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 DashXReactNativePackage : BaseReactPackage() {
|
|
10
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
11
|
+
return if (name == DashXReactNativeModuleImpl.NAME) {
|
|
12
|
+
DashXReactNativeModule(reactContext)
|
|
13
|
+
} else {
|
|
14
|
+
null
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
19
|
+
return ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
DashXReactNativeModuleImpl.NAME to ReactModuleInfo(
|
|
22
|
+
DashXReactNativeModuleImpl.NAME,
|
|
23
|
+
DashXReactNativeModuleImpl.NAME,
|
|
24
|
+
false, // canOverrideExistingModule
|
|
25
|
+
false, // needsEagerInit
|
|
26
|
+
false, // hasConstants
|
|
27
|
+
false, // isCxxModule
|
|
28
|
+
true // isTurboModule
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package com.dashx.rn.sdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
|
+
import com.facebook.react.bridge.ReactMethod
|
|
7
|
+
import com.facebook.react.bridge.ReadableMap
|
|
8
|
+
|
|
9
|
+
class DashXReactNativeModule(reactContext: ReactApplicationContext) :
|
|
10
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
11
|
+
|
|
12
|
+
private val impl = DashXReactNativeModuleImpl(reactContext)
|
|
13
|
+
|
|
14
|
+
override fun getName(): String = DashXReactNativeModuleImpl.NAME
|
|
15
|
+
|
|
16
|
+
override fun initialize() {
|
|
17
|
+
super.initialize()
|
|
18
|
+
impl.initialize()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override fun invalidate() {
|
|
22
|
+
impl.invalidate()
|
|
23
|
+
super.invalidate()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@ReactMethod
|
|
27
|
+
fun configure(options: ReadableMap) = impl.configure(options)
|
|
28
|
+
|
|
29
|
+
@ReactMethod
|
|
30
|
+
fun identify(options: ReadableMap?) = impl.identify(options)
|
|
31
|
+
|
|
32
|
+
@ReactMethod
|
|
33
|
+
fun setIdentity(uid: String?, token: String?) = impl.setIdentity(uid, token)
|
|
34
|
+
|
|
35
|
+
@ReactMethod
|
|
36
|
+
fun reset() = impl.reset()
|
|
37
|
+
|
|
38
|
+
@ReactMethod
|
|
39
|
+
fun track(event: String, data: ReadableMap?) = impl.track(event, data)
|
|
40
|
+
|
|
41
|
+
@ReactMethod
|
|
42
|
+
fun screen(screenName: String, data: ReadableMap?) = impl.screen(screenName, data)
|
|
43
|
+
|
|
44
|
+
@ReactMethod
|
|
45
|
+
fun fetchRecord(urn: String, options: ReadableMap?, promise: Promise) =
|
|
46
|
+
impl.fetchRecord(urn, options, promise)
|
|
47
|
+
|
|
48
|
+
@ReactMethod
|
|
49
|
+
fun searchRecords(resource: String, options: ReadableMap?, promise: Promise) =
|
|
50
|
+
impl.searchRecords(resource, options, promise)
|
|
51
|
+
|
|
52
|
+
@ReactMethod
|
|
53
|
+
fun fetchStoredPreferences(promise: Promise) = impl.fetchStoredPreferences(promise)
|
|
54
|
+
|
|
55
|
+
@ReactMethod
|
|
56
|
+
fun saveStoredPreferences(preferenceData: ReadableMap?, promise: Promise) =
|
|
57
|
+
impl.saveStoredPreferences(preferenceData, promise)
|
|
58
|
+
|
|
59
|
+
@ReactMethod
|
|
60
|
+
fun subscribe() = impl.subscribe()
|
|
61
|
+
|
|
62
|
+
@ReactMethod
|
|
63
|
+
fun unsubscribe() = impl.unsubscribe()
|
|
64
|
+
|
|
65
|
+
@ReactMethod
|
|
66
|
+
fun setLogLevel(level: Double) = impl.setLogLevel(level)
|
|
67
|
+
|
|
68
|
+
@ReactMethod
|
|
69
|
+
fun enableLifecycleTracking() = impl.enableLifecycleTracking()
|
|
70
|
+
|
|
71
|
+
@ReactMethod
|
|
72
|
+
fun enableAdTracking() = impl.enableAdTracking()
|
|
73
|
+
|
|
74
|
+
@ReactMethod
|
|
75
|
+
fun processURL(url: String, source: String?, forwardToLinkHandler: Boolean) =
|
|
76
|
+
impl.processURL(url, source, forwardToLinkHandler)
|
|
77
|
+
|
|
78
|
+
@ReactMethod
|
|
79
|
+
fun trackNotificationNavigation(action: ReadableMap?, notificationId: String?) =
|
|
80
|
+
impl.trackNotificationNavigation(action, notificationId)
|
|
81
|
+
|
|
82
|
+
@ReactMethod
|
|
83
|
+
fun uploadAsset(filePath: String, resource: String, attribute: String, promise: Promise) =
|
|
84
|
+
impl.uploadAsset(filePath, resource, attribute, promise)
|
|
85
|
+
|
|
86
|
+
@ReactMethod
|
|
87
|
+
fun fetchAsset(assetId: String, promise: Promise) = impl.fetchAsset(assetId, promise)
|
|
88
|
+
|
|
89
|
+
@ReactMethod
|
|
90
|
+
fun requestNotificationPermission(promise: Promise) =
|
|
91
|
+
impl.requestNotificationPermission(promise)
|
|
92
|
+
|
|
93
|
+
@ReactMethod
|
|
94
|
+
fun getNotificationPermissionStatus(promise: Promise) =
|
|
95
|
+
impl.getNotificationPermissionStatus(promise)
|
|
96
|
+
|
|
97
|
+
@ReactMethod
|
|
98
|
+
fun addListener(eventName: String) {
|
|
99
|
+
// Required for RN built-in Event Emitter Calls
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@ReactMethod
|
|
103
|
+
fun removeListeners(count: Double) {
|
|
104
|
+
// Required for RN built-in Event Emitter Calls
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.dashx.rn.sdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
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 DashXReactNativePackage : BaseReactPackage() {
|
|
10
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
11
|
+
return if (name == DashXReactNativeModuleImpl.NAME) {
|
|
12
|
+
DashXReactNativeModule(reactContext)
|
|
13
|
+
} else {
|
|
14
|
+
null
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
19
|
+
return ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
DashXReactNativeModuleImpl.NAME to ReactModuleInfo(
|
|
22
|
+
DashXReactNativeModuleImpl.NAME,
|
|
23
|
+
DashXReactNativeModuleImpl.NAME,
|
|
24
|
+
false, // canOverrideExistingModule
|
|
25
|
+
false, // needsEagerInit
|
|
26
|
+
false, // hasConstants
|
|
27
|
+
false, // isCxxModule
|
|
28
|
+
false // isTurboModule
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { EmitterSubscription } from 'react-native';
|
|
|
7
7
|
export enum DashXErrorCode {
|
|
8
8
|
/** An unspecified error occurred. */
|
|
9
9
|
EUNSPECIFIED = 'EUNSPECIFIED',
|
|
10
|
+
/** The operation is not supported on this platform. */
|
|
11
|
+
EUNSUPPORTED = 'EUNSUPPORTED',
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export interface ConfigureOptions {
|
|
@@ -92,7 +94,7 @@ export interface DashXInstance {
|
|
|
92
94
|
/**
|
|
93
95
|
* Set user identity using a UID and authentication token.
|
|
94
96
|
*/
|
|
95
|
-
setIdentity(uid: string, token
|
|
97
|
+
setIdentity(uid: string, token?: string | null): void;
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
100
|
* Clear the current user identity and reset SDK state.
|
package/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
|
2
|
+
import NativeDashXReactNative from './src/NativeDashXReactNative';
|
|
2
3
|
|
|
3
4
|
function getLinkingError() {
|
|
4
5
|
return (
|
|
@@ -12,8 +13,10 @@ function getLinkingError() {
|
|
|
12
13
|
let _nativeDashX = null;
|
|
13
14
|
function getNativeDashX() {
|
|
14
15
|
if (!_nativeDashX) {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const nativeModule =
|
|
17
|
+
NativeDashXReactNative || NativeModules.DashXReactNative;
|
|
18
|
+
_nativeDashX = nativeModule
|
|
19
|
+
? nativeModule
|
|
17
20
|
: new Proxy(
|
|
18
21
|
{},
|
|
19
22
|
{
|
|
@@ -60,12 +63,7 @@ const DashX = {
|
|
|
60
63
|
'DashX.setIdentity: uid is required and must be a string'
|
|
61
64
|
);
|
|
62
65
|
}
|
|
63
|
-
|
|
64
|
-
throw new Error(
|
|
65
|
-
'DashX.setIdentity: token is required and must be a string'
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
return getNativeDashX().setIdentity(uid, token);
|
|
66
|
+
return getNativeDashX().setIdentity(uid, token || null);
|
|
69
67
|
},
|
|
70
68
|
|
|
71
69
|
reset() {
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
import UserNotifications
|
|
4
|
+
import DashX
|
|
5
|
+
#if canImport(FirebaseMessaging)
|
|
6
|
+
import FirebaseMessaging
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
/// The result of handling a notification response.
|
|
10
|
+
/// Consumers can use `url` to navigate and `actionIdentifier` for custom action handling.
|
|
11
|
+
@objc(DashXNotificationResponseResult)
|
|
12
|
+
public final class DashXNotificationResponseResult: NSObject {
|
|
13
|
+
@objc public let userInfo: [AnyHashable: Any]
|
|
14
|
+
@objc public let actionIdentifier: String
|
|
15
|
+
/// The DashX notification URL, if present in the payload.
|
|
16
|
+
@objc public let url: URL?
|
|
17
|
+
|
|
18
|
+
init(userInfo: [AnyHashable: Any], actionIdentifier: String, url: URL?) {
|
|
19
|
+
self.userInfo = userInfo
|
|
20
|
+
self.actionIdentifier = actionIdentifier
|
|
21
|
+
self.url = url
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@objc(DashXNotificationHandler)
|
|
26
|
+
public final class DashXNotificationHandler: NSObject {
|
|
27
|
+
|
|
28
|
+
@objc
|
|
29
|
+
public static func handleRemoteNotification(
|
|
30
|
+
userInfo: [AnyHashable: Any],
|
|
31
|
+
completionHandler: @escaping (UIBackgroundFetchResult) -> Void
|
|
32
|
+
) {
|
|
33
|
+
#if canImport(FirebaseMessaging)
|
|
34
|
+
Messaging.messaging().appDidReceiveMessage(userInfo)
|
|
35
|
+
#endif
|
|
36
|
+
|
|
37
|
+
DashXEventEmitter.instance.dispatch(
|
|
38
|
+
name: "messageReceived",
|
|
39
|
+
body: bridgeSafePayload(from: userInfo)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
guard let dashxData = userInfo.dashxNotificationData() else {
|
|
43
|
+
completionHandler(.noData)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
DashX.trackMessage(message: userInfo, event: .delivered)
|
|
48
|
+
|
|
49
|
+
let content = UNMutableNotificationContent()
|
|
50
|
+
content.sound = UNNotificationSound.default
|
|
51
|
+
content.title = dashxData.title
|
|
52
|
+
content.body = dashxData.body
|
|
53
|
+
content.userInfo = userInfo
|
|
54
|
+
content.categoryIdentifier = Constants.DASHX_NOTIFICATION_CATEGORY_IDENTIFIER
|
|
55
|
+
|
|
56
|
+
var actions: [UNNotificationAction] = []
|
|
57
|
+
if let actionButtons = dashxData.actionButtons {
|
|
58
|
+
for button in actionButtons {
|
|
59
|
+
actions.append(
|
|
60
|
+
UNNotificationAction(
|
|
61
|
+
identifier: button.identifier,
|
|
62
|
+
title: button.label,
|
|
63
|
+
options: [.foreground]
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let category = UNNotificationCategory(
|
|
70
|
+
identifier: Constants.DASHX_NOTIFICATION_CATEGORY_IDENTIFIER,
|
|
71
|
+
actions: actions,
|
|
72
|
+
intentIdentifiers: [],
|
|
73
|
+
options: .customDismissAction
|
|
74
|
+
)
|
|
75
|
+
UNUserNotificationCenter.current().setNotificationCategories([category])
|
|
76
|
+
|
|
77
|
+
if let imagePath = dashxData.image, let imageURL = URL(string: imagePath) {
|
|
78
|
+
createNotificationWithImage(id: dashxData.id, imageURL: imageURL, content: content)
|
|
79
|
+
} else {
|
|
80
|
+
createNotification(id: dashxData.id, content: content)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
completionHandler(.newData)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@objc
|
|
87
|
+
@discardableResult
|
|
88
|
+
public static func handleNotificationResponse(_ response: UNNotificationResponse) -> DashXNotificationResponseResult {
|
|
89
|
+
let userInfo = response.notification.request.content.userInfo
|
|
90
|
+
|
|
91
|
+
#if canImport(FirebaseMessaging)
|
|
92
|
+
Messaging.messaging().appDidReceiveMessage(userInfo)
|
|
93
|
+
#endif
|
|
94
|
+
|
|
95
|
+
DashXEventEmitter.instance.dispatch(
|
|
96
|
+
name: "messageReceived",
|
|
97
|
+
body: bridgeSafePayload(from: userInfo)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if response.actionIdentifier == UNNotificationDismissActionIdentifier {
|
|
101
|
+
DashX.trackMessage(message: userInfo, event: .dismissed)
|
|
102
|
+
} else {
|
|
103
|
+
DashX.trackMessage(message: userInfo, event: .clicked)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let notificationUrl = userInfo.dashxNotificationUrl()
|
|
107
|
+
|
|
108
|
+
return DashXNotificationResponseResult(
|
|
109
|
+
userInfo: userInfo,
|
|
110
|
+
actionIdentifier: response.actionIdentifier,
|
|
111
|
+
url: notificationUrl
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// MARK: - Device Token
|
|
116
|
+
|
|
117
|
+
/// Bridges the APNS device token to Firebase Cloud Messaging and registers
|
|
118
|
+
/// the FCM token with DashX. Call this from
|
|
119
|
+
/// `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`.
|
|
120
|
+
@objc
|
|
121
|
+
public static func handleDeviceToken(_ deviceToken: Data) {
|
|
122
|
+
#if canImport(FirebaseMessaging)
|
|
123
|
+
Messaging.messaging().setAPNSToken(deviceToken, type: .unknown)
|
|
124
|
+
Messaging.messaging().token { token, error in
|
|
125
|
+
if let token = token {
|
|
126
|
+
DashX.setFCMToken(to: token)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
#endif
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// MARK: - Foreground Notification
|
|
133
|
+
|
|
134
|
+
/// Handles a notification that arrived while the app is in the foreground.
|
|
135
|
+
/// Tracks the delivery event, emits `messageReceived` to JS, forwards to
|
|
136
|
+
/// Firebase analytics, and presents the notification as a banner with sound.
|
|
137
|
+
/// Call this from `userNotificationCenter(_:willPresent:withCompletionHandler:)`.
|
|
138
|
+
@objc
|
|
139
|
+
public static func handleForegroundNotification(
|
|
140
|
+
_ notification: UNNotification,
|
|
141
|
+
completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
|
142
|
+
) {
|
|
143
|
+
let userInfo = notification.request.content.userInfo
|
|
144
|
+
|
|
145
|
+
#if canImport(FirebaseMessaging)
|
|
146
|
+
Messaging.messaging().appDidReceiveMessage(userInfo)
|
|
147
|
+
#endif
|
|
148
|
+
|
|
149
|
+
DashX.trackMessage(message: userInfo, event: .delivered)
|
|
150
|
+
|
|
151
|
+
DashXEventEmitter.instance.dispatch(
|
|
152
|
+
name: "messageReceived",
|
|
153
|
+
body: bridgeSafePayload(from: userInfo)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
completionHandler([.banner, .sound])
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// MARK: - Private Helpers
|
|
160
|
+
|
|
161
|
+
private static func createNotification(id: String, content: UNMutableNotificationContent) {
|
|
162
|
+
let request = UNNotificationRequest(identifier: id, content: content, trigger: nil)
|
|
163
|
+
UNUserNotificationCenter.current().add(request) { error in
|
|
164
|
+
if let error = error {
|
|
165
|
+
DashXLog.d(tag: #function, "Failed to schedule notification: \(error.localizedDescription)")
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private static func createNotificationWithImage(
|
|
171
|
+
id: String,
|
|
172
|
+
imageURL: URL,
|
|
173
|
+
content: UNMutableNotificationContent
|
|
174
|
+
) {
|
|
175
|
+
let task = URLSession.shared.downloadTask(with: imageURL) { location, _, error in
|
|
176
|
+
guard let location = location, error == nil else {
|
|
177
|
+
createNotification(id: id, content: content)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let tmpDir = FileManager.default.temporaryDirectory
|
|
182
|
+
let tmpFile = tmpDir.appendingPathComponent(UUID().uuidString + ".png")
|
|
183
|
+
|
|
184
|
+
do {
|
|
185
|
+
try FileManager.default.moveItem(at: location, to: tmpFile)
|
|
186
|
+
let attachment = try UNNotificationAttachment(
|
|
187
|
+
identifier: "\(id)-attachment",
|
|
188
|
+
url: tmpFile,
|
|
189
|
+
options: nil
|
|
190
|
+
)
|
|
191
|
+
content.attachments = [attachment]
|
|
192
|
+
createNotification(id: id, content: content)
|
|
193
|
+
} catch {
|
|
194
|
+
createNotification(id: id, content: content)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
task.resume()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private static func bridgeSafePayload(from userInfo: [AnyHashable: Any]) -> [String: Any] {
|
|
201
|
+
userInfo.reduce(into: [String: Any]()) { result, pair in
|
|
202
|
+
let key = String(describing: pair.key)
|
|
203
|
+
let value = pair.value
|
|
204
|
+
if let str = value as? String {
|
|
205
|
+
result[key] = str
|
|
206
|
+
} else if let num = value as? NSNumber {
|
|
207
|
+
result[key] = num
|
|
208
|
+
} else if let dict = value as? [AnyHashable: Any] {
|
|
209
|
+
result[key] = bridgeSafePayload(from: dict)
|
|
210
|
+
} else if let arr = value as? [Any] {
|
|
211
|
+
result[key] = arr.map { item -> Any in
|
|
212
|
+
if let str = item as? String { return str }
|
|
213
|
+
if let num = item as? NSNumber { return num }
|
|
214
|
+
if let d = item as? [AnyHashable: Any] { return bridgeSafePayload(from: d) }
|
|
215
|
+
return "\(item)"
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
result[key] = "\(value)"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
package/ios/DashXReactNative.mm
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#import <React/RCTBridgeModule.h>
|
|
2
2
|
#import <React/RCTEventEmitter.h>
|
|
3
3
|
|
|
4
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
5
|
+
#import "RNDashXReactNativeSpec.h"
|
|
6
|
+
#endif
|
|
7
|
+
|
|
4
8
|
@interface RCT_EXTERN_MODULE(DashXReactNative, RCTEventEmitter)
|
|
5
9
|
|
|
6
10
|
RCT_EXTERN_METHOD(configure:(NSDictionary *)options);
|
|
@@ -13,13 +17,13 @@ RCT_EXTERN_METHOD(track:(NSString *)event data:(NSDictionary * _Nullable)data);
|
|
|
13
17
|
|
|
14
18
|
RCT_EXTERN_METHOD(screen:(NSString *)screenName data:(NSDictionary * _Nullable)data);
|
|
15
19
|
|
|
16
|
-
RCT_EXTERN_METHOD(fetchRecord:(NSString *)urn options:(NSDictionary * _Nullable)options
|
|
20
|
+
RCT_EXTERN_METHOD(fetchRecord:(NSString *)urn options:(NSDictionary * _Nullable)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
17
21
|
|
|
18
|
-
RCT_EXTERN_METHOD(searchRecords:(NSString *)resource options:(NSDictionary * _Nullable)options
|
|
22
|
+
RCT_EXTERN_METHOD(searchRecords:(NSString *)resource options:(NSDictionary * _Nullable)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
19
23
|
|
|
20
|
-
RCT_EXTERN_METHOD(fetchStoredPreferences:(RCTPromiseResolveBlock)resolve
|
|
24
|
+
RCT_EXTERN_METHOD(fetchStoredPreferences:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
21
25
|
|
|
22
|
-
RCT_EXTERN_METHOD(saveStoredPreferences:(NSDictionary *)preferenceData
|
|
26
|
+
RCT_EXTERN_METHOD(saveStoredPreferences:(NSDictionary *)preferenceData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
23
27
|
|
|
24
28
|
RCT_EXTERN_METHOD(reset);
|
|
25
29
|
|
|
@@ -27,18 +31,37 @@ RCT_EXTERN_METHOD(subscribe);
|
|
|
27
31
|
|
|
28
32
|
RCT_EXTERN_METHOD(unsubscribe);
|
|
29
33
|
|
|
30
|
-
RCT_EXTERN_METHOD(setLogLevel:(
|
|
34
|
+
RCT_EXTERN_METHOD(setLogLevel:(double)level);
|
|
31
35
|
|
|
32
|
-
RCT_EXTERN_METHOD(uploadAsset:(NSString *)filePath resource:(NSString *)resource attribute:(NSString *)attribute
|
|
36
|
+
RCT_EXTERN_METHOD(uploadAsset:(NSString *)filePath resource:(NSString *)resource attribute:(NSString *)attribute resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
33
37
|
|
|
34
|
-
RCT_EXTERN_METHOD(fetchAsset:(NSString *)assetId
|
|
38
|
+
RCT_EXTERN_METHOD(fetchAsset:(NSString *)assetId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
35
39
|
|
|
36
40
|
RCT_EXTERN_METHOD(enableLifecycleTracking);
|
|
37
41
|
|
|
38
42
|
RCT_EXTERN_METHOD(enableAdTracking);
|
|
39
43
|
|
|
40
|
-
RCT_EXTERN_METHOD(
|
|
44
|
+
RCT_EXTERN_METHOD(processURL:(NSString *)url source:(NSString * _Nullable)source forwardToLinkHandler:(BOOL)forwardToLinkHandler);
|
|
45
|
+
|
|
46
|
+
RCT_EXTERN_METHOD(trackNotificationNavigation:(NSDictionary * _Nullable)action notificationId:(NSString * _Nullable)notificationId);
|
|
47
|
+
|
|
48
|
+
RCT_EXTERN_METHOD(requestNotificationPermission:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
49
|
+
|
|
50
|
+
RCT_EXTERN_METHOD(getNotificationPermissionStatus:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
|
51
|
+
|
|
52
|
+
@end
|
|
53
|
+
|
|
54
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
55
|
+
@interface DashXReactNative () <NativeDashXReactNativeSpec>
|
|
56
|
+
@end
|
|
57
|
+
|
|
58
|
+
@implementation DashXReactNative (TurboModule)
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
61
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
62
|
+
{
|
|
63
|
+
return std::make_shared<facebook::react::NativeDashXReactNativeSpecJSI>(params);
|
|
64
|
+
}
|
|
43
65
|
|
|
44
66
|
@end
|
|
67
|
+
#endif
|