rn-linkrunner 1.1.1 → 2.0.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/LinkrunnerSDK.podspec +23 -0
- package/README.md +10 -297
- package/android/build.gradle +148 -0
- package/android/gradle.properties +52 -0
- package/android/settings.gradle +6 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/io/linkrunner/LinkrunnerModule.kt +398 -0
- package/android/src/main/java/io/linkrunner/LinkrunnerPackage.kt +16 -0
- package/android/src/main/java/io/linkrunner/utils/MapUtils.kt +136 -0
- package/android/src/main/java/io/linkrunner/utils/ModelConverter.kt +226 -0
- package/ios/LinkrunnerSDK.h +5 -0
- package/ios/LinkrunnerSDK.m +31 -0
- package/ios/LinkrunnerSDK.swift +217 -0
- package/ios/Podfile +19 -0
- package/lib/commonjs/index.js +132 -271
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/index.js +132 -272
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/index.d.ts +8 -21
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +16 -14
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/react-native.config.js +17 -0
- package/src/index.ts +151 -337
- package/src/types.ts +17 -14
- package/lib/commonjs/helper.js +0 -125
- package/lib/commonjs/helper.js.map +0 -1
- package/lib/module/helper.js +0 -113
- package/lib/module/helper.js.map +0 -1
- package/lib/typescript/helper.d.ts +0 -6
- package/lib/typescript/helper.d.ts.map +0 -1
- package/src/helper.ts +0 -144
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
package io.linkrunner
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
5
|
+
import com.facebook.react.bridge.Promise
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
8
|
+
import com.facebook.react.bridge.ReactMethod
|
|
9
|
+
import com.facebook.react.bridge.ReadableMap
|
|
10
|
+
import com.facebook.react.bridge.WritableMap
|
|
11
|
+
import io.linkrunner.sdk.BuildConfig
|
|
12
|
+
import io.linkrunner.sdk.LinkRunner
|
|
13
|
+
import io.linkrunner.sdk.models.request.UserDataRequest
|
|
14
|
+
import io.linkrunner.sdk.models.IntegrationData
|
|
15
|
+
import io.linkrunner.utils.MapUtils
|
|
16
|
+
import io.linkrunner.utils.ModelConverter
|
|
17
|
+
import kotlinx.coroutines.CoroutineScope
|
|
18
|
+
import kotlinx.coroutines.Dispatchers
|
|
19
|
+
import kotlinx.coroutines.SupervisorJob
|
|
20
|
+
import kotlinx.coroutines.launch
|
|
21
|
+
import kotlinx.coroutines.withContext
|
|
22
|
+
|
|
23
|
+
class LinkrunnerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
24
|
+
|
|
25
|
+
private val TAG = "LinkrunnerModule"
|
|
26
|
+
private val linkrunnerSDK = LinkRunner.getInstance()
|
|
27
|
+
private val moduleScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
28
|
+
|
|
29
|
+
override fun getName(): String {
|
|
30
|
+
return "LinkrunnerSDK"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@ReactMethod
|
|
34
|
+
fun init(token: String, options: ReadableMap?, promise: Promise) {
|
|
35
|
+
try {
|
|
36
|
+
// Extract optional parameters
|
|
37
|
+
val secretKey = options?.getString("secretKey")
|
|
38
|
+
val keyId = options?.getString("keyId")
|
|
39
|
+
|
|
40
|
+
if (token.isEmpty()) {
|
|
41
|
+
promise.reject("INIT_ERROR", "Token is required")
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
moduleScope.launch {
|
|
46
|
+
try {
|
|
47
|
+
val result = linkrunnerSDK.init(
|
|
48
|
+
context = reactContext,
|
|
49
|
+
token = token,
|
|
50
|
+
secretKey = secretKey,
|
|
51
|
+
keyId = keyId
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
withContext(Dispatchers.Main) {
|
|
55
|
+
if (result.isSuccess) {
|
|
56
|
+
val response = Arguments.createMap()
|
|
57
|
+
response.putString("status", "success")
|
|
58
|
+
response.putString("message", "Linkrunner SDK initialized successfully")
|
|
59
|
+
promise.resolve(response)
|
|
60
|
+
} else {
|
|
61
|
+
promise.reject(
|
|
62
|
+
"INIT_ERROR",
|
|
63
|
+
"Failed to initialize Linkrunner: ${result.exceptionOrNull()?.message}",
|
|
64
|
+
result.exceptionOrNull()
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (e: Exception) {
|
|
69
|
+
withContext(Dispatchers.Main) {
|
|
70
|
+
promise.reject("INIT_ERROR", "Exception calling init: ${e.message}", e)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
promise.reject("INIT_ERROR", "Failed to initialize Linkrunner: ${e.message}", e)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@ReactMethod
|
|
80
|
+
fun signup(userData: ReadableMap, data: ReadableMap?, promise: Promise) {
|
|
81
|
+
try {
|
|
82
|
+
// Convert ReadableMap to native Map for userData
|
|
83
|
+
val userDataMap = MapUtils.readableMapToMap(userData)
|
|
84
|
+
|
|
85
|
+
// Convert ReadableMap to native Map for additionalData
|
|
86
|
+
val additionalData = if (data != null && !data.toHashMap().isEmpty()) {
|
|
87
|
+
MapUtils.readableMapToMap(data)
|
|
88
|
+
} else {
|
|
89
|
+
null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
moduleScope.launch {
|
|
93
|
+
try {
|
|
94
|
+
val result = linkrunnerSDK.signup(
|
|
95
|
+
userData = ModelConverter.toUserDataRequest(userDataMap),
|
|
96
|
+
additionalData = additionalData
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
withContext(Dispatchers.Main) {
|
|
100
|
+
if (result.isSuccess) {
|
|
101
|
+
val response = Arguments.createMap()
|
|
102
|
+
response.putString("status", "success")
|
|
103
|
+
response.putString("message", "User signed up successfully")
|
|
104
|
+
promise.resolve(response)
|
|
105
|
+
} else {
|
|
106
|
+
promise.reject(
|
|
107
|
+
"SIGNUP_ERROR",
|
|
108
|
+
"Failed to signup user: ${result.exceptionOrNull()?.message}",
|
|
109
|
+
result.exceptionOrNull()
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (e: Exception) {
|
|
114
|
+
withContext(Dispatchers.Main) {
|
|
115
|
+
promise.reject("SIGNUP_ERROR", "Exception during signup: ${e.message}", e)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} catch (e: Exception) {
|
|
120
|
+
promise.reject("SIGNUP_ERROR", "Failed to signup user: ${e.message}", e)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@ReactMethod
|
|
125
|
+
fun setUserData(userData: ReadableMap, promise: Promise) {
|
|
126
|
+
try {
|
|
127
|
+
// Convert ReadableMap to native Map for userData
|
|
128
|
+
val userDataMap = MapUtils.readableMapToMap(userData)
|
|
129
|
+
|
|
130
|
+
moduleScope.launch {
|
|
131
|
+
try {
|
|
132
|
+
val result = linkrunnerSDK.setUserData(ModelConverter.toUserDataRequest(userDataMap))
|
|
133
|
+
|
|
134
|
+
withContext(Dispatchers.Main) {
|
|
135
|
+
if (result.isSuccess) {
|
|
136
|
+
val response = Arguments.createMap()
|
|
137
|
+
response.putString("status", "success")
|
|
138
|
+
response.putString("message", "User data set successfully")
|
|
139
|
+
promise.resolve(response)
|
|
140
|
+
} else {
|
|
141
|
+
promise.reject(
|
|
142
|
+
"SET_USER_DATA_ERROR",
|
|
143
|
+
"Failed to set user data: ${result.exceptionOrNull()?.message}",
|
|
144
|
+
result.exceptionOrNull()
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (e: Exception) {
|
|
149
|
+
withContext(Dispatchers.Main) {
|
|
150
|
+
promise.reject("SET_USER_DATA_ERROR", "Exception setting user data: ${e.message}", e)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch (e: Exception) {
|
|
155
|
+
promise.reject("SET_USER_DATA_ERROR", "Failed to set user data: ${e.message}", e)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@ReactMethod
|
|
160
|
+
fun trackEvent(eventName: String, eventData: ReadableMap?, promise: Promise) {
|
|
161
|
+
if (eventName.isEmpty()) {
|
|
162
|
+
promise.reject("TRACK_EVENT_ERROR", "Event name is required")
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
val eventDataMap = if (eventData != null) {
|
|
168
|
+
MapUtils.readableMapToMap(eventData)
|
|
169
|
+
} else {
|
|
170
|
+
emptyMap()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
moduleScope.launch {
|
|
174
|
+
try {
|
|
175
|
+
val result = linkrunnerSDK.trackEvent(eventName, eventDataMap)
|
|
176
|
+
|
|
177
|
+
withContext(Dispatchers.Main) {
|
|
178
|
+
if (result.isSuccess) {
|
|
179
|
+
val response = Arguments.createMap()
|
|
180
|
+
response.putString("status", "success")
|
|
181
|
+
response.putString("message", "Event tracked successfully")
|
|
182
|
+
promise.resolve(response)
|
|
183
|
+
} else {
|
|
184
|
+
promise.reject(
|
|
185
|
+
"TRACK_EVENT_ERROR",
|
|
186
|
+
"Failed to track event: ${result.exceptionOrNull()?.message}",
|
|
187
|
+
result.exceptionOrNull()
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch (e: Exception) {
|
|
192
|
+
withContext(Dispatchers.Main) {
|
|
193
|
+
promise.reject("TRACK_EVENT_ERROR", "Exception tracking event: ${e.message}", e)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} catch (e: Exception) {
|
|
198
|
+
promise.reject("TRACK_EVENT_ERROR", "Failed to track event: ${e.message}", e)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@ReactMethod
|
|
203
|
+
fun capturePayment(paymentData: ReadableMap, promise: Promise) {
|
|
204
|
+
if (paymentData == null) {
|
|
205
|
+
promise.reject("PAYMENT_ERROR", "Payment data is required")
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
val paymentDataMap = MapUtils.readableMapToMap(paymentData)
|
|
211
|
+
|
|
212
|
+
moduleScope.launch {
|
|
213
|
+
try {
|
|
214
|
+
val result = linkrunnerSDK.capturePayment(ModelConverter.toCapturePaymentRequest(paymentDataMap))
|
|
215
|
+
|
|
216
|
+
withContext(Dispatchers.Main) {
|
|
217
|
+
if (result.isSuccess) {
|
|
218
|
+
val response = Arguments.createMap()
|
|
219
|
+
response.putString("status", "success")
|
|
220
|
+
response.putString("message", "Payment captured successfully")
|
|
221
|
+
promise.resolve(response)
|
|
222
|
+
} else {
|
|
223
|
+
promise.reject(
|
|
224
|
+
"PAYMENT_ERROR",
|
|
225
|
+
"Failed to capture payment: ${result.exceptionOrNull()?.message}",
|
|
226
|
+
result.exceptionOrNull()
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} catch (e: Exception) {
|
|
231
|
+
withContext(Dispatchers.Main) {
|
|
232
|
+
promise.reject("PAYMENT_ERROR", "Exception capturing payment: ${e.message}", e)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch (e: Exception) {
|
|
237
|
+
promise.reject("PAYMENT_ERROR", "Failed to capture payment: ${e.message}", e)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@ReactMethod
|
|
242
|
+
fun removePayment(paymentData: ReadableMap, promise: Promise) {
|
|
243
|
+
if (paymentData == null) {
|
|
244
|
+
promise.reject("REMOVE_PAYMENT_ERROR", "Payment data is required")
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
val paymentDataMap = MapUtils.readableMapToMap(paymentData)
|
|
250
|
+
|
|
251
|
+
moduleScope.launch {
|
|
252
|
+
try {
|
|
253
|
+
val result = linkrunnerSDK.removePayment(ModelConverter.toRemovePaymentRequest(paymentDataMap))
|
|
254
|
+
|
|
255
|
+
withContext(Dispatchers.Main) {
|
|
256
|
+
if (result.isSuccess) {
|
|
257
|
+
val response = Arguments.createMap()
|
|
258
|
+
response.putString("status", "success")
|
|
259
|
+
response.putString("message", "Payment removed successfully")
|
|
260
|
+
promise.resolve(response)
|
|
261
|
+
} else {
|
|
262
|
+
promise.reject(
|
|
263
|
+
"REMOVE_PAYMENT_ERROR",
|
|
264
|
+
"Failed to remove payment: ${result.exceptionOrNull()?.message}",
|
|
265
|
+
result.exceptionOrNull()
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch (e: Exception) {
|
|
270
|
+
withContext(Dispatchers.Main) {
|
|
271
|
+
promise.reject("REMOVE_PAYMENT_ERROR", "Exception removing payment: ${e.message}", e)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
} catch (e: Exception) {
|
|
276
|
+
promise.reject("REMOVE_PAYMENT_ERROR", "Failed to remove payment: ${e.message}", e)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@ReactMethod
|
|
281
|
+
fun getAttributionData(promise: Promise) {
|
|
282
|
+
moduleScope.launch {
|
|
283
|
+
try {
|
|
284
|
+
val result = linkrunnerSDK.getAttributionData()
|
|
285
|
+
|
|
286
|
+
withContext(Dispatchers.Main) {
|
|
287
|
+
if (result.isSuccess) {
|
|
288
|
+
val attributionData = result.getOrNull()
|
|
289
|
+
|
|
290
|
+
// Convert the attribution data to a WritableMap
|
|
291
|
+
val response = Arguments.createMap()
|
|
292
|
+
|
|
293
|
+
if (attributionData != null) {
|
|
294
|
+
// Add the deeplink if it exists
|
|
295
|
+
attributionData.deeplink?.let { deeplink ->
|
|
296
|
+
response.putString("deeplink", deeplink)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Convert campaign data to a WritableMap if it exists
|
|
300
|
+
attributionData.campaignData?.let { campaignData ->
|
|
301
|
+
val campaignDataMap = Arguments.createMap()
|
|
302
|
+
campaignDataMap.putString("id", campaignData.id)
|
|
303
|
+
campaignDataMap.putString("name", campaignData.name)
|
|
304
|
+
|
|
305
|
+
campaignData.adNetwork?.let { adNetwork ->
|
|
306
|
+
campaignDataMap.putString("adNetwork", adNetwork)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
campaignDataMap.putString("type", campaignData.type)
|
|
310
|
+
campaignDataMap.putString("installedAt", campaignData.installedAt)
|
|
311
|
+
|
|
312
|
+
campaignData.storeClickAt?.let { storeClickAt ->
|
|
313
|
+
campaignDataMap.putString("storeClickAt", storeClickAt)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
campaignDataMap.putString("groupName", campaignData.groupName)
|
|
317
|
+
campaignDataMap.putString("assetName", campaignData.assetName)
|
|
318
|
+
campaignDataMap.putString("assetGroupName", campaignData.assetGroupName)
|
|
319
|
+
|
|
320
|
+
response.putMap("campaignData", campaignDataMap)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
response.putString("status", "success")
|
|
325
|
+
response.putString("message", "Attribution data retrieved successfully")
|
|
326
|
+
|
|
327
|
+
promise.resolve(response)
|
|
328
|
+
} else {
|
|
329
|
+
promise.reject(
|
|
330
|
+
"ATTRIBUTION_DATA_ERROR",
|
|
331
|
+
"Failed to get attribution data: ${result.exceptionOrNull()?.message}",
|
|
332
|
+
result.exceptionOrNull()
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch (e: Exception) {
|
|
337
|
+
withContext(Dispatchers.Main) {
|
|
338
|
+
promise.reject("ATTRIBUTION_DATA_ERROR", "Exception getting attribution data: ${e.message}", e)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@ReactMethod
|
|
345
|
+
fun setAdditionalData(integrationDataMap: ReadableMap?, promise: Promise) {
|
|
346
|
+
if (integrationDataMap == null || integrationDataMap.toHashMap().isEmpty()) {
|
|
347
|
+
promise.reject("ADDITIONAL_DATA_ERROR", "Integration data is required")
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
val additionalData = MapUtils.readableMapToMap(integrationDataMap)
|
|
353
|
+
|
|
354
|
+
moduleScope.launch {
|
|
355
|
+
try {
|
|
356
|
+
// Convert Map to IntegrationData
|
|
357
|
+
val integrationData = ModelConverter.toIntegrationData(additionalData)
|
|
358
|
+
val result = linkrunnerSDK.setAdditionalData(integrationData)
|
|
359
|
+
|
|
360
|
+
withContext(Dispatchers.Main) {
|
|
361
|
+
if (result.isSuccess) {
|
|
362
|
+
val response = Arguments.createMap()
|
|
363
|
+
response.putString("status", "success")
|
|
364
|
+
response.putString("message", "Additional data set successfully")
|
|
365
|
+
promise.resolve(response)
|
|
366
|
+
} else {
|
|
367
|
+
promise.reject(
|
|
368
|
+
"ADDITIONAL_DATA_ERROR",
|
|
369
|
+
"Failed to set additional data: ${result.exceptionOrNull()?.message}",
|
|
370
|
+
result.exceptionOrNull()
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} catch (e: Exception) {
|
|
375
|
+
withContext(Dispatchers.Main) {
|
|
376
|
+
promise.reject("ADDITIONAL_DATA_ERROR", "Exception setting additional data: ${e.message}", e)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch (e: Exception) {
|
|
381
|
+
promise.reject("ADDITIONAL_DATA_ERROR", "Failed to set additional data: ${e.message}", e)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
@ReactMethod
|
|
386
|
+
fun enablePIIHashing(enabled: Boolean) {
|
|
387
|
+
try {
|
|
388
|
+
// Call the SDK's enablePIIHashing method
|
|
389
|
+
linkrunnerSDK.enablePIIHashing(enabled)
|
|
390
|
+
|
|
391
|
+
if (BuildConfig.DEBUG) {
|
|
392
|
+
Log.d(TAG, "Linkrunner: PII hashing ${if (enabled) "enabled" else "disabled"}")
|
|
393
|
+
}
|
|
394
|
+
} catch (e: Exception) {
|
|
395
|
+
Log.e(TAG, "Failed to ${if (enabled) "enable" else "disable"} PII hashing", e)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package io.linkrunner
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
class LinkrunnerPackage : ReactPackage {
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
+
return listOf(LinkrunnerModule(reactContext))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
+
return emptyList()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
package io.linkrunner.utils
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
import com.facebook.react.bridge.ReadableType
|
|
7
|
+
import com.facebook.react.bridge.WritableArray
|
|
8
|
+
import com.facebook.react.bridge.WritableMap
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Utility functions to convert between React Native types and Kotlin types
|
|
12
|
+
*/
|
|
13
|
+
object MapUtils {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Convert ReadableMap to Kotlin Map
|
|
17
|
+
*/
|
|
18
|
+
fun readableMapToMap(readableMap: ReadableMap): Map<String, Any> {
|
|
19
|
+
val map = mutableMapOf<String, Any>()
|
|
20
|
+
val iterator = readableMap.keySetIterator()
|
|
21
|
+
while (iterator.hasNextKey()) {
|
|
22
|
+
val key = iterator.nextKey()
|
|
23
|
+
when (readableMap.getType(key)) {
|
|
24
|
+
ReadableType.Null -> map[key] = ""
|
|
25
|
+
ReadableType.Boolean -> map[key] = readableMap.getBoolean(key)
|
|
26
|
+
ReadableType.Number -> map[key] = readableMap.getDouble(key)
|
|
27
|
+
ReadableType.String -> map[key] = readableMap.getString(key) ?: ""
|
|
28
|
+
ReadableType.Map -> map[key] = readableMapToMap(readableMap.getMap(key)!!)
|
|
29
|
+
ReadableType.Array -> map[key] = readableArrayToList(readableMap.getArray(key)!!)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return map
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Convert ReadableArray to Kotlin List
|
|
37
|
+
*/
|
|
38
|
+
private fun readableArrayToList(readableArray: ReadableArray): List<Any> {
|
|
39
|
+
val list = mutableListOf<Any>()
|
|
40
|
+
for (i in 0 until readableArray.size()) {
|
|
41
|
+
when (readableArray.getType(i)) {
|
|
42
|
+
ReadableType.Null -> list.add("")
|
|
43
|
+
ReadableType.Boolean -> list.add(readableArray.getBoolean(i))
|
|
44
|
+
ReadableType.Number -> list.add(readableArray.getDouble(i))
|
|
45
|
+
ReadableType.String -> list.add(readableArray.getString(i) ?: "")
|
|
46
|
+
ReadableType.Map -> list.add(readableMapToMap(readableArray.getMap(i)!!))
|
|
47
|
+
ReadableType.Array -> list.add(readableArrayToList(readableArray.getArray(i)!!))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return list
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Convert Kotlin Map to WritableMap
|
|
55
|
+
*/
|
|
56
|
+
fun mapToWritableMap(map: Map<String, Any?>?): WritableMap {
|
|
57
|
+
val writableMap = Arguments.createMap()
|
|
58
|
+
|
|
59
|
+
if (map == null) {
|
|
60
|
+
return writableMap
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for ((key, value) in map) {
|
|
64
|
+
when (value) {
|
|
65
|
+
null -> writableMap.putNull(key)
|
|
66
|
+
is Boolean -> writableMap.putBoolean(key, value)
|
|
67
|
+
is Int -> writableMap.putInt(key, value)
|
|
68
|
+
is Double -> writableMap.putDouble(key, value)
|
|
69
|
+
is Float -> writableMap.putDouble(key, value.toDouble())
|
|
70
|
+
is String -> writableMap.putString(key, value)
|
|
71
|
+
is Map<*, *> -> {
|
|
72
|
+
@Suppress("UNCHECKED_CAST")
|
|
73
|
+
writableMap.putMap(key, mapToWritableMap(value as Map<String, Any?>))
|
|
74
|
+
}
|
|
75
|
+
is List<*> -> {
|
|
76
|
+
@Suppress("UNCHECKED_CAST")
|
|
77
|
+
writableMap.putArray(key, listToWritableArray(value as List<Any?>))
|
|
78
|
+
}
|
|
79
|
+
else -> writableMap.putString(key, value.toString())
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return writableMap
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert Kotlin List to WritableArray
|
|
88
|
+
*/
|
|
89
|
+
fun listToWritableArray(list: List<Any?>?): WritableArray {
|
|
90
|
+
val writableArray = Arguments.createArray()
|
|
91
|
+
|
|
92
|
+
if (list == null) {
|
|
93
|
+
return writableArray
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (value in list) {
|
|
97
|
+
when (value) {
|
|
98
|
+
null -> writableArray.pushNull()
|
|
99
|
+
is Boolean -> writableArray.pushBoolean(value)
|
|
100
|
+
is Int -> writableArray.pushInt(value)
|
|
101
|
+
is Double -> writableArray.pushDouble(value)
|
|
102
|
+
is Float -> writableArray.pushDouble(value.toDouble())
|
|
103
|
+
is String -> writableArray.pushString(value)
|
|
104
|
+
is Map<*, *> -> {
|
|
105
|
+
@Suppress("UNCHECKED_CAST")
|
|
106
|
+
writableArray.pushMap(mapToWritableMap(value as Map<String, Any?>))
|
|
107
|
+
}
|
|
108
|
+
is List<*> -> {
|
|
109
|
+
@Suppress("UNCHECKED_CAST")
|
|
110
|
+
writableArray.pushArray(listToWritableArray(value as List<Any?>))
|
|
111
|
+
}
|
|
112
|
+
else -> writableArray.pushString(value.toString())
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return writableArray
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Extension functions for safe retrieval from maps
|
|
121
|
+
*/
|
|
122
|
+
inline fun <reified T> Map<String, Any?>.get(key: String, defaultValue: T): T {
|
|
123
|
+
val value = this[key]
|
|
124
|
+
return when {
|
|
125
|
+
value == null -> defaultValue
|
|
126
|
+
value is T -> value
|
|
127
|
+
T::class.java == String::class.java && value != null -> value.toString() as T
|
|
128
|
+
T::class.java == Int::class.java && value is Number -> value.toInt() as T
|
|
129
|
+
T::class.java == Double::class.java && value is Number -> value.toDouble() as T
|
|
130
|
+
T::class.java == Float::class.java && value is Number -> value.toFloat() as T
|
|
131
|
+
T::class.java == Long::class.java && value is Number -> value.toLong() as T
|
|
132
|
+
T::class.java == Boolean::class.java && value is Number -> (value.toInt() != 0) as T
|
|
133
|
+
else -> defaultValue
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|