@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
package/.DS_Store
CHANGED
|
Binary file
|
|
@@ -8,7 +8,24 @@
|
|
|
8
8
|
"Bash(swift build:*)",
|
|
9
9
|
"Bash(xcodebuild -scheme DashX -destination 'generic/platform=iOS' build)",
|
|
10
10
|
"Bash(./gradlew :dashx:compileDebugKotlin)",
|
|
11
|
-
"Bash(./gradlew :dashx:compileDebugKotlin --info)"
|
|
11
|
+
"Bash(./gradlew :dashx:compileDebugKotlin --info)",
|
|
12
|
+
"Bash(gh pr:*)",
|
|
13
|
+
"WebFetch(domain:github.com)",
|
|
14
|
+
"WebFetch(domain:patch-diff.githubusercontent.com)",
|
|
15
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
16
|
+
"Bash(ls -la /Users/gulshan/Work/dashx-react-native/ios/DashXRCTAppDelegate*)",
|
|
17
|
+
"Bash(npx jest *)",
|
|
18
|
+
"Bash(ls *)",
|
|
19
|
+
"Bash(grep *)",
|
|
20
|
+
"Read(//private/tmp/**)",
|
|
21
|
+
"Bash(javap -p com/dashx/android/DashX.class)",
|
|
22
|
+
"Bash(javap -p 'com/dashx/android/DashX$Companion.class')",
|
|
23
|
+
"Bash(jar xf *)",
|
|
24
|
+
"Bash(javap 'com/dashx/android/DashX$Companion.class')",
|
|
25
|
+
"Bash(javap 'com/dashx/android/data/Asset.class')",
|
|
26
|
+
"Bash(javap 'com/dashx/android/data/AssetData.class')",
|
|
27
|
+
"Read(//Users/gulshan/Work/dashx-ios/**)",
|
|
28
|
+
"WebSearch"
|
|
12
29
|
]
|
|
13
30
|
}
|
|
14
31
|
}
|
package/.gitignore
CHANGED
package/DashXReactNative.podspec
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
-
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
4
|
|
|
6
5
|
Pod::Spec.new do |s|
|
|
7
6
|
s.name = "DashXReactNative"
|
|
@@ -17,30 +16,10 @@ Pod::Spec.new do |s|
|
|
|
17
16
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
17
|
s.requires_arc = true
|
|
19
18
|
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
install_modules_dependencies(s)
|
|
24
|
-
else
|
|
25
|
-
s.dependency "React-Core"
|
|
19
|
+
# React Native >= 0.71 provides install_modules_dependencies.
|
|
20
|
+
# Since our minimum supported RN version is 0.74, we can call it directly.
|
|
21
|
+
install_modules_dependencies(s)
|
|
26
22
|
|
|
27
|
-
# Don't install the dependencies when we run `pod install` in the old architecture.
|
|
28
|
-
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
29
|
-
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
30
|
-
s.pod_target_xcconfig = {
|
|
31
|
-
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
32
|
-
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
33
|
-
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
34
|
-
}
|
|
35
|
-
s.dependency "React-Codegen"
|
|
36
|
-
s.dependency "RCT-Folly"
|
|
37
|
-
s.dependency "RCTRequired"
|
|
38
|
-
s.dependency "RCTTypeSafety"
|
|
39
|
-
s.dependency "ReactCommon/turbomodule/core"
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
s.dependency "React-RCTAppDelegate"
|
|
44
23
|
# DashX iOS SDK — consumers must provide the source in their Podfile:
|
|
45
24
|
# pod 'DashX', :git => 'https://github.com/dashxhq/dashx-ios.git', :tag => '1.1.9'
|
|
46
25
|
# For local development:
|
package/README.md
CHANGED
|
@@ -26,7 +26,9 @@ _DashX SDK for React Native_
|
|
|
26
26
|
|
|
27
27
|
## Install
|
|
28
28
|
|
|
29
|
-
Requires **React Native 0.
|
|
29
|
+
Requires **React Native 0.74+**, **iOS 13.0+**, **Android SDK 26+**, and **Node.js 16+**.
|
|
30
|
+
|
|
31
|
+
Supports both the **Old Architecture** (bridge) and **New Architecture** (TurboModule) from a single install.
|
|
30
32
|
|
|
31
33
|
> **Note:** This SDK uses native modules and is **not compatible with Expo Go**. Use a [development build](https://docs.expo.dev/develop/development-builds/introduction/) if you are using Expo.
|
|
32
34
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const mockNativeModule = {
|
|
2
|
+
configure: jest.fn(),
|
|
3
|
+
identify: jest.fn(),
|
|
4
|
+
setIdentity: jest.fn(),
|
|
5
|
+
reset: jest.fn(),
|
|
6
|
+
track: jest.fn(),
|
|
7
|
+
screen: jest.fn(),
|
|
8
|
+
fetchRecord: jest.fn(),
|
|
9
|
+
searchRecords: jest.fn(),
|
|
10
|
+
fetchStoredPreferences: jest.fn(),
|
|
11
|
+
saveStoredPreferences: jest.fn(),
|
|
12
|
+
subscribe: jest.fn(),
|
|
13
|
+
unsubscribe: jest.fn(),
|
|
14
|
+
setLogLevel: jest.fn(),
|
|
15
|
+
uploadAsset: jest.fn(),
|
|
16
|
+
fetchAsset: jest.fn(),
|
|
17
|
+
enableLifecycleTracking: jest.fn(),
|
|
18
|
+
enableAdTracking: jest.fn(),
|
|
19
|
+
processURL: jest.fn(),
|
|
20
|
+
trackNotificationNavigation: jest.fn(),
|
|
21
|
+
requestNotificationPermission: jest.fn(),
|
|
22
|
+
getNotificationPermissionStatus: jest.fn(),
|
|
23
|
+
addListener: jest.fn(),
|
|
24
|
+
removeListeners: jest.fn(),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
jest.mock('react-native', () => ({
|
|
28
|
+
NativeModules: {
|
|
29
|
+
DashXReactNative: mockNativeModule,
|
|
30
|
+
},
|
|
31
|
+
NativeEventEmitter: jest.fn().mockImplementation(() => ({
|
|
32
|
+
addListener: jest.fn(() => ({ remove: jest.fn() })),
|
|
33
|
+
})),
|
|
34
|
+
Platform: {
|
|
35
|
+
OS: 'ios',
|
|
36
|
+
select: jest.fn((obj) => obj.default || ''),
|
|
37
|
+
},
|
|
38
|
+
TurboModuleRegistry: {
|
|
39
|
+
get: jest.fn(() => null),
|
|
40
|
+
getEnforcing: jest.fn(() => {
|
|
41
|
+
throw new Error('not available');
|
|
42
|
+
}),
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// TurboModule returns null -> should fall back to NativeModules
|
|
47
|
+
jest.mock('../src/NativeDashXReactNative', () => ({
|
|
48
|
+
__esModule: true,
|
|
49
|
+
default: null,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
const DashX = require('../index').default;
|
|
53
|
+
|
|
54
|
+
describe('NativeModules fallback', () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
jest.clearAllMocks();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('falls back to NativeModules.DashXReactNative when TurboModule is null', () => {
|
|
60
|
+
DashX.configure({ publicKey: 'pk_test' });
|
|
61
|
+
expect(mockNativeModule.configure).toHaveBeenCalledWith({
|
|
62
|
+
publicKey: 'pk_test',
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('calls track on the fallback module', () => {
|
|
67
|
+
DashX.track('event_name', { key: 'value' });
|
|
68
|
+
expect(mockNativeModule.track).toHaveBeenCalledWith('event_name', {
|
|
69
|
+
key: 'value',
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
package/__tests__/index.test.js
CHANGED
|
@@ -37,6 +37,15 @@ jest.mock('react-native', () => ({
|
|
|
37
37
|
OS: 'ios',
|
|
38
38
|
select: jest.fn((obj) => obj.default || ''),
|
|
39
39
|
},
|
|
40
|
+
TurboModuleRegistry: {
|
|
41
|
+
get: jest.fn(() => mockNativeModule),
|
|
42
|
+
getEnforcing: jest.fn(() => mockNativeModule),
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
jest.mock('../src/NativeDashXReactNative', () => ({
|
|
47
|
+
__esModule: true,
|
|
48
|
+
default: mockNativeModule,
|
|
40
49
|
}));
|
|
41
50
|
|
|
42
51
|
const DashX = require('../index').default;
|
|
@@ -101,10 +110,14 @@ describe('setIdentity', () => {
|
|
|
101
110
|
expect(() => DashX.setIdentity(null, 'token')).toThrow('uid is required');
|
|
102
111
|
});
|
|
103
112
|
|
|
104
|
-
it('
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
it('passes null token through to native', () => {
|
|
114
|
+
DashX.setIdentity('uid', null);
|
|
115
|
+
expect(mockNativeModule.setIdentity).toHaveBeenCalledWith('uid', null);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('passes undefined token as null to native', () => {
|
|
119
|
+
DashX.setIdentity('uid');
|
|
120
|
+
expect(mockNativeModule.setIdentity).toHaveBeenCalledWith('uid', null);
|
|
108
121
|
});
|
|
109
122
|
});
|
|
110
123
|
|
package/android/build.gradle
CHANGED
|
@@ -3,6 +3,14 @@ import groovy.json.JsonSlurper
|
|
|
3
3
|
apply plugin: 'com.android.library'
|
|
4
4
|
apply plugin: 'kotlin-android'
|
|
5
5
|
|
|
6
|
+
def isNewArchitectureEnabled() {
|
|
7
|
+
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (isNewArchitectureEnabled()) {
|
|
11
|
+
apply plugin: 'com.facebook.react'
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
def getVersionFromNpm() {
|
|
7
15
|
def inputFile = new File("../package.json")
|
|
8
16
|
def packageJson = new JsonSlurper().parseText(inputFile.text)
|
|
@@ -30,6 +38,16 @@ android {
|
|
|
30
38
|
abortOnError false
|
|
31
39
|
}
|
|
32
40
|
|
|
41
|
+
sourceSets {
|
|
42
|
+
main {
|
|
43
|
+
if (isNewArchitectureEnabled()) {
|
|
44
|
+
java.srcDirs += ['src/newarch/java']
|
|
45
|
+
} else {
|
|
46
|
+
java.srcDirs += ['src/oldarch/java']
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
33
51
|
packaging {
|
|
34
52
|
pickFirst 'lib/arm64-v8a/libc++_shared.so'
|
|
35
53
|
pickFirst 'lib/arm64-v8a/libfbjni.so'
|
|
@@ -42,6 +60,14 @@ android {
|
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
62
|
|
|
63
|
+
if (isNewArchitectureEnabled()) {
|
|
64
|
+
react {
|
|
65
|
+
jsRootDir = file("../src/")
|
|
66
|
+
libraryName = "RNDashXReactNativeSpec"
|
|
67
|
+
codegenJavaPackageName = "com.dashx.rn.sdk"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
45
71
|
repositories {
|
|
46
72
|
mavenCentral()
|
|
47
73
|
google()
|
|
@@ -55,5 +81,5 @@ dependencies {
|
|
|
55
81
|
// Ensure Firebase Messaging types are available for FCM service subclass
|
|
56
82
|
implementation 'com.google.firebase:firebase-messaging-ktx:23.1.2'
|
|
57
83
|
|
|
58
|
-
implementation "com.dashx:dashx-android:1.2.
|
|
84
|
+
implementation "com.dashx:dashx-android:1.2.5"
|
|
59
85
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
2
|
xmlns:tools="http://schemas.android.com/tools">
|
|
3
|
+
|
|
4
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
5
|
+
|
|
3
6
|
<application>
|
|
4
7
|
<!-- Replace dashx-android's FCM service with our subclass that also emits to JS. -->
|
|
5
8
|
<service
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
package com.dashx.rn.sdk
|
|
2
|
+
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.app.Activity
|
|
5
|
+
import android.app.NotificationChannel
|
|
6
|
+
import android.app.NotificationManager
|
|
7
|
+
import android.content.Context
|
|
8
|
+
import android.content.pm.PackageManager
|
|
9
|
+
import android.os.Build
|
|
10
|
+
import androidx.core.app.ActivityCompat
|
|
11
|
+
import androidx.core.content.ContextCompat
|
|
12
|
+
import com.dashx.rn.sdk.util.*
|
|
13
|
+
import com.dashx.android.DashX
|
|
14
|
+
import com.dashx.android.DashXLog
|
|
15
|
+
import com.dashx.android.DashXLog.LogLevel
|
|
16
|
+
import com.dashx.android.data.Asset
|
|
17
|
+
import com.facebook.react.bridge.*
|
|
18
|
+
import com.facebook.react.modules.core.PermissionAwareActivity
|
|
19
|
+
import com.facebook.react.modules.core.PermissionListener
|
|
20
|
+
import kotlinx.serialization.decodeFromString
|
|
21
|
+
import kotlinx.serialization.encodeToString
|
|
22
|
+
import kotlinx.serialization.json.Json
|
|
23
|
+
import kotlinx.serialization.json.JsonObject
|
|
24
|
+
|
|
25
|
+
class DashXReactNativeModuleImpl(private val reactContext: ReactApplicationContext) {
|
|
26
|
+
private val tag = DashXReactNativeModuleImpl::class.java.simpleName
|
|
27
|
+
|
|
28
|
+
fun initialize() {
|
|
29
|
+
MessageReceivedEventHolder.reactContext = reactContext
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fun invalidate() {
|
|
33
|
+
MessageReceivedEventHolder.reactContext = null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun setLogLevel(level: Double) {
|
|
37
|
+
// JS contract: 0 = off, 1 = errors, 2 = debug
|
|
38
|
+
// Native SDK: OFF = -1, ERROR = 0, INFO = 1, DEBUG = 2
|
|
39
|
+
val logLevel = when (level.toInt()) {
|
|
40
|
+
0 -> LogLevel.OFF
|
|
41
|
+
1 -> LogLevel.ERROR
|
|
42
|
+
2 -> LogLevel.DEBUG
|
|
43
|
+
else -> LogLevel.OFF
|
|
44
|
+
}
|
|
45
|
+
DashXLog.setLogLevel(logLevel)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fun configure(options: ReadableMap) {
|
|
49
|
+
createDefaultNotificationChannel()
|
|
50
|
+
DashX.configure(
|
|
51
|
+
reactContext,
|
|
52
|
+
options.getString("publicKey")!!,
|
|
53
|
+
options.getStringIfPresent("baseURI"),
|
|
54
|
+
options.getStringIfPresent("targetEnvironment")
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private fun createDefaultNotificationChannel() {
|
|
59
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
60
|
+
val notificationManager = reactContext.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager ?: return
|
|
61
|
+
|
|
62
|
+
val existing = notificationManager.getNotificationChannel(DEFAULT_CHANNEL_ID)
|
|
63
|
+
if (existing != null) return
|
|
64
|
+
|
|
65
|
+
val channel = NotificationChannel(
|
|
66
|
+
DEFAULT_CHANNEL_ID,
|
|
67
|
+
DEFAULT_CHANNEL_NAME,
|
|
68
|
+
NotificationManager.IMPORTANCE_HIGH
|
|
69
|
+
).apply {
|
|
70
|
+
enableVibration(true)
|
|
71
|
+
setShowBadge(true)
|
|
72
|
+
lockscreenVisibility = android.app.Notification.VISIBILITY_PUBLIC
|
|
73
|
+
}
|
|
74
|
+
notificationManager.createNotificationChannel(channel)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fun identify(options: ReadableMap?) {
|
|
78
|
+
val optionsHashMap = options?.toHashMap()
|
|
79
|
+
?.filterValues { it != null }
|
|
80
|
+
?.mapValues { it.value.toString() }
|
|
81
|
+
DashX.identify(optionsHashMap?.let { HashMap(it) })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fun setIdentity(uid: String?, token: String?) {
|
|
85
|
+
DashX.setIdentity(uid, token)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fun reset() {
|
|
89
|
+
DashX.reset()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fun track(event: String, data: ReadableMap?) {
|
|
93
|
+
val jsonData = try {
|
|
94
|
+
data?.toHashMap()
|
|
95
|
+
?.filterValues { it != null }
|
|
96
|
+
?.mapValues { it.value.toString() }
|
|
97
|
+
?.let { HashMap(it) }
|
|
98
|
+
} catch (e: Exception) {
|
|
99
|
+
DashXLog.d(tag, "Failed to convert track data, sending event without data: ${e.message}")
|
|
100
|
+
null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
DashX.track(event, jsonData)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fun screen(screenName: String, data: ReadableMap?) {
|
|
107
|
+
val screenData = data?.toHashMap()
|
|
108
|
+
?.filterValues { it != null }
|
|
109
|
+
?.mapValues { it.value.toString() }
|
|
110
|
+
?.let { HashMap(it) }
|
|
111
|
+
?: HashMap()
|
|
112
|
+
DashX.screen(screenName, screenData)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fun fetchRecord(urn: String, options: ReadableMap?, promise: Promise) {
|
|
116
|
+
val preview = options?.getBooleanIfPresent("preview")
|
|
117
|
+
val language = options?.getStringIfPresent("language")
|
|
118
|
+
val fields = try {
|
|
119
|
+
convertReadableArrayToKJsonList(options?.getArray("fields"))
|
|
120
|
+
} catch (e: Exception) {
|
|
121
|
+
DashXLog.d(tag, e.message)
|
|
122
|
+
promise.reject(E_UNSPECIFIED, "Error parsing fields: ${e.message}")
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
val include = try {
|
|
126
|
+
convertReadableArrayToKJsonList(options?.getArray("include"))
|
|
127
|
+
} catch (e: Exception) {
|
|
128
|
+
DashXLog.d(tag, e.message)
|
|
129
|
+
promise.reject(E_UNSPECIFIED, "Error parsing include: ${e.message}")
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
val exclude = try {
|
|
133
|
+
convertReadableArrayToKJsonList(options?.getArray("exclude"))
|
|
134
|
+
} catch (e: Exception) {
|
|
135
|
+
DashXLog.d(tag, e.message)
|
|
136
|
+
promise.reject(E_UNSPECIFIED, "Error parsing exclude: ${e.message}")
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
DashX.fetchRecord(
|
|
141
|
+
urn = urn,
|
|
142
|
+
preview = preview,
|
|
143
|
+
language = language,
|
|
144
|
+
fields = fields,
|
|
145
|
+
include = include,
|
|
146
|
+
exclude = exclude,
|
|
147
|
+
onError = {
|
|
148
|
+
promise.reject(E_UNSPECIFIED, it.message)
|
|
149
|
+
},
|
|
150
|
+
onSuccess = {
|
|
151
|
+
promise.resolve(convertJsonToMap(convertKJsonToJson(it)))
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fun searchRecords(resource: String, options: ReadableMap?, promise: Promise) {
|
|
157
|
+
val jsonFilter = try {
|
|
158
|
+
convertMapToJson(options?.getMap("filter"))?.let { convertJsonToKJson(it) }
|
|
159
|
+
} catch (e: Exception) {
|
|
160
|
+
DashXLog.d(tag, e.message)
|
|
161
|
+
promise.reject(E_UNSPECIFIED, "Error parsing filter: ${e.message}")
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
val order = try {
|
|
165
|
+
convertReadableArrayToKJsonList(options?.getArray("order"))
|
|
166
|
+
} catch (e: Exception) {
|
|
167
|
+
DashXLog.d(tag, e.message)
|
|
168
|
+
promise.reject(E_UNSPECIFIED, "Error parsing order: ${e.message}")
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
val limit = options?.getIntIfPresent("limit")
|
|
172
|
+
val preview = options?.getBooleanIfPresent("preview")
|
|
173
|
+
val language = options?.getStringIfPresent("language")
|
|
174
|
+
val fields = try {
|
|
175
|
+
convertReadableArrayToKJsonList(options?.getArray("fields"))
|
|
176
|
+
} catch (e: Exception) {
|
|
177
|
+
DashXLog.d(tag, e.message)
|
|
178
|
+
promise.reject(E_UNSPECIFIED, "Error parsing fields: ${e.message}")
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
val include = try {
|
|
182
|
+
convertReadableArrayToKJsonList(options?.getArray("include"))
|
|
183
|
+
} catch (e: Exception) {
|
|
184
|
+
DashXLog.d(tag, e.message)
|
|
185
|
+
promise.reject(E_UNSPECIFIED, "Error parsing include: ${e.message}")
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
val exclude = try {
|
|
189
|
+
convertReadableArrayToKJsonList(options?.getArray("exclude"))
|
|
190
|
+
} catch (e: Exception) {
|
|
191
|
+
DashXLog.d(tag, e.message)
|
|
192
|
+
promise.reject(E_UNSPECIFIED, "Error parsing exclude: ${e.message}")
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
DashX.searchRecords(
|
|
197
|
+
resource = resource,
|
|
198
|
+
filter = jsonFilter,
|
|
199
|
+
order = order,
|
|
200
|
+
limit = limit,
|
|
201
|
+
preview = preview,
|
|
202
|
+
language = language,
|
|
203
|
+
fields = fields,
|
|
204
|
+
include = include,
|
|
205
|
+
exclude = exclude,
|
|
206
|
+
onError = {
|
|
207
|
+
promise.reject(E_UNSPECIFIED, it.message)
|
|
208
|
+
},
|
|
209
|
+
onSuccess = { records ->
|
|
210
|
+
val readableArray = Arguments.createArray()
|
|
211
|
+
records.forEach {
|
|
212
|
+
readableArray.pushMap(convertJsonToMap(convertKJsonToJson(it)))
|
|
213
|
+
}
|
|
214
|
+
promise.resolve(readableArray)
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fun fetchStoredPreferences(promise: Promise) {
|
|
220
|
+
DashX.fetchStoredPreferences(
|
|
221
|
+
onError = {
|
|
222
|
+
promise.reject(E_UNSPECIFIED, it.message)
|
|
223
|
+
},
|
|
224
|
+
onSuccess = { content ->
|
|
225
|
+
promise.resolve(convertJsonToMap(convertKJsonToJson(Json.decodeFromString<JsonObject>(Json.encodeToString(content)))))
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
fun saveStoredPreferences(preferenceData: ReadableMap?, promise: Promise) {
|
|
231
|
+
val preferencesString = MapUtil.toJSONElement(preferenceData).toString()
|
|
232
|
+
|
|
233
|
+
DashX.saveStoredPreferences(
|
|
234
|
+
Json.decodeFromString<JsonObject>(preferencesString),
|
|
235
|
+
onError = {
|
|
236
|
+
promise.reject(E_UNSPECIFIED, it.message)
|
|
237
|
+
},
|
|
238
|
+
onSuccess = { content ->
|
|
239
|
+
promise.resolve(convertJsonToMap(convertKJsonToJson(Json.decodeFromString<JsonObject>(Json.encodeToString(content)))))
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
fun subscribe() {
|
|
245
|
+
DashX.subscribe()
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fun unsubscribe() {
|
|
249
|
+
DashX.unsubscribe()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// --- iOS-only methods: no-op stubs on Android (JS guards prevent these from being called) ---
|
|
253
|
+
|
|
254
|
+
fun enableLifecycleTracking() {
|
|
255
|
+
// iOS-only; no-op on Android
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
fun enableAdTracking() {
|
|
259
|
+
// iOS-only; no-op on Android
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fun processURL(url: String, source: String?, forwardToLinkHandler: Boolean) {
|
|
263
|
+
// iOS-only; no-op on Android
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fun trackNotificationNavigation(action: ReadableMap?, notificationId: String?) {
|
|
267
|
+
// iOS-only; no-op on Android
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
fun uploadAsset(filePath: String, resource: String, attribute: String, promise: Promise) {
|
|
271
|
+
val file = java.io.File(filePath)
|
|
272
|
+
DashX.uploadAsset(
|
|
273
|
+
file = file,
|
|
274
|
+
resource = resource,
|
|
275
|
+
attribute = attribute,
|
|
276
|
+
onSuccess = { asset ->
|
|
277
|
+
try {
|
|
278
|
+
val jsonString = Json.encodeToString(asset)
|
|
279
|
+
val jsonObject = org.json.JSONObject(jsonString)
|
|
280
|
+
promise.resolve(convertJsonToMap(jsonObject))
|
|
281
|
+
} catch (e: Exception) {
|
|
282
|
+
promise.resolve(null)
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
onError = {
|
|
286
|
+
promise.reject(E_UNSPECIFIED, it.message)
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
fun fetchAsset(assetId: String, promise: Promise) {
|
|
292
|
+
DashX.fetchAsset(
|
|
293
|
+
assetId = assetId,
|
|
294
|
+
onSuccess = { asset ->
|
|
295
|
+
try {
|
|
296
|
+
val jsonString = Json.encodeToString(asset)
|
|
297
|
+
val jsonObject = org.json.JSONObject(jsonString)
|
|
298
|
+
promise.resolve(convertJsonToMap(jsonObject))
|
|
299
|
+
} catch (e: Exception) {
|
|
300
|
+
promise.resolve(null)
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
onError = {
|
|
304
|
+
promise.reject(E_UNSPECIFIED, it.message)
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fun requestNotificationPermission(promise: Promise) {
|
|
310
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
311
|
+
promise.resolve(PERMISSION_AUTHORIZED)
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
val alreadyGranted = ContextCompat.checkSelfPermission(
|
|
316
|
+
reactContext,
|
|
317
|
+
Manifest.permission.POST_NOTIFICATIONS
|
|
318
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
319
|
+
if (alreadyGranted) {
|
|
320
|
+
promise.resolve(PERMISSION_AUTHORIZED)
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
val activity = reactContext.currentActivity as? PermissionAwareActivity
|
|
325
|
+
if (activity == null) {
|
|
326
|
+
promise.reject(E_UNSPECIFIED, "No current activity to request permission from")
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
331
|
+
.edit()
|
|
332
|
+
.putBoolean(KEY_PERMISSION_REQUESTED, true)
|
|
333
|
+
.apply()
|
|
334
|
+
|
|
335
|
+
val listener = PermissionListener { requestCode, _, grantResults ->
|
|
336
|
+
if (requestCode != NOTIFICATION_PERMISSION_REQUEST_CODE) return@PermissionListener false
|
|
337
|
+
val granted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
|
|
338
|
+
promise.resolve(if (granted) PERMISSION_AUTHORIZED else PERMISSION_DENIED)
|
|
339
|
+
true
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
activity.requestPermissions(
|
|
343
|
+
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
|
344
|
+
NOTIFICATION_PERMISSION_REQUEST_CODE,
|
|
345
|
+
listener
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
fun getNotificationPermissionStatus(promise: Promise) {
|
|
350
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
351
|
+
promise.resolve(PERMISSION_AUTHORIZED)
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
val granted = ContextCompat.checkSelfPermission(
|
|
355
|
+
reactContext,
|
|
356
|
+
Manifest.permission.POST_NOTIFICATIONS
|
|
357
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
358
|
+
if (granted) {
|
|
359
|
+
promise.resolve(PERMISSION_AUTHORIZED)
|
|
360
|
+
} else {
|
|
361
|
+
val wasRequested = reactContext
|
|
362
|
+
.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
363
|
+
.getBoolean(KEY_PERMISSION_REQUESTED, false)
|
|
364
|
+
promise.resolve(if (wasRequested) PERMISSION_DENIED else PERMISSION_NOT_DETERMINED)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
companion object {
|
|
369
|
+
const val NAME = "DashXReactNative"
|
|
370
|
+
private const val E_UNSPECIFIED = "EUNSPECIFIED"
|
|
371
|
+
private const val DEFAULT_CHANNEL_ID = "default_dashx_notification_channel"
|
|
372
|
+
private const val DEFAULT_CHANNEL_NAME = "Notifications"
|
|
373
|
+
|
|
374
|
+
private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 3571
|
|
375
|
+
|
|
376
|
+
private const val PREFS_NAME = "dashx_rn_prefs"
|
|
377
|
+
private const val KEY_PERMISSION_REQUESTED = "notification_permission_requested"
|
|
378
|
+
|
|
379
|
+
// Mirrors iOS UNAuthorizationStatus values used by the JS layer.
|
|
380
|
+
private const val PERMISSION_NOT_DETERMINED = 0
|
|
381
|
+
private const val PERMISSION_DENIED = 1
|
|
382
|
+
private const val PERMISSION_AUTHORIZED = 2
|
|
383
|
+
}
|
|
384
|
+
}
|