@dashx/react-native 1.5.0 → 1.5.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.
- package/README.md +1 -1
- package/ios/DashXReactNative.mm +7 -7
- package/ios/DashXReactNative.swift +15 -11
- package/package.json +13 -11
- package/.claude/settings.local.json +0 -7
- package/.claude/worktrees/autocapture-conventions-review-78f665/.gitattributes +0 -1
- package/.claude/worktrees/autocapture-conventions-review-78f665/.github/workflows/ci.yml +0 -67
- package/.claude/worktrees/autocapture-conventions-review-78f665/.gitignore +0 -49
- package/.claude/worktrees/autocapture-conventions-review-78f665/.nvmrc +0 -1
- package/.claude/worktrees/autocapture-conventions-review-78f665/CHANGELOG.md +0 -144
- package/.claude/worktrees/autocapture-conventions-review-78f665/CONTRIBUTING.md +0 -190
- package/.claude/worktrees/autocapture-conventions-review-78f665/DashXReactNative.podspec +0 -38
- package/.claude/worktrees/autocapture-conventions-review-78f665/LICENSE +0 -21
- package/.claude/worktrees/autocapture-conventions-review-78f665/README.md +0 -97
- package/.claude/worktrees/autocapture-conventions-review-78f665/__tests__/fallback.test.js +0 -72
- package/.claude/worktrees/autocapture-conventions-review-78f665/__tests__/index.test.js +0 -702
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gitignore +0 -10
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/8.14.3/checksums/checksums.lock +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/8.14.3/fileChanges/last-build.bin +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/8.14.3/fileHashes/fileHashes.lock +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/8.14.3/gc.properties +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/.gradle/vcs-1/gc.properties +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/build/reports/problems/problems-report.html +0 -663
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/build.gradle +0 -121
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/gradle.properties +0 -1
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/gradlew +0 -185
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/gradlew.bat +0 -89
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/settings.gradle +0 -27
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/AndroidManifest.xml +0 -19
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/java/com/dashx/rn/sdk/DashXNotificationClickedListener.kt +0 -113
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/java/com/dashx/rn/sdk/DashXReactNativeFirebaseMessagingService.kt +0 -72
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/java/com/dashx/rn/sdk/DashXReactNativeModuleImpl.kt +0 -445
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/java/com/dashx/rn/sdk/MessageReceivedEventHolder.kt +0 -14
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/java/com/dashx/rn/sdk/ReactNativeLibraryInfoProvider.kt +0 -8
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/java/com/dashx/rn/sdk/util/Utils.kt +0 -208
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/main/resources/META-INF/services/com.dashx.android.DashXLibraryInfoProvider +0 -1
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/newarch/java/com/dashx/rn/sdk/DashXReactNativeModule.kt +0 -85
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/newarch/java/com/dashx/rn/sdk/DashXReactNativePackage.kt +0 -33
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/oldarch/java/com/dashx/rn/sdk/DashXReactNativeModule.kt +0 -106
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/oldarch/java/com/dashx/rn/sdk/DashXReactNativePackage.kt +0 -33
- package/.claude/worktrees/autocapture-conventions-review-78f665/android/src/test/java/com/dashx/rn/sdk/util/UtilsTest.kt +0 -305
- package/.claude/worktrees/autocapture-conventions-review-78f665/babel.config.js +0 -3
- package/.claude/worktrees/autocapture-conventions-review-78f665/index.d.ts +0 -321
- package/.claude/worktrees/autocapture-conventions-review-78f665/index.js +0 -300
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXEventEmitter.swift +0 -15
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXNotificationHandler.swift +0 -301
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXReactNative-Bridging-Header.h +0 -3
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXReactNative.mm +0 -77
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXReactNative.swift +0 -244
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXReactNative.xcodeproj/project.pbxproj +0 -280
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXReactNative.xcworkspace/contents.xcworkspacedata +0 -7
- package/.claude/worktrees/autocapture-conventions-review-78f665/ios/DashXReactNative.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/.claude/worktrees/autocapture-conventions-review-78f665/package.json +0 -123
- package/.claude/worktrees/autocapture-conventions-review-78f665/scripts/generate-bridge-shim.js +0 -259
- package/.claude/worktrees/autocapture-conventions-review-78f665/src/NativeDashXReactNative.ts +0 -42
- package/.claude/worktrees/autocapture-conventions-review-78f665/yarn.lock +0 -6244
- package/.gitattributes +0 -1
- package/.github/workflows/ci.yml +0 -67
- package/.gitignore +0 -49
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -144
- package/CONTRIBUTING.md +0 -190
- package/__tests__/fallback.test.js +0 -72
- package/__tests__/index.test.js +0 -702
- package/android/.gitignore +0 -10
- package/android/.gradle/8.14.3/checksums/checksums.lock +0 -0
- package/android/.gradle/8.14.3/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.14.3/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.14.3/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build/reports/problems/problems-report.html +0 -663
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/android/gradlew +0 -185
- package/android/gradlew.bat +0 -89
- package/android/src/test/java/com/dashx/rn/sdk/util/UtilsTest.kt +0 -305
- package/babel.config.js +0 -3
- package/scripts/generate-bridge-shim.js +0 -259
- package/yarn.lock +0 -6244
|
@@ -1,445 +0,0 @@
|
|
|
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.Intent
|
|
9
|
-
import android.content.pm.PackageManager
|
|
10
|
-
import android.os.Build
|
|
11
|
-
import android.provider.Settings
|
|
12
|
-
import androidx.core.app.ActivityCompat
|
|
13
|
-
import androidx.core.content.ContextCompat
|
|
14
|
-
import com.dashx.rn.sdk.util.*
|
|
15
|
-
import com.dashx.android.DashX
|
|
16
|
-
import com.dashx.android.DashXActivityLifecycleCallbacks
|
|
17
|
-
import com.dashx.android.DashXLog
|
|
18
|
-
import com.dashx.android.DashXLog.LogLevel
|
|
19
|
-
import com.dashx.android.data.Asset
|
|
20
|
-
import com.facebook.react.bridge.*
|
|
21
|
-
import com.facebook.react.modules.core.PermissionAwareActivity
|
|
22
|
-
import com.facebook.react.modules.core.PermissionListener
|
|
23
|
-
import kotlinx.serialization.json.Json
|
|
24
|
-
import kotlinx.serialization.json.JsonObject
|
|
25
|
-
import kotlinx.serialization.json.buildJsonObject
|
|
26
|
-
import kotlinx.serialization.json.encodeToJsonElement
|
|
27
|
-
import kotlinx.serialization.json.jsonObject
|
|
28
|
-
|
|
29
|
-
class DashXReactNativeModuleImpl(private val reactContext: ReactApplicationContext) {
|
|
30
|
-
private val tag = DashXReactNativeModuleImpl::class.java.simpleName
|
|
31
|
-
|
|
32
|
-
// Bridges SDK notification-click callbacks to the JS
|
|
33
|
-
// `DashX.onNotificationClicked` listener. Registered in `initialize`,
|
|
34
|
-
// removed in `invalidate` so we don't leak across RN reloads.
|
|
35
|
-
private val notificationClickedListener = DashXNotificationClickedListener(reactContext)
|
|
36
|
-
|
|
37
|
-
fun initialize() {
|
|
38
|
-
MessageReceivedEventHolder.reactContext = reactContext
|
|
39
|
-
DashX.registerNotificationListener(notificationClickedListener)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
fun invalidate() {
|
|
43
|
-
MessageReceivedEventHolder.reactContext = null
|
|
44
|
-
DashX.unregisterNotificationListener(notificationClickedListener)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
fun setLogLevel(level: Double) {
|
|
48
|
-
// JS contract: 0 = off, 1 = errors, 2 = debug
|
|
49
|
-
// Native SDK: OFF = -1, ERROR = 0, INFO = 1, DEBUG = 2
|
|
50
|
-
val logLevel = when (level.toInt()) {
|
|
51
|
-
0 -> LogLevel.OFF
|
|
52
|
-
1 -> LogLevel.ERROR
|
|
53
|
-
2 -> LogLevel.DEBUG
|
|
54
|
-
else -> LogLevel.OFF
|
|
55
|
-
}
|
|
56
|
-
DashXLog.setLogLevel(logLevel)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
fun configure(options: ReadableMap) {
|
|
60
|
-
createDefaultNotificationChannel()
|
|
61
|
-
DashX.configure(
|
|
62
|
-
reactContext,
|
|
63
|
-
options.getString("publicKey")!!,
|
|
64
|
-
options.getStringIfPresent("baseURI"),
|
|
65
|
-
options.getStringIfPresent("targetEnvironment")
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private fun createDefaultNotificationChannel() {
|
|
70
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
71
|
-
val notificationManager = reactContext.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager ?: return
|
|
72
|
-
|
|
73
|
-
val existing = notificationManager.getNotificationChannel(DEFAULT_CHANNEL_ID)
|
|
74
|
-
if (existing != null) return
|
|
75
|
-
|
|
76
|
-
val channel = NotificationChannel(
|
|
77
|
-
DEFAULT_CHANNEL_ID,
|
|
78
|
-
DEFAULT_CHANNEL_NAME,
|
|
79
|
-
NotificationManager.IMPORTANCE_HIGH
|
|
80
|
-
).apply {
|
|
81
|
-
enableVibration(true)
|
|
82
|
-
setShowBadge(true)
|
|
83
|
-
lockscreenVisibility = android.app.Notification.VISIBILITY_PUBLIC
|
|
84
|
-
}
|
|
85
|
-
notificationManager.createNotificationChannel(channel)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
fun identify(options: ReadableMap?) {
|
|
89
|
-
val optionsHashMap = options?.toHashMap()
|
|
90
|
-
?.filterValues { it != null }
|
|
91
|
-
?.mapValues { it.value.toString() }
|
|
92
|
-
DashX.identify(optionsHashMap?.let { HashMap(it) })
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
fun setIdentity(uid: String?, token: String?) {
|
|
96
|
-
DashX.setIdentity(uid, token)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
fun reset() {
|
|
100
|
-
DashX.reset()
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
fun track(event: String, data: ReadableMap?) {
|
|
104
|
-
val jsonData = try {
|
|
105
|
-
data?.toHashMap()
|
|
106
|
-
?.filterValues { it != null }
|
|
107
|
-
?.mapValues { it.value.toString() }
|
|
108
|
-
?.let { HashMap(it) }
|
|
109
|
-
} catch (e: Exception) {
|
|
110
|
-
DashXLog.d(tag, "Failed to convert track data, sending event without data: ${e.message}")
|
|
111
|
-
null
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
DashX.track(event, jsonData)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
fun screen(screenName: String, data: ReadableMap?) {
|
|
118
|
-
val screenData = data?.toHashMap()
|
|
119
|
-
?.filterValues { it != null }
|
|
120
|
-
?.mapValues { it.value.toString() }
|
|
121
|
-
?.let { HashMap(it) }
|
|
122
|
-
?: HashMap()
|
|
123
|
-
DashX.screen(screenName, screenData)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
fun fetchRecord(urn: String, options: ReadableMap?, promise: Promise) {
|
|
127
|
-
val preview = options?.getBooleanIfPresent("preview")
|
|
128
|
-
val language = options?.getStringIfPresent("language")
|
|
129
|
-
val fields = try {
|
|
130
|
-
convertReadableArrayToKJsonList(options?.getArray("fields"))
|
|
131
|
-
} catch (e: Exception) {
|
|
132
|
-
DashXLog.d(tag, e.message)
|
|
133
|
-
promise.reject(E_UNSPECIFIED, "Error parsing fields: ${e.message}")
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
val include = try {
|
|
137
|
-
convertReadableArrayToKJsonList(options?.getArray("include"))
|
|
138
|
-
} catch (e: Exception) {
|
|
139
|
-
DashXLog.d(tag, e.message)
|
|
140
|
-
promise.reject(E_UNSPECIFIED, "Error parsing include: ${e.message}")
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
val exclude = try {
|
|
144
|
-
convertReadableArrayToKJsonList(options?.getArray("exclude"))
|
|
145
|
-
} catch (e: Exception) {
|
|
146
|
-
DashXLog.d(tag, e.message)
|
|
147
|
-
promise.reject(E_UNSPECIFIED, "Error parsing exclude: ${e.message}")
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
DashX.fetchRecord(
|
|
152
|
-
urn = urn,
|
|
153
|
-
preview = preview,
|
|
154
|
-
language = language,
|
|
155
|
-
fields = fields,
|
|
156
|
-
include = include,
|
|
157
|
-
exclude = exclude,
|
|
158
|
-
onError = {
|
|
159
|
-
promise.reject(E_UNSPECIFIED, it.message)
|
|
160
|
-
},
|
|
161
|
-
onSuccess = {
|
|
162
|
-
promise.resolve(convertJsonToMap(it))
|
|
163
|
-
}
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
fun searchRecords(resource: String, options: ReadableMap?, promise: Promise) {
|
|
168
|
-
val jsonFilter = try {
|
|
169
|
-
convertMapToJson(options?.getMap("filter"))
|
|
170
|
-
} catch (e: Exception) {
|
|
171
|
-
DashXLog.d(tag, e.message)
|
|
172
|
-
promise.reject(E_UNSPECIFIED, "Error parsing filter: ${e.message}")
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
val order = try {
|
|
176
|
-
convertReadableArrayToKJsonList(options?.getArray("order"))
|
|
177
|
-
} catch (e: Exception) {
|
|
178
|
-
DashXLog.d(tag, e.message)
|
|
179
|
-
promise.reject(E_UNSPECIFIED, "Error parsing order: ${e.message}")
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
val limit = options?.getIntIfPresent("limit")
|
|
183
|
-
val preview = options?.getBooleanIfPresent("preview")
|
|
184
|
-
val language = options?.getStringIfPresent("language")
|
|
185
|
-
val fields = try {
|
|
186
|
-
convertReadableArrayToKJsonList(options?.getArray("fields"))
|
|
187
|
-
} catch (e: Exception) {
|
|
188
|
-
DashXLog.d(tag, e.message)
|
|
189
|
-
promise.reject(E_UNSPECIFIED, "Error parsing fields: ${e.message}")
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
val include = try {
|
|
193
|
-
convertReadableArrayToKJsonList(options?.getArray("include"))
|
|
194
|
-
} catch (e: Exception) {
|
|
195
|
-
DashXLog.d(tag, e.message)
|
|
196
|
-
promise.reject(E_UNSPECIFIED, "Error parsing include: ${e.message}")
|
|
197
|
-
return
|
|
198
|
-
}
|
|
199
|
-
val exclude = try {
|
|
200
|
-
convertReadableArrayToKJsonList(options?.getArray("exclude"))
|
|
201
|
-
} catch (e: Exception) {
|
|
202
|
-
DashXLog.d(tag, e.message)
|
|
203
|
-
promise.reject(E_UNSPECIFIED, "Error parsing exclude: ${e.message}")
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
DashX.searchRecords(
|
|
208
|
-
resource = resource,
|
|
209
|
-
filter = jsonFilter,
|
|
210
|
-
order = order,
|
|
211
|
-
limit = limit,
|
|
212
|
-
preview = preview,
|
|
213
|
-
language = language,
|
|
214
|
-
fields = fields,
|
|
215
|
-
include = include,
|
|
216
|
-
exclude = exclude,
|
|
217
|
-
onError = {
|
|
218
|
-
promise.reject(E_UNSPECIFIED, it.message)
|
|
219
|
-
},
|
|
220
|
-
onSuccess = { records ->
|
|
221
|
-
val readableArray = Arguments.createArray()
|
|
222
|
-
records.forEach {
|
|
223
|
-
readableArray.pushMap(convertJsonToMap(it))
|
|
224
|
-
}
|
|
225
|
-
promise.resolve(readableArray)
|
|
226
|
-
}
|
|
227
|
-
)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
fun fetchStoredPreferences(promise: Promise) {
|
|
231
|
-
DashX.fetchStoredPreferences(
|
|
232
|
-
onError = {
|
|
233
|
-
promise.reject(E_UNSPECIFIED, it.message)
|
|
234
|
-
},
|
|
235
|
-
// The SDK hands back a typed wrapper whose `preferenceData`
|
|
236
|
-
// field is the raw kotlinx `JsonObject`. Unwrap and flatten so
|
|
237
|
-
// the JS side receives the prefs directly — matches how the iOS
|
|
238
|
-
// bridge resolves `DashXClient.fetchStoredPreferences`.
|
|
239
|
-
onSuccess = { content ->
|
|
240
|
-
promise.resolve(convertJsonToMap(content.preferenceData))
|
|
241
|
-
}
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
fun saveStoredPreferences(preferenceData: ReadableMap?, promise: Promise) {
|
|
246
|
-
// A null/empty payload maps to an empty JSON object — matches the
|
|
247
|
-
// SDK's "I received preferences but they're empty" contract and
|
|
248
|
-
// replaces a pre-migration NPE path that crashed when the caller
|
|
249
|
-
// passed `null`.
|
|
250
|
-
val preferencesJson = convertMapToJson(preferenceData) ?: buildJsonObject { }
|
|
251
|
-
|
|
252
|
-
DashX.saveStoredPreferences(
|
|
253
|
-
preferencesJson,
|
|
254
|
-
onError = {
|
|
255
|
-
promise.reject(E_UNSPECIFIED, it.message)
|
|
256
|
-
},
|
|
257
|
-
// Mutation response is just `{ success: Boolean }` — resolve the
|
|
258
|
-
// Promise with that Boolean, matching what the iOS bridge does.
|
|
259
|
-
onSuccess = { content ->
|
|
260
|
-
promise.resolve(content.success)
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
fun subscribe() {
|
|
266
|
-
DashX.subscribe()
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
fun unsubscribe(promise: Promise) {
|
|
270
|
-
DashX.unsubscribe(
|
|
271
|
-
onSuccess = { didUnsubscribe ->
|
|
272
|
-
val result = Arguments.createMap().apply {
|
|
273
|
-
putBoolean("success", didUnsubscribe)
|
|
274
|
-
}
|
|
275
|
-
promise.resolve(result)
|
|
276
|
-
},
|
|
277
|
-
onError = { promise.reject(E_UNSPECIFIED, it.message) }
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
fun enableLifecycleTracking() {
|
|
282
|
-
// The SDK's tracking flags are unsynchronized statics read on the main thread.
|
|
283
|
-
val application = reactContext.applicationContext
|
|
284
|
-
UiThreadUtil.runOnUiThread {
|
|
285
|
-
DashXActivityLifecycleCallbacks.enableActivityLifecycleTracking(application)
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// --- iOS-only methods: no-op stubs on Android (JS guards prevent these from being called) ---
|
|
290
|
-
|
|
291
|
-
fun enableAdTracking() {
|
|
292
|
-
// iOS-only; no-op on Android
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
fun processURL(url: String, source: String?, forwardToLinkHandler: Boolean) {
|
|
296
|
-
// iOS-only; no-op on Android
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
fun trackNotificationNavigation(action: ReadableMap?, notificationId: String?) {
|
|
300
|
-
// iOS-only; no-op on Android
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
fun uploadAsset(filePath: String, resource: String, attribute: String, promise: Promise) {
|
|
304
|
-
val file = java.io.File(filePath)
|
|
305
|
-
DashX.uploadAsset(
|
|
306
|
-
file = file,
|
|
307
|
-
resource = resource,
|
|
308
|
-
attribute = attribute,
|
|
309
|
-
onSuccess = { asset ->
|
|
310
|
-
try {
|
|
311
|
-
// `Asset` is `@Serializable` — go straight to a
|
|
312
|
-
// `JsonElement` tree without the string + parser hop.
|
|
313
|
-
promise.resolve(convertJsonToMap(Json.encodeToJsonElement(asset).jsonObject))
|
|
314
|
-
} catch (e: Exception) {
|
|
315
|
-
promise.resolve(null)
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
onError = {
|
|
319
|
-
promise.reject(E_UNSPECIFIED, it.message)
|
|
320
|
-
}
|
|
321
|
-
)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
fun fetchAsset(assetId: String, promise: Promise) {
|
|
325
|
-
DashX.fetchAsset(
|
|
326
|
-
assetId = assetId,
|
|
327
|
-
onSuccess = { asset ->
|
|
328
|
-
try {
|
|
329
|
-
promise.resolve(convertJsonToMap(Json.encodeToJsonElement(asset).jsonObject))
|
|
330
|
-
} catch (e: Exception) {
|
|
331
|
-
promise.resolve(null)
|
|
332
|
-
}
|
|
333
|
-
},
|
|
334
|
-
onError = {
|
|
335
|
-
promise.reject(E_UNSPECIFIED, it.message)
|
|
336
|
-
}
|
|
337
|
-
)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
fun requestNotificationPermission(fallbackToSettings: Boolean, promise: Promise) {
|
|
341
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
342
|
-
promise.resolve(PERMISSION_AUTHORIZED)
|
|
343
|
-
return
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
val alreadyGranted = ContextCompat.checkSelfPermission(
|
|
347
|
-
reactContext,
|
|
348
|
-
Manifest.permission.POST_NOTIFICATIONS
|
|
349
|
-
) == PackageManager.PERMISSION_GRANTED
|
|
350
|
-
if (alreadyGranted) {
|
|
351
|
-
promise.resolve(PERMISSION_AUTHORIZED)
|
|
352
|
-
return
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
val activity = reactContext.currentActivity
|
|
356
|
-
if (activity == null) {
|
|
357
|
-
promise.reject(E_UNSPECIFIED, "No current activity to request permission from")
|
|
358
|
-
return
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// "Permanently denied" detection on Android 13+: we asked at least
|
|
362
|
-
// once before AND the system says we shouldn't show a rationale.
|
|
363
|
-
// In that state `requestPermissions()` auto-resolves DENIED without
|
|
364
|
-
// any UI, so re-prompting is futile — Settings is the only path.
|
|
365
|
-
val prefs = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
366
|
-
val previouslyRequested = prefs.getBoolean(KEY_PERMISSION_REQUESTED, false)
|
|
367
|
-
val permanentlyDenied = previouslyRequested && !ActivityCompat.shouldShowRequestPermissionRationale(
|
|
368
|
-
activity,
|
|
369
|
-
Manifest.permission.POST_NOTIFICATIONS
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
if (fallbackToSettings && permanentlyDenied) {
|
|
373
|
-
openAppNotificationSettings()
|
|
374
|
-
promise.resolve(PERMISSION_DENIED)
|
|
375
|
-
return
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
val permAwareActivity = activity as? PermissionAwareActivity
|
|
379
|
-
if (permAwareActivity == null) {
|
|
380
|
-
promise.reject(E_UNSPECIFIED, "Activity does not support permission requests")
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
prefs.edit()
|
|
385
|
-
.putBoolean(KEY_PERMISSION_REQUESTED, true)
|
|
386
|
-
.apply()
|
|
387
|
-
|
|
388
|
-
val listener = PermissionListener { requestCode, _, grantResults ->
|
|
389
|
-
if (requestCode != NOTIFICATION_PERMISSION_REQUEST_CODE) return@PermissionListener false
|
|
390
|
-
val granted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
|
|
391
|
-
promise.resolve(if (granted) PERMISSION_AUTHORIZED else PERMISSION_DENIED)
|
|
392
|
-
true
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
permAwareActivity.requestPermissions(
|
|
396
|
-
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
|
397
|
-
NOTIFICATION_PERMISSION_REQUEST_CODE,
|
|
398
|
-
listener
|
|
399
|
-
)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private fun openAppNotificationSettings() {
|
|
403
|
-
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
|
|
404
|
-
putExtra(Settings.EXTRA_APP_PACKAGE, reactContext.packageName)
|
|
405
|
-
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
406
|
-
}
|
|
407
|
-
reactContext.startActivity(intent)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
fun getNotificationPermissionStatus(promise: Promise) {
|
|
411
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
412
|
-
promise.resolve(PERMISSION_AUTHORIZED)
|
|
413
|
-
return
|
|
414
|
-
}
|
|
415
|
-
val granted = ContextCompat.checkSelfPermission(
|
|
416
|
-
reactContext,
|
|
417
|
-
Manifest.permission.POST_NOTIFICATIONS
|
|
418
|
-
) == PackageManager.PERMISSION_GRANTED
|
|
419
|
-
if (granted) {
|
|
420
|
-
promise.resolve(PERMISSION_AUTHORIZED)
|
|
421
|
-
} else {
|
|
422
|
-
val wasRequested = reactContext
|
|
423
|
-
.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
424
|
-
.getBoolean(KEY_PERMISSION_REQUESTED, false)
|
|
425
|
-
promise.resolve(if (wasRequested) PERMISSION_DENIED else PERMISSION_NOT_DETERMINED)
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
companion object {
|
|
430
|
-
const val NAME = "DashXReactNative"
|
|
431
|
-
private const val E_UNSPECIFIED = "EUNSPECIFIED"
|
|
432
|
-
private const val DEFAULT_CHANNEL_ID = "default_dashx_notification_channel"
|
|
433
|
-
private const val DEFAULT_CHANNEL_NAME = "Notifications"
|
|
434
|
-
|
|
435
|
-
private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 3571
|
|
436
|
-
|
|
437
|
-
private const val PREFS_NAME = "dashx_rn_prefs"
|
|
438
|
-
private const val KEY_PERMISSION_REQUESTED = "notification_permission_requested"
|
|
439
|
-
|
|
440
|
-
// Mirrors iOS UNAuthorizationStatus values used by the JS layer.
|
|
441
|
-
private const val PERMISSION_NOT_DETERMINED = 0
|
|
442
|
-
private const val PERMISSION_DENIED = 1
|
|
443
|
-
private const val PERMISSION_AUTHORIZED = 2
|
|
444
|
-
}
|
|
445
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
package com.dashx.rn.sdk
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Holds a reference to the React context so that [DashXReactNativeFirebaseMessagingService]
|
|
7
|
-
* can emit "messageReceived" events to JS when FCM messages arrive.
|
|
8
|
-
* Set by [DashXReactNativeModule], cleared when the module is invalidated.
|
|
9
|
-
*/
|
|
10
|
-
object MessageReceivedEventHolder {
|
|
11
|
-
@Volatile
|
|
12
|
-
var reactContext: ReactApplicationContext? = null
|
|
13
|
-
internal set
|
|
14
|
-
}
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
@file:JvmName("Utils")
|
|
2
|
-
|
|
3
|
-
package com.dashx.rn.sdk.util
|
|
4
|
-
|
|
5
|
-
import com.facebook.react.bridge.*
|
|
6
|
-
import kotlinx.serialization.json.JsonArray
|
|
7
|
-
import kotlinx.serialization.json.JsonNull
|
|
8
|
-
import kotlinx.serialization.json.JsonObject
|
|
9
|
-
import kotlinx.serialization.json.JsonPrimitive
|
|
10
|
-
import kotlinx.serialization.json.add
|
|
11
|
-
import kotlinx.serialization.json.booleanOrNull
|
|
12
|
-
import kotlinx.serialization.json.buildJsonArray
|
|
13
|
-
import kotlinx.serialization.json.buildJsonObject
|
|
14
|
-
import kotlinx.serialization.json.doubleOrNull
|
|
15
|
-
import kotlinx.serialization.json.put
|
|
16
|
-
|
|
17
|
-
// JSON conversion helpers between React Native's ReadableMap/ReadableArray/
|
|
18
|
-
// WritableMap/WritableArray types and kotlinx.serialization's JsonObject/
|
|
19
|
-
// JsonArray. Kept kotlinx-only on purpose — the DashX Android SDK uses
|
|
20
|
-
// kotlinx-serialization end-to-end (see `mapScalar("JSON", "kotlinx...JsonObject")`
|
|
21
|
-
// in dashx-android/dashx/build.gradle), so threading a single JSON library
|
|
22
|
-
// through the bridge avoids string round-trips and the type-confusion class
|
|
23
|
-
// of bugs that arose when Gson/org.json/kotlinx all coexisted.
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Factory seam for Writable instances. Production defaults to the JNI-backed
|
|
27
|
-
* `WritableNativeMap` / `WritableNativeArray`. JVM unit tests swap these to
|
|
28
|
-
* `JavaOnlyMap` / `JavaOnlyArray` (pure-Java, no JNI) — the native writables
|
|
29
|
-
* crash in a plain JUnit harness because their constructors call into
|
|
30
|
-
* `initHybrid()`. `internal` visibility keeps the seam invisible to SDK
|
|
31
|
-
* consumers.
|
|
32
|
-
*/
|
|
33
|
-
internal object WritableFactory {
|
|
34
|
-
var createMap: () -> WritableMap = { WritableNativeMap() }
|
|
35
|
-
var createArray: () -> WritableArray = { WritableNativeArray() }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Convert a [ReadableMap] (RN → native boundary) into a kotlinx [JsonObject].
|
|
40
|
-
* Returns `null` when [readableMap] is null, so callers can distinguish
|
|
41
|
-
* "absent" from "empty".
|
|
42
|
-
*
|
|
43
|
-
* Numbers are coerced to [Double] on purpose — RN has a single JS `Number`
|
|
44
|
-
* type and the DashX backend's JSON scalars are agnostic to int/double.
|
|
45
|
-
*/
|
|
46
|
-
fun convertMapToJson(readableMap: ReadableMap?): JsonObject? {
|
|
47
|
-
val iterator = readableMap?.keySetIterator() ?: return null
|
|
48
|
-
return buildJsonObject {
|
|
49
|
-
while (iterator.hasNextKey()) {
|
|
50
|
-
val key = iterator.nextKey()
|
|
51
|
-
when (readableMap.getType(key)) {
|
|
52
|
-
ReadableType.Null -> put(key, JsonNull)
|
|
53
|
-
ReadableType.Boolean -> put(key, readableMap.getBoolean(key))
|
|
54
|
-
ReadableType.Number -> put(key, readableMap.getDouble(key))
|
|
55
|
-
ReadableType.String -> put(key, readableMap.getString(key))
|
|
56
|
-
ReadableType.Map -> put(key, convertMapToJson(readableMap.getMap(key)) ?: JsonNull)
|
|
57
|
-
ReadableType.Array -> put(key, convertArrayToJson(readableMap.getArray(key)) ?: JsonNull)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Convert a [ReadableArray] into a kotlinx [JsonArray]. Mirrors
|
|
65
|
-
* [convertMapToJson] — null in, null out.
|
|
66
|
-
*/
|
|
67
|
-
fun convertArrayToJson(readableArray: ReadableArray?): JsonArray? {
|
|
68
|
-
if (readableArray == null) return null
|
|
69
|
-
return buildJsonArray {
|
|
70
|
-
for (i in 0 until readableArray.size()) {
|
|
71
|
-
when (readableArray.getType(i)) {
|
|
72
|
-
ReadableType.Null -> add(JsonNull)
|
|
73
|
-
ReadableType.Boolean -> add(readableArray.getBoolean(i))
|
|
74
|
-
ReadableType.Number -> add(readableArray.getDouble(i))
|
|
75
|
-
ReadableType.String -> add(readableArray.getString(i))
|
|
76
|
-
ReadableType.Map -> add(convertMapToJson(readableArray.getMap(i)) ?: JsonNull)
|
|
77
|
-
ReadableType.Array -> add(convertArrayToJson(readableArray.getArray(i)) ?: JsonNull)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Convert a [ReadableArray] of maps into a list of [JsonObject]s. Used for
|
|
85
|
-
* the `fields` / `include` / `exclude` / `order` option arrays on
|
|
86
|
-
* `fetchRecord` / `searchRecords`. Non-map entries are silently dropped to
|
|
87
|
-
* preserve the behaviour of the pre-migration helper.
|
|
88
|
-
*/
|
|
89
|
-
fun convertReadableArrayToKJsonList(readableArray: ReadableArray?): List<JsonObject>? {
|
|
90
|
-
if (readableArray == null || readableArray.size() == 0) return null
|
|
91
|
-
return (0 until readableArray.size()).mapNotNull { i ->
|
|
92
|
-
if (readableArray.getType(i) == ReadableType.Map) {
|
|
93
|
-
readableArray.getMap(i)?.let { convertMapToJson(it) }
|
|
94
|
-
} else {
|
|
95
|
-
null
|
|
96
|
-
}
|
|
97
|
-
}.ifEmpty { null }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Convert a kotlinx [JsonObject] (native → RN boundary) into a [WritableMap].
|
|
102
|
-
* Returns `null` when [jsonObject] is null.
|
|
103
|
-
*
|
|
104
|
-
* Numeric primitives are coerced to [Double] (`putDouble`) to match JS's
|
|
105
|
-
* single `Number` type. JSON nulls map to `putNull(key)`. Non-finite /
|
|
106
|
-
* non-numeric primitives fall back to string form.
|
|
107
|
-
*/
|
|
108
|
-
fun convertJsonToMap(jsonObject: JsonObject?): WritableMap? {
|
|
109
|
-
jsonObject ?: return null
|
|
110
|
-
val map: WritableMap = WritableFactory.createMap()
|
|
111
|
-
for ((key, element) in jsonObject) {
|
|
112
|
-
when (element) {
|
|
113
|
-
is JsonNull -> map.putNull(key)
|
|
114
|
-
is JsonPrimitive -> putPrimitive(map, key, element)
|
|
115
|
-
is JsonObject -> map.putMap(key, convertJsonToMap(element))
|
|
116
|
-
is JsonArray -> map.putArray(key, convertJsonToArray(element))
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return map
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Convert a kotlinx [JsonArray] into a [WritableArray]. Mirrors
|
|
124
|
-
* [convertJsonToMap]. Null-safe.
|
|
125
|
-
*/
|
|
126
|
-
fun convertJsonToArray(jsonArray: JsonArray?): WritableArray? {
|
|
127
|
-
jsonArray ?: return null
|
|
128
|
-
val array: WritableArray = WritableFactory.createArray()
|
|
129
|
-
for (element in jsonArray) {
|
|
130
|
-
when (element) {
|
|
131
|
-
is JsonNull -> array.pushNull()
|
|
132
|
-
is JsonPrimitive -> pushPrimitive(array, element)
|
|
133
|
-
is JsonObject -> array.pushMap(convertJsonToMap(element))
|
|
134
|
-
is JsonArray -> array.pushArray(convertJsonToArray(element))
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return array
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Shared primitive dispatch — keeps the two public functions free of repeated
|
|
141
|
-
// isString / booleanOrNull / doubleOrNull ladders. [primitive] is guaranteed
|
|
142
|
-
// not to be [JsonNull] by the caller (JsonNull is a subtype of JsonPrimitive).
|
|
143
|
-
private fun putPrimitive(map: WritableMap, key: String, primitive: JsonPrimitive) {
|
|
144
|
-
when {
|
|
145
|
-
primitive.isString -> map.putString(key, primitive.content)
|
|
146
|
-
primitive.booleanOrNull != null -> map.putBoolean(key, primitive.booleanOrNull!!)
|
|
147
|
-
primitive.doubleOrNull != null -> map.putDouble(key, primitive.doubleOrNull!!)
|
|
148
|
-
else -> map.putString(key, primitive.content)
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private fun pushPrimitive(array: WritableArray, primitive: JsonPrimitive) {
|
|
153
|
-
when {
|
|
154
|
-
primitive.isString -> array.pushString(primitive.content)
|
|
155
|
-
primitive.booleanOrNull != null -> array.pushBoolean(primitive.booleanOrNull!!)
|
|
156
|
-
primitive.doubleOrNull != null -> array.pushDouble(primitive.doubleOrNull!!)
|
|
157
|
-
else -> array.pushString(primitive.content)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Generic `Map<*, *>` → `WritableMap` converter. Preserved from the original
|
|
163
|
-
* [Utils] surface — no JSON libraries involved, but used by callers that
|
|
164
|
-
* need to hand arbitrary Kotlin maps to the RN bridge.
|
|
165
|
-
*/
|
|
166
|
-
@JvmOverloads
|
|
167
|
-
@Throws(Exception::class)
|
|
168
|
-
fun convertToWritableMap(
|
|
169
|
-
map: Map<*, *>,
|
|
170
|
-
blacklist: List<String> = emptyList<String>()
|
|
171
|
-
): WritableMap {
|
|
172
|
-
val writableMap: WritableMap = WritableFactory.createMap()
|
|
173
|
-
val iterator: Iterator<String> = map.keys.iterator() as Iterator<String>
|
|
174
|
-
while (iterator.hasNext()) {
|
|
175
|
-
val key = iterator.next()
|
|
176
|
-
|
|
177
|
-
if (blacklist.contains(key)) {
|
|
178
|
-
continue
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
when (val value = map[key]) {
|
|
182
|
-
is Boolean -> writableMap.putBoolean(key, value)
|
|
183
|
-
is Int -> writableMap.putInt(key, value)
|
|
184
|
-
is Double -> writableMap.putDouble(key, value)
|
|
185
|
-
is String -> writableMap.putString(key, value)
|
|
186
|
-
else -> writableMap.putString(key, value.toString())
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return writableMap
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// --- ReadableMap convenience accessors (unchanged from the previous Utils). ---
|
|
193
|
-
|
|
194
|
-
fun ReadableMap.getMapIfPresent(key: String): ReadableMap? {
|
|
195
|
-
return if (this.hasKey(key)) this.getMap(key) else null
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
fun ReadableMap.getIntIfPresent(key: String): Int? {
|
|
199
|
-
return if (this.hasKey(key)) this.getInt(key) else null
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
fun ReadableMap.getStringIfPresent(key: String): String? {
|
|
203
|
-
return if (this.hasKey(key)) this.getString(key) else null
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
fun ReadableMap.getBooleanIfPresent(key: String): Boolean? {
|
|
207
|
-
return if (this.hasKey(key)) this.getBoolean(key) else null
|
|
208
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
com.dashx.rn.sdk.ReactNativeLibraryInfoProvider
|