react-native-nami-sdk 3.3.3 → 3.3.5
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/android/build.gradle +2 -2
- package/android/src/main/java/com/namiml/reactnative/NamiCampaignManagerBridge.kt +135 -19
- package/android/src/main/java/com/namiml/reactnative/NamiFlowManagerBridge.kt +5 -1
- package/android/src/main/java/com/namiml/reactnative/NamiOverlayControlBridge.kt +31 -0
- package/android/src/main/java/com/namiml/reactnative/ReactOverlayActivity.kt +2 -0
- package/dist/specs/NativeNamiCampaignManager.d.ts +2 -0
- package/dist/specs/NativeNamiPaywallManager.d.ts +1 -1
- package/dist/src/NamiCampaignManager.d.ts +2 -0
- package/dist/src/NamiPaywallManager.d.ts +1 -1
- package/dist/src/version.d.ts +1 -1
- package/ios/NamiCampaignManagerBridge.m +4 -0
- package/ios/NamiCampaignManagerBridge.swift +32 -0
- package/package.json +6 -15
- package/react-native-nami-sdk.podspec +1 -1
- package/specs/NativeNamiCampaignManager.ts +7 -0
- package/specs/NativeNamiPaywallManager.ts +1 -1
- package/src/NamiCampaignManager.ts +11 -0
- package/src/NamiOverlayControl.tsx +12 -1
- package/src/NamiPaywallManager.ts +1 -1
- package/src/version.ts +1 -1
package/android/build.gradle
CHANGED
|
@@ -85,8 +85,8 @@ dependencies {
|
|
|
85
85
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
86
86
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
|
87
87
|
|
|
88
|
-
playImplementation "com.namiml:sdk-android:3.3.
|
|
89
|
-
amazonImplementation "com.namiml:sdk-amazon:3.3.
|
|
88
|
+
playImplementation "com.namiml:sdk-android:3.3.5.1"
|
|
89
|
+
amazonImplementation "com.namiml:sdk-amazon:3.3.5.1"
|
|
90
90
|
|
|
91
91
|
implementation "com.facebook.react:react-native:+" // From node_modules
|
|
92
92
|
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
|
|
@@ -3,23 +3,24 @@ package com.namiml.reactnative
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.net.Uri
|
|
5
5
|
import android.util.Log
|
|
6
|
+
import androidx.core.net.toUri
|
|
6
7
|
import com.facebook.react.bridge.*
|
|
7
8
|
import com.facebook.react.module.annotations.ReactModule
|
|
8
9
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
9
10
|
import com.facebook.react.turbomodule.core.interfaces.TurboModule
|
|
10
11
|
import com.namiml.billing.NamiPurchase
|
|
12
|
+
import com.namiml.campaign.LaunchCampaignError
|
|
11
13
|
import com.namiml.campaign.LaunchCampaignResult
|
|
12
14
|
import com.namiml.campaign.NamiCampaign
|
|
13
15
|
import com.namiml.campaign.NamiCampaignManager
|
|
14
16
|
import com.namiml.paywall.model.NamiPaywallEvent
|
|
15
17
|
import com.namiml.paywall.model.PaywallLaunchContext
|
|
16
|
-
import androidx.core.net.toUri
|
|
17
18
|
|
|
18
19
|
@ReactModule(name = NamiCampaignManagerBridgeModule.NAME)
|
|
19
20
|
class NamiCampaignManagerBridgeModule internal constructor(
|
|
20
|
-
private val reactContext: ReactApplicationContext
|
|
21
|
-
) : ReactContextBaseJavaModule(reactContext),
|
|
22
|
-
|
|
21
|
+
private val reactContext: ReactApplicationContext,
|
|
22
|
+
) : ReactContextBaseJavaModule(reactContext),
|
|
23
|
+
TurboModule {
|
|
23
24
|
companion object {
|
|
24
25
|
const val NAME = "RNNamiCampaignManager"
|
|
25
26
|
const val CAMPAIGN_ID = "campaignId"
|
|
@@ -42,10 +43,7 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
42
43
|
const val NAMI_PAYWALL_EVENT = "NamiPaywallEvent"
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
override fun getName(): String {
|
|
47
|
-
return NAME
|
|
48
|
-
}
|
|
46
|
+
override fun getName(): String = NAME
|
|
49
47
|
|
|
50
48
|
private fun campaignToReadableMap(campaign: NamiCampaign): ReadableMap {
|
|
51
49
|
val readableMap = Arguments.createMap()
|
|
@@ -94,7 +92,37 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
94
92
|
val keyIterator = attr.keySetIterator()
|
|
95
93
|
while (keyIterator.hasNextKey()) {
|
|
96
94
|
val key = keyIterator.nextKey()
|
|
97
|
-
|
|
95
|
+
try {
|
|
96
|
+
// Handle different data types and store with proper types
|
|
97
|
+
val value: Any =
|
|
98
|
+
when (attr.getType(key)) {
|
|
99
|
+
ReadableType.String -> attr.getString(key) ?: ""
|
|
100
|
+
ReadableType.Boolean -> attr.getBoolean(key)
|
|
101
|
+
ReadableType.Number -> {
|
|
102
|
+
// Try to get as int first, then as double
|
|
103
|
+
try {
|
|
104
|
+
attr.getInt(key)
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
attr.getDouble(key)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
ReadableType.Null -> ""
|
|
110
|
+
ReadableType.Array -> attr.getArray(key)?.toArrayList() ?: emptyList<Any>()
|
|
111
|
+
ReadableType.Map -> attr.getMap(key)?.toHashMap() ?: emptyMap<String, Any>()
|
|
112
|
+
}
|
|
113
|
+
customAttributes[key] = value
|
|
114
|
+
} catch (e: Exception) {
|
|
115
|
+
// Log the error and try to store the value as a string representation
|
|
116
|
+
Log.w(NAME, "Error parsing customAttribute '$key': ${e.message}. Attempting string conversion.")
|
|
117
|
+
try {
|
|
118
|
+
// Fallback: try to get the dynamic value and convert to string
|
|
119
|
+
val dynamicValue = attr.getDynamic(key)
|
|
120
|
+
customAttributes[key] = dynamicValue.asString() ?: ""
|
|
121
|
+
} catch (fallbackError: Exception) {
|
|
122
|
+
// If all else fails, log and skip this attribute
|
|
123
|
+
Log.e(NAME, "Failed to parse customAttribute '$key': ${fallbackError.message}. Skipping.")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
98
126
|
}
|
|
99
127
|
Log.d(NAME, "customAttributes $customAttributes")
|
|
100
128
|
}
|
|
@@ -120,8 +148,7 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
120
148
|
|
|
121
149
|
if (theActivity != null) {
|
|
122
150
|
reactApplicationContext.runOnUiQueueThread {
|
|
123
|
-
val paywallActionCallback = {
|
|
124
|
-
paywallEvent: NamiPaywallEvent ->
|
|
151
|
+
val paywallActionCallback = { paywallEvent: NamiPaywallEvent ->
|
|
125
152
|
handlePaywallCallback(
|
|
126
153
|
paywallEvent,
|
|
127
154
|
actionCallback,
|
|
@@ -184,7 +211,13 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
184
211
|
putString("id", paywallEvent.sku?.id ?: "")
|
|
185
212
|
putString("skuId", paywallEvent.sku?.skuId ?: "")
|
|
186
213
|
putString("name", paywallEvent.sku?.name ?: "")
|
|
187
|
-
putString(
|
|
214
|
+
putString(
|
|
215
|
+
"type",
|
|
216
|
+
paywallEvent.sku
|
|
217
|
+
?.type
|
|
218
|
+
.toString()
|
|
219
|
+
.lowercase(),
|
|
220
|
+
)
|
|
188
221
|
}
|
|
189
222
|
resultMap.putMap(SKU, skuMap)
|
|
190
223
|
}
|
|
@@ -222,8 +255,8 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
222
255
|
emitEvent(NAMI_PAYWALL_EVENT, resultMap)
|
|
223
256
|
}
|
|
224
257
|
|
|
225
|
-
private fun createPurchaseArray(purchases: List<NamiPurchase>?): WritableArray
|
|
226
|
-
|
|
258
|
+
private fun createPurchaseArray(purchases: List<NamiPurchase>?): WritableArray =
|
|
259
|
+
WritableNativeArray().apply {
|
|
227
260
|
purchases?.forEach { purchase ->
|
|
228
261
|
try {
|
|
229
262
|
pushMap(purchase.toPurchaseDict())
|
|
@@ -232,7 +265,6 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
232
265
|
}
|
|
233
266
|
}
|
|
234
267
|
}
|
|
235
|
-
}
|
|
236
268
|
|
|
237
269
|
private fun emitEvent(
|
|
238
270
|
event: String,
|
|
@@ -246,17 +278,46 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
246
278
|
}
|
|
247
279
|
}
|
|
248
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Maps Android LaunchCampaignError enum values to iOS-compatible error codes
|
|
283
|
+
* to ensure consistent error handling across platforms in React Native.
|
|
284
|
+
*/
|
|
285
|
+
private fun mapErrorCodeToIOS(androidError: LaunchCampaignError): Int =
|
|
286
|
+
when (androidError) {
|
|
287
|
+
LaunchCampaignError.DEFAULT_CAMPAIGN_NOT_FOUND -> 0
|
|
288
|
+
LaunchCampaignError.LABELED_CAMPAIGN_NOT_FOUND -> 1
|
|
289
|
+
LaunchCampaignError.CAMPAIGN_DATA_NOT_FOUND -> 2
|
|
290
|
+
LaunchCampaignError.PAYWALL_ALREADY_DISPLAYED -> 3
|
|
291
|
+
LaunchCampaignError.SDK_NOT_INITIALIZED -> 4
|
|
292
|
+
LaunchCampaignError.URL_CAMPAIGN_NOT_FOUND -> 6
|
|
293
|
+
LaunchCampaignError.PRODUCT_GROUPS_NOT_FOUND -> 8
|
|
294
|
+
}
|
|
295
|
+
|
|
249
296
|
private fun handleResult(
|
|
250
297
|
result: LaunchCampaignResult,
|
|
251
298
|
resultCallback: Callback,
|
|
252
299
|
) {
|
|
253
|
-
val resultMap = Arguments.createMap()
|
|
254
300
|
when (result) {
|
|
255
301
|
is LaunchCampaignResult.Success -> {
|
|
256
302
|
resultCallback.invoke(true)
|
|
257
303
|
}
|
|
258
304
|
is LaunchCampaignResult.Failure -> {
|
|
259
|
-
|
|
305
|
+
val error = result.error
|
|
306
|
+
val errorInfo =
|
|
307
|
+
if (error is LaunchCampaignError) {
|
|
308
|
+
Arguments.createMap().apply {
|
|
309
|
+
putInt("code", mapErrorCodeToIOS(error))
|
|
310
|
+
putString("domain", "LaunchCampaignError")
|
|
311
|
+
putString("message", error.errorMessage.ifEmpty { error.toString() })
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
Arguments.createMap().apply {
|
|
315
|
+
putInt("code", -1)
|
|
316
|
+
putString("domain", "Unknown")
|
|
317
|
+
putString("message", error.toString())
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
resultCallback.invoke(false, errorInfo)
|
|
260
321
|
}
|
|
261
322
|
}
|
|
262
323
|
}
|
|
@@ -279,13 +340,38 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
279
340
|
val isCampaignAvailable =
|
|
280
341
|
when {
|
|
281
342
|
campaignSource == null -> NamiCampaignManager.isCampaignAvailable()
|
|
282
|
-
campaignSource.toUri().scheme != null ->
|
|
283
|
-
campaignSource.toUri())
|
|
343
|
+
campaignSource.toUri().scheme != null ->
|
|
344
|
+
NamiCampaignManager.isCampaignAvailable(campaignSource.toUri())
|
|
284
345
|
else -> NamiCampaignManager.isCampaignAvailable(campaignSource)
|
|
285
346
|
}
|
|
286
347
|
promise.resolve(isCampaignAvailable)
|
|
287
348
|
}
|
|
288
349
|
|
|
350
|
+
@ReactMethod
|
|
351
|
+
fun isFlow(
|
|
352
|
+
label: String?,
|
|
353
|
+
withUrl: String?,
|
|
354
|
+
promise: Promise,
|
|
355
|
+
) {
|
|
356
|
+
try {
|
|
357
|
+
val uri =
|
|
358
|
+
if (!withUrl.isNullOrEmpty()) {
|
|
359
|
+
val parsedUri = Uri.parse(withUrl)
|
|
360
|
+
if (parsedUri.scheme.isNullOrEmpty()) {
|
|
361
|
+
promise.reject("ISFLOW_ERROR", "Invalid URL format: missing scheme", null)
|
|
362
|
+
return
|
|
363
|
+
}
|
|
364
|
+
parsedUri
|
|
365
|
+
} else {
|
|
366
|
+
null
|
|
367
|
+
}
|
|
368
|
+
val result = NamiCampaignManager.isFlow(label = label, uri = uri)
|
|
369
|
+
promise.resolve(result)
|
|
370
|
+
} catch (e: Exception) {
|
|
371
|
+
promise.reject("ISFLOW_ERROR", "Failed to check if campaign is flow: ${e.message}", e)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
289
375
|
@ReactMethod
|
|
290
376
|
fun refresh(promise: Promise) {
|
|
291
377
|
NamiCampaignManager.refresh { campaigns ->
|
|
@@ -317,4 +403,34 @@ class NamiCampaignManagerBridgeModule internal constructor(
|
|
|
317
403
|
@ReactMethod
|
|
318
404
|
fun removeListeners(count: Int?) {
|
|
319
405
|
}
|
|
406
|
+
|
|
407
|
+
@ReactMethod
|
|
408
|
+
fun productGroups(
|
|
409
|
+
label: String?,
|
|
410
|
+
withUrl: String?,
|
|
411
|
+
promise: Promise,
|
|
412
|
+
) {
|
|
413
|
+
try {
|
|
414
|
+
val uri =
|
|
415
|
+
if (!withUrl.isNullOrEmpty()) {
|
|
416
|
+
val parsedUri = Uri.parse(withUrl)
|
|
417
|
+
if (parsedUri.scheme.isNullOrEmpty()) {
|
|
418
|
+
promise.reject("PRODUCTGROUPS_ERROR", "Invalid URL format: missing scheme", null)
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
parsedUri
|
|
422
|
+
} else {
|
|
423
|
+
null
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
val productGroups = NamiCampaignManager.getProductGroups(label = label, uri = uri)
|
|
427
|
+
val array = WritableNativeArray()
|
|
428
|
+
productGroups.forEach { group: String ->
|
|
429
|
+
array.pushString(group)
|
|
430
|
+
}
|
|
431
|
+
promise.resolve(array)
|
|
432
|
+
} catch (e: Exception) {
|
|
433
|
+
promise.reject("PRODUCTGROUPS_ERROR", "Failed to get product groups: ${e.message}", e)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
320
436
|
}
|
|
@@ -52,7 +52,11 @@ class NamiFlowManagerBridgeModule internal constructor(
|
|
|
52
52
|
@ReactMethod
|
|
53
53
|
fun resume() {
|
|
54
54
|
Handler(Looper.getMainLooper()).postDelayed({
|
|
55
|
-
|
|
55
|
+
if (reactApplicationContext.hasCurrentActivity()) {
|
|
56
|
+
NamiFlowManager.resume(reactApplicationContext.currentActivity)
|
|
57
|
+
} else {
|
|
58
|
+
NamiFlowManager.resume()
|
|
59
|
+
}
|
|
56
60
|
}, 100L)
|
|
57
61
|
}
|
|
58
62
|
|
|
@@ -18,12 +18,31 @@ class NamiOverlayControlBridgeModule(private val ctx: ReactApplicationContext)
|
|
|
18
18
|
const val NAME = "RNNamiOverlayControl"
|
|
19
19
|
var currentOverlayActivity: ReactOverlayActivity? = null
|
|
20
20
|
var lastValidActivity: Activity? = null
|
|
21
|
+
private var isPresentingOverlay = false
|
|
22
|
+
private val pendingPromises = mutableListOf<Promise>()
|
|
23
|
+
|
|
24
|
+
// Internal method to clear the presenting flag (for use by ReactOverlayActivity)
|
|
25
|
+
internal fun clearPresentingFlag() {
|
|
26
|
+
isPresentingOverlay = false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Internal method to check if an overlay is currently being presented
|
|
30
|
+
internal fun isOverlayActive(): Boolean {
|
|
31
|
+
return isPresentingOverlay || currentOverlayActivity != null
|
|
32
|
+
}
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
override fun getName() = NAME
|
|
24
36
|
|
|
25
37
|
@ReactMethod
|
|
26
38
|
fun presentOverlay(promise: Promise) {
|
|
39
|
+
// Check if we're already presenting an overlay
|
|
40
|
+
if (isPresentingOverlay || currentOverlayActivity != null) {
|
|
41
|
+
// If there's already an active overlay, reject the new call
|
|
42
|
+
promise.reject("OVERLAY_ALREADY_ACTIVE", "An overlay is already being presented or is currently active")
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
var theActivity: Activity? = null
|
|
28
47
|
if (reactApplicationContext.hasCurrentActivity()) {
|
|
29
48
|
theActivity = reactApplicationContext.currentActivity
|
|
@@ -55,6 +74,8 @@ class NamiOverlayControlBridgeModule(private val ctx: ReactApplicationContext)
|
|
|
55
74
|
return
|
|
56
75
|
}
|
|
57
76
|
|
|
77
|
+
// Set flag to indicate we're presenting an overlay
|
|
78
|
+
isPresentingOverlay = true
|
|
58
79
|
startOverlayActivity(theActivity, promise)
|
|
59
80
|
}
|
|
60
81
|
|
|
@@ -88,11 +109,15 @@ class NamiOverlayControlBridgeModule(private val ctx: ReactApplicationContext)
|
|
|
88
109
|
promise.resolve(null)
|
|
89
110
|
} catch (uiError: Exception) {
|
|
90
111
|
Log.e(NAME, "Error in UI thread: ${uiError.message}", uiError)
|
|
112
|
+
// Clear the presenting flag on error
|
|
113
|
+
isPresentingOverlay = false
|
|
91
114
|
promise.reject("UI_THREAD_ERROR", "Failed in UI thread: ${uiError.message}", uiError)
|
|
92
115
|
}
|
|
93
116
|
}
|
|
94
117
|
} catch (e: Exception) {
|
|
95
118
|
Log.e(NAME, "Error presenting overlay: ${e.message}", e)
|
|
119
|
+
// Clear the presenting flag on error
|
|
120
|
+
isPresentingOverlay = false
|
|
96
121
|
promise.reject("PRESENT_OVERLAY_ERROR", "Failed to present overlay: ${e.message}", e)
|
|
97
122
|
}
|
|
98
123
|
}
|
|
@@ -137,6 +162,8 @@ class NamiOverlayControlBridgeModule(private val ctx: ReactApplicationContext)
|
|
|
137
162
|
overlay.overridePendingTransition(0, 0)
|
|
138
163
|
}
|
|
139
164
|
currentOverlayActivity = null
|
|
165
|
+
// Clear the presenting flag when overlay is finished
|
|
166
|
+
isPresentingOverlay = false
|
|
140
167
|
|
|
141
168
|
// Wait for activity to actually finish before resolving promise
|
|
142
169
|
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
@@ -153,12 +180,16 @@ class NamiOverlayControlBridgeModule(private val ctx: ReactApplicationContext)
|
|
|
153
180
|
@Suppress("DEPRECATION")
|
|
154
181
|
overridePendingTransition(0, 0)
|
|
155
182
|
}
|
|
183
|
+
// Clear the presenting flag when overlay is finished
|
|
184
|
+
isPresentingOverlay = false
|
|
156
185
|
|
|
157
186
|
// Wait for activity to actually finish before resolving promise
|
|
158
187
|
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
159
188
|
promise.resolve(null)
|
|
160
189
|
}, 100)
|
|
161
190
|
} ?: run {
|
|
191
|
+
// Clear the presenting flag even if no activity was found
|
|
192
|
+
isPresentingOverlay = false
|
|
162
193
|
promise.resolve(null)
|
|
163
194
|
}
|
|
164
195
|
}
|
|
@@ -24,6 +24,8 @@ class ReactOverlayActivity : ReactActivity() {
|
|
|
24
24
|
// Clear the reference when activity is destroyed
|
|
25
25
|
if (NamiOverlayControlBridgeModule.currentOverlayActivity == this) {
|
|
26
26
|
NamiOverlayControlBridgeModule.currentOverlayActivity = null
|
|
27
|
+
// Also clear the presenting flag to prevent stuck states
|
|
28
|
+
NamiOverlayControlBridgeModule.clearPresentingFlag()
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
}
|
|
@@ -71,6 +71,7 @@ export interface Spec extends TurboModule {
|
|
|
71
71
|
value?: string;
|
|
72
72
|
}[]>;
|
|
73
73
|
isCampaignAvailable(source?: string): Promise<boolean>;
|
|
74
|
+
isFlow(label?: string | null, withUrl?: string | null): Promise<boolean>;
|
|
74
75
|
refresh(): Promise<{
|
|
75
76
|
id?: string;
|
|
76
77
|
rule?: string;
|
|
@@ -80,6 +81,7 @@ export interface Spec extends TurboModule {
|
|
|
80
81
|
value?: string;
|
|
81
82
|
}[]>;
|
|
82
83
|
registerAvailableCampaignsHandler(): void;
|
|
84
|
+
productGroups(label: string | null, withUrl: string | null): Promise<string[]>;
|
|
83
85
|
}
|
|
84
86
|
declare const _default: Spec;
|
|
85
87
|
export default _default;
|
|
@@ -64,7 +64,7 @@ export interface Spec extends TurboModule {
|
|
|
64
64
|
isHidden(): Promise<boolean>;
|
|
65
65
|
isPaywallOpen(): Promise<boolean>;
|
|
66
66
|
buySkuCancel(): void;
|
|
67
|
-
setProductDetails(productDetails: string, allowOffers
|
|
67
|
+
setProductDetails(productDetails: string, allowOffers: boolean): void;
|
|
68
68
|
setAppSuppliedVideoDetails(url: string, name?: string): void;
|
|
69
69
|
allowUserInteraction(allowed: boolean): void;
|
|
70
70
|
}
|
|
@@ -17,6 +17,7 @@ export declare const NamiCampaignManager: {
|
|
|
17
17
|
value?: string;
|
|
18
18
|
}[]>;
|
|
19
19
|
isCampaignAvailable: (campaignName: string | null) => Promise<boolean>;
|
|
20
|
+
isFlow: (label?: string | null, withUrl?: string | null) => Promise<boolean>;
|
|
20
21
|
refresh: () => Promise<{
|
|
21
22
|
id?: string;
|
|
22
23
|
rule?: string;
|
|
@@ -26,4 +27,5 @@ export declare const NamiCampaignManager: {
|
|
|
26
27
|
value?: string;
|
|
27
28
|
}[]>;
|
|
28
29
|
registerAvailableCampaignsHandler: (callback: (campaigns: NamiCampaign[]) => void) => (() => void);
|
|
30
|
+
getProductGroups: (label?: string | null, withUrl?: string | null) => Promise<string[]>;
|
|
29
31
|
};
|
|
@@ -24,7 +24,7 @@ export declare const NamiPaywallManager: {
|
|
|
24
24
|
isHidden: () => Promise<boolean>;
|
|
25
25
|
isPaywallOpen: () => Promise<boolean>;
|
|
26
26
|
buySkuCancel: () => void;
|
|
27
|
-
setProductDetails: (productDetails: string, allowOffers
|
|
27
|
+
setProductDetails: (productDetails: string, allowOffers: boolean) => void;
|
|
28
28
|
setAppSuppliedVideoDetails: (url: string, name?: string) => void;
|
|
29
29
|
allowUserInteraction: (allowed: boolean) => void;
|
|
30
30
|
};
|
package/dist/src/version.d.ts
CHANGED
|
@@ -16,10 +16,14 @@ RCT_EXTERN_METHOD(allCampaigns:(RCTPromiseResolveBlock)resolve rejecter:(RCTProm
|
|
|
16
16
|
|
|
17
17
|
RCT_EXTERN_METHOD(isCampaignAvailable:(nullable NSString *)campaignSource resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
18
18
|
|
|
19
|
+
RCT_EXTERN_METHOD(isFlow:(nullable NSString *)label withUrl:(nullable NSString *)withUrl resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
20
|
+
|
|
19
21
|
RCT_EXTERN_METHOD(refresh:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
20
22
|
|
|
21
23
|
RCT_EXTERN_METHOD(registerAvailableCampaignsHandler)
|
|
22
24
|
|
|
25
|
+
RCT_EXTERN_METHOD(productGroups:(nullable NSString *)label withUrl:(nullable NSString *)withUrl resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
26
|
+
|
|
23
27
|
+ (BOOL)requiresMainQueueSetup {
|
|
24
28
|
return YES;
|
|
25
29
|
}
|
|
@@ -289,6 +289,22 @@ class RNNamiCampaignManager: RCTEventEmitter {
|
|
|
289
289
|
resolve(isCampaignAvailable)
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
@objc(isFlow:withUrl:resolver:rejecter:)
|
|
293
|
+
func isFlow(
|
|
294
|
+
label: String?,
|
|
295
|
+
withUrl: String?,
|
|
296
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
297
|
+
reject _: @escaping RCTPromiseRejectBlock
|
|
298
|
+
) {
|
|
299
|
+
var url: URL?
|
|
300
|
+
if let withUrl = withUrl {
|
|
301
|
+
url = URL(string: withUrl)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let result = NamiCampaignManager.isFlow(label: label, url: url)
|
|
305
|
+
resolve(result)
|
|
306
|
+
}
|
|
307
|
+
|
|
292
308
|
@objc(refresh:rejecter:)
|
|
293
309
|
func refresh(resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
|
|
294
310
|
NamiCampaignManager.refresh { campaigns in
|
|
@@ -304,4 +320,20 @@ class RNNamiCampaignManager: RCTEventEmitter {
|
|
|
304
320
|
self.safeSend(withName: "AvailableCampaignsChanged", body: dictionaries)
|
|
305
321
|
}
|
|
306
322
|
}
|
|
323
|
+
|
|
324
|
+
@objc(productGroups:withUrl:resolver:rejecter:)
|
|
325
|
+
func productGroups(
|
|
326
|
+
label: String?,
|
|
327
|
+
withUrl: String?,
|
|
328
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
329
|
+
reject _: @escaping RCTPromiseRejectBlock
|
|
330
|
+
) {
|
|
331
|
+
var url: URL?
|
|
332
|
+
if let withUrl = withUrl {
|
|
333
|
+
url = URL(string: withUrl)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let productGroups = NamiCampaignManager.productGroups(label: label, url: url)
|
|
337
|
+
resolve(productGroups)
|
|
338
|
+
}
|
|
307
339
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nami-sdk",
|
|
3
|
-
"version": "3.3.
|
|
4
|
-
"description": "React Native SDK for Nami - No-code paywall
|
|
3
|
+
"version": "3.3.5",
|
|
4
|
+
"description": "React Native SDK for Nami - No-code paywall and onboarding flows with A/B testing.",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"codegenConfig": {
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
"keywords": [
|
|
36
36
|
"in-app-purchase",
|
|
37
37
|
"ios",
|
|
38
|
-
"ipados",
|
|
39
38
|
"tvos",
|
|
40
39
|
"paywall",
|
|
41
40
|
"react-native",
|
|
@@ -43,7 +42,8 @@
|
|
|
43
42
|
"subscriptions",
|
|
44
43
|
"iap",
|
|
45
44
|
"play-billing",
|
|
46
|
-
"payments"
|
|
45
|
+
"payments",
|
|
46
|
+
"onboarding"
|
|
47
47
|
],
|
|
48
48
|
"author": {
|
|
49
49
|
"username": "hellonami",
|
|
@@ -52,12 +52,8 @@
|
|
|
52
52
|
},
|
|
53
53
|
"contributors": [
|
|
54
54
|
{
|
|
55
|
-
"name": "
|
|
56
|
-
"email": "
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"name": "Kendall Gelner",
|
|
60
|
-
"email": "kendall.gelner@namiml.com"
|
|
55
|
+
"name": "Dan Burcaw",
|
|
56
|
+
"email": "dan.burcaw@namiml.com"
|
|
61
57
|
}
|
|
62
58
|
],
|
|
63
59
|
"license": "SEE LICENSE FILE",
|
|
@@ -70,11 +66,6 @@
|
|
|
70
66
|
"react": ">=18",
|
|
71
67
|
"react-native": ">=0.73"
|
|
72
68
|
},
|
|
73
|
-
"peerDependenciesMeta": {
|
|
74
|
-
"react-native": {
|
|
75
|
-
"optional": true
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
69
|
"devDependencies": {
|
|
79
70
|
"@react-native/eslint-config": "^0.80.0",
|
|
80
71
|
"@types/jest": "^29.5.2",
|
|
@@ -78,6 +78,8 @@ export interface Spec extends TurboModule {
|
|
|
78
78
|
|
|
79
79
|
isCampaignAvailable(source?: string): Promise<boolean>;
|
|
80
80
|
|
|
81
|
+
isFlow(label?: string | null, withUrl?: string | null): Promise<boolean>;
|
|
82
|
+
|
|
81
83
|
refresh(): Promise<
|
|
82
84
|
{
|
|
83
85
|
id?: string;
|
|
@@ -90,6 +92,11 @@ export interface Spec extends TurboModule {
|
|
|
90
92
|
>;
|
|
91
93
|
|
|
92
94
|
registerAvailableCampaignsHandler(): void;
|
|
95
|
+
|
|
96
|
+
productGroups(
|
|
97
|
+
label: string | null,
|
|
98
|
+
withUrl: string | null,
|
|
99
|
+
): Promise<string[]>;
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
export default TurboModuleRegistry.getEnforcing<Spec>('RNNamiCampaignManager');
|
|
@@ -76,7 +76,7 @@ export interface Spec extends TurboModule {
|
|
|
76
76
|
|
|
77
77
|
buySkuCancel(): void;
|
|
78
78
|
|
|
79
|
-
setProductDetails(productDetails: string, allowOffers
|
|
79
|
+
setProductDetails(productDetails: string, allowOffers: boolean): void;
|
|
80
80
|
|
|
81
81
|
setAppSuppliedVideoDetails(url: string, name?: string): void;
|
|
82
82
|
|
|
@@ -97,6 +97,10 @@ export const NamiCampaignManager = {
|
|
|
97
97
|
);
|
|
98
98
|
},
|
|
99
99
|
|
|
100
|
+
isFlow: async (label?: string | null, withUrl?: string | null) => {
|
|
101
|
+
return await RNNamiCampaignManager.isFlow(label, withUrl);
|
|
102
|
+
},
|
|
103
|
+
|
|
100
104
|
refresh: async () => {
|
|
101
105
|
return await RNNamiCampaignManager.refresh();
|
|
102
106
|
},
|
|
@@ -111,4 +115,11 @@ export const NamiCampaignManager = {
|
|
|
111
115
|
RNNamiCampaignManager.registerAvailableCampaignsHandler?.();
|
|
112
116
|
return () => sub.remove();
|
|
113
117
|
},
|
|
118
|
+
|
|
119
|
+
getProductGroups: async (label?: string | null, withUrl?: string | null) => {
|
|
120
|
+
return await RNNamiCampaignManager.productGroups(
|
|
121
|
+
label ?? null,
|
|
122
|
+
withUrl ?? null,
|
|
123
|
+
);
|
|
124
|
+
},
|
|
114
125
|
};
|
|
@@ -16,7 +16,18 @@ export const NamiOverlayControl = {
|
|
|
16
16
|
emitter,
|
|
17
17
|
|
|
18
18
|
presentOverlay(): Promise<void> {
|
|
19
|
-
return RNNamiOverlayControl.presentOverlay()
|
|
19
|
+
return RNNamiOverlayControl.presentOverlay().catch((error) => {
|
|
20
|
+
// Handle the case where an overlay is already active
|
|
21
|
+
if (error.code === 'OVERLAY_ALREADY_ACTIVE') {
|
|
22
|
+
console.warn(
|
|
23
|
+
'NamiOverlayControl: An overlay is already being presented. Ignoring duplicate call.',
|
|
24
|
+
);
|
|
25
|
+
// Return a resolved promise to maintain backward compatibility
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
}
|
|
28
|
+
// Re-throw other errors
|
|
29
|
+
throw error;
|
|
30
|
+
});
|
|
20
31
|
},
|
|
21
32
|
|
|
22
33
|
finishOverlay(result?: any): Promise<void> {
|
|
@@ -106,7 +106,7 @@ export const NamiPaywallManager = {
|
|
|
106
106
|
RNNamiPaywallManager.buySkuCancel();
|
|
107
107
|
},
|
|
108
108
|
|
|
109
|
-
setProductDetails: (productDetails: string, allowOffers
|
|
109
|
+
setProductDetails: (productDetails: string, allowOffers: boolean): void => {
|
|
110
110
|
RNNamiPaywallManager.setProductDetails(productDetails, allowOffers);
|
|
111
111
|
},
|
|
112
112
|
|
package/src/version.ts
CHANGED