react-native-nami-sdk 3.3.4 → 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.
@@ -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.4"
89
- amazonImplementation "com.namiml:sdk-amazon:3.3.4"
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"
@@ -354,14 +354,17 @@ class NamiCampaignManagerBridgeModule internal constructor(
354
354
  promise: Promise,
355
355
  ) {
356
356
  try {
357
- val uri = if (!withUrl.isNullOrEmpty()) {
358
- val parsedUri = Uri.parse(withUrl)
359
- if (parsedUri.scheme.isNullOrEmpty()) {
360
- promise.reject("ISFLOW_ERROR", "Invalid URL format: missing scheme", null)
361
- return
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
362
367
  }
363
- parsedUri
364
- } else null
365
368
  val result = NamiCampaignManager.isFlow(label = label, uri = uri)
366
369
  promise.resolve(result)
367
370
  } catch (e: Exception) {
@@ -400,4 +403,34 @@ class NamiCampaignManagerBridgeModule internal constructor(
400
403
  @ReactMethod
401
404
  fun removeListeners(count: Int?) {
402
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
+ }
403
436
  }
@@ -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
  }
@@ -81,6 +81,7 @@ export interface Spec extends TurboModule {
81
81
  value?: string;
82
82
  }[]>;
83
83
  registerAvailableCampaignsHandler(): void;
84
+ productGroups(label: string | null, withUrl: string | null): Promise<string[]>;
84
85
  }
85
86
  declare const _default: Spec;
86
87
  export default _default;
@@ -27,4 +27,5 @@ export declare const NamiCampaignManager: {
27
27
  value?: string;
28
28
  }[]>;
29
29
  registerAvailableCampaignsHandler: (callback: (campaigns: NamiCampaign[]) => void) => (() => void);
30
+ getProductGroups: (label?: string | null, withUrl?: string | null) => Promise<string[]>;
30
31
  };
@@ -2,4 +2,4 @@
2
2
  * Auto-generated file. Do not edit manually.
3
3
  * React Native Nami SDK version.
4
4
  */
5
- export declare const NAMI_REACT_NATIVE_VERSION = "3.3.4";
5
+ export declare const NAMI_REACT_NATIVE_VERSION = "3.3.5";
@@ -22,6 +22,8 @@ RCT_EXTERN_METHOD(refresh:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRe
22
22
 
23
23
  RCT_EXTERN_METHOD(registerAvailableCampaignsHandler)
24
24
 
25
+ RCT_EXTERN_METHOD(productGroups:(nullable NSString *)label withUrl:(nullable NSString *)withUrl resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
26
+
25
27
  + (BOOL)requiresMainQueueSetup {
26
28
  return YES;
27
29
  }
@@ -320,4 +320,20 @@ class RNNamiCampaignManager: RCTEventEmitter {
320
320
  self.safeSend(withName: "AvailableCampaignsChanged", body: dictionaries)
321
321
  }
322
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
+ }
323
339
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nami-sdk",
3
- "version": "3.3.4",
3
+ "version": "3.3.5",
4
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",
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
21
21
  s.requires_arc = true
22
22
  s.swift_version = '5.0' # or your supported version
23
23
 
24
- s.dependency 'Nami', '3.3.4.1'
24
+ s.dependency 'Nami', '3.3.5'
25
25
  s.dependency 'React'
26
26
 
27
27
  pod_target_xcconfig = {
@@ -92,6 +92,11 @@ export interface Spec extends TurboModule {
92
92
  >;
93
93
 
94
94
  registerAvailableCampaignsHandler(): void;
95
+
96
+ productGroups(
97
+ label: string | null,
98
+ withUrl: string | null,
99
+ ): Promise<string[]>;
95
100
  }
96
101
 
97
102
  export default TurboModuleRegistry.getEnforcing<Spec>('RNNamiCampaignManager');
@@ -115,4 +115,11 @@ export const NamiCampaignManager = {
115
115
  RNNamiCampaignManager.registerAvailableCampaignsHandler?.();
116
116
  return () => sub.remove();
117
117
  },
118
+
119
+ getProductGroups: async (label?: string | null, withUrl?: string | null) => {
120
+ return await RNNamiCampaignManager.productGroups(
121
+ label ?? null,
122
+ withUrl ?? null,
123
+ );
124
+ },
118
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> {
package/src/version.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Auto-generated file. Do not edit manually.
3
3
  * React Native Nami SDK version.
4
4
  */
5
- export const NAMI_REACT_NATIVE_VERSION = '3.3.4';
5
+ export const NAMI_REACT_NATIVE_VERSION = '3.3.5';